src.ctf.lttng-live: remove some goto error-handling master
authorSimon Marchi <simon.marchi@efficios.com>
Thu, 7 Dec 2023 17:00:23 +0000 (17:00 +0000)
committerSimon Marchi <simon.marchi@efficios.com>
Wed, 17 Apr 2024 17:57:53 +0000 (13:57 -0400)
Change-Id: I9b6d967d54c63d7f7544bb0d1a1eb778a8df64d4
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/12396
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
Tested-by: jenkins <jenkins@lttng.org>
1003 files changed:
.clang-format
.clang-tidy [new file with mode: 0644]
.editorconfig
.gitignore
.reuse/dep5 [new file with mode: 0644]
CONTRIBUTING.adoc
LICENSE
LICENSES/BSD-2-Clause [deleted file]
LICENSES/BSD-2-Clause.txt [new file with mode: 0644]
LICENSES/BSD-4-Clause [deleted file]
LICENSES/BSD-4-Clause.txt [new file with mode: 0644]
LICENSES/BSL-1.0 [deleted file]
LICENSES/BSL-1.0.txt [new file with mode: 0644]
LICENSES/CC-BY-SA-4.0 [deleted file]
LICENSES/CC-BY-SA-4.0.txt [new file with mode: 0644]
LICENSES/FSFAP.txt [new file with mode: 0644]
LICENSES/FSFULLR.txt [new file with mode: 0644]
LICENSES/GPL-2.0 [deleted file]
LICENSES/GPL-2.0-only.txt [new file with mode: 0644]
LICENSES/GPL-2.0-or-later.txt [new file with mode: 0644]
LICENSES/GPL-3.0 [deleted file]
LICENSES/GPL-3.0-or-later.txt [new file with mode: 0644]
LICENSES/LGPL-2.1 [deleted file]
LICENSES/LGPL-2.1-only.txt [new file with mode: 0644]
LICENSES/LicenseRef-Autoconf-exception-macro.txt [new file with mode: 0644]
LICENSES/MIT [deleted file]
LICENSES/MIT.txt [new file with mode: 0644]
LICENSES/PSF-2.0.txt [new file with mode: 0644]
Makefile.am
README.adoc
configure.ac
dev-requirements.txt
doc/Makefile.am
doc/api/Makefile.am [deleted file]
doc/api/libbabeltrace2/Doxyfile.in
doc/api/libbabeltrace2/examples/distill.c
doc/api/libbabeltrace2/examples/epitome.c
doc/bindings/Makefile.am [deleted file]
doc/bindings/python/ext/bt2sphinxurl.py
doc/bindings/python/source/common.rst
doc/bindings/python/source/conf.py
doc/bindings/python/source/examples.rst
doc/bindings/python/source/index.rst
doc/bindings/python/source/installation.rst
doc/contributing-images/Makefile.am [deleted file]
doc/man/babeltrace2-filter.lttng-utils.debug-info.7.txt
doc/man/babeltrace2-filter.utils.trimmer.7.txt
doc/man/babeltrace2-query.1.txt
doc/man/babeltrace2-sink.ctf.fs.7.txt
doc/man/babeltrace2-sink.text.details.7.txt
doc/man/babeltrace2-sink.text.pretty.7.txt
doc/man/babeltrace2-sink.utils.counter.7.txt
doc/man/babeltrace2-source.ctf.fs.7.txt
doc/man/babeltrace2-source.text.dmesg.7.txt
include/babeltrace2/babeltrace.h
include/babeltrace2/error-reporting.h
include/babeltrace2/func-status.h
include/babeltrace2/graph/component-class-dev.h
include/babeltrace2/graph/component-class.h
include/babeltrace2/graph/component-descriptor-set.h
include/babeltrace2/graph/component.h
include/babeltrace2/graph/connection.h
include/babeltrace2/graph/graph.h
include/babeltrace2/graph/interrupter.h
include/babeltrace2/graph/message-iterator-class.h
include/babeltrace2/graph/message-iterator.h
include/babeltrace2/graph/message.h
include/babeltrace2/graph/port.h
include/babeltrace2/graph/private-query-executor.h
include/babeltrace2/graph/query-executor.h
include/babeltrace2/graph/self-component-class.h
include/babeltrace2/graph/self-component-port.h
include/babeltrace2/graph/self-component.h
include/babeltrace2/graph/self-message-iterator.h
include/babeltrace2/integer-range-set.h
include/babeltrace2/logging-defs.h
include/babeltrace2/logging.h
include/babeltrace2/plugin/plugin-dev.h
include/babeltrace2/plugin/plugin-loading.h
include/babeltrace2/trace-ir/clock-class.h
include/babeltrace2/trace-ir/clock-snapshot.h
include/babeltrace2/trace-ir/event-class.h
include/babeltrace2/trace-ir/event.h
include/babeltrace2/trace-ir/field-class.h
include/babeltrace2/trace-ir/field-path.h
include/babeltrace2/trace-ir/field.h
include/babeltrace2/trace-ir/packet.h
include/babeltrace2/trace-ir/stream-class.h
include/babeltrace2/trace-ir/stream.h
include/babeltrace2/trace-ir/trace-class.h
include/babeltrace2/trace-ir/trace.h
include/babeltrace2/types.h
include/babeltrace2/util.h
include/babeltrace2/value.h
include/babeltrace2/version.h
m4/ax_append_compile_flags.m4
m4/ax_append_flag.m4
m4/ax_c___attribute__.m4
m4/ax_check_compile_flag.m4
m4/ax_cxx_compile_stdcxx.m4
m4/ax_pkg_swig.m4
m4/ax_pthread.m4
m4/ax_require_defined.m4
pyproject.toml
setup.cfg
src/Makefile.am
src/Makefile.common.inc [new file with mode: 0644]
src/argpar/Makefile.am [deleted file]
src/argpar/argpar.h
src/autodisc/Makefile.am [deleted file]
src/autodisc/autodisc.c
src/babeltrace2-ctf-writer.pc.in
src/babeltrace2.pc.in
src/bindings/Makefile.am [deleted file]
src/bindings/python/Makefile.am [deleted file]
src/bindings/python/bt2/.gitignore
src/bindings/python/bt2/Makefile.am
src/bindings/python/bt2/bt2/__init__.py
src/bindings/python/bt2/bt2/clock_class.py
src/bindings/python/bt2/bt2/clock_snapshot.py
src/bindings/python/bt2/bt2/component.py
src/bindings/python/bt2/bt2/component_descriptor.py
src/bindings/python/bt2/bt2/connection.py
src/bindings/python/bt2/bt2/error.py
src/bindings/python/bt2/bt2/event.py
src/bindings/python/bt2/bt2/event_class.py
src/bindings/python/bt2/bt2/field.py
src/bindings/python/bt2/bt2/field_class.py
src/bindings/python/bt2/bt2/field_path.py
src/bindings/python/bt2/bt2/graph.py
src/bindings/python/bt2/bt2/integer_range_set.py
src/bindings/python/bt2/bt2/interrupter.py
src/bindings/python/bt2/bt2/message.py
src/bindings/python/bt2/bt2/message_iterator.py
src/bindings/python/bt2/bt2/mip.py
src/bindings/python/bt2/bt2/native_bt.i
src/bindings/python/bt2/bt2/native_bt_autodisc.i.h
src/bindings/python/bt2/bt2/native_bt_component_class.i.h
src/bindings/python/bt2/bt2/native_bt_error.i.h
src/bindings/python/bt2/bt2/object.py
src/bindings/python/bt2/bt2/packet.py
src/bindings/python/bt2/bt2/plugin.py
src/bindings/python/bt2/bt2/port.py
src/bindings/python/bt2/bt2/py_plugin.py
src/bindings/python/bt2/bt2/query_executor.py
src/bindings/python/bt2/bt2/stream.py
src/bindings/python/bt2/bt2/stream_class.py
src/bindings/python/bt2/bt2/trace.py
src/bindings/python/bt2/bt2/trace_class.py
src/bindings/python/bt2/bt2/trace_collection_message_iterator.py
src/bindings/python/bt2/bt2/utils.py
src/bindings/python/bt2/bt2/value.py
src/bindings/python/bt2/setup.py.in
src/cli/Makefile.am
src/cli/babeltrace2-cfg-cli-args-connect.h
src/cli/babeltrace2-cfg-cli-args-default.c
src/cli/babeltrace2-cfg-cli-args.c
src/cli/babeltrace2-cfg-cli-args.h
src/cli/babeltrace2-cfg.h
src/cli/babeltrace2-plugins.h
src/cli/babeltrace2-query.c
src/cli/babeltrace2-query.h
src/cli/babeltrace2.c
src/cli/logging.c [deleted file]
src/cli/logging.cpp [new file with mode: 0644]
src/cli/logging.h
src/clock-correlation-validator/clock-correlation-validator.cpp [new file with mode: 0644]
src/clock-correlation-validator/clock-correlation-validator.h [new file with mode: 0644]
src/clock-correlation-validator/clock-correlation-validator.hpp [new file with mode: 0644]
src/common/Makefile.am [deleted file]
src/common/assert.h
src/common/common.c
src/common/common.h
src/common/list.h
src/common/macros.h
src/common/mmap-align.h
src/common/prio-heap.c [new file with mode: 0644]
src/common/prio-heap.h [new file with mode: 0644]
src/common/uuid.c
src/common/uuid.h
src/compat/Makefile.am [deleted file]
src/compat/memstream.h
src/compat/mman.c
src/compat/mman.h
src/compat/socket.h [deleted file]
src/compat/socket.hpp [new file with mode: 0644]
src/cpp-common/Makefile.am [deleted file]
src/cpp-common/bt2/borrowed-object-iterator.hpp [new file with mode: 0644]
src/cpp-common/bt2/borrowed-object-proxy.hpp [new file with mode: 0644]
src/cpp-common/bt2/borrowed-object.hpp [new file with mode: 0644]
src/cpp-common/bt2/clock-class.hpp
src/cpp-common/bt2/clock-snapshot.hpp
src/cpp-common/bt2/component-class-dev.hpp [new file with mode: 0644]
src/cpp-common/bt2/component-class.hpp [new file with mode: 0644]
src/cpp-common/bt2/component-port.hpp [new file with mode: 0644]
src/cpp-common/bt2/error.hpp [new file with mode: 0644]
src/cpp-common/bt2/exc.hpp [new file with mode: 0644]
src/cpp-common/bt2/field-class.hpp
src/cpp-common/bt2/field-path.hpp
src/cpp-common/bt2/field.hpp
src/cpp-common/bt2/graph.hpp [new file with mode: 0644]
src/cpp-common/bt2/integer-range-set.hpp
src/cpp-common/bt2/integer-range.hpp
src/cpp-common/bt2/internal/borrowed-obj.hpp [deleted file]
src/cpp-common/bt2/internal/comp-cls-bridge.hpp [new file with mode: 0644]
src/cpp-common/bt2/internal/shared-obj.hpp [deleted file]
src/cpp-common/bt2/internal/utils.hpp
src/cpp-common/bt2/lib-error.hpp [deleted file]
src/cpp-common/bt2/logging.hpp
src/cpp-common/bt2/message-array.hpp [new file with mode: 0644]
src/cpp-common/bt2/message-iterator.hpp [new file with mode: 0644]
src/cpp-common/bt2/message.hpp
src/cpp-common/bt2/optional-borrowed-object.hpp [new file with mode: 0644]
src/cpp-common/bt2/plugin-dev.hpp [new file with mode: 0644]
src/cpp-common/bt2/plugin-load.hpp [new file with mode: 0644]
src/cpp-common/bt2/plugin-set.hpp [new file with mode: 0644]
src/cpp-common/bt2/plugin.hpp [new file with mode: 0644]
src/cpp-common/bt2/private-query-executor.hpp [new file with mode: 0644]
src/cpp-common/bt2/query-executor.hpp [new file with mode: 0644]
src/cpp-common/bt2/raw-value-proxy.hpp [new file with mode: 0644]
src/cpp-common/bt2/self-component-class.hpp [new file with mode: 0644]
src/cpp-common/bt2/self-component-port.hpp [new file with mode: 0644]
src/cpp-common/bt2/self-message-iterator-configuration.hpp [new file with mode: 0644]
src/cpp-common/bt2/self-message-iterator.hpp [new file with mode: 0644]
src/cpp-common/bt2/shared-object.hpp [new file with mode: 0644]
src/cpp-common/bt2/trace-ir.hpp
src/cpp-common/bt2/type-traits.hpp [new file with mode: 0644]
src/cpp-common/bt2/value.hpp
src/cpp-common/bt2/wrap.hpp [new file with mode: 0644]
src/cpp-common/bt2c/align.hpp [new file with mode: 0644]
src/cpp-common/bt2c/c-string-view.hpp [new file with mode: 0644]
src/cpp-common/bt2c/call.hpp [new file with mode: 0644]
src/cpp-common/bt2c/contains.hpp [new file with mode: 0644]
src/cpp-common/bt2c/data-len.hpp [new file with mode: 0644]
src/cpp-common/bt2c/dummy.cpp [new file with mode: 0644]
src/cpp-common/bt2c/endian.hpp [new file with mode: 0644]
src/cpp-common/bt2c/exc.hpp [new file with mode: 0644]
src/cpp-common/bt2c/file-utils.cpp [new file with mode: 0644]
src/cpp-common/bt2c/file-utils.hpp [new file with mode: 0644]
src/cpp-common/bt2c/fmt.hpp [new file with mode: 0644]
src/cpp-common/bt2c/glib-up.hpp [new file with mode: 0644]
src/cpp-common/bt2c/libc-up.hpp [new file with mode: 0644]
src/cpp-common/bt2c/logging.hpp [new file with mode: 0644]
src/cpp-common/bt2c/prio-heap.hpp [new file with mode: 0644]
src/cpp-common/bt2c/read-fixed-len-int.hpp [new file with mode: 0644]
src/cpp-common/bt2c/safe-ops.hpp [new file with mode: 0644]
src/cpp-common/bt2c/span.hpp [new file with mode: 0644]
src/cpp-common/bt2c/std-int.hpp [new file with mode: 0644]
src/cpp-common/bt2c/type-traits.hpp [new file with mode: 0644]
src/cpp-common/bt2c/uuid.hpp [new file with mode: 0644]
src/cpp-common/bt2c/vector.hpp [new file with mode: 0644]
src/cpp-common/bt2s/make-unique.hpp [new file with mode: 0644]
src/cpp-common/bt2s/optional.hpp [new file with mode: 0644]
src/cpp-common/bt2s/span.hpp [new file with mode: 0644]
src/cpp-common/bt2s/string-view.hpp [new file with mode: 0644]
src/cpp-common/optional.hpp [deleted file]
src/cpp-common/string_view.hpp [deleted file]
src/cpp-common/uuid-view.hpp [deleted file]
src/cpp-common/vendor/fmt/args.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/chrono.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/color.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/compile.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/core.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/format-inl.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/format.cc [new file with mode: 0644]
src/cpp-common/vendor/fmt/format.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/os.cc [new file with mode: 0644]
src/cpp-common/vendor/fmt/os.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/ostream.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/printf.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/ranges.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/std.h [new file with mode: 0644]
src/cpp-common/vendor/fmt/xchar.h [new file with mode: 0644]
src/cpp-common/vendor/nlohmann/json.hpp [new file with mode: 0644]
src/cpp-common/vendor/optional-lite/optional.hpp [new file with mode: 0644]
src/cpp-common/vendor/optional-lite/optional.hpp.license [new file with mode: 0644]
src/cpp-common/vendor/span-lite/span.hpp [new file with mode: 0644]
src/cpp-common/vendor/span-lite/span.hpp.license [new file with mode: 0644]
src/cpp-common/vendor/string-view-lite/string_view.hpp [new file with mode: 0644]
src/cpp-common/vendor/string-view-lite/string_view.hpp.license [new file with mode: 0644]
src/cpp-common/vendor/wise-enum/optional.h [new file with mode: 0644]
src/cpp-common/vendor/wise-enum/optional_common.h [new file with mode: 0644]
src/cpp-common/vendor/wise-enum/wise_enum.h [new file with mode: 0644]
src/cpp-common/vendor/wise-enum/wise_enum_detail.h [new file with mode: 0644]
src/cpp-common/vendor/wise-enum/wise_enum_generated.h [new file with mode: 0644]
src/cpp-tester/Makefile.am [deleted file]
src/cpp-tester/cpp-tester.cpp [deleted file]
src/ctf-writer/Makefile.am [deleted file]
src/ctf-writer/assert-pre.h
src/ctf-writer/attributes.c
src/ctf-writer/attributes.h
src/ctf-writer/clock-class.c
src/ctf-writer/clock-class.h
src/ctf-writer/clock.c
src/ctf-writer/clock.h
src/ctf-writer/event-class.c
src/ctf-writer/event-class.h
src/ctf-writer/event.c
src/ctf-writer/event.h
src/ctf-writer/field-path.c
src/ctf-writer/field-path.h
src/ctf-writer/field-types.c
src/ctf-writer/field-types.h
src/ctf-writer/field-wrapper.c
src/ctf-writer/field-wrapper.h
src/ctf-writer/fields.c
src/ctf-writer/fields.h
src/ctf-writer/functor.c
src/ctf-writer/functor.h
src/ctf-writer/object-pool.c
src/ctf-writer/object.c
src/ctf-writer/resolve.c
src/ctf-writer/resolve.h
src/ctf-writer/stream-class.c
src/ctf-writer/stream-class.h
src/ctf-writer/stream.c
src/ctf-writer/stream.h
src/ctf-writer/trace.c
src/ctf-writer/trace.h
src/ctf-writer/utils.c
src/ctf-writer/utils.h
src/ctf-writer/validation.c
src/ctf-writer/validation.h
src/ctf-writer/values.c
src/ctf-writer/values.h
src/ctf-writer/visitor.c
src/ctf-writer/visitor.h
src/ctf-writer/writer.c
src/ctf-writer/writer.h
src/ctfser/Makefile.am [deleted file]
src/ctfser/ctfser.c
src/ctfser/ctfser.h
src/fd-cache/Makefile.am [deleted file]
src/fd-cache/fd-cache.c
src/fd-cache/fd-cache.h
src/gen-version-i.sh [new file with mode: 0755]
src/lib/Makefile.am [deleted file]
src/lib/assert-cond-base.h
src/lib/assert-cond.c
src/lib/assert-cond.h
src/lib/babeltrace2.c
src/lib/current-thread.c
src/lib/error.c
src/lib/error.h
src/lib/graph/Makefile.am [deleted file]
src/lib/graph/component-class-sink-simple.c
src/lib/graph/component-class-sink-simple.h
src/lib/graph/component-class.c
src/lib/graph/component-class.h
src/lib/graph/component-descriptor-set.c
src/lib/graph/component-descriptor-set.h
src/lib/graph/component-filter.c
src/lib/graph/component-filter.h
src/lib/graph/component-sink.c
src/lib/graph/component-sink.h
src/lib/graph/component-source.c
src/lib/graph/component-source.h
src/lib/graph/component.c
src/lib/graph/component.h
src/lib/graph/connection.c
src/lib/graph/connection.h
src/lib/graph/graph.c
src/lib/graph/graph.h
src/lib/graph/interrupter.c
src/lib/graph/iterator.c
src/lib/graph/iterator.h [new file with mode: 0644]
src/lib/graph/message-iterator-class.c
src/lib/graph/message-iterator-class.h
src/lib/graph/message/Makefile.am [deleted file]
src/lib/graph/message/discarded-items.c
src/lib/graph/message/event.c
src/lib/graph/message/event.h
src/lib/graph/message/iterator.h [deleted file]
src/lib/graph/message/message-iterator-inactivity.c
src/lib/graph/message/message.c
src/lib/graph/message/message.h
src/lib/graph/message/packet.c
src/lib/graph/message/packet.h
src/lib/graph/message/stream.c
src/lib/graph/message/stream.h
src/lib/graph/mip.c
src/lib/graph/port.c
src/lib/graph/port.h
src/lib/graph/query-executor.c
src/lib/integer-range-set.c
src/lib/integer-range-set.h
src/lib/lib-logging.c
src/lib/logging.c
src/lib/logging.h
src/lib/object-pool.c
src/lib/object-pool.h
src/lib/object.h
src/lib/plugin/Makefile.am [deleted file]
src/lib/plugin/plugin-so.c
src/lib/plugin/plugin-so.h
src/lib/plugin/plugin.c
src/lib/plugin/plugin.h
src/lib/prio-heap/Makefile.am [deleted file]
src/lib/prio-heap/prio-heap.c [deleted file]
src/lib/prio-heap/prio-heap.h [deleted file]
src/lib/property.h
src/lib/trace-ir/Makefile.am [deleted file]
src/lib/trace-ir/attributes.c
src/lib/trace-ir/attributes.h
src/lib/trace-ir/clock-class.c
src/lib/trace-ir/clock-class.h
src/lib/trace-ir/clock-snapshot.c
src/lib/trace-ir/clock-snapshot.h
src/lib/trace-ir/event-class.c
src/lib/trace-ir/event-class.h
src/lib/trace-ir/event.c
src/lib/trace-ir/event.h
src/lib/trace-ir/field-class.c
src/lib/trace-ir/field-class.h
src/lib/trace-ir/field-path.c
src/lib/trace-ir/field-path.h
src/lib/trace-ir/field-wrapper.c
src/lib/trace-ir/field-wrapper.h
src/lib/trace-ir/field.c
src/lib/trace-ir/field.h
src/lib/trace-ir/packet.c
src/lib/trace-ir/packet.h
src/lib/trace-ir/resolve-field-path.c
src/lib/trace-ir/resolve-field-path.h
src/lib/trace-ir/stream-class.c
src/lib/trace-ir/stream-class.h
src/lib/trace-ir/stream.c
src/lib/trace-ir/stream.h
src/lib/trace-ir/trace-class.c
src/lib/trace-ir/trace-class.h
src/lib/trace-ir/trace.c
src/lib/trace-ir/trace.h
src/lib/trace-ir/utils.c
src/lib/trace-ir/utils.h
src/lib/util.c
src/lib/value.c
src/lib/value.h
src/logging/Makefile.am [deleted file]
src/logging/comp-logging.h
src/logging/log-api.c [new file with mode: 0644]
src/logging/log-api.h [new file with mode: 0644]
src/logging/log.c [deleted file]
src/logging/log.h
src/param-parse/Makefile.am [deleted file]
src/param-parse/param-parse.c
src/param-parse/param-parse.h
src/plugins/Makefile.am [deleted file]
src/plugins/common/Makefile.am [deleted file]
src/plugins/common/muxing/Makefile.am [deleted file]
src/plugins/common/muxing/muxing.c
src/plugins/common/muxing/muxing.h
src/plugins/common/param-validation/Makefile.am [deleted file]
src/plugins/common/param-validation/param-validation.c
src/plugins/common/param-validation/param-validation.h
src/plugins/ctf/Makefile.am [deleted file]
src/plugins/ctf/common/Makefile.am [deleted file]
src/plugins/ctf/common/bfcr/Makefile.am [deleted file]
src/plugins/ctf/common/bfcr/bfcr.cpp [deleted file]
src/plugins/ctf/common/bfcr/bfcr.hpp [deleted file]
src/plugins/ctf/common/bfcr/btr.gdb [deleted file]
src/plugins/ctf/common/metadata/Makefile.am [deleted file]
src/plugins/ctf/common/metadata/ast.hpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.hpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-resolve.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-translate.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-alignments.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-meanings.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-validate.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-visitors.hpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.cpp [deleted file]
src/plugins/ctf/common/metadata/ctf-meta.hpp [deleted file]
src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.cpp [deleted file]
src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.hpp [deleted file]
src/plugins/ctf/common/metadata/decoder.cpp [deleted file]
src/plugins/ctf/common/metadata/decoder.hpp [deleted file]
src/plugins/ctf/common/metadata/lexer.lpp [deleted file]
src/plugins/ctf/common/metadata/logging.cpp [deleted file]
src/plugins/ctf/common/metadata/logging.hpp [deleted file]
src/plugins/ctf/common/metadata/objstack.cpp [deleted file]
src/plugins/ctf/common/metadata/objstack.hpp [deleted file]
src/plugins/ctf/common/metadata/parser-wrap.hpp [deleted file]
src/plugins/ctf/common/metadata/parser.ypp [deleted file]
src/plugins/ctf/common/metadata/scanner-symbols.hpp [deleted file]
src/plugins/ctf/common/metadata/scanner.hpp [deleted file]
src/plugins/ctf/common/metadata/visitor-generate-ir.cpp [deleted file]
src/plugins/ctf/common/metadata/visitor-parent-links.cpp [deleted file]
src/plugins/ctf/common/metadata/visitor-semantic-validator.cpp [deleted file]
src/plugins/ctf/common/msg-iter/Makefile.am [deleted file]
src/plugins/ctf/common/msg-iter/msg-iter.cpp [deleted file]
src/plugins/ctf/common/msg-iter/msg-iter.hpp [deleted file]
src/plugins/ctf/common/print.hpp [deleted file]
src/plugins/ctf/common/src/bfcr/bfcr.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/bfcr/bfcr.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/clk-cls-cfg.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ast.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-configure-ir-trace.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-configure-ir-trace.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-resolve.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-translate.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-alignments.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-default-clock-classes.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-in-ir.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-meanings.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-stream-class-config.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-text-array-sequence.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-value-storing-indexes.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-validate.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-visitors.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-warn-meaningless-header-fields.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-meta.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/decoder-packetized-file-stream-to-buf.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/decoder-packetized-file-stream-to-buf.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/decoder.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/decoder.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/lexer.lpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/logging.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/objstack.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/objstack.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/parser-wrap.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/parser.ypp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/scanner-symbols.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/scanner.hpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/visitor-generate-ir.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/visitor-parent-links.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/visitor-semantic-validator.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/msg-iter/msg-iter.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/msg-iter/msg-iter.hpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/Makefile.am [deleted file]
src/plugins/ctf/fs-sink/fs-sink-ctf-meta.hpp
src/plugins/ctf/fs-sink/fs-sink-stream.cpp
src/plugins/ctf/fs-sink/fs-sink-stream.hpp
src/plugins/ctf/fs-sink/fs-sink-trace.cpp
src/plugins/ctf/fs-sink/fs-sink-trace.hpp
src/plugins/ctf/fs-sink/fs-sink.cpp
src/plugins/ctf/fs-sink/fs-sink.hpp
src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.cpp
src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.hpp
src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.cpp
src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.hpp
src/plugins/ctf/fs-src/Makefile.am [deleted file]
src/plugins/ctf/fs-src/data-stream-file.cpp
src/plugins/ctf/fs-src/data-stream-file.hpp
src/plugins/ctf/fs-src/file.cpp
src/plugins/ctf/fs-src/file.hpp
src/plugins/ctf/fs-src/fs.cpp
src/plugins/ctf/fs-src/fs.hpp
src/plugins/ctf/fs-src/lttng-index.hpp
src/plugins/ctf/fs-src/metadata.cpp
src/plugins/ctf/fs-src/metadata.hpp
src/plugins/ctf/fs-src/query.cpp
src/plugins/ctf/fs-src/query.hpp
src/plugins/ctf/lttng-live/Makefile.am [deleted file]
src/plugins/ctf/lttng-live/data-stream.cpp
src/plugins/ctf/lttng-live/data-stream.hpp
src/plugins/ctf/lttng-live/lttng-live.cpp
src/plugins/ctf/lttng-live/lttng-live.hpp
src/plugins/ctf/lttng-live/lttng-viewer-abi.hpp
src/plugins/ctf/lttng-live/metadata.cpp
src/plugins/ctf/lttng-live/metadata.hpp
src/plugins/ctf/lttng-live/viewer-connection.cpp
src/plugins/ctf/lttng-live/viewer-connection.hpp
src/plugins/ctf/plugin.cpp
src/plugins/lttng-utils/Makefile.am [deleted file]
src/plugins/lttng-utils/debug-info/Makefile.am [deleted file]
src/plugins/lttng-utils/debug-info/bin-info.c
src/plugins/lttng-utils/debug-info/bin-info.h
src/plugins/lttng-utils/debug-info/crc32.h
src/plugins/lttng-utils/debug-info/debug-info.c
src/plugins/lttng-utils/debug-info/debug-info.h
src/plugins/lttng-utils/debug-info/dwarf.c
src/plugins/lttng-utils/debug-info/dwarf.h
src/plugins/lttng-utils/debug-info/trace-ir-data-copy.c
src/plugins/lttng-utils/debug-info/trace-ir-data-copy.h
src/plugins/lttng-utils/debug-info/trace-ir-mapping.c
src/plugins/lttng-utils/debug-info/trace-ir-mapping.h
src/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.c
src/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.h
src/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.c
src/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.h
src/plugins/lttng-utils/debug-info/utils.c
src/plugins/lttng-utils/debug-info/utils.h
src/plugins/text/Makefile.am [deleted file]
src/plugins/text/details/Makefile.am [deleted file]
src/plugins/text/details/details.c
src/plugins/text/details/details.h
src/plugins/text/details/obj-lifetime-mgmt.c
src/plugins/text/details/obj-lifetime-mgmt.h
src/plugins/text/details/write.c
src/plugins/text/details/write.h
src/plugins/text/dmesg/Makefile.am [deleted file]
src/plugins/text/dmesg/dmesg.c
src/plugins/text/dmesg/dmesg.h
src/plugins/text/pretty/Makefile.am [deleted file]
src/plugins/text/pretty/pretty.c
src/plugins/text/pretty/pretty.h
src/plugins/text/pretty/print.c
src/plugins/utils/Makefile.am [deleted file]
src/plugins/utils/counter/Makefile.am [deleted file]
src/plugins/utils/counter/counter.c
src/plugins/utils/counter/counter.h
src/plugins/utils/dummy/Makefile.am [deleted file]
src/plugins/utils/dummy/dummy.c
src/plugins/utils/dummy/dummy.h
src/plugins/utils/muxer/Makefile.am [deleted file]
src/plugins/utils/muxer/comp.cpp [new file with mode: 0644]
src/plugins/utils/muxer/comp.hpp [new file with mode: 0644]
src/plugins/utils/muxer/msg-iter.cpp [new file with mode: 0644]
src/plugins/utils/muxer/msg-iter.hpp [new file with mode: 0644]
src/plugins/utils/muxer/muxer.c [deleted file]
src/plugins/utils/muxer/muxer.h [deleted file]
src/plugins/utils/muxer/upstream-msg-iter.cpp [new file with mode: 0644]
src/plugins/utils/muxer/upstream-msg-iter.hpp [new file with mode: 0644]
src/plugins/utils/plugin.c [deleted file]
src/plugins/utils/plugin.cpp [new file with mode: 0644]
src/plugins/utils/trimmer/Makefile.am [deleted file]
src/plugins/utils/trimmer/trimmer.c
src/plugins/utils/trimmer/trimmer.h
src/py-common/Makefile.am [deleted file]
src/py-common/py-common.c
src/py-common/py-common.h
src/python-plugin-provider/Makefile.am [deleted file]
src/python-plugin-provider/python-plugin-provider.c
src/string-format/Makefile.am [deleted file]
src/string-format/format-error.c
src/string-format/format-error.h
src/string-format/format-plugin-comp-cls-name.h
tests/Makefile.am
tests/bindings/python/bt2/test-python-bt2.sh [new file with mode: 0755]
tests/bindings/python/bt2/test_clock_class.py
tests/bindings/python/bt2/test_component.py
tests/bindings/python/bt2/test_component_class.py
tests/bindings/python/bt2/test_component_descriptor.py
tests/bindings/python/bt2/test_connection.py
tests/bindings/python/bt2/test_error.py
tests/bindings/python/bt2/test_event.py
tests/bindings/python/bt2/test_event_class.py
tests/bindings/python/bt2/test_field.py
tests/bindings/python/bt2/test_field_class.py
tests/bindings/python/bt2/test_graph.py
tests/bindings/python/bt2/test_integer_range_set.py
tests/bindings/python/bt2/test_interrupter.py
tests/bindings/python/bt2/test_message.py
tests/bindings/python/bt2/test_message_iterator.py
tests/bindings/python/bt2/test_mip.py
tests/bindings/python/bt2/test_package.py
tests/bindings/python/bt2/test_packet.py
tests/bindings/python/bt2/test_plugin.py
tests/bindings/python/bt2/test_port.py
tests/bindings/python/bt2/test_python_bt2 [deleted file]
tests/bindings/python/bt2/test_query_executor.py
tests/bindings/python/bt2/test_stream.py
tests/bindings/python/bt2/test_stream_class.py
tests/bindings/python/bt2/test_trace.py
tests/bindings/python/bt2/test_trace_class.py
tests/bindings/python/bt2/test_trace_collection_message_iterator.py
tests/bindings/python/bt2/test_value.py
tests/bindings/python/bt2/utils.py
tests/bitfield/Makefile.am
tests/bitfield/test-bitfield.c [new file with mode: 0644]
tests/bitfield/test_bitfield.c [deleted file]
tests/cli/convert/test-auto-source-discovery-grouping.sh [new file with mode: 0755]
tests/cli/convert/test-auto-source-discovery-log-level.sh [new file with mode: 0755]
tests/cli/convert/test-auto-source-discovery-params.sh [new file with mode: 0755]
tests/cli/convert/test-convert-args.sh [new file with mode: 0755]
tests/cli/convert/test_auto_source_discovery_grouping [deleted file]
tests/cli/convert/test_auto_source_discovery_log_level [deleted file]
tests/cli/convert/test_auto_source_discovery_params [deleted file]
tests/cli/convert/test_convert_args [deleted file]
tests/cli/list-plugins/test-list-plugins.sh [new file with mode: 0755]
tests/cli/list-plugins/test_list_plugins [deleted file]
tests/cli/params/test-params.sh [new file with mode: 0755]
tests/cli/params/test_params [deleted file]
tests/cli/query/test-query.sh [new file with mode: 0755]
tests/cli/query/test_query [deleted file]
tests/cli/test-exit-status.sh [new file with mode: 0755]
tests/cli/test-help.sh [new file with mode: 0755]
tests/cli/test-intersection.sh [new file with mode: 0755]
tests/cli/test-output-ctf-metadata.sh [new file with mode: 0755]
tests/cli/test-output-path-ctf-non-lttng-trace.sh [new file with mode: 0755]
tests/cli/test-packet-seq-num.sh [new file with mode: 0755]
tests/cli/test-trace-copy.sh [new file with mode: 0755]
tests/cli/test-trace-read.sh [new file with mode: 0755]
tests/cli/test-trimmer.sh [new file with mode: 0755]
tests/cli/test_exit_status [deleted file]
tests/cli/test_help [deleted file]
tests/cli/test_intersection [deleted file]
tests/cli/test_output_ctf_metadata [deleted file]
tests/cli/test_output_path_ctf_non_lttng_trace [deleted file]
tests/cli/test_packet_seq_num [deleted file]
tests/cli/test_trace_copy [deleted file]
tests/cli/test_trace_read [deleted file]
tests/cli/test_trimmer [deleted file]
tests/cpp-common/test-c-string-view.cpp [new file with mode: 0644]
tests/cpp-common/test-uuid.cpp [new file with mode: 0644]
tests/ctf-writer/Makefile.am
tests/ctf-writer/ctf-writer.c [new file with mode: 0644]
tests/ctf-writer/ctf_writer.c [deleted file]
tests/ctf-writer/test-ctf-writer.sh [new file with mode: 0755]
tests/ctf-writer/test_ctf_writer [deleted file]
tests/data/auto-source-discovery/grouping/bt_plugin_test.py
tests/data/auto-source-discovery/params-log-level/bt_plugin_test.py
tests/data/cli/exit-status/bt_plugin_test_cli_exit_status.py [new file with mode: 0644]
tests/data/cli/exit_status/bt_plugin_test_cli_exit_status.py [deleted file]
tests/data/cli/test-output-ctf-metadata.ref [new file with mode: 0644]
tests/data/cli/test_output_ctf_metadata.ref [deleted file]
tests/data/ctf-traces/fail/invalid-sequence-length-field-class/metadata [new file with mode: 0644]
tests/data/ctf-traces/fail/invalid-variant-selector-field-class/metadata [new file with mode: 0644]
tests/data/ctf-traces/live/new-streams/first-trace.mctf [new file with mode: 0644]
tests/data/ctf-traces/live/new-streams/second-trace.mctf [new file with mode: 0644]
tests/data/ctf-traces/live/split-metadata/channel0_0 [new file with mode: 0644]
tests/data/ctf-traces/live/split-metadata/index/channel0_0.idx [new file with mode: 0644]
tests/data/ctf-traces/live/split-metadata/metadata [new file with mode: 0644]
tests/data/ctf-traces/live/split_metadata/channel0_0 [deleted file]
tests/data/ctf-traces/live/split_metadata/index/channel0_0.idx [deleted file]
tests/data/ctf-traces/live/split_metadata/metadata [deleted file]
tests/data/ctf-traces/live/stored-values.mctf [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/2-lost-before-last/metadata [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/2-lost-before-last/test_stream_0 [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/metadata [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/test_stream_0 [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/test_stream_1 [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/metadata [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/test_stream_0 [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/test_stream_1 [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/index/stream_0.idx [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/metadata [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/stream_0 [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/no-lost-not-starting-at-0/metadata [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/no-lost-not-starting-at-0/test_stream_0 [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/no-lost/metadata [new file with mode: 0644]
tests/data/ctf-traces/packet-seq-num/no-lost/test_stream_0 [new file with mode: 0644]
tests/data/ctf-traces/packet_seq_num/2_lost_before_last/metadata [deleted file]
tests/data/ctf-traces/packet_seq_num/2_lost_before_last/test_stream_0 [deleted file]
tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/metadata [deleted file]
tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/test_stream_0 [deleted file]
tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/test_stream_1 [deleted file]
tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/metadata [deleted file]
tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/test_stream_0 [deleted file]
tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/test_stream_1 [deleted file]
tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/index/stream_0.idx [deleted file]
tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/metadata [deleted file]
tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/stream_0 [deleted file]
tests/data/ctf-traces/packet_seq_num/no_lost/metadata [deleted file]
tests/data/ctf-traces/packet_seq_num/no_lost/test_stream_0 [deleted file]
tests/data/ctf-traces/packet_seq_num/no_lost_not_starting_at_0/metadata [deleted file]
tests/data/ctf-traces/packet_seq_num/no_lost_not_starting_at_0/test_stream_0 [deleted file]
tests/data/ctf-traces/succeed/crlf-metadata/channel0_0 [new file with mode: 0644]
tests/data/ctf-traces/succeed/crlf-metadata/metadata [new file with mode: 0644]
tests/data/ctf-traces/succeed/debug-info/channel0_0
tests/data/ctf-traces/succeed/lf-metadata/channel0_0 [new file with mode: 0644]
tests/data/ctf-traces/succeed/lf-metadata/metadata [new file with mode: 0644]
tests/data/plugins/flt.lttng-utils.debug-info/Makefile
tests/data/plugins/flt.lttng-utils.debug-info/README.md
tests/data/plugins/flt.lttng-utils.debug-info/bt_plugin_test_debug_info.py
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build-id/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build_id/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug-link/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug-link/libhello-so.debug [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug_link/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug_link/libhello_so.debug [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/dwarf-full/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/dwarf_full/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/elf-only/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/elf_only/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build-id/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build_id/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug-link/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug-link/libhello-so.debug [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug_link/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug_link/libhello_so.debug [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/dwarf-full/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/dwarf_full/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/elf-only/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/elf_only/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build-id/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build_id/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug-link/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug-link/libhello-so.debug [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug_link/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug_link/libhello_so.debug [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/dwarf-full/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/dwarf_full/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/elf-only/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/elf_only/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/trace-debug-info.expect
tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/build-id/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/debug-link/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/debug-link/libhello-so.debug [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/dwarf-full/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/elf-only/libhello-so [new file with mode: 0755]
tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/build_id/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/debug_link/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/debug_link/libhello_so.debug [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/dwarf_full/libhello_so [deleted file]
tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/elf_only/libhello_so [deleted file]
tests/data/plugins/flt.utils.muxer/bt_plugin_muxer_test.py
tests/data/plugins/flt.utils.muxer/succeed/basic-timestamp-ordering.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/basic_timestamp_ordering.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff-event-class-id.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff-event-class-name.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff-inactivity-msg-cs.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff-stream-class-id.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff-stream-class-name.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff-stream-class-no-name.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff-stream-id.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff-stream-name.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff-stream-no-name.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff-trace-name.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/diff_event_class_id.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff_event_class_name.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff_inactivity_msg_cs.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff_stream_class_id.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff_stream_class_name.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff_stream_class_no_name.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff_stream_id.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff_stream_name.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff_stream_no_name.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/diff_trace_name.expect [deleted file]
tests/data/plugins/flt.utils.muxer/succeed/multi-iter-ordering.expect [new file with mode: 0644]
tests/data/plugins/flt.utils.muxer/succeed/multi_iter_ordering.expect [deleted file]
tests/data/plugins/src.ctf.fs/field/bt_plugin_test_text.py [new file with mode: 0644]
tests/data/plugins/src.ctf.fs/field/ctf-1/pass-fixed-len-uint-32-be.mp [new file with mode: 0644]
tests/data/plugins/src.ctf.fs/field/ctf-1/pass-fixed-len-uint-32-le.mp [new file with mode: 0644]
tests/data/plugins/src.ctf.fs/field/ctf-1/pass-static-len-array-of-struct.mp [new file with mode: 0644]
tests/data/plugins/src.ctf.fs/field/ctf-1/pass-struct-empty.mp [new file with mode: 0644]
tests/data/plugins/src.ctf.fs/field/ctf-1/pass-struct.mp [new file with mode: 0644]
tests/data/plugins/src.ctf.fs/field/ctf-1/pass-variant.mp [new file with mode: 0644]
tests/data/plugins/src.ctf.fs/field/data_from_mp.py [new file with mode: 0644]
tests/data/plugins/src.ctf.fs/query/metadata-info-crlf-metadata.expect [new file with mode: 0644]
tests/data/plugins/src.ctf.fs/query/metadata-info-lf-metadata.expect [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/cli-multi-domains.expect
tests/data/plugins/src.ctf.lttng-live/inactivity-discarded-packet.expect [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/inactivity-discarded-packet.json [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/inactivity_discarded_packet.expect [deleted file]
tests/data/plugins/src.ctf.lttng-live/inactivity_discarded_packet.json [deleted file]
tests/data/plugins/src.ctf.lttng-live/list-sessions.json [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/list_sessions.json [deleted file]
tests/data/plugins/src.ctf.lttng-live/lttng_live_server.py
tests/data/plugins/src.ctf.lttng-live/multi-domains-inverse.json [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/multi-domains.json [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/multi_domains.json [deleted file]
tests/data/plugins/src.ctf.lttng-live/multi_domains_inverse.json [deleted file]
tests/data/plugins/src.ctf.lttng-live/new-streams.expect [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/new-streams.json [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/rate-limited.json [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/rate_limited.json [deleted file]
tests/data/plugins/src.ctf.lttng-live/split-metadata.expect [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/split-metadata.json [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/split_metadata.expect [deleted file]
tests/data/plugins/src.ctf.lttng-live/split_metadata.json [deleted file]
tests/data/plugins/src.ctf.lttng-live/stored-values.expect [new file with mode: 0644]
tests/data/plugins/src.ctf.lttng-live/stored-values.json [new file with mode: 0644]
tests/lib/Makefile.am
tests/lib/conds/Makefile.am [deleted file]
tests/lib/conds/clk-cls-compat-postconds-triggers.cpp [new file with mode: 0644]
tests/lib/conds/clk-cls-compat-postconds-triggers.hpp [new file with mode: 0644]
tests/lib/conds/conds-triggers.c [deleted file]
tests/lib/conds/conds-triggers.cpp [new file with mode: 0644]
tests/lib/conds/test-conds.sh [new file with mode: 0755]
tests/lib/conds/test.py
tests/lib/conds/test_conds [deleted file]
tests/lib/conds/utils.c [deleted file]
tests/lib/conds/utils.cpp [new file with mode: 0644]
tests/lib/conds/utils.h [deleted file]
tests/lib/conds/utils.hpp [new file with mode: 0644]
tests/lib/plugin.c [deleted file]
tests/lib/test-bt-uuid.c [new file with mode: 0644]
tests/lib/test-bt-values.c [new file with mode: 0644]
tests/lib/test-fields-bin.cpp [new file with mode: 0644]
tests/lib/test-fields.sh [new file with mode: 0755]
tests/lib/test-graph-topo.c [new file with mode: 0644]
tests/lib/test-plugin-init-fail-plugin/plugin-init-fail.cpp [new file with mode: 0644]
tests/lib/test-plugin-init-fail.cpp [new file with mode: 0644]
tests/lib/test-plugin-init-fail.sh [new file with mode: 0755]
tests/lib/test-plugin-plugins/Makefile.am [deleted file]
tests/lib/test-plugin-plugins/minimal.c [deleted file]
tests/lib/test-plugin-plugins/sfs.c [deleted file]
tests/lib/test-plugins-plugins/minimal.c [new file with mode: 0644]
tests/lib/test-plugins-plugins/sfs.c [new file with mode: 0644]
tests/lib/test-plugins.c [new file with mode: 0644]
tests/lib/test-plugins.sh [new file with mode: 0755]
tests/lib/test-remove-destruction-listener-in-destruction-listener.c [new file with mode: 0644]
tests/lib/test-simple-sink.c [new file with mode: 0644]
tests/lib/test-trace-ir-ref.c [new file with mode: 0644]
tests/lib/test_bt_uuid.c [deleted file]
tests/lib/test_bt_values.c [deleted file]
tests/lib/test_graph_topo.c [deleted file]
tests/lib/test_plugin [deleted file]
tests/lib/test_remove_destruction_listener_in_destruction_listener.c [deleted file]
tests/lib/test_simple_sink.c [deleted file]
tests/lib/test_trace_ir_ref.c [deleted file]
tests/lib/utils/run-in.cpp [new file with mode: 0644]
tests/lib/utils/run-in.hpp [new file with mode: 0644]
tests/param-validation/Makefile.am
tests/param-validation/test-param-validation.c [new file with mode: 0644]
tests/param-validation/test_param_validation.c [deleted file]
tests/plugins/flt.lttng-utils.debug-info/Makefile.am
tests/plugins/flt.lttng-utils.debug-info/test-bin-info-i386-linux-gnu.sh [new file with mode: 0755]
tests/plugins/flt.lttng-utils.debug-info/test-bin-info-powerpc-linux-gnu.sh [new file with mode: 0755]
tests/plugins/flt.lttng-utils.debug-info/test-bin-info-powerpc64le-linux-gnu.sh [new file with mode: 0755]
tests/plugins/flt.lttng-utils.debug-info/test-bin-info-x86-64-linux-gnu.sh [new file with mode: 0755]
tests/plugins/flt.lttng-utils.debug-info/test-bin-info.c [new file with mode: 0644]
tests/plugins/flt.lttng-utils.debug-info/test-dwarf-i386-linux-gnu.sh [new file with mode: 0755]
tests/plugins/flt.lttng-utils.debug-info/test-dwarf-powerpc-linux-gnu.sh [new file with mode: 0755]
tests/plugins/flt.lttng-utils.debug-info/test-dwarf-powerpc64le-linux-gnu.sh [new file with mode: 0755]
tests/plugins/flt.lttng-utils.debug-info/test-dwarf-x86-64-linux-gnu.sh [new file with mode: 0755]
tests/plugins/flt.lttng-utils.debug-info/test-dwarf.c [new file with mode: 0644]
tests/plugins/flt.lttng-utils.debug-info/test-succeed.sh [new file with mode: 0755]
tests/plugins/flt.lttng-utils.debug-info/test_bin_info.c [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_bin_info_i386-linux-gnu [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_bin_info_powerpc-linux-gnu [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_bin_info_powerpc64le-linux-gnu [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_bin_info_x86_64-linux-gnu [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_dwarf.c [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_dwarf_i386-linux-gnu [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_dwarf_powerpc-linux-gnu [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_dwarf_powerpc64le-linux-gnu [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_dwarf_x86_64-linux-gnu [deleted file]
tests/plugins/flt.lttng-utils.debug-info/test_succeed [deleted file]
tests/plugins/flt.utils.muxer/succeed/Makefile.am
tests/plugins/flt.utils.muxer/succeed/test-succeed.sh [new file with mode: 0755]
tests/plugins/flt.utils.muxer/succeed/test_succeed [deleted file]
tests/plugins/flt.utils.muxer/test-clock-compatibility.cpp [new file with mode: 0644]
tests/plugins/flt.utils.muxer/test-clock-compatibility.sh [new file with mode: 0755]
tests/plugins/flt.utils.trimmer/Makefile.am
tests/plugins/flt.utils.trimmer/test-trimming.sh [new file with mode: 0755]
tests/plugins/flt.utils.trimmer/test_trimming [deleted file]
tests/plugins/sink.ctf.fs/Makefile.am
tests/plugins/sink.ctf.fs/succeed/Makefile.am
tests/plugins/sink.ctf.fs/succeed/test-succeed.sh [new file with mode: 0755]
tests/plugins/sink.ctf.fs/succeed/test_succeed [deleted file]
tests/plugins/sink.ctf.fs/test-assume-single-trace.sh [new file with mode: 0755]
tests/plugins/sink.ctf.fs/test-stream-names.sh [new file with mode: 0755]
tests/plugins/sink.ctf.fs/test_assume_single_trace [deleted file]
tests/plugins/sink.ctf.fs/test_stream_names [deleted file]
tests/plugins/sink.text.details/succeed/test-succeed.sh [new file with mode: 0755]
tests/plugins/sink.text.details/succeed/test_succeed [deleted file]
tests/plugins/sink.text.pretty/Makefile.am
tests/plugins/sink.text.pretty/test-enum.sh [new file with mode: 0755]
tests/plugins/sink.text.pretty/test-pretty-python.sh [new file with mode: 0755]
tests/plugins/sink.text.pretty/test_enum [deleted file]
tests/plugins/sink.text.pretty/test_pretty.py
tests/plugins/sink.text.pretty/test_pretty_python [deleted file]
tests/plugins/src.ctf.fs/Makefile.am
tests/plugins/src.ctf.fs/fail/test-fail.sh [new file with mode: 0755]
tests/plugins/src.ctf.fs/fail/test_fail [deleted file]
tests/plugins/src.ctf.fs/field/test-field.sh [new file with mode: 0755]
tests/plugins/src.ctf.fs/query/test-query-metadata-info.sh [new file with mode: 0755]
tests/plugins/src.ctf.fs/query/test-query-support-info.sh [new file with mode: 0755]
tests/plugins/src.ctf.fs/query/test-query-trace-info.sh [new file with mode: 0755]
tests/plugins/src.ctf.fs/query/test_query_metadata_info [deleted file]
tests/plugins/src.ctf.fs/query/test_query_support_info [deleted file]
tests/plugins/src.ctf.fs/query/test_query_support_info.py
tests/plugins/src.ctf.fs/query/test_query_trace_info [deleted file]
tests/plugins/src.ctf.fs/query/test_query_trace_info.py
tests/plugins/src.ctf.fs/succeed/Makefile.am
tests/plugins/src.ctf.fs/succeed/test-succeed.sh [new file with mode: 0755]
tests/plugins/src.ctf.fs/succeed/test_succeed [deleted file]
tests/plugins/src.ctf.fs/test-deterministic-ordering.sh [new file with mode: 0755]
tests/plugins/src.ctf.fs/test_deterministic_ordering [deleted file]
tests/plugins/src.ctf.lttng-live/test-live.sh [new file with mode: 0755]
tests/plugins/src.ctf.lttng-live/test_live [deleted file]
tests/python-plugin-provider/test-python-plugin-provider.sh [new file with mode: 0755]
tests/python-plugin-provider/test_python_plugin_provider [deleted file]
tests/python-plugin-provider/test_python_plugin_provider.py
tests/utils/Makefile.am
tests/utils/common.c
tests/utils/common.h
tests/utils/env.sh.in
tests/utils/python/cli_params_to_string.py
tests/utils/python/mctf.py [new file with mode: 0644]
tests/utils/python/moultipart.py [new file with mode: 0644]
tests/utils/python/normand.py [new file with mode: 0644]
tests/utils/python/split_sort_compare.py
tests/utils/python/testrunner.py
tests/utils/python/tjson.py [new file with mode: 0644]
tests/utils/python/typing/typing.py [new file with mode: 0644]
tests/utils/python/typing/typing.py.license [new file with mode: 0644]
tests/utils/run-in-py-env.sh [new file with mode: 0755]
tests/utils/run_python_bt2 [deleted file]
tests/utils/tap-driver.sh
tests/utils/tap/Makefile.am
tests/utils/tap/tap.c
tests/utils/tap/tap.h
tests/utils/utils.sh
tools/format-cpp [deleted file]
tools/format-cpp.sh [new file with mode: 0755]
tools/lint-py.sh [new file with mode: 0755]
tools/shellcheck.sh [new file with mode: 0755]

index 300e046c5f7a8ead68809eb8b4bb45cb89a69525..cba562531157c82aefa5a16f861ef044fa5388e0 100644 (file)
@@ -79,7 +79,33 @@ ForEachMacros: [
   'bt_list_for_each_prev',
   'bt_list_for_each_prev_safe',
 ]
-IncludeBlocks: Preserve
+IncludeBlocks: Regroup
+IncludeCategories:
+  # Babeltrace 2 public headers
+  - Regex: '^<babeltrace2/.+>$'
+    Priority: 3
+  # System C headers
+  - Regex: '^<.+\.h>$'
+    Priority: 2
+  # System C++ headers
+  - Regex: '^<.+>$'
+    Priority: 1
+  # Logging headers
+  - Regex: '^"(logging\.hpp|logging/comp-logging\.h|logging/log\.h)"$'
+    Priority: 4
+  # Common headers
+  - Regex: '^"(argpar|autodisc|common|compat|cpp-common|ctfser|fd-cache|param-parse|py-common|logging|string-format)/.+"$'
+    Priority: 5
+  # Plugins common headers
+  - Regex: '^"plugins/common/.+"$'
+    Priority: 6
+  # tap header file
+  - Regex: '^"(tap/)?tap\.h"$'
+    Priority: 8
+  # The rest (local headers)
+  - Regex: '.+'
+    Priority: 7
+IncludeIsMainRegex: "please_dont_do_that"
 IndentAccessModifiers: false
 IndentCaseBlocks: false
 IndentCaseLabels: false
@@ -98,7 +124,7 @@ PPIndentWidth: 4
 PointerAlignment: Right
 ReferenceAlignment: Left
 ReflowComments: false
-SortIncludes: false
+SortIncludes: CaseInsensitive
 SortUsingDeclarations: false
 SpaceAfterCStyleCast: true
 SpaceAfterLogicalNot: false
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644 (file)
index 0000000..acbb142
--- /dev/null
@@ -0,0 +1,103 @@
+Checks:          '-*,
+                  bugprone-argument-comment,
+                  bugprone-assert-side-effect,
+                  bugprone-assignment-in-if-condition,
+                  bugprone-bad-signal-to-kill-thread,
+                  bugprone-bool-pointer-implicit-conversion,
+                  bugprone-copy-constructor-init,
+                  bugprone-dangling-handle,
+                  bugprone-exception-escape,
+                  bugprone-fold-init-type,
+                  bugprone-forward-declaration-namespace,
+                  bugprone-forwarding-reference-overload,
+                  bugprone-inaccurate-erase,
+                  bugprone-incorrect-roundings,
+                  bugprone-infinite-loop,
+                  bugprone-integer-division,
+                  bugprone-macro-parentheses,
+                  bugprone-macro-repeated-side-effects,
+                  bugprone-misplaced-operator-in-strlen-in-alloc,
+                  bugprone-misplaced-pointer-arithmetic-in-alloc,
+                  bugprone-move-forwarding-reference,
+                  bugprone-multiple-statement-macro,
+                  bugprone-not-null-terminated-result,
+                  bugprone-parent-virtual-call,
+                  bugprone-posix-return,
+                  bugprone-shared-ptr-array-mismatch,
+                  bugprone-signal-handler,
+                  bugprone-signed-char-misuse,
+                  bugprone-sizeof-container,
+                  bugprone-sizeof-expression,
+                  bugprone-standalone-empty,
+                  bugprone-string-constructor,
+                  bugprone-string-integer-assignment,
+                  bugprone-string-literal-with-embedded-nul,
+                  bugprone-suspicious-enum-usage,
+                  bugprone-suspicious-include,
+                  bugprone-suspicious-memory-comparison
+                  bugprone-suspicious-memset-usage,
+                  bugprone-suspicious-missing-comma,
+                  bugprone-suspicious-realloc-usage,
+                  bugprone-suspicious-semicolon,
+                  bugprone-suspicious-string-compare,
+                  bugprone-swapped-arguments,
+                  bugprone-terminating-continue,
+                  bugprone-throw-keyword-missing,
+                  bugprone-too-small-loop-variable,
+                  bugprone-unchecked-optional-access
+                  bugprone-undefined-memory-manipulation,
+                  bugprone-undelegated-constructor,
+                  bugprone-unhandled-exception-at-new,
+                  bugprone-unhandled-self-assignment,
+                  bugprone-unused-raii,
+                  bugprone-unused-return-value,
+                  bugprone-use-after-move,
+                  bugprone-virtual-near-miss,
+                  bugprone-unused-raii,
+                  bugprone-use-after-move,
+                  cppcoreguidelines-avoid-const-or-ref-data-members,
+                  cppcoreguidelines-pro-type-const-cast,
+                  cppcoreguidelines-slicing,
+                  cppcoreguidelines-special-member-functions,
+                  cppcoreguidelines-virtual-class-destructor,
+                  google-build-explicit-make-pair,
+                  google-explicit-constructor,
+                  misc-const-correctness,
+                  misc-misleading-identifier,
+                  misc-non-copyable-objects,
+                  misc-throw-by-value-catch-by-reference,
+                  misc-unused-parameters,
+                  misc-unused-using-decls,
+                  modernize-avoid-bind,
+                  modernize-concat-nested-namespaces,
+                  modernize-loop-convert,
+                  modernize-make-shared,
+                  modernize-make-unique,
+                  modernize-pass-by-value,
+                  modernize-redundant-void-arg,
+                  modernize-replace-auto-ptr,
+                  modernize-replace-random-shuffle,
+                  modernize-replace-auto-ptr,
+                  modernize-shrink-to-fit,
+                  modernize-use-bool-literals,
+                  modernize-use-default-member-init,
+                  modernize-use-emplace,
+                  modernize-use-equals-default,
+                  modernize-use-equals-delete,
+                  modernize-use-noexcept,
+                  modernize-use-nullptr,
+                  modernize-use-override,
+                  modernize-use-transparent-functors,
+                  modernize-use-using,
+                  performance-*,
+                  -performance-no-int-to-ptr,
+                  readability-redundant-member-init,
+                  readability-simplify-boolean-expr'
+FormatStyle: 'file'
+CheckOptions:
+  - key:          bugprone-assert-side-effect.AssertMacros
+    value:        BT_ASSERT,BT_ASSERT_DBG
+  - key:          cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
+    value:        true
+  - key:          cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted
+    value:        true
index 9461f5ba77c4432693913c11e8d82535a7b477e9..7fca3371465aca276315dfa81db3497a6813155c 100644 (file)
@@ -9,6 +9,10 @@ indent_size = 8
 indent_style = tab
 tab_width = 8
 
-[*.{py,hpp,cpp}]
+[*.{py,py.in,hpp,cpp}]
 indent_style = space
 indent_size = 4
+
+[*.{h,c}]
+indent_style = tab
+indent_size = 8
index 58610444071ce925d0ba68ad758c930c1532dce3..2267bc7ba0fc5191b6c7d108c4985f8688b65c23 100644 (file)
@@ -1,17 +1,18 @@
-/tests/bitfield/test_bitfield
-/tests/argpar/test_argpar
-/tests/ctf-writer/ctf_writer
+/tests/bitfield/test-bitfield
+/tests/argpar/test-argpar
+/tests/ctf-writer/ctf-writer
 /tests/lib/plugin
-/tests/lib/test_bt_uuid
-/tests/lib/test_bt_values
-/tests/lib/test_graph_topo
-/tests/lib/test_trace_ir_ref
-/tests/lib/test_simple_sink
-/tests/lib/test_remove_destruction_listener_in_destruction_listener
+/tests/lib/test-bt-uuid
+/tests/lib/test-bt-values
+/tests/lib/test-fields-bin
+/tests/lib/test-graph-topo
+/tests/lib/test-trace-ir-ref
+/tests/lib/test-simple-sink
+/tests/lib/test-remove-destruction-listener-in-destruction-listener
 /tests/lib/conds/conds-triggers
-/tests/param-validation/test_param_validation
-/tests/plugins/flt.lttng-utils.debug-info/test_bin_info
-/tests/plugins/flt.lttng-utils.debug-info/test_dwarf
+/tests/param-validation/test-param-validation
+/tests/plugins/flt.lttng-utils.debug-info/test-bin-info
+/tests/plugins/flt.lttng-utils.debug-info/test-dwarf
 /tests/plugins/src.ctf.fs/succeed/gen-trace-simple
 /tests/plugins/sink.ctf.fs/succeed/gen-trace-float
 /tests/plugins/sink.ctf.fs/succeed/gen-trace-double
 .deps
 *.bkp
 *.trs
-/src/plugins/ctf/common/metadata/lexer.cpp
-/src/plugins/ctf/common/metadata/parser.cpp
-/src/plugins/ctf/common/metadata/parser.hpp
-/src/plugins/ctf/common/metadata/parser.output
+.dirstamp
+/src/plugins/ctf/common/src/metadata/tsdl/lexer.cpp
+/src/plugins/ctf/common/src/metadata/tsdl/parser.cpp
+/src/plugins/ctf/common/src/metadata/tsdl/parser.hpp
+/src/plugins/ctf/common/src/metadata/tsdl/parser.output
+/src/bindings/python/bt2/bt2.egg-info
+/src/bindings/python/bt2/dist
 /src/cli/babeltrace2
 /src/cli/babeltrace2.bin
 /src/cli/babeltrace2-log
diff --git a/.reuse/dep5 b/.reuse/dep5
new file mode 100644 (file)
index 0000000..2f272d8
--- /dev/null
@@ -0,0 +1,12 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+
+Files:
+  src/cpp-common/vendor/fmt/*.h
+  src/cpp-common/vendor/fmt/*.cc
+Copyright: 2012 - present, Victor Zverovich
+License: MIT
+
+Files:
+  src/cpp-common/vendor/wise-enum/*.h
+Copyright: 2017 - present, Nir Friedman
+License: BSL-1.0
index 9af7a419c8f1968534ab28ccdb2111f716a7bb00..37105c6c696af503273b5068d8d77051ea290ede 100644 (file)
@@ -2,7 +2,7 @@
 
 = Babeltrace{nbsp}2 contributor's guide
 Jérémie Galarneau, Philippe Proulx
-1 December 2020
+22 February 2024
 :toc: left
 :toclevels: 3
 :icons: font
@@ -10,6 +10,7 @@ Jérémie Galarneau, Philippe Proulx
 :bt2: Babeltrace{nbsp}2
 :c-cpp: C/{cpp}
 :cpp11: {cpp}11
+:fmt: pass:[{fmt}]
 
 This is a partial contributor's guide for the
 https://babeltrace.org[{bt2}] project. If you have any
@@ -439,7 +440,6 @@ level, for example:
 +
 [source,c]
 ----
-BT_HIDDEN
 char *bt_common_get_home_plugin_path(int log_level);
 ----
 +
@@ -1148,7 +1148,6 @@ struct my_comp {
     /* ... */
 };
 
-BT_HIDDEN
 bt_self_component_status my_comp_init(
         bt_self_component_source *self_comp_src,
         bt_value *params, void *init_method_data)
@@ -1493,7 +1492,7 @@ tests to run in, so nothing more is needed.
 If building in-tree, you can run single tests from the tree directly:
 
 ----
-$ ./tests/plugins/sink.text.pretty/test_enum
+$ ./tests/plugins/sink.text.pretty/test-enum.sh
 ----
 
 If building out-of-tree, you can get the appropriate environment by sourcing
@@ -1502,15 +1501,16 @@ want to run tests.
 
 ----
 $ source /path/to/my/build/tests/utils/env.sh
-$ ./tests/plugins/sink.text.pretty/test_enum
+$ ./tests/plugins/sink.text.pretty/test-enum.sh
 ----
 
 ==== Python
 
-You can use the `tests/utils/run_python_bt2` script to run any command
-within an environment making the build's `bt2` Python package available.
+You can use the `tests/utils/run-in-py-env.sh` script to run any command
+within an environment making the build's `bt2` Python package available,
+as well as the testing utility Python modules.
 
-`run_python_bt2` uses <<test-env,`utils.sh`>> which needs to know the
+`run-in-py-env.sh` uses <<test-env,`utils.sh`>> which needs to know the
 build directory, so make sure you set the `BT_TESTS_BUILDDIR`
 environment variable correctly _if you build out of tree_, for example:
 
@@ -1519,10 +1519,10 @@ $ export BT_TESTS_BUILDDIR=/path/to/build/babeltrace/tests
 ----
 
 You can run any command which needs the `bt2` Python package through
-`run_python_bt2`, for example:
+`run-in-py-env.sh`, for example:
 
 ----
-$ ./tests/utils/run_python_bt2 ipython3
+$ ./tests/utils/run-in-py-env.sh ipython3
 ----
 
 === Report format
@@ -1568,13 +1568,13 @@ To run all the `bt2` Python package tests:
 * Run:
 +
 ----
-$ ./tests/utils/run_python_bt2 ./tests/bindings/python/bt2/test_python_bt2
+$ ./tests/utils/run-in-py-env.sh ./tests/bindings/python/bt2/test-python-bt2.sh
 ----
 +
 or:
 +
 ----
-$ ./tests/utils/run_python_bt2 python3 ./tests/utils/python/testrunner.py \
+$ ./tests/utils/run-in-py-env.sh python3 ./tests/utils/python/testrunner.py \
   ./tests/bindings/python/bt2/ -p '*.py'
 ----
 
@@ -1584,7 +1584,7 @@ To run **all the tests** in a test module (for example,
 * Run:
 +
 ----
-$ ./tests/utils/run_python_bt2 python3 ./tests/utils/python/testrunner.py \
+$ ./tests/utils/run-in-py-env.sh python3 ./tests/utils/python/testrunner.py \
   ./tests/bindings/python/bt2 -t test_value
 ----
 
@@ -1594,7 +1594,7 @@ To run a **specific test case** (for example, `RealValueTestCase` within
 * Run:
 +
 ----
-$ ./tests/utils/run_python_bt2 python3 ./tests/utils/python/testrunner.py \
+$ ./tests/utils/run-in-py-env.sh python3 ./tests/utils/python/testrunner.py \
   ./tests/bindings/python/bt2/ -t test_value.RealValueTestCase
 ----
 
@@ -1604,13 +1604,14 @@ To run a **specific test** (for example,
 * Run:
 +
 ----
-$ ./tests/utils/run_python_bt2 python3 ./tests/utils/python/testrunner.py \
+$ ./tests/utils/run-in-py-env.sh python3 ./tests/utils/python/testrunner.py \
   ./tests/bindings/python/bt2/ -t test_value.RealValueTestCase.test_assign_pos_int
 ----
 
 == {cpp} usage
 
-Some parts of {bt2} are written in {cpp}.
+A significant part and, in general, all the new code of {bt2} is written
+in {cpp}.
 
 This section shows what's important to know about {cpp} to contribute
 to {bt2}.
@@ -1623,13 +1624,206 @@ In other words, libbabeltrace2 _must_ expose a pure C99 API to preserve
 ABI compatibility over time.
 ====
 
-=== Standard and dependencies
+=== Standard
 
 The {bt2} project is configured to use the {cpp11} standard.
 
 {cpp11} makes it possible to build {bt2} with a broad range of
 compilers, from GCC{nbsp}4.8 and Clang{nbsp}3.3.
 
+=== Common {cpp} code
+
+Many parts of the project need common {cpp} code. You'll find all of it
+under `src/cpp-common`.
+
+In general, anything under a namespace named `internal` is internal to
+the API containing it. For example, everything under the `bt2::internal`
+namespace is internal to the `bt2` namespace and therefore isn't meant
+to be used outside the `src/cpp-common/bt2` directory.
+
+==== `bt2`: libbabeltrace2 {cpp} bindings
+
+`src/cpp-common/bt2` contains our internal {cpp} bindings of
+the libbabeltrace2 C{nbsp}API, under the `bt2` namespace.
+
+Those bindings are designed to have, as much as possible, no performance
+impact. Anything which inherits `bt2::BorrowedObject` contains a single
+libbabeltrace2 object pointer.
+
+Pass and return borrowed objects _by value_ (copy).
+
+In general, the following holds:
+
+[options="header,autowidth",cols="2"]
+|===
+|{cpp} expression
+|Equivalent C{nbsp}expression
+
+|`bt2::Xyz`
+|`bt_xyz *`
+
+|`const bt2::Xyz`
+|`bt_xyz * const`
+
+|`bt2::ConstXyz`
+|`const bt_xyz *`
+
+|`const bt2::ConstXyz`
+|`const bt_xyz * const`
+|===
+
+==== `bt2c`: general common {cpp} code
+
+Similar to the role of `src/common` for C code.
+
+In general, everything in here is under the `bt2c` namespace.
+
+Notable files are:
+
+`align.hpp`::
+    The `bt2c::align()` function template: a wrapper of
+    `src/common/align.h`.
+
+`c-string-view.hpp`::
+    The `bt2c::CStringView` class: a view on a constant null-terminated
+    C{nbsp}string.
++
+We have this because `bt2s::string_view` doesn't imply null termination,
+only a beginning and a length.
++
+A `bt2c::CStringView` instance is convertible to `const char *` and may
+be empty (the underlying pointer is null).
+
+`call.hpp`::
+    The `bt2c::call()` function template: a partial implementation of
+    https://en.cppreference.com/w/cpp/utility/functional[INVOKE].
++
+We use this mostly to assign the result of calling an immediately
+invoked function expression (lambda) to an `auto` variable without
+risking to assign the lambda itself, should we forget the calling
+parentheses:
++
+[source,cpp]
+----
+const auto res = bt2c::call([&] {
+    /* Complex initialization */
+});
+----
+
+`endian.hpp`::
+    Typed wrappers of `src/compat/endian.h`.
+
+`exc.hpp`::
+    Common exception classes.
+
+`fmt.hpp`::
+    Common https://fmt.dev/[{fmt}] formatters.
+
+`logging.hpp`::
+    The `bt2c::Logger` class and helper `BT_CPPLOG*()` macros for any
+    {cpp} logging.
++
+When possible, prefer using this over the C{nbsp}logging API.
++
+One important benefit is that this API uses {fmt} to format the logging
+message instead of `vsnprintf()`.
+
+`prio-heap.hpp`::
+    The `bt2c::PrioHeap` class template: an efficient heap data
+    structure.
+
+`read-fixed-len-int.hpp`::
+    The function templates `bt2c::readFixedLenInt()`,
+    `bt2c::readFixedLenIntLe()`, and `bt2c::readFixedLenIntBe()`: read a
+    fixed-length integer from a byte buffer.
+
+`safe-ops.hpp`::
+    The `bt2c::safe*()` function templates: arithmetic operations which
+    assert that there's no possible overflow.
+
+`std-int.hpp`::
+    The `bt2c::StdIntT` type alias template: becomes one of the
+    `std::*int*_t` types depending on its parameters.
++
+For example, `bt2c::StdIntT<32, true>` is `std::int32_t`.
+
+`type-traits.hpp`::
+    Common type traits.
+
+`uuid.hpp`::
+    The following classes:
+
+`bt2c::Uuid`:::
+    Container of a 16-byte
+    https://en.wikipedia.org/wiki/Universally_unique_identifier[UUID].
++
+Provides the static `generate()` method as well as conversion to
+`bt2c::UuidView`.
+
+`bt2c::UuidView`:::
+    View on a UUID (not a container).
++
+Provides byte access, comparison, as well as string conversion methods.
++
+Also provides conversion to `bt2c::Uuid`.
+
+`vector.hpp`::
+    The `bt2c::vectorFastRemove()` function template: remove an element
+    from an `std::vector` instance quickly when the order isn't
+    important.
+
+==== `bt2s`: drop-in replacements of {cpp}14 to {cpp}20 STL features
+
+Everything under the `bt2s` namespace has its equivalent under the `std`
+namespace, but in {cpp} versions we don't yet have access to, namely:
+
+`make_unique.hpp`::
+    `bt2s::make_unique()`, a drop-in replacement of `std::make_unique()`
+    ({cpp}14).
+
+`optional.hpp`::
+    Drop-in replacement of the `std::optional` API ({cpp}17).
+
+`span.hpp`::
+    Drop-in replacement of the `std::span` API ({cpp}20).
+
+`string-view.hpp`::
+    Drop-in replacement of the `std::string_view` API ({cpp}17).
+
+==== `vendor`: copies of {cpp} dependencies
+
+This directory contains copies of the source code of {cpp} dependencies
+to avoid packaging issues.
+
+They are:
+
+`fmt`::
+    https://fmt.dev/[{fmt}].
+
+`nlohmann`::
+    https://json.nlohmann.me/[JSON for Modern C++].
+
+`optional-lite`::
+    https://github.com/martinmoene/optional-lite[optional lite].
++
+IMPORTANT: Use the symbols of `src/cpp-common/bt2s/optional.hpp`, under
+the `bt2s` namespace, instead of using this directly.
+
+`span-lite`::
+    https://github.com/martinmoene/span-lite[span lite].
++
+IMPORTANT: Use the symbols of `src/cpp-common/bt2s/span.hpp`, under the
+`bt2s` namespace, instead of using this directly.
+
+`string-view-lite`::
+    https://github.com/martinmoene/string-view-lite[string_view lite].
++
+IMPORTANT: Use the symbols of `src/cpp-common/bt2s/string-view.hpp`,
+under the `bt2s` namespace, instead of using this directly.
+
+`wise_enum`::
+    https://github.com/quicknir/wise_enum[wise_enum].
+
 === Automake/Libtool requirements
 
 To add a {cpp} source file to a part of the project, use the `.cpp`
@@ -1676,19 +1870,25 @@ https://clang.llvm.org/docs/ClangFormatStyleOptions.html[style] of the
 You _must_ format modified and new {cpp} files with clang-format before
 you create a contribution patch.
 
-You need clang-format{nbsp}14 to use the project's `.clang-format` file.
+You need clang-format{nbsp}15 to use the project's `.clang-format` file.
 
 To automatically format all the project's {cpp} files, run:
 
 ----
-$ ./tools/format-cpp
+$ ./tools/format-cpp.sh
+----
+
+To only format the {cpp} files of a given directory:
+
+----
+$ ./tools/format-cpp.sh ./src/cli
 ----
 
 Use the `FORMATTER` environment variable to override the default
 formatter (`clang-format{nbsp}-i`):
 
 ----
-$ FORMATTER='clang-format-10 -i' ./tools/format-cpp
+$ FORMATTER='my-clang-format-15 -i' ./tools/format-cpp.sh
 ----
 
 ==== Naming
@@ -1700,25 +1900,26 @@ $ FORMATTER='clang-format-10 -i' ./tools/format-cpp
 * Use camel case with an uppercase first letter for:
 ** Types: `Pistachio`, `NutManager`.
 ** Template parameters: `PlanetT`, `TotalSize`.
+** Enumerators: `Type::SignedInt`, `Scope::Function`.
 
 * Use snake case with uppercase letters for:
 ** Definition/macro names: `MARK_AS_UNUSED()`, `SOME_FEATURE_EXISTS`.
-** Enumerators: `Type::SIGNED_INT`, `Scope::FUNCTION`.
 
 * Use only lowercase letters and digits for namespaces: `mylib`, `bt2`.
 
-* Use the suffix `T` for type template parameters:
+* Use the `T` suffix for type template parameters and the `V` suffix for
+  non-type template parameters:
 +
 [source,cpp]
 ----
-template <typename NameT, typename ItemT>
+template <typename NameT, typename ItemT, unsigned int SizeV = 0>
 ----
 
-* Name a template parameter pack `Args`.
+* Name a template parameter pack `ArgTs`.
 +
 [source,cpp]
 ----
-template <typename NameT, typename... Args>
+template <typename NameT, typename... ArgTs>
 ----
 
 * Use an underscore prefix for private and protected methods and member
@@ -1726,12 +1927,17 @@ template <typename NameT, typename... Args>
 
 * Use the prefix `_m` for private and protected member variable names:
   `_mLogger`, `_mSize`, `_mFieldClass`.
++
+This is to avoid name clashes with private/protected getters/setters.
 
-* Name setters and getters like the property name, without `set` and
+* Name setters and getters like the property name, without the `set` and
   `get` prefixes.
 
 * Use the `is` or `has` prefix, if possible, to name the functions which
   return `bool`.
++
+However, try to keep the name readable. For example, prefer
+`colorIsBlue()` over `isColorBlue()`.
 
 === Coding convention
 
@@ -1753,25 +1959,145 @@ Here are a few important reminders:
 For a class named `MyClass`, name the corresponding files `my-class.hpp`
 and `my-class.cpp`.
 
-* When defining a class, put constructors as the first methods, whatever
-  their access (public/protected/private), then the destructor, and then
-  the rest.
+* Use the `inline` keyword, not `static inline`, for header-only
+  functions that are not templates.
 
-* Declare variables as close to where they are used as possible.
+* When defining a class, use this order:
++
+--
+. Friends (without any preceding access specifier).
+
+. Public types and type aliases.
++
+Private/protected types may be here too if they can't be lower.
+
+. Constructors, whatever their access.
+
+. Destructor (always public).
+
+. Copy-assignment and move-assignment operators.
+
+. Public methods.
+
+. Protected types and type aliases.
+
+. Protected methods.
+
+. Private types and type aliases.
+
+. Private methods.
+
+. Protected data members.
+
+. Private data members.
+--
+
+* Declare variables as close to where they're used as possible.
+
+* In general, try to avoid variables if it doesn't lead to more lines.
++
+For example, given:
++
+[source,cpp]
+----
+const auto size = getSize(myObj, 23);
+auto& obj = this->_objForSize(size);
+
+abc::sendObj(obj, SendOpts::WAIT);
+----
++
+Prefer this:
++
+[source,cpp]
+----
+abc::sendObj(this->_objForSize(getSize(myObj, 23)), SendOpts::WAIT);
+----
+
+* If you really need variables, then scope them to avoid "`leaking`"
+  them:
++
+[source,cpp]
+----
+doSomeStuff(123, &obj);
+
+{
+    const auto status = tryChange(obj);
+
+    BT_CPPLOGD("New status: {}.", status);
+    BT_ASSERT(status == Status::CONTINUE);
+}
+
+doMoreStuff(&obj);
+----
++
+This also means to either use a dedicated, named method/function or to
+use `bt2c::call()` with an immediately invoked function expression
+(lambda) to perform complex initialization of an ideally `const`
+variable:
++
+[source,cpp]
+----
+const auto size = bt2c::call([this] {
+    auto& sender = this->_curSender();
+
+    if (sender.strategy() == Strategy::ACK) {
+        return sender.strategySize();
+    } else if (sender.strategy() == Strategy::NACK) {
+        return 0;
+    }
+
+    return _mDefSize;
+});
+----
+
+* Always use `bt2c::call()` to call an immediately invoked function
+  expression (see the previous point).
+
+* If possible, initialize object members without a default value with
+  the initializer list of a constructor, not in the constructor body.
++
+If the initialization is complex, either use a dedicated, named
+method/function or `bt2c::call()` with an immediately invoked function
+expression (lambda):
++
+[source,cpp]
+----
+MyObj::MyObj(const std::size_t size) :
+    _mSize {size},
+    _mOtherObj {bt2c::call([size] {
+        /* Complex initialization here */
+    })}
+{
+}
+----
 
 * Use `auto` when possible.
++
+Use `auto&` instead of `const auto&` when you know that the type is
+`const` anyway.
++
+Don't use `auto *`.
 
-* Use `const` as much as possible, even for pointer
-  (`+const char* const+`) and numeric values (`const unsigned int`)
+* Use `const` as much as possible, even for pointers
+  (`+const char * const+`) and numeric values (`const unsigned int`)
   which never need to change.
 
+* Prefer the `pass:[MyObj myObj {...}]` initialization form over
+  `pass:[auto myObj = MyObj {...}]`.
+
 * Implement simple setters, getters, and one-liners in header files and
-  everything else that's not a template in source files.
+  everything else that's not a template in source files, including
+  constructors.
 
 * Make methods `const noexcept` or `const` as much as possible.
 
 * Make constructors `explicit` unless you really need an implicit
-  constructor (which is rare).
+  constructor (which is rare), including default constructors:
++
+[source,cpp]
+----
+explicit Meow();
+----
 
 * Use `std::unique_ptr` to manage memory when possible.
 +
@@ -1811,7 +2137,11 @@ If the value is optional:::
 
 * Use type aliases (`using`), not type definitions (`typedef`).
 
-* Use anonymous namespaces for local functions instead of `static`.
+* In a `.cpp` file, use anonymous namespaces for local symbols instead
+  of `static` or `inline`.
+
+* Prefer a function in an anonymous namespace in a `.cpp` file over a
+  private static method if it doesn't need private access to an object.
 
 * Don't pollute the global namespace:
 ** Don't use `using namespace xyz` anywhere.
@@ -1856,7 +2186,7 @@ Example:
 void Obj::doSomething(std::string str)
 {
     _mName = std::move(str);
-    // ...
+    /* ... */
 }
 ----
 
@@ -1868,9 +2198,8 @@ convention.
 [source,cpp]
 ----
 /*
+ * SPDX-FileCopyrightText: 2020 Harry Burnett <hburnett@reese.choco>
  * SPDX-License-Identifier: MIT
- *
- * Copyright 2020 Harry Burnett <hburnett@reese.choco>
  */
 
 #ifndef BABELTRACE_BABY_HPP
@@ -1889,27 +2218,31 @@ class Toy;
  */
 class Baby : public Human
 {
+    friend class Parent;
+
 public:
     using Toys = std::unordered_set<Toy>;
 
     enum class Gender
     {
-        MALE,
-        FEMALE,
-        UNKNOWN,
+        Male,
+        Female,
+        Other,
     };
 
-    Baby() = default;
+    explicit Baby() = default;
     explicit Baby(const Toys& toys);
     Baby(const Baby&) = delete;
     Baby(Baby&&) = delete;
-    Baby& operator=(const Baby&) = delete;
-    Baby& operator=(Baby&&) = delete;
 
 protected:
-    explicit Baby(Gender initialGender = Gender::UNKNOWN);
+    explicit Baby(Gender initialGender = Gender::OTHER);
 
 public:
+    ~Baby();
+    Baby& operator=(const Baby&) = delete;
+    Baby& operator=(Baby&&) = delete;
+
     /*
      * Eats `weight` grams of food.
      */
@@ -1945,8 +2278,18 @@ private:
     Toys _mToys;
 };
 
-} // namespace life
+} /* namespace life */
 
-#endif // BABELTRACE_BABY_HPP
+#endif /* BABELTRACE_BABY_HPP */
 ----
 ====
+
+== Python Usage
+
+=== Formatting
+
+All Python code must be formatted using the version of
+https://github.com/psf/black[Black] specified in `dev-requirements.txt`.
+
+All Python imports must be sorted using the version of
+https://pycqa.github.io/isort/[isort] indicated in `dev-requirements.txt`.
diff --git a/LICENSE b/LICENSE
index 1beedf98e90d7984e3664f9dd44b399307f50b3c..d3b6dcb7965181e7e34903e05428a56e91c27dbf 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -92,6 +92,32 @@ This applies to:
 
        doc/api/libbabeltrace2/*
 
+The typing Python module is provided under the terms of the Python Software
+Foundation License 2:
+
+       SPDX-License-Identifier: PSF-2.0
+
+According with:
+
+       LICENSES/PSF-2.0
+
+This applies to:
+
+       tests/utils/python/typing/typing.py
+
+The Normand Python module is provided under the terms of the
+MIT License:
+
+       SPDX-License-Identifier: MIT
+
+According with:
+
+       LICENSES/MIT
+
+This applies to:
+
+       tests/utils/python/normand.py
+
 
 In addition, other licenses may also apply, see SPDX-License-Identifier in
 individual files.
diff --git a/LICENSES/BSD-2-Clause b/LICENSES/BSD-2-Clause
deleted file mode 100644 (file)
index 9522a8f..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-Valid-License-Identifier: BSD-2-Clause
-SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html
-Usage-Guide:
-  To use the BSD 2-Clause "Simplified" License License put the following
-  SPDX tag/value pair into a comment according to the placement
-  guidelines in the licensing rules documentation:
-    SPDX-License-Identifier: BSD-2-Clause
-License-Text:
-
-Copyright (c) <year> <owner>. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
-and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt
new file mode 100644 (file)
index 0000000..9522a8f
--- /dev/null
@@ -0,0 +1,31 @@
+Valid-License-Identifier: BSD-2-Clause
+SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html
+Usage-Guide:
+  To use the BSD 2-Clause "Simplified" License License put the following
+  SPDX tag/value pair into a comment according to the placement
+  guidelines in the licensing rules documentation:
+    SPDX-License-Identifier: BSD-2-Clause
+License-Text:
+
+Copyright (c) <year> <owner>. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSES/BSD-4-Clause b/LICENSES/BSD-4-Clause
deleted file mode 100644 (file)
index 72ac05f..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-Valid-License-Identifier: BSD-4-Clause
-SPDX-URL: https://spdx.org/licenses/BSD-4-Clause.html
-Usage-Guide:
-  To use the BSD 4-Clause "Original" or "Old" License put the following
-  SPDX tag/value pair into a comment according to the placement
-  guidelines in the licensing rules documentation:
-    SPDX-License-Identifier: BSD-4-Clause
-License-Text:
-
-Copyright (c) 1993 The Regents of the University of California. All
-rights reserved.
-
-This software was developed by the Computer Systems Engineering group
-at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
-contributed to Berkeley.
-
-All advertising materials mentioning features or use of this software
-must display the following acknowledgement: This product includes
-software developed by the University of California, Lawrence Berkeley
-Laboratory.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-    1. Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
-
-    2. Redistributions in binary form must reproduce the above copyright
-    notice, this list of conditions and the following disclaimer in
-    the documentation and/or other materials provided with the
-    distribution.
-
-    3. All advertising materials mentioning features or use of this
-    software must display the following acknowledgement: This product
-    includes software developed by the University of California,
-    Berkeley and its contributors.
-
-    4. Neither the name of the University nor the names of its
-    contributors may be used to endorse or promote products derived
-    from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSES/BSD-4-Clause.txt b/LICENSES/BSD-4-Clause.txt
new file mode 100644 (file)
index 0000000..72ac05f
--- /dev/null
@@ -0,0 +1,53 @@
+Valid-License-Identifier: BSD-4-Clause
+SPDX-URL: https://spdx.org/licenses/BSD-4-Clause.html
+Usage-Guide:
+  To use the BSD 4-Clause "Original" or "Old" License put the following
+  SPDX tag/value pair into a comment according to the placement
+  guidelines in the licensing rules documentation:
+    SPDX-License-Identifier: BSD-4-Clause
+License-Text:
+
+Copyright (c) 1993 The Regents of the University of California. All
+rights reserved.
+
+This software was developed by the Computer Systems Engineering group
+at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+contributed to Berkeley.
+
+All advertising materials mentioning features or use of this software
+must display the following acknowledgement: This product includes
+software developed by the University of California, Lawrence Berkeley
+Laboratory.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+    3. All advertising materials mentioning features or use of this
+    software must display the following acknowledgement: This product
+    includes software developed by the University of California,
+    Berkeley and its contributors.
+
+    4. Neither the name of the University nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSES/BSL-1.0 b/LICENSES/BSL-1.0
deleted file mode 100644 (file)
index 36b7cd9..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/LICENSES/BSL-1.0.txt b/LICENSES/BSL-1.0.txt
new file mode 100644 (file)
index 0000000..36b7cd9
--- /dev/null
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/LICENSES/CC-BY-SA-4.0 b/LICENSES/CC-BY-SA-4.0
deleted file mode 100644 (file)
index b3427f5..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-Valid-License-Identifier: CC-BY-SA-4.0
-SPDX-URL: https://spdx.org/licenses/CC-BY-SA-4.0.html
-Usage-Guide:
-  To use the Creative Commons Attribution Share Alike 4.0 International
-  License put the following SPDX tag/value pair into a comment according
-  to the placement guidelines in the licensing rules documentation:
-    SPDX-License-Identifier: CC-BY-SA-4.0
-License-Text:
-
-Creative Commons Attribution-ShareAlike 4.0 International Creative Commons
-Corporation ("Creative Commons") is not a law firm and does not provide legal
-services or legal advice. Distribution of Creative Commons public licenses
-does not create a lawyer-client or other relationship. Creative Commons makes
-its licenses and related information available on an "as-is" basis. Creative
-Commons gives no warranties regarding its licenses, any material licensed
-under their terms and conditions, or any related information. Creative Commons
-disclaims all liability for damages resulting from their use to the fullest
-extent possible.
-
-Using Creative Commons Public Licenses
-
-Creative Commons public licenses provide a standard set of terms and conditions
-that creators and other rights holders may use to share original works of
-authorship and other material subject to copyright and certain other rights
-specified in the public license below. The following considerations are for
-informational purposes only, are not exhaustive, and do not form part of our
-licenses.
-
-Considerations for licensors: Our public licenses are intended for use by
-those authorized to give the public permission to use material in ways otherwise
-restricted by copyright and certain other rights. Our licenses are irrevocable.
-Licensors should read and understand the terms and conditions of the license
-they choose before applying it. Licensors should also secure all rights necessary
-before applying our licenses so that the public can reuse the material as
-expected. Licensors should clearly mark any material not subject to the license.
-This includes other CC-licensed material, or material used under an exception
-or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors
-
-Considerations for the public: By using one of our public licenses, a licensor
-grants the public permission to use the licensed material under specified
-terms and conditions. If the licensor's permission is not necessary for any
-reason–for example, because of any applicable exception or limitation to copyright–then
-that use is not regulated by the license. Our licenses grant only permissions
-under copyright and certain other rights that a licensor has authority to
-grant. Use of the licensed material may still be restricted for other reasons,
-including because others have copyright or other rights in the material. A
-licensor may make special requests, such as asking that all changes be marked
-or described.
-
-Although not required by our licenses, you are encouraged to respect those
-requests where reasonable. More considerations for the public : wiki.creativecommons.org/Considerations_for_licensees
-
-Creative Commons Attribution-ShareAlike 4.0 International Public License
-
-By exercising the Licensed Rights (defined below), You accept and agree to
-be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike
-4.0 International Public License ("Public License"). To the extent this Public
-License may be interpreted as a contract, You are granted the Licensed Rights
-in consideration of Your acceptance of these terms and conditions, and the
-Licensor grants You such rights in consideration of benefits the Licensor
-receives from making the Licensed Material available under these terms and
-conditions.
-
-Section 1 – Definitions.
-
-a. Adapted Material means material subject to Copyright and Similar Rights
-that is derived from or based upon the Licensed Material and in which the
-Licensed Material is translated, altered, arranged, transformed, or otherwise
-modified in a manner requiring permission under the Copyright and Similar
-Rights held by the Licensor. For purposes of this Public License, where the
-Licensed Material is a musical work, performance, or sound recording, Adapted
-Material is always produced where the Licensed Material is synched in timed
-relation with a moving image.
-
-b. Adapter's License means the license You apply to Your Copyright and Similar
-Rights in Your contributions to Adapted Material in accordance with the terms
-and conditions of this Public License.
-
-c. BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses,
-approved by Creative Commons as essentially the equivalent of this Public
-License.
-
-d. Copyright and Similar Rights means copyright and/or similar rights closely
-related to copyright including, without limitation, performance, broadcast,
-sound recording, and Sui Generis Database Rights, without regard to how the
-rights are labeled or categorized. For purposes of this Public License, the
-rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
-
-e. Effective Technological Measures means those measures that, in the absence
-of proper authority, may not be circumvented under laws fulfilling obligations
-under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996,
-and/or similar international agreements.
-
-f. Exceptions and Limitations means fair use, fair dealing, and/or any other
-exception or limitation to Copyright and Similar Rights that applies to Your
-use of the Licensed Material.
-
-g. License Elements means the license attributes listed in the name of a Creative
-Commons Public License. The License Elements of this Public License are Attribution
-and ShareAlike.
-
-h. Licensed Material means the artistic or literary work, database, or other
-material to which the Licensor applied this Public License.
-
-i. Licensed Rights means the rights granted to You subject to the terms and
-conditions of this Public License, which are limited to all Copyright and
-Similar Rights that apply to Your use of the Licensed Material and that the
-Licensor has authority to license.
-
-j. Licensor means the individual(s) or entity(ies) granting rights under this
-Public License.
-
-k. Share means to provide material to the public by any means or process that
-requires permission under the Licensed Rights, such as reproduction, public
-display, public performance, distribution, dissemination, communication, or
-importation, and to make material available to the public including in ways
-that members of the public may access the material from a place and at a time
-individually chosen by them.
-
-l. Sui Generis Database Rights means rights other than copyright resulting
-from Directive 96/9/EC of the European Parliament and of the Council of 11
-March 1996 on the legal protection of databases, as amended and/or succeeded,
-as well as other essentially equivalent rights anywhere in the world.
-
-m. You means the individual or entity exercising the Licensed Rights under
-this Public License. Your has a corresponding meaning.
-
-Section 2 – Scope.
-
-   a. License grant.
-
-1. Subject to the terms and conditions of this Public License, the Licensor
-hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive,
-irrevocable license to exercise the Licensed Rights in the Licensed Material
-to:
-
-         A. reproduce and Share the Licensed Material, in whole or in part; and
-
-         B. produce, reproduce, and Share Adapted Material.
-
-2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions
-and Limitations apply to Your use, this Public License does not apply, and
-You do not need to comply with its terms and conditions.
-
-      3. Term. The term of this Public License is specified in Section 6(a).
-
-4. Media and formats; technical modifications allowed. The Licensor authorizes
-You to exercise the Licensed Rights in all media and formats whether now known
-or hereafter created, and to make technical modifications necessary to do
-so. The Licensor waives and/or agrees not to assert any right or authority
-to forbid You from making technical modifications necessary to exercise the
-Licensed Rights, including technical modifications necessary to circumvent
-Effective Technological Measures. For purposes of this Public License, simply
-making modifications authorized by this Section 2(a)(4) never produces Adapted
-Material.
-
-      5. Downstream recipients.
-
-A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed
-Material automatically receives an offer from the Licensor to exercise the
-Licensed Rights under the terms and conditions of this Public License.
-
-B. Additional offer from the Licensor – Adapted Material. Every recipient
-of Adapted Material from You automatically receives an offer from the Licensor
-to exercise the Licensed Rights in the Adapted Material under the conditions
-of the Adapter's License You apply.
-
-C. No downstream restrictions. You may not offer or impose any additional
-or different terms or conditions on, or apply any Effective Technological
-Measures to, the Licensed Material if doing so restricts exercise of the Licensed
-Rights by any recipient of the Licensed Material.
-
-6. No endorsement. Nothing in this Public License constitutes or may be construed
-as permission to assert or imply that You are, or that Your use of the Licensed
-Material is, connected with, or sponsored, endorsed, or granted official status
-by, the Licensor or others designated to receive attribution as provided in
-Section 3(a)(1)(A)(i).
-
-   b. Other rights.
-
-1. Moral rights, such as the right of integrity, are not licensed under this
-Public License, nor are publicity, privacy, and/or other similar personality
-rights; however, to the extent possible, the Licensor waives and/or agrees
-not to assert any such rights held by the Licensor to the limited extent necessary
-to allow You to exercise the Licensed Rights, but not otherwise.
-
-2. Patent and trademark rights are not licensed under this Public License.
-
-3. To the extent possible, the Licensor waives any right to collect royalties
-from You for the exercise of the Licensed Rights, whether directly or through
-a collecting society under any voluntary or waivable statutory or compulsory
-licensing scheme. In all other cases the Licensor expressly reserves any right
-to collect such royalties.
-
-Section 3 – License Conditions.
-
-Your exercise of the Licensed Rights is expressly made subject to the following
-conditions.
-
-   a. Attribution.
-
-1. If You Share the Licensed Material (including in modified form), You must:
-
-A. retain the following if it is supplied by the Licensor with the Licensed
-Material:
-
-i. identification of the creator(s) of the Licensed Material and any others
-designated to receive attribution, in any reasonable manner requested by the
-Licensor (including by pseudonym if designated);
-
-            ii. a copyright notice;
-
-            iii. a notice that refers to this Public License;
-
-            iv. a notice that refers to the disclaimer of warranties;
-
-v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
-
-B. indicate if You modified the Licensed Material and retain an indication
-of any previous modifications; and
-
-C. indicate the Licensed Material is licensed under this Public License, and
-include the text of, or the URI or hyperlink to, this Public License.
-
-2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner
-based on the medium, means, and context in which You Share the Licensed Material.
-For example, it may be reasonable to satisfy the conditions by providing a
-URI or hyperlink to a resource that includes the required information.
-
-3. If requested by the Licensor, You must remove any of the information required
-by Section 3(a)(1)(A) to the extent reasonably practicable.
-
-b. ShareAlike.In addition to the conditions in Section 3(a), if You Share
-Adapted Material You produce, the following conditions also apply.
-
-1. The Adapter's License You apply must be a Creative Commons license with
-the same License Elements, this version or later, or a BY-SA Compatible License.
-
-2. You must include the text of, or the URI or hyperlink to, the Adapter's
-License You apply. You may satisfy this condition in any reasonable manner
-based on the medium, means, and context in which You Share Adapted Material.
-
-3. You may not offer or impose any additional or different terms or conditions
-on, or apply any Effective Technological Measures to, Adapted Material that
-restrict exercise of the rights granted under the Adapter's License You apply.
-
-Section 4 – Sui Generis Database Rights.
-
-Where the Licensed Rights include Sui Generis Database Rights that apply to
-Your use of the Licensed Material:
-
-a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract,
-reuse, reproduce, and Share all or a substantial portion of the contents of
-the database;
-
-b. if You include all or a substantial portion of the database contents in
-a database in which You have Sui Generis Database Rights, then the database
-in which You have Sui Generis Database Rights (but not its individual contents)
-is Adapted Material, including for purposes of Section 3(b); and
-
-c. You must comply with the conditions in Section 3(a) if You Share all or
-a substantial portion of the contents of the database.
-
-For the avoidance of doubt, this Section 4 supplements and does not replace
-Your obligations under this Public License where the Licensed Rights include
-other Copyright and Similar Rights.
-
-Section 5 – Disclaimer of Warranties and Limitation of Liability.
-
-a. Unless otherwise separately undertaken by the Licensor, to the extent possible,
-the Licensor offers the Licensed Material as-is and as-available, and makes
-no representations or warranties of any kind concerning the Licensed Material,
-whether express, implied, statutory, or other. This includes, without limitation,
-warranties of title, merchantability, fitness for a particular purpose, non-infringement,
-absence of latent or other defects, accuracy, or the presence or absence of
-errors, whether or not known or discoverable. Where disclaimers of warranties
-are not allowed in full or in part, this disclaimer may not apply to You.
-
-b. To the extent possible, in no event will the Licensor be liable to You
-on any legal theory (including, without limitation, negligence) or otherwise
-for any direct, special, indirect, incidental, consequential, punitive, exemplary,
-or other losses, costs, expenses, or damages arising out of this Public License
-or use of the Licensed Material, even if the Licensor has been advised of
-the possibility of such losses, costs, expenses, or damages. Where a limitation
-of liability is not allowed in full or in part, this limitation may not apply
-to You.
-
-c. The disclaimer of warranties and limitation of liability provided above
-shall be interpreted in a manner that, to the extent possible, most closely
-approximates an absolute disclaimer and waiver of all liability.
-
-Section 6 – Term and Termination.
-
-a. This Public License applies for the term of the Copyright and Similar Rights
-licensed here. However, if You fail to comply with this Public License, then
-Your rights under this Public License terminate automatically.
-
-b. Where Your right to use the Licensed Material has terminated under Section
-6(a), it reinstates:
-
-1. automatically as of the date the violation is cured, provided it is cured
-within 30 days of Your discovery of the violation; or
-
-      2. upon express reinstatement by the Licensor.
-
-c. For the avoidance of doubt, this Section 6(b) does not affect any right
-the Licensor may have to seek remedies for Your violations of this Public
-License.
-
-d. For the avoidance of doubt, the Licensor may also offer the Licensed Material
-under separate terms or conditions or stop distributing the Licensed Material
-at any time; however, doing so will not terminate this Public License.
-
-   e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
-
-Section 7 – Other Terms and Conditions.
-
-a. The Licensor shall not be bound by any additional or different terms or
-conditions communicated by You unless expressly agreed.
-
-b. Any arrangements, understandings, or agreements regarding the Licensed
-Material not stated herein are separate from and independent of the terms
-and conditions of this Public License.
-
-Section 8 – Interpretation.
-
-a. For the avoidance of doubt, this Public License does not, and shall not
-be interpreted to, reduce, limit, restrict, or impose conditions on any use
-of the Licensed Material that could lawfully be made without permission under
-this Public License.
-
-b. To the extent possible, if any provision of this Public License is deemed
-unenforceable, it shall be automatically reformed to the minimum extent necessary
-to make it enforceable. If the provision cannot be reformed, it shall be severed
-from this Public License without affecting the enforceability of the remaining
-terms and conditions.
-
-c. No term or condition of this Public License will be waived and no failure
-to comply consented to unless expressly agreed to by the Licensor.
-
-d. Nothing in this Public License constitutes or may be interpreted as a limitation
-upon, or waiver of, any privileges and immunities that apply to the Licensor
-or You, including from the legal processes of any jurisdiction or authority.
-
-Creative Commons is not a party to its public licenses. Notwithstanding, Creative
-Commons may elect to apply one of its public licenses to material it publishes
-and in those instances will be considered the "Licensor." The text of the
-Creative Commons public licenses is dedicated to the public domain under the
-CC0 Public Domain Dedication. Except for the limited purpose of indicating
-that material is shared under a Creative Commons public license or as otherwise
-permitted by the Creative Commons policies published at creativecommons.org/policies,
-Creative Commons does not authorize the use of the trademark "Creative Commons"
-or any other trademark or logo of Creative Commons without its prior written
-consent including, without limitation, in connection with any unauthorized
-modifications to any of its public licenses or any other arrangements, understandings,
-or agreements concerning use of licensed material. For the avoidance of doubt,
-this paragraph does not form part of the public licenses.
-
-Creative Commons may be contacted at creativecommons.org.
diff --git a/LICENSES/CC-BY-SA-4.0.txt b/LICENSES/CC-BY-SA-4.0.txt
new file mode 100644 (file)
index 0000000..b3427f5
--- /dev/null
@@ -0,0 +1,359 @@
+Valid-License-Identifier: CC-BY-SA-4.0
+SPDX-URL: https://spdx.org/licenses/CC-BY-SA-4.0.html
+Usage-Guide:
+  To use the Creative Commons Attribution Share Alike 4.0 International
+  License put the following SPDX tag/value pair into a comment according
+  to the placement guidelines in the licensing rules documentation:
+    SPDX-License-Identifier: CC-BY-SA-4.0
+License-Text:
+
+Creative Commons Attribution-ShareAlike 4.0 International Creative Commons
+Corporation ("Creative Commons") is not a law firm and does not provide legal
+services or legal advice. Distribution of Creative Commons public licenses
+does not create a lawyer-client or other relationship. Creative Commons makes
+its licenses and related information available on an "as-is" basis. Creative
+Commons gives no warranties regarding its licenses, any material licensed
+under their terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the fullest
+extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and conditions
+that creators and other rights holders may use to share original works of
+authorship and other material subject to copyright and certain other rights
+specified in the public license below. The following considerations are for
+informational purposes only, are not exhaustive, and do not form part of our
+licenses.
+
+Considerations for licensors: Our public licenses are intended for use by
+those authorized to give the public permission to use material in ways otherwise
+restricted by copyright and certain other rights. Our licenses are irrevocable.
+Licensors should read and understand the terms and conditions of the license
+they choose before applying it. Licensors should also secure all rights necessary
+before applying our licenses so that the public can reuse the material as
+expected. Licensors should clearly mark any material not subject to the license.
+This includes other CC-licensed material, or material used under an exception
+or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors
+
+Considerations for the public: By using one of our public licenses, a licensor
+grants the public permission to use the licensed material under specified
+terms and conditions. If the licensor's permission is not necessary for any
+reason–for example, because of any applicable exception or limitation to copyright–then
+that use is not regulated by the license. Our licenses grant only permissions
+under copyright and certain other rights that a licensor has authority to
+grant. Use of the licensed material may still be restricted for other reasons,
+including because others have copyright or other rights in the material. A
+licensor may make special requests, such as asking that all changes be marked
+or described.
+
+Although not required by our licenses, you are encouraged to respect those
+requests where reasonable. More considerations for the public : wiki.creativecommons.org/Considerations_for_licensees
+
+Creative Commons Attribution-ShareAlike 4.0 International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree to
+be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike
+4.0 International Public License ("Public License"). To the extent this Public
+License may be interpreted as a contract, You are granted the Licensed Rights
+in consideration of Your acceptance of these terms and conditions, and the
+Licensor grants You such rights in consideration of benefits the Licensor
+receives from making the Licensed Material available under these terms and
+conditions.
+
+Section 1 – Definitions.
+
+a. Adapted Material means material subject to Copyright and Similar Rights
+that is derived from or based upon the Licensed Material and in which the
+Licensed Material is translated, altered, arranged, transformed, or otherwise
+modified in a manner requiring permission under the Copyright and Similar
+Rights held by the Licensor. For purposes of this Public License, where the
+Licensed Material is a musical work, performance, or sound recording, Adapted
+Material is always produced where the Licensed Material is synched in timed
+relation with a moving image.
+
+b. Adapter's License means the license You apply to Your Copyright and Similar
+Rights in Your contributions to Adapted Material in accordance with the terms
+and conditions of this Public License.
+
+c. BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses,
+approved by Creative Commons as essentially the equivalent of this Public
+License.
+
+d. Copyright and Similar Rights means copyright and/or similar rights closely
+related to copyright including, without limitation, performance, broadcast,
+sound recording, and Sui Generis Database Rights, without regard to how the
+rights are labeled or categorized. For purposes of this Public License, the
+rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
+
+e. Effective Technological Measures means those measures that, in the absence
+of proper authority, may not be circumvented under laws fulfilling obligations
+under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996,
+and/or similar international agreements.
+
+f. Exceptions and Limitations means fair use, fair dealing, and/or any other
+exception or limitation to Copyright and Similar Rights that applies to Your
+use of the Licensed Material.
+
+g. License Elements means the license attributes listed in the name of a Creative
+Commons Public License. The License Elements of this Public License are Attribution
+and ShareAlike.
+
+h. Licensed Material means the artistic or literary work, database, or other
+material to which the Licensor applied this Public License.
+
+i. Licensed Rights means the rights granted to You subject to the terms and
+conditions of this Public License, which are limited to all Copyright and
+Similar Rights that apply to Your use of the Licensed Material and that the
+Licensor has authority to license.
+
+j. Licensor means the individual(s) or entity(ies) granting rights under this
+Public License.
+
+k. Share means to provide material to the public by any means or process that
+requires permission under the Licensed Rights, such as reproduction, public
+display, public performance, distribution, dissemination, communication, or
+importation, and to make material available to the public including in ways
+that members of the public may access the material from a place and at a time
+individually chosen by them.
+
+l. Sui Generis Database Rights means rights other than copyright resulting
+from Directive 96/9/EC of the European Parliament and of the Council of 11
+March 1996 on the legal protection of databases, as amended and/or succeeded,
+as well as other essentially equivalent rights anywhere in the world.
+
+m. You means the individual or entity exercising the Licensed Rights under
+this Public License. Your has a corresponding meaning.
+
+Section 2 – Scope.
+
+   a. License grant.
+
+1. Subject to the terms and conditions of this Public License, the Licensor
+hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive,
+irrevocable license to exercise the Licensed Rights in the Licensed Material
+to:
+
+         A. reproduce and Share the Licensed Material, in whole or in part; and
+
+         B. produce, reproduce, and Share Adapted Material.
+
+2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions
+and Limitations apply to Your use, this Public License does not apply, and
+You do not need to comply with its terms and conditions.
+
+      3. Term. The term of this Public License is specified in Section 6(a).
+
+4. Media and formats; technical modifications allowed. The Licensor authorizes
+You to exercise the Licensed Rights in all media and formats whether now known
+or hereafter created, and to make technical modifications necessary to do
+so. The Licensor waives and/or agrees not to assert any right or authority
+to forbid You from making technical modifications necessary to exercise the
+Licensed Rights, including technical modifications necessary to circumvent
+Effective Technological Measures. For purposes of this Public License, simply
+making modifications authorized by this Section 2(a)(4) never produces Adapted
+Material.
+
+      5. Downstream recipients.
+
+A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed
+Material automatically receives an offer from the Licensor to exercise the
+Licensed Rights under the terms and conditions of this Public License.
+
+B. Additional offer from the Licensor – Adapted Material. Every recipient
+of Adapted Material from You automatically receives an offer from the Licensor
+to exercise the Licensed Rights in the Adapted Material under the conditions
+of the Adapter's License You apply.
+
+C. No downstream restrictions. You may not offer or impose any additional
+or different terms or conditions on, or apply any Effective Technological
+Measures to, the Licensed Material if doing so restricts exercise of the Licensed
+Rights by any recipient of the Licensed Material.
+
+6. No endorsement. Nothing in this Public License constitutes or may be construed
+as permission to assert or imply that You are, or that Your use of the Licensed
+Material is, connected with, or sponsored, endorsed, or granted official status
+by, the Licensor or others designated to receive attribution as provided in
+Section 3(a)(1)(A)(i).
+
+   b. Other rights.
+
+1. Moral rights, such as the right of integrity, are not licensed under this
+Public License, nor are publicity, privacy, and/or other similar personality
+rights; however, to the extent possible, the Licensor waives and/or agrees
+not to assert any such rights held by the Licensor to the limited extent necessary
+to allow You to exercise the Licensed Rights, but not otherwise.
+
+2. Patent and trademark rights are not licensed under this Public License.
+
+3. To the extent possible, the Licensor waives any right to collect royalties
+from You for the exercise of the Licensed Rights, whether directly or through
+a collecting society under any voluntary or waivable statutory or compulsory
+licensing scheme. In all other cases the Licensor expressly reserves any right
+to collect such royalties.
+
+Section 3 – License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the following
+conditions.
+
+   a. Attribution.
+
+1. If You Share the Licensed Material (including in modified form), You must:
+
+A. retain the following if it is supplied by the Licensor with the Licensed
+Material:
+
+i. identification of the creator(s) of the Licensed Material and any others
+designated to receive attribution, in any reasonable manner requested by the
+Licensor (including by pseudonym if designated);
+
+            ii. a copyright notice;
+
+            iii. a notice that refers to this Public License;
+
+            iv. a notice that refers to the disclaimer of warranties;
+
+v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
+
+B. indicate if You modified the Licensed Material and retain an indication
+of any previous modifications; and
+
+C. indicate the Licensed Material is licensed under this Public License, and
+include the text of, or the URI or hyperlink to, this Public License.
+
+2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner
+based on the medium, means, and context in which You Share the Licensed Material.
+For example, it may be reasonable to satisfy the conditions by providing a
+URI or hyperlink to a resource that includes the required information.
+
+3. If requested by the Licensor, You must remove any of the information required
+by Section 3(a)(1)(A) to the extent reasonably practicable.
+
+b. ShareAlike.In addition to the conditions in Section 3(a), if You Share
+Adapted Material You produce, the following conditions also apply.
+
+1. The Adapter's License You apply must be a Creative Commons license with
+the same License Elements, this version or later, or a BY-SA Compatible License.
+
+2. You must include the text of, or the URI or hyperlink to, the Adapter's
+License You apply. You may satisfy this condition in any reasonable manner
+based on the medium, means, and context in which You Share Adapted Material.
+
+3. You may not offer or impose any additional or different terms or conditions
+on, or apply any Effective Technological Measures to, Adapted Material that
+restrict exercise of the rights granted under the Adapter's License You apply.
+
+Section 4 – Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that apply to
+Your use of the Licensed Material:
+
+a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract,
+reuse, reproduce, and Share all or a substantial portion of the contents of
+the database;
+
+b. if You include all or a substantial portion of the database contents in
+a database in which You have Sui Generis Database Rights, then the database
+in which You have Sui Generis Database Rights (but not its individual contents)
+is Adapted Material, including for purposes of Section 3(b); and
+
+c. You must comply with the conditions in Section 3(a) if You Share all or
+a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not replace
+Your obligations under this Public License where the Licensed Rights include
+other Copyright and Similar Rights.
+
+Section 5 – Disclaimer of Warranties and Limitation of Liability.
+
+a. Unless otherwise separately undertaken by the Licensor, to the extent possible,
+the Licensor offers the Licensed Material as-is and as-available, and makes
+no representations or warranties of any kind concerning the Licensed Material,
+whether express, implied, statutory, or other. This includes, without limitation,
+warranties of title, merchantability, fitness for a particular purpose, non-infringement,
+absence of latent or other defects, accuracy, or the presence or absence of
+errors, whether or not known or discoverable. Where disclaimers of warranties
+are not allowed in full or in part, this disclaimer may not apply to You.
+
+b. To the extent possible, in no event will the Licensor be liable to You
+on any legal theory (including, without limitation, negligence) or otherwise
+for any direct, special, indirect, incidental, consequential, punitive, exemplary,
+or other losses, costs, expenses, or damages arising out of this Public License
+or use of the Licensed Material, even if the Licensor has been advised of
+the possibility of such losses, costs, expenses, or damages. Where a limitation
+of liability is not allowed in full or in part, this limitation may not apply
+to You.
+
+c. The disclaimer of warranties and limitation of liability provided above
+shall be interpreted in a manner that, to the extent possible, most closely
+approximates an absolute disclaimer and waiver of all liability.
+
+Section 6 – Term and Termination.
+
+a. This Public License applies for the term of the Copyright and Similar Rights
+licensed here. However, if You fail to comply with this Public License, then
+Your rights under this Public License terminate automatically.
+
+b. Where Your right to use the Licensed Material has terminated under Section
+6(a), it reinstates:
+
+1. automatically as of the date the violation is cured, provided it is cured
+within 30 days of Your discovery of the violation; or
+
+      2. upon express reinstatement by the Licensor.
+
+c. For the avoidance of doubt, this Section 6(b) does not affect any right
+the Licensor may have to seek remedies for Your violations of this Public
+License.
+
+d. For the avoidance of doubt, the Licensor may also offer the Licensed Material
+under separate terms or conditions or stop distributing the Licensed Material
+at any time; however, doing so will not terminate this Public License.
+
+   e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
+
+Section 7 – Other Terms and Conditions.
+
+a. The Licensor shall not be bound by any additional or different terms or
+conditions communicated by You unless expressly agreed.
+
+b. Any arrangements, understandings, or agreements regarding the Licensed
+Material not stated herein are separate from and independent of the terms
+and conditions of this Public License.
+
+Section 8 – Interpretation.
+
+a. For the avoidance of doubt, this Public License does not, and shall not
+be interpreted to, reduce, limit, restrict, or impose conditions on any use
+of the Licensed Material that could lawfully be made without permission under
+this Public License.
+
+b. To the extent possible, if any provision of this Public License is deemed
+unenforceable, it shall be automatically reformed to the minimum extent necessary
+to make it enforceable. If the provision cannot be reformed, it shall be severed
+from this Public License without affecting the enforceability of the remaining
+terms and conditions.
+
+c. No term or condition of this Public License will be waived and no failure
+to comply consented to unless expressly agreed to by the Licensor.
+
+d. Nothing in this Public License constitutes or may be interpreted as a limitation
+upon, or waiver of, any privileges and immunities that apply to the Licensor
+or You, including from the legal processes of any jurisdiction or authority.
+
+Creative Commons is not a party to its public licenses. Notwithstanding, Creative
+Commons may elect to apply one of its public licenses to material it publishes
+and in those instances will be considered the "Licensor." The text of the
+Creative Commons public licenses is dedicated to the public domain under the
+CC0 Public Domain Dedication. Except for the limited purpose of indicating
+that material is shared under a Creative Commons public license or as otherwise
+permitted by the Creative Commons policies published at creativecommons.org/policies,
+Creative Commons does not authorize the use of the trademark "Creative Commons"
+or any other trademark or logo of Creative Commons without its prior written
+consent including, without limitation, in connection with any unauthorized
+modifications to any of its public licenses or any other arrangements, understandings,
+or agreements concerning use of licensed material. For the avoidance of doubt,
+this paragraph does not form part of the public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/LICENSES/FSFAP.txt b/LICENSES/FSFAP.txt
new file mode 100644 (file)
index 0000000..32bc8a8
--- /dev/null
@@ -0,0 +1 @@
+Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved.  This file is offered as-is, without any warranty.
diff --git a/LICENSES/FSFULLR.txt b/LICENSES/FSFULLR.txt
new file mode 100644 (file)
index 0000000..2acb219
--- /dev/null
@@ -0,0 +1,3 @@
+Copyright 1996-2006 Free Software Foundation, Inc.
+
+This file is free software; the Free Software Foundation gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved.
diff --git a/LICENSES/GPL-2.0 b/LICENSES/GPL-2.0
deleted file mode 100644 (file)
index cdd31bf..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-Valid-License-Identifier: GPL-2.0-only
-Valid-License-Identifier: GPL-2.0-or-later
-SPDX-URL: https://spdx.org/licenses/GPL-2.0.html
-Usage-Guide:
-  To use this license in source code, put one of the following SPDX
-  tag/value pairs into a comment according to the placement
-  guidelines in the licensing rules documentation.
-  For 'GNU General Public License (GPL) version 2 only' use:
-    SPDX-License-Identifier: GPL-2.0-only
-  For 'GNU General Public License (GPL) version 2 or any later version' use:
-    SPDX-License-Identifier: GPL-2.0-or-later
-License-Text:
-
-                   GNU GENERAL PUBLIC LICENSE
-                      Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                           Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-\f
-                   GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-\f
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-\f
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-\f
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                           NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-                    END OF TERMS AND CONDITIONS
-\f
-           How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
diff --git a/LICENSES/GPL-2.0-only.txt b/LICENSES/GPL-2.0-only.txt
new file mode 100644 (file)
index 0000000..17cb286
--- /dev/null
@@ -0,0 +1,117 @@
+GNU GENERAL PUBLIC LICENSE
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+     a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+
+     b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+
+     c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+     a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+     b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+     c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+     one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
+
+     This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
+
+     Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
+
+     Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
diff --git a/LICENSES/GPL-2.0-or-later.txt b/LICENSES/GPL-2.0-or-later.txt
new file mode 100644 (file)
index 0000000..17cb286
--- /dev/null
@@ -0,0 +1,117 @@
+GNU GENERAL PUBLIC LICENSE
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+     a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+
+     b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+
+     c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+     a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+     b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+     c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+     one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
+
+     This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
+
+     Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
+
+     Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
diff --git a/LICENSES/GPL-3.0 b/LICENSES/GPL-3.0
deleted file mode 100644 (file)
index 4c0b529..0000000
+++ /dev/null
@@ -1,638 +0,0 @@
-Valid-License-Identifier: GPL-3.0-only
-Valid-License-Identifier: GPL-3.0-or-later
-SPDX-URL: https://spdx.org/licenses/GPL-3.0.html
-Usage-Guide:
-  To use this license in source code, put one of the following SPDX
-  tag/value pairs into a comment according to the placement
-  guidelines in the licensing rules documentation.
-  For 'GNU General Public License (GPL) version 3 only' use:
-    SPDX-License-Identifier: GPL-3.0-only
-  For 'GNU General Public License (GPL) version 3 or any later version' use:
-    SPDX-License-Identifier: GPL-3.0-or-later
-License-Text:
-
-GNU GENERAL PUBLIC LICENSE
-
-Version 3, 29 June 2007
-
-Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
-
-Everyone is permitted to copy and distribute verbatim copies of this license
-document, but changing it is not allowed.
-
-Preamble
-
-The GNU General Public License is a free, copyleft license for software and
-other kinds of works.
-
-The licenses for most software and other practical works are designed to take
-away your freedom to share and change the works. By contrast, the GNU General
-Public License is intended to guarantee your freedom to share and change all
-versions of a program--to make sure it remains free software for all its users.
-We, the Free Software Foundation, use the GNU General Public License for most
-of our software; it applies also to any other work released this way by its
-authors. You can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our
-General Public Licenses are designed to make sure that you have the freedom
-to distribute copies of free software (and charge for them if you wish), that
-you receive source code or can get it if you want it, that you can change
-the software or use pieces of it in new free programs, and that you know you
-can do these things.
-
-To protect your rights, we need to prevent others from denying you these rights
-or asking you to surrender the rights. Therefore, you have certain responsibilities
-if you distribute copies of the software, or if you modify it: responsibilities
-to respect the freedom of others.
-
-For example, if you distribute copies of such a program, whether gratis or
-for a fee, you must pass on to the recipients the same freedoms that you received.
-You must make sure that they, too, receive or can get the source code. And
-you must show them these terms so they know their rights.
-
-Developers that use the GNU GPL protect your rights with two steps: (1) assert
-copyright on the software, and (2) offer you this License giving you legal
-permission to copy, distribute and/or modify it.
-
-For the developers' and authors' protection, the GPL clearly explains that
-there is no warranty for this free software. For both users' and authors'
-sake, the GPL requires that modified versions be marked as changed, so that
-their problems will not be attributed erroneously to authors of previous versions.
-
-Some devices are designed to deny users access to install or run modified
-versions of the software inside them, although the manufacturer can do so.
-This is fundamentally incompatible with the aim of protecting users' freedom
-to change the software. The systematic pattern of such abuse occurs in the
-area of products for individuals to use, which is precisely where it is most
-unacceptable. Therefore, we have designed this version of the GPL to prohibit
-the practice for those products. If such problems arise substantially in other
-domains, we stand ready to extend this provision to those domains in future
-versions of the GPL, as needed to protect the freedom of users.
-
-Finally, every program is threatened constantly by software patents. States
-should not allow patents to restrict development and use of software on general-purpose
-computers, but in those that do, we wish to avoid the special danger that
-patents applied to a free program could make it effectively proprietary. To
-prevent this, the GPL assures that patents cannot be used to render the program
-non-free.
-
-The precise terms and conditions for copying, distribution and modification
-follow.
-
-TERMS AND CONDITIONS
-
-   0. Definitions.
-
-   "This License" refers to version 3 of the GNU General Public License.
-
-"Copyright" also means copyright-like laws that apply to other kinds of works,
-such as semiconductor masks.
-
-"The Program" refers to any copyrightable work licensed under this License.
-Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals
-or organizations.
-
-To "modify" a work means to copy from or adapt all or part of the work in
-a fashion requiring copyright permission, other than the making of an exact
-copy. The resulting work is called a "modified version" of the earlier work
-or a work "based on" the earlier work.
-
-A "covered work" means either the unmodified Program or a work based on the
-Program.
-
-To "propagate" a work means to do anything with it that, without permission,
-would make you directly or secondarily liable for infringement under applicable
-copyright law, except executing it on a computer or modifying a private copy.
-Propagation includes copying, distribution (with or without modification),
-making available to the public, and in some countries other activities as
-well.
-
-To "convey" a work means any kind of propagation that enables other parties
-to make or receive copies. Mere interaction with a user through a computer
-network, with no transfer of a copy, is not conveying.
-
-An interactive user interface displays "Appropriate Legal Notices" to the
-extent that it includes a convenient and prominently visible feature that
-(1) displays an appropriate copyright notice, and (2) tells the user that
-there is no warranty for the work (except to the extent that warranties are
-provided), that licensees may convey the work under this License, and how
-to view a copy of this License. If the interface presents a list of user commands
-or options, such as a menu, a prominent item in the list meets this criterion.
-
-   1. Source Code.
-
-The "source code" for a work means the preferred form of the work for making
-modifications to it. "Object code" means any non-source form of a work.
-
-A "Standard Interface" means an interface that either is an official standard
-defined by a recognized standards body, or, in the case of interfaces specified
-for a particular programming language, one that is widely used among developers
-working in that language.
-
-The "System Libraries" of an executable work include anything, other than
-the work as a whole, that (a) is included in the normal form of packaging
-a Major Component, but which is not part of that Major Component, and (b)
-serves only to enable use of the work with that Major Component, or to implement
-a Standard Interface for which an implementation is available to the public
-in source code form. A "Major Component", in this context, means a major essential
-component (kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to produce
-the work, or an object code interpreter used to run it.
-
-The "Corresponding Source" for a work in object code form means all the source
-code needed to generate, install, and (for an executable work) run the object
-code and to modify the work, including scripts to control those activities.
-However, it does not include the work's System Libraries, or general-purpose
-tools or generally available free programs which are used unmodified in performing
-those activities but which are not part of the work. For example, Corresponding
-Source includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically linked
-subprograms that the work is specifically designed to require, such as by
-intimate data communication or control flow between those subprograms and
-other parts of the work.
-
-The Corresponding Source need not include anything that users can regenerate
-automatically from other parts of the Corresponding Source.
-
-   The Corresponding Source for a work in source code form is that same work.
-
-   2. Basic Permissions.
-
-All rights granted under this License are granted for the term of copyright
-on the Program, and are irrevocable provided the stated conditions are met.
-This License explicitly affirms your unlimited permission to run the unmodified
-Program. The output from running a covered work is covered by this License
-only if the output, given its content, constitutes a covered work. This License
-acknowledges your rights of fair use or other equivalent, as provided by copyright
-law.
-
-You may make, run and propagate covered works that you do not convey, without
-conditions so long as your license otherwise remains in force. You may convey
-covered works to others for the sole purpose of having them make modifications
-exclusively for you, or provide you with facilities for running those works,
-provided that you comply with the terms of this License in conveying all material
-for which you do not control copyright. Those thus making or running the covered
-works for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of your copyrighted
-material outside their relationship with you.
-
-Conveying under any other circumstances is permitted solely under the conditions
-stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
-
-   3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-No covered work shall be deemed part of an effective technological measure
-under any applicable law fulfilling obligations under article 11 of the WIPO
-copyright treaty adopted on 20 December 1996, or similar laws prohibiting
-or restricting circumvention of such measures.
-
-When you convey a covered work, you waive any legal power to forbid circumvention
-of technological measures to the extent such circumvention is effected by
-exercising rights under this License with respect to the covered work, and
-you disclaim any intention to limit operation or modification of the work
-as a means of enforcing, against the work's users, your or third parties'
-legal rights to forbid circumvention of technological measures.
-
-   4. Conveying Verbatim Copies.
-
-You may convey verbatim copies of the Program's source code as you receive
-it, in any medium, provided that you conspicuously and appropriately publish
-on each copy an appropriate copyright notice; keep intact all notices stating
-that this License and any non-permissive terms added in accord with section
-7 apply to the code; keep intact all notices of the absence of any warranty;
-and give all recipients a copy of this License along with the Program.
-
-You may charge any price or no price for each copy that you convey, and you
-may offer support or warranty protection for a fee.
-
-   5. Conveying Modified Source Versions.
-
-You may convey a work based on the Program, or the modifications to produce
-it from the Program, in the form of source code under the terms of section
-4, provided that you also meet all of these conditions:
-
-a) The work must carry prominent notices stating that you modified it, and
-giving a relevant date.
-
-b) The work must carry prominent notices stating that it is released under
-this License and any conditions added under section 7. This requirement modifies
-the requirement in section 4 to "keep intact all notices".
-
-c) You must license the entire work, as a whole, under this License to anyone
-who comes into possession of a copy. This License will therefore apply, along
-with any applicable section 7 additional terms, to the whole of the work,
-and all its parts, regardless of how they are packaged. This License gives
-no permission to license the work in any other way, but it does not invalidate
-such permission if you have separately received it.
-
-d) If the work has interactive user interfaces, each must display Appropriate
-Legal Notices; however, if the Program has interactive interfaces that do
-not display Appropriate Legal Notices, your work need not make them do so.
-
-A compilation of a covered work with other separate and independent works,
-which are not by their nature extensions of the covered work, and which are
-not combined with it such as to form a larger program, in or on a volume of
-a storage or distribution medium, is called an "aggregate" if the compilation
-and its resulting copyright are not used to limit the access or legal rights
-of the compilation's users beyond what the individual works permit. Inclusion
-of a covered work in an aggregate does not cause this License to apply to
-the other parts of the aggregate.
-
-   6. Conveying Non-Source Forms.
-
-You may convey a covered work in object code form under the terms of sections
-4 and 5, provided that you also convey the machine-readable Corresponding
-Source under the terms of this License, in one of these ways:
-
-a) Convey the object code in, or embodied in, a physical product (including
-a physical distribution medium), accompanied by the Corresponding Source fixed
-on a durable physical medium customarily used for software interchange.
-
-b) Convey the object code in, or embodied in, a physical product (including
-a physical distribution medium), accompanied by a written offer, valid for
-at least three years and valid for as long as you offer spare parts or customer
-support for that product model, to give anyone who possesses the object code
-either (1) a copy of the Corresponding Source for all the software in the
-product that is covered by this License, on a durable physical medium customarily
-used for software interchange, for a price no more than your reasonable cost
-of physically performing this conveying of source, or (2) access to copy the
-Corresponding Source from a network server at no charge.
-
-c) Convey individual copies of the object code with a copy of the written
-offer to provide the Corresponding Source. This alternative is allowed only
-occasionally and noncommercially, and only if you received the object code
-with such an offer, in accord with subsection 6b.
-
-d) Convey the object code by offering access from a designated place (gratis
-or for a charge), and offer equivalent access to the Corresponding Source
-in the same way through the same place at no further charge. You need not
-require recipients to copy the Corresponding Source along with the object
-code. If the place to copy the object code is a network server, the Corresponding
-Source may be on a different server (operated by you or a third party) that
-supports equivalent copying facilities, provided you maintain clear directions
-next to the object code saying where to find the Corresponding Source. Regardless
-of what server hosts the Corresponding Source, you remain obligated to ensure
-that it is available for as long as needed to satisfy these requirements.
-
-e) Convey the object code using peer-to-peer transmission, provided you inform
-other peers where the object code and Corresponding Source of the work are
-being offered to the general public at no charge under subsection 6d.
-
-A separable portion of the object code, whose source code is excluded from
-the Corresponding Source as a System Library, need not be included in conveying
-the object code work.
-
-A "User Product" is either (1) a "consumer product", which means any tangible
-personal property which is normally used for personal, family, or household
-purposes, or (2) anything designed or sold for incorporation into a dwelling.
-In determining whether a product is a consumer product, doubtful cases shall
-be resolved in favor of coverage. For a particular product received by a particular
-user, "normally used" refers to a typical or common use of that class of product,
-regardless of the status of the particular user or of the way in which the
-particular user actually uses, or expects or is expected to use, the product.
-A product is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent the
-only significant mode of use of the product.
-
-"Installation Information" for a User Product means any methods, procedures,
-authorization keys, or other information required to install and execute modified
-versions of a covered work in that User Product from a modified version of
-its Corresponding Source. The information must suffice to ensure that the
-continued functioning of the modified object code is in no case prevented
-or interfered with solely because modification has been made.
-
-If you convey an object code work under this section in, or with, or specifically
-for use in, a User Product, and the conveying occurs as part of a transaction
-in which the right of possession and use of the User Product is transferred
-to the recipient in perpetuity or for a fixed term (regardless of how the
-transaction is characterized), the Corresponding Source conveyed under this
-section must be accompanied by the Installation Information. But this requirement
-does not apply if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has been installed
-in ROM).
-
-The requirement to provide Installation Information does not include a requirement
-to continue to provide support service, warranty, or updates for a work that
-has been modified or installed by the recipient, or for the User Product in
-which it has been modified or installed. Access to a network may be denied
-when the modification itself materially and adversely affects the operation
-of the network or violates the rules and protocols for communication across
-the network.
-
-Corresponding Source conveyed, and Installation Information provided, in accord
-with this section must be in a format that is publicly documented (and with
-an implementation available to the public in source code form), and must require
-no special password or key for unpacking, reading or copying.
-
-   7. Additional Terms.
-
-"Additional permissions" are terms that supplement the terms of this License
-by making exceptions from one or more of its conditions. Additional permissions
-that are applicable to the entire Program shall be treated as though they
-were included in this License, to the extent that they are valid under applicable
-law. If additional permissions apply only to part of the Program, that part
-may be used separately under those permissions, but the entire Program remains
-governed by this License without regard to the additional permissions.
-
-When you convey a copy of a covered work, you may at your option remove any
-additional permissions from that copy, or from any part of it. (Additional
-permissions may be written to require their own removal in certain cases when
-you modify the work.) You may place additional permissions on material, added
-by you to a covered work, for which you have or can give appropriate copyright
-permission.
-
-Notwithstanding any other provision of this License, for material you add
-to a covered work, you may (if authorized by the copyright holders of that
-material) supplement the terms of this License with terms:
-
-a) Disclaiming warranty or limiting liability differently from the terms of
-sections 15 and 16 of this License; or
-
-b) Requiring preservation of specified reasonable legal notices or author
-attributions in that material or in the Appropriate Legal Notices displayed
-by works containing it; or
-
-c) Prohibiting misrepresentation of the origin of that material, or requiring
-that modified versions of such material be marked in reasonable ways as different
-from the original version; or
-
-d) Limiting the use for publicity purposes of names of licensors or authors
-of the material; or
-
-e) Declining to grant rights under trademark law for use of some trade names,
-trademarks, or service marks; or
-
-f) Requiring indemnification of licensors and authors of that material by
-anyone who conveys the material (or modified versions of it) with contractual
-assumptions of liability to the recipient, for any liability that these contractual
-assumptions directly impose on those licensors and authors.
-
-All other non-permissive additional terms are considered "further restrictions"
-within the meaning of section 10. If the Program as you received it, or any
-part of it, contains a notice stating that it is governed by this License
-along with a term that is a further restriction, you may remove that term.
-If a license document contains a further restriction but permits relicensing
-or conveying under this License, you may add to a covered work material governed
-by the terms of that license document, provided that the further restriction
-does not survive such relicensing or conveying.
-
-If you add terms to a covered work in accord with this section, you must place,
-in the relevant source files, a statement of the additional terms that apply
-to those files, or a notice indicating where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the form
-of a separately written license, or stated as exceptions; the above requirements
-apply either way.
-
-   8. Termination.
-
-You may not propagate or modify a covered work except as expressly provided
-under this License. Any attempt otherwise to propagate or modify it is void,
-and will automatically terminate your rights under this License (including
-any patent licenses granted under the third paragraph of section 11).
-
-However, if you cease all violation of this License, then your license from
-a particular copyright holder is reinstated (a) provisionally, unless and
-until the copyright holder explicitly and finally terminates your license,
-and (b) permanently, if the copyright holder fails to notify you of the violation
-by some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated permanently
-if the copyright holder notifies you of the violation by some reasonable means,
-this is the first time you have received notice of violation of this License
-(for any work) from that copyright holder, and you cure the violation prior
-to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses
-of parties who have received copies or rights from you under this License.
-If your rights have been terminated and not permanently reinstated, you do
-not qualify to receive new licenses for the same material under section 10.
-
-   9. Acceptance Not Required for Having Copies.
-
-You are not required to accept this License in order to receive or run a copy
-of the Program. Ancillary propagation of a covered work occurring solely as
-a consequence of using peer-to-peer transmission to receive a copy likewise
-does not require acceptance. However, nothing other than this License grants
-you permission to propagate or modify any covered work. These actions infringe
-copyright if you do not accept this License. Therefore, by modifying or propagating
-a covered work, you indicate your acceptance of this License to do so.
-
-   10. Automatic Licensing of Downstream Recipients.
-
-Each time you convey a covered work, the recipient automatically receives
-a license from the original licensors, to run, modify and propagate that work,
-subject to this License. You are not responsible for enforcing compliance
-by third parties with this License.
-
-An "entity transaction" is a transaction transferring control of an organization,
-or substantially all assets of one, or subdividing an organization, or merging
-organizations. If propagation of a covered work results from an entity transaction,
-each party to that transaction who receives a copy of the work also receives
-whatever licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the Corresponding
-Source of the work from the predecessor in interest, if the predecessor has
-it or can get it with reasonable efforts.
-
-You may not impose any further restrictions on the exercise of the rights
-granted or affirmed under this License. For example, you may not impose a
-license fee, royalty, or other charge for exercise of rights granted under
-this License, and you may not initiate litigation (including a cross-claim
-or counterclaim in a lawsuit) alleging that any patent claim is infringed
-by making, using, selling, offering for sale, or importing the Program or
-any portion of it.
-
-   11. Patents.
-
-A "contributor" is a copyright holder who authorizes use under this License
-of the Program or a work on which the Program is based. The work thus licensed
-is called the contributor's "contributor version".
-
-A contributor's "essential patent claims" are all patent claims owned or controlled
-by the contributor, whether already acquired or hereafter acquired, that would
-be infringed by some manner, permitted by this License, of making, using,
-or selling its contributor version, but do not include claims that would be
-infringed only as a consequence of further modification of the contributor
-version. For purposes of this definition, "control" includes the right to
-grant patent sublicenses in a manner consistent with the requirements of this
-License.
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free patent
-license under the contributor's essential patent claims, to make, use, sell,
-offer for sale, import and otherwise run, modify and propagate the contents
-of its contributor version.
-
-In the following three paragraphs, a "patent license" is any express agreement
-or commitment, however denominated, not to enforce a patent (such as an express
-permission to practice a patent or covenant not to sue for patent infringement).
-To "grant" such a patent license to a party means to make such an agreement
-or commitment not to enforce a patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license, and the
-Corresponding Source of the work is not available for anyone to copy, free
-of charge and under the terms of this License, through a publicly available
-network server or other readily accessible means, then you must either (1)
-cause the Corresponding Source to be so available, or (2) arrange to deprive
-yourself of the benefit of the patent license for this particular work, or
-(3) arrange, in a manner consistent with the requirements of this License,
-to extend the patent license to downstream recipients. "Knowingly relying"
-means you have actual knowledge that, but for the patent license, your conveying
-the covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that country
-that you have reason to believe are valid.
-
-If, pursuant to or in connection with a single transaction or arrangement,
-you convey, or propagate by procuring conveyance of, a covered work, and grant
-a patent license to some of the parties receiving the covered work authorizing
-them to use, propagate, modify or convey a specific copy of the covered work,
-then the patent license you grant is automatically extended to all recipients
-of the covered work and works based on it.
-
-A patent license is "discriminatory" if it does not include within the scope
-of its coverage, prohibits the exercise of, or is conditioned on the non-exercise
-of one or more of the rights that are specifically granted under this License.
-You may not convey a covered work if you are a party to an arrangement with
-a third party that is in the business of distributing software, under which
-you make payment to the third party based on the extent of your activity of
-conveying the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory patent
-license (a) in connection with copies of the covered work conveyed by you
-(or copies made from those copies), or (b) primarily for and in connection
-with specific products or compilations that contain the covered work, unless
-you entered into that arrangement, or that patent license was granted, prior
-to 28 March 2007.
-
-Nothing in this License shall be construed as excluding or limiting any implied
-license or other defenses to infringement that may otherwise be available
-to you under applicable patent law.
-
-   12. No Surrender of Others' Freedom.
-
-If conditions are imposed on you (whether by court order, agreement or otherwise)
-that contradict the conditions of this License, they do not excuse you from
-the conditions of this License. If you cannot convey a covered work so as
-to satisfy simultaneously your obligations under this License and any other
-pertinent obligations, then as a consequence you may not convey it at all.
-For example, if you agree to terms that obligate you to collect a royalty
-for further conveying from those to whom you convey the Program, the only
-way you could satisfy both those terms and this License would be to refrain
-entirely from conveying the Program.
-
-   13. Use with the GNU Affero General Public License.
-
-Notwithstanding any other provision of this License, you have permission to
-link or combine any covered work with a work licensed under version 3 of the
-GNU Affero General Public License into a single combined work, and to convey
-the resulting work. The terms of this License will continue to apply to the
-part which is the covered work, but the special requirements of the GNU Affero
-General Public License, section 13, concerning interaction through a network
-will apply to the combination as such.
-
-   14. Revised Versions of this License.
-
-The Free Software Foundation may publish revised and/or new versions of the
-GNU General Public License from time to time. Such new versions will be similar
-in spirit to the present version, but may differ in detail to address new
-problems or concerns.
-
-Each version is given a distinguishing version number. If the Program specifies
-that a certain numbered version of the GNU General Public License "or any
-later version" applies to it, you have the option of following the terms and
-conditions either of that numbered version or of any later version published
-by the Free Software Foundation. If the Program does not specify a version
-number of the GNU General Public License, you may choose any version ever
-published by the Free Software Foundation.
-
-If the Program specifies that a proxy can decide which future versions of
-the GNU General Public License can be used, that proxy's public statement
-of acceptance of a version permanently authorizes you to choose that version
-for the Program.
-
-Later license versions may give you additional or different permissions. However,
-no additional obligations are imposed on any author or copyright holder as
-a result of your choosing to follow a later version.
-
-   15. Disclaimer of Warranty.
-
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
-LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
-EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
-PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
-CORRECTION.
-
-   16. Limitation of Liability.
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
-ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM
-AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
-INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO
-USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
-INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
-PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
-PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-   17. Interpretation of Sections 15 and 16.
-
-If the disclaimer of warranty and limitation of liability provided above cannot
-be given local legal effect according to their terms, reviewing courts shall
-apply local law that most closely approximates an absolute waiver of all civil
-liability in connection with the Program, unless a warranty or assumption
-of liability accompanies a copy of the Program in return for a fee. END OF
-TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible
-use to the public, the best way to achieve this is to make it free software
-which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to attach
-them to the start of each source file to most effectively state the exclusion
-of warranty; and each file should have at least the "copyright" line and a
-pointer to where the full notice is found.
-
-<one line to give the program's name and a brief idea of what it does.>
-
-Copyright (C) <year> <name of author>
-
-This program is free software: you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later
-version.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with
-this program. If not, see <https://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program does terminal interaction, make it output a short notice like
-this when it starts in an interactive mode:
-
-<program> Copyright (C) <year> <name of author>
-
-This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-
-This is free software, and you are welcome to redistribute it under certain
-conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands might
-be different; for a GUI interface, you would use an "about box".
-
-You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary. For
-more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
-
-The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Lesser General Public
-License instead of this License. But first, please read <https://www.gnu.org/
-licenses /why-not-lgpl.html>.
diff --git a/LICENSES/GPL-3.0-or-later.txt b/LICENSES/GPL-3.0-or-later.txt
new file mode 100644 (file)
index 0000000..f6cdd22
--- /dev/null
@@ -0,0 +1,232 @@
+GNU GENERAL PUBLIC LICENSE
+Version 3, 29 June 2007
+
+Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The GNU General Public License is a free, copyleft license for software and other kinds of works.
+
+The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
+
+To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.
+
+For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
+
+Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
+
+Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+TERMS AND CONDITIONS
+
+0. Definitions.
+
+“This License” refers to version 3 of the GNU General Public License.
+
+“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
+
+“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.
+
+To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.
+
+A “covered work” means either the unmodified Program or a work based on the Program.
+
+To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
+
+To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
+
+An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
+
+1. Source Code.
+The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.
+
+A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
+
+The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
+
+The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
+
+The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same work.
+
+2. Basic Permissions.
+All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
+
+You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
+
+3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
+
+When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
+
+4. Conveying Verbatim Copies.
+You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
+
+5. Conveying Modified Source Versions.
+You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
+
+     a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
+
+     b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
+
+     c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
+
+     d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
+
+A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
+
+6. Conveying Non-Source Forms.
+You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
+
+     a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
+
+     b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
+
+     c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
+
+     d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
+
+     e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
+
+A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
+
+“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
+
+The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
+
+Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
+
+7. Additional Terms.
+“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
+
+Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
+
+     a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
+
+     b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
+
+     c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
+
+     d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
+
+     e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
+
+     f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
+
+All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
+
+8. Termination.
+You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
+
+However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
+
+Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
+
+9. Acceptance Not Required for Having Copies.
+You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
+
+10. Automatic Licensing of Downstream Recipients.
+Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
+
+An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
+
+11. Patents.
+A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.
+
+A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
+
+In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
+
+A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
+
+12. No Surrender of Others' Freedom.
+If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
+
+13. Use with the GNU Affero General Public License.
+Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.
+
+14. Revised Versions of this License.
+The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
+
+Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
+
+15. Disclaimer of Warranty.
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+16. Limitation of Liability.
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+17. Interpretation of Sections 15 and 16.
+If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
+
+     <one line to give the program's name and a brief idea of what it does.>
+     Copyright (C) <year>  <name of author>
+
+     This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public License along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
+
+     <program>  Copyright (C) <year>  <name of author>
+     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+     This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.
+
+You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
+
+The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <https://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/LICENSES/LGPL-2.1 b/LICENSES/LGPL-2.1
deleted file mode 100644 (file)
index 8738a8d..0000000
+++ /dev/null
@@ -1,503 +0,0 @@
-Valid-License-Identifier: LGPL-2.1-only
-Valid-License-Identifier: LGPL-2.1-or-later
-SPDX-URL: https://spdx.org/licenses/LGPL-2.1.html
-Usage-Guide:
-  To use this license in source code, put one of the following SPDX
-  tag/value pairs into a comment according to the placement
-  guidelines in the licensing rules documentation.
-  For 'GNU Lesser General Public License (LGPL) version 2.1 only' use:
-    SPDX-License-Identifier: LGPL-2.1-only
-  For 'GNU Lesser General Public License (LGPL) version 2.1 or any later
-  version' use:
-    SPDX-License-Identifier: LGPL-2.1-or-later
-License-Text:
-
-GNU LESSER GENERAL PUBLIC LICENSE
-Version 2.1, February 1999
-
-Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this
-license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL. It also counts as
-the successor of the GNU Library Public License, version 2, hence the
-version number 2.1.]
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to
-share and change it. By contrast, the GNU General Public Licenses are
-intended to guarantee your freedom to share and change free software--to
-make sure the software is free for all its users.
-
-This license, the Lesser General Public License, applies to some specially
-designated software packages--typically libraries--of the Free Software
-Foundation and other authors who decide to use it. You can use it too, but
-we suggest you first think carefully about whether this license or the
-ordinary General Public License is the better strategy to use in any
-particular case, based on the explanations below.
-
-When we speak of free software, we are referring to freedom of use, not
-price. Our General Public Licenses are designed to make sure that you have
-the freedom to distribute copies of free software (and charge for this
-service if you wish); that you receive source code or can get it if you
-want it; that you can change the software and use pieces of it in new free
-programs; and that you are informed that you can do these things.
-
-To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights. These restrictions translate to certain responsibilities for you if
-you distribute copies of the library or if you modify it.
-
-For example, if you distribute copies of the library, whether gratis or for
-a fee, you must give the recipients all the rights that we gave you. You
-must make sure that they, too, receive or can get the source code. If you
-link other code with the library, you must provide complete object files to
-the recipients, so that they can relink them with the library after making
-changes to the library and recompiling it. And you must show them these
-terms so they know their rights.
-
-We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-To protect each distributor, we want to make it very clear that there is no
-warranty for the free library. Also, if the library is modified by someone
-else and passed on, the recipients should know that what they have is not
-the original version, so that the original author's reputation will not be
-affected by problems that might be introduced by others.
-
-Finally, software patents pose a constant threat to the existence of any
-free program. We wish to make sure that a company cannot effectively
-restrict the users of a free program by obtaining a restrictive license
-from a patent holder. Therefore, we insist that any patent license obtained
-for a version of the library must be consistent with the full freedom of
-use specified in this license.
-
-Most GNU software, including some libraries, is covered by the ordinary GNU
-General Public License. This license, the GNU Lesser General Public
-License, applies to certain designated libraries, and is quite different
-from the ordinary General Public License. We use this license for certain
-libraries in order to permit linking those libraries into non-free
-programs.
-
-When a program is linked with a library, whether statically or using a
-shared library, the combination of the two is legally speaking a combined
-work, a derivative of the original library. The ordinary General Public
-License therefore permits such linking only if the entire combination fits
-its criteria of freedom. The Lesser General Public License permits more lax
-criteria for linking other code with the library.
-
-We call this license the "Lesser" General Public License because it does
-Less to protect the user's freedom than the ordinary General Public
-License. It also provides other free software developers Less of an
-advantage over competing non-free programs. These disadvantages are the
-reason we use the ordinary General Public License for many
-libraries. However, the Lesser license provides advantages in certain
-special circumstances.
-
-For example, on rare occasions, there may be a special need to encourage
-the widest possible use of a certain library, so that it becomes a de-facto
-standard. To achieve this, non-free programs must be allowed to use the
-library. A more frequent case is that a free library does the same job as
-widely used non-free libraries. In this case, there is little to gain by
-limiting the free library to free software only, so we use the Lesser
-General Public License.
-
-In other cases, permission to use a particular library in non-free programs
-enables a greater number of people to use a large body of free
-software. For example, permission to use the GNU C Library in non-free
-programs enables many more people to use the whole GNU operating system, as
-well as its variant, the GNU/Linux operating system.
-
-Although the Lesser General Public License is Less protective of the users'
-freedom, it does ensure that the user of a program that is linked with the
-Library has the freedom and the wherewithal to run that program using a
-modified version of the Library.
-
-The precise terms and conditions for copying, distribution and modification
-follow. Pay close attention to the difference between a "work based on the
-library" and a "work that uses the library". The former contains code
-derived from the library, whereas the latter must be combined with the
-library in order to run.
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License Agreement applies to any software library or other program
-   which contains a notice placed by the copyright holder or other
-   authorized party saying it may be distributed under the terms of this
-   Lesser General Public License (also called "this License"). Each
-   licensee is addressed as "you".
-
-   A "library" means a collection of software functions and/or data
-   prepared so as to be conveniently linked with application programs
-   (which use some of those functions and data) to form executables.
-
-   The "Library", below, refers to any such software library or work which
-   has been distributed under these terms. A "work based on the Library"
-   means either the Library or any derivative work under copyright law:
-   that is to say, a work containing the Library or a portion of it, either
-   verbatim or with modifications and/or translated straightforwardly into
-   another language. (Hereinafter, translation is included without
-   limitation in the term "modification".)
-
-   "Source code" for a work means the preferred form of the work for making
-   modifications to it. For a library, complete source code means all the
-   source code for all modules it contains, plus any associated interface
-   definition files, plus the scripts used to control compilation and
-   installation of the library.
-
-    Activities other than copying, distribution and modification are not
-    covered by this License; they are outside its scope. The act of running
-    a program using the Library is not restricted, and output from such a
-    program is covered only if its contents constitute a work based on the
-    Library (independent of the use of the Library in a tool for writing
-    it). Whether that is true depends on what the Library does and what the
-    program that uses the Library does.
-
-1. You may copy and distribute verbatim copies of the Library's complete
-   source code as you receive it, in any medium, provided that you
-   conspicuously and appropriately publish on each copy an appropriate
-   copyright notice and disclaimer of warranty; keep intact all the notices
-   that refer to this License and to the absence of any warranty; and
-   distribute a copy of this License along with the Library.
-
-   You may charge a fee for the physical act of transferring a copy, and
-   you may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Library or any portion of it,
-   thus forming a work based on the Library, and copy and distribute such
-   modifications or work under the terms of Section 1 above, provided that
-   you also meet all of these conditions:
-
-   a) The modified work must itself be a software library.
-
-   b) You must cause the files modified to carry prominent notices stating
-      that you changed the files and the date of any change.
-
-   c) You must cause the whole of the work to be licensed at no charge to
-      all third parties under the terms of this License.
-
-   d) If a facility in the modified Library refers to a function or a table
-      of data to be supplied by an application program that uses the
-      facility, other than as an argument passed when the facility is
-      invoked, then you must make a good faith effort to ensure that, in
-      the event an application does not supply such function or table, the
-      facility still operates, and performs whatever part of its purpose
-      remains meaningful.
-
-   (For example, a function in a library to compute square roots has a
-    purpose that is entirely well-defined independent of the
-    application. Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must be
-    optional: if the application does not supply it, the square root
-    function must still compute square roots.)
-
-   These requirements apply to the modified work as a whole. If
-   identifiable sections of that work are not derived from the Library, and
-   can be reasonably considered independent and separate works in
-   themselves, then this License, and its terms, do not apply to those
-   sections when you distribute them as separate works. But when you
-   distribute the same sections as part of a whole which is a work based on
-   the Library, the distribution of the whole must be on the terms of this
-   License, whose permissions for other licensees extend to the entire
-   whole, and thus to each and every part regardless of who wrote it.
-
-   Thus, it is not the intent of this section to claim rights or contest
-   your rights to work written entirely by you; rather, the intent is to
-   exercise the right to control the distribution of derivative or
-   collective works based on the Library.
-
-   In addition, mere aggregation of another work not based on the Library
-   with the Library (or with a work based on the Library) on a volume of a
-   storage or distribution medium does not bring the other work under the
-   scope of this License.
-
-3. You may opt to apply the terms of the ordinary GNU General Public
-   License instead of this License to a given copy of the Library. To do
-   this, you must alter all the notices that refer to this License, so that
-   they refer to the ordinary GNU General Public License, version 2,
-   instead of to this License. (If a newer version than version 2 of the
-   ordinary GNU General Public License has appeared, then you can specify
-   that version instead if you wish.) Do not make any other change in these
-   notices.
-
-   Once this change is made in a given copy, it is irreversible for that
-   copy, so the ordinary GNU General Public License applies to all
-   subsequent copies and derivative works made from that copy.
-
-   This option is useful when you wish to copy part of the code of the
-   Library into a program that is not a library.
-
-4. You may copy and distribute the Library (or a portion or derivative of
-   it, under Section 2) in object code or executable form under the terms
-   of Sections 1 and 2 above provided that you accompany it with the
-   complete corresponding machine-readable source code, which must be
-   distributed under the terms of Sections 1 and 2 above on a medium
-   customarily used for software interchange.
-
-   If distribution of object code is made by offering access to copy from a
-   designated place, then offering equivalent access to copy the source
-   code from the same place satisfies the requirement to distribute the
-   source code, even though third parties are not compelled to copy the
-   source along with the object code.
-
-5. A program that contains no derivative of any portion of the Library, but
-   is designed to work with the Library by being compiled or linked with
-   it, is called a "work that uses the Library". Such a work, in isolation,
-   is not a derivative work of the Library, and therefore falls outside the
-   scope of this License.
-
-   However, linking a "work that uses the Library" with the Library creates
-   an executable that is a derivative of the Library (because it contains
-   portions of the Library), rather than a "work that uses the
-   library". The executable is therefore covered by this License. Section 6
-   states terms for distribution of such executables.
-
-   When a "work that uses the Library" uses material from a header file
-   that is part of the Library, the object code for the work may be a
-   derivative work of the Library even though the source code is
-   not. Whether this is true is especially significant if the work can be
-   linked without the Library, or if the work is itself a library. The
-   threshold for this to be true is not precisely defined by law.
-
-   If such an object file uses only numerical parameters, data structure
-   layouts and accessors, and small macros and small inline functions (ten
-   lines or less in length), then the use of the object file is
-   unrestricted, regardless of whether it is legally a derivative
-   work. (Executables containing this object code plus portions of the
-   Library will still fall under Section 6.)
-
-   Otherwise, if the work is a derivative of the Library, you may
-   distribute the object code for the work under the terms of Section
-   6. Any executables containing that work also fall under Section 6,
-   whether or not they are linked directly with the Library itself.
-
-6. As an exception to the Sections above, you may also combine or link a
-   "work that uses the Library" with the Library to produce a work
-   containing portions of the Library, and distribute that work under terms
-   of your choice, provided that the terms permit modification of the work
-   for the customer's own use and reverse engineering for debugging such
-   modifications.
-
-   You must give prominent notice with each copy of the work that the
-   Library is used in it and that the Library and its use are covered by
-   this License. You must supply a copy of this License. If the work during
-   execution displays copyright notices, you must include the copyright
-   notice for the Library among them, as well as a reference directing the
-   user to the copy of this License. Also, you must do one of these things:
-
-   a) Accompany the work with the complete corresponding machine-readable
-      source code for the Library including whatever changes were used in
-      the work (which must be distributed under Sections 1 and 2 above);
-      and, if the work is an executable linked with the Library, with the
-      complete machine-readable "work that uses the Library", as object
-      code and/or source code, so that the user can modify the Library and
-      then relink to produce a modified executable containing the modified
-      Library. (It is understood that the user who changes the contents of
-      definitions files in the Library will not necessarily be able to
-      recompile the application to use the modified definitions.)
-
-   b) Use a suitable shared library mechanism for linking with the
-      Library. A suitable mechanism is one that (1) uses at run time a copy
-      of the library already present on the user's computer system, rather
-      than copying library functions into the executable, and (2) will
-      operate properly with a modified version of the library, if the user
-      installs one, as long as the modified version is interface-compatible
-      with the version that the work was made with.
-
-   c) Accompany the work with a written offer, valid for at least three
-      years, to give the same user the materials specified in Subsection
-      6a, above, for a charge no more than the cost of performing this
-      distribution.
-
-   d) If distribution of the work is made by offering access to copy from a
-      designated place, offer equivalent access to copy the above specified
-      materials from the same place.
-
-   e) Verify that the user has already received a copy of these materials
-      or that you have already sent this user a copy.
-
-   For an executable, the required form of the "work that uses the Library"
-   must include any data and utility programs needed for reproducing the
-   executable from it. However, as a special exception, the materials to be
-   distributed need not include anything that is normally distributed (in
-   either source or binary form) with the major components (compiler,
-   kernel, and so on) of the operating system on which the executable runs,
-   unless that component itself accompanies the executable.
-
-   It may happen that this requirement contradicts the license restrictions
-   of other proprietary libraries that do not normally accompany the
-   operating system. Such a contradiction means you cannot use both them
-   and the Library together in an executable that you distribute.
-
-7. You may place library facilities that are a work based on the Library
-   side-by-side in a single library together with other library facilities
-   not covered by this License, and distribute such a combined library,
-   provided that the separate distribution of the work based on the Library
-   and of the other library facilities is otherwise permitted, and provided
-   that you do these two things:
-
-   a) Accompany the combined library with a copy of the same work based on
-      the Library, uncombined with any other library facilities. This must
-      be distributed under the terms of the Sections above.
-
-   b) Give prominent notice with the combined library of the fact that part
-      of it is a work based on the Library, and explaining where to find
-      the accompanying uncombined form of the same work.
-
-8. You may not copy, modify, sublicense, link with, or distribute the
-   Library except as expressly provided under this License. Any attempt
-   otherwise to copy, modify, sublicense, link with, or distribute the
-   Library is void, and will automatically terminate your rights under this
-   License. However, parties who have received copies, or rights, from you
-   under this License will not have their licenses terminated so long as
-   such parties remain in full compliance.
-
-9. You are not required to accept this License, since you have not signed
-   it. However, nothing else grants you permission to modify or distribute
-   the Library or its derivative works. These actions are prohibited by law
-   if you do not accept this License. Therefore, by modifying or
-   distributing the Library (or any work based on the Library), you
-   indicate your acceptance of this License to do so, and all its terms and
-   conditions for copying, distributing or modifying the Library or works
-   based on it.
-
-10. Each time you redistribute the Library (or any work based on the
-    Library), the recipient automatically receives a license from the
-    original licensor to copy, distribute, link with or modify the Library
-    subject to these terms and conditions. You may not impose any further
-    restrictions on the recipients' exercise of the rights granted
-    herein. You are not responsible for enforcing compliance by third
-    parties with this License.
-
-11. If, as a consequence of a court judgment or allegation of patent
-    infringement or for any other reason (not limited to patent issues),
-    conditions are imposed on you (whether by court order, agreement or
-    otherwise) that contradict the conditions of this License, they do not
-    excuse you from the conditions of this License. If you cannot
-    distribute so as to satisfy simultaneously your obligations under this
-    License and any other pertinent obligations, then as a consequence you
-    may not distribute the Library at all. For example, if a patent license
-    would not permit royalty-free redistribution of the Library by all
-    those who receive copies directly or indirectly through you, then the
-    only way you could satisfy both it and this License would be to refrain
-    entirely from distribution of the Library.
-
-    If any portion of this section is held invalid or unenforceable under
-    any particular circumstance, the balance of the section is intended to
-    apply, and the section as a whole is intended to apply in other
-    circumstances.
-
-    It is not the purpose of this section to induce you to infringe any
-    patents or other property right claims or to contest validity of any
-    such claims; this section has the sole purpose of protecting the
-    integrity of the free software distribution system which is implemented
-    by public license practices. Many people have made generous
-    contributions to the wide range of software distributed through that
-    system in reliance on consistent application of that system; it is up
-    to the author/donor to decide if he or she is willing to distribute
-    software through any other system and a licensee cannot impose that
-    choice.
-
-    This section is intended to make thoroughly clear what is believed to
-    be a consequence of the rest of this License.
-
-12. If the distribution and/or use of the Library is restricted in certain
-    countries either by patents or by copyrighted interfaces, the original
-    copyright holder who places the Library under this License may add an
-    explicit geographical distribution limitation excluding those
-    countries, so that distribution is permitted only in or among countries
-    not thus excluded. In such case, this License incorporates the
-    limitation as if written in the body of this License.
-
-13. The Free Software Foundation may publish revised and/or new versions of
-    the Lesser General Public License from time to time. Such new versions
-    will be similar in spirit to the present version, but may differ in
-    detail to address new problems or concerns.
-
-    Each version is given a distinguishing version number. If the Library
-    specifies a version number of this License which applies to it and "any
-    later version", you have the option of following the terms and
-    conditions either of that version or of any later version published by
-    the Free Software Foundation. If the Library does not specify a license
-    version number, you may choose any version ever published by the Free
-    Software Foundation.
-
-14. If you wish to incorporate parts of the Library into other free
-    programs whose distribution conditions are incompatible with these,
-    write to the author to ask for permission. For software which is
-    copyrighted by the Free Software Foundation, write to the Free Software
-    Foundation; we sometimes make exceptions for this. Our decision will be
-    guided by the two goals of preserving the free status of all
-    derivatives of our free software and of promoting the sharing and reuse
-    of software generally.
-
-NO WARRANTY
-
-15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-    FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-    OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-    PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
-    EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
-    ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH
-    YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
-    NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-    REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
-    DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
-    DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY
-    (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
-    INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
-    THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR
-    OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Libraries
-
-If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change. You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
-To apply these terms, attach the following notices to the library. It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-one line to give the library's name and an idea of what it does.
-Copyright (C) year name of author
-
-This library is free software; you can redistribute it and/or modify it
-under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or (at
-your option) any later version.
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
-for more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation,
-Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add
-information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary. Here is a sample; alter the names:
-
-Yoyodyne, Inc., hereby disclaims all copyright interest in
-the library `Frob' (a library for tweaking knobs) written
-by James Random Hacker.
-
-signature of Ty Coon, 1 April 1990
-Ty Coon, President of Vice
-That's all there is to it!
diff --git a/LICENSES/LGPL-2.1-only.txt b/LICENSES/LGPL-2.1-only.txt
new file mode 100644 (file)
index 0000000..c9aa530
--- /dev/null
@@ -0,0 +1,175 @@
+GNU LESSER GENERAL PUBLIC LICENSE
+
+Version 2.1, February 1999
+
+Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.]
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.
+
+This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.
+
+When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things.
+
+To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.
+
+For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights.
+
+We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.
+
+To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others.
+
+Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.
+
+Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs.
+
+When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library.
+
+We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances.
+
+For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.
+
+In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.
+
+Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.
+
+The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you".
+
+A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.
+
+The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)
+
+"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.
+
+1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+     a) The modified work must itself be a software library.
+
+     b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
+
+     c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
+
+     d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.
+
+(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.
+
+Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.
+
+This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
+
+4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.
+
+If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.
+
+5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.
+
+However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.
+
+When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.
+
+If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)
+
+Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.
+
+6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.
+
+You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
+
+     a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)
+
+     b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with.
+
+     c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
+
+     d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
+
+     e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
+
+For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.
+
+7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:
+
+     a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.
+
+     b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
+
+8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.
+
+10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License.
+
+11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
+
+14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+
+If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).
+
+To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+     one line to give the library's name and an idea of what it does.
+     Copyright (C) year  name of author
+
+     This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+the library `Frob' (a library for tweaking knobs) written
+by James Random Hacker.
+
+signature of Ty Coon, 1 April 1990
+Ty Coon, President of Vice
+That's all there is to it!
diff --git a/LICENSES/LicenseRef-Autoconf-exception-macro.txt b/LICENSES/LicenseRef-Autoconf-exception-macro.txt
new file mode 100644 (file)
index 0000000..8b5b467
--- /dev/null
@@ -0,0 +1,12 @@
+As a special exception, the respective Autoconf Macro's copyright owner
+gives unlimited permission to copy, distribute and modify the configure
+scripts that are the output of Autoconf when processing the Macro. You
+need not follow the terms of the GNU General Public License when using
+or distributing such scripts, even though portions of the text of the
+Macro appear in them. The GNU General Public License (GPL) does govern
+all other use of the material that constitutes the Autoconf Macro.
+
+This special exception to the GPL applies to versions of the Autoconf
+Macro released by the Autoconf Archive. When you make and distribute a
+modified version of the Autoconf Macro, you may extend this special
+exception to the GPL to apply to your modified version as well.
diff --git a/LICENSES/MIT b/LICENSES/MIT
deleted file mode 100644 (file)
index f33a68c..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-Valid-License-Identifier: MIT
-SPDX-URL: https://spdx.org/licenses/MIT.html
-Usage-Guide:
-  To use the MIT License put the following SPDX tag/value pair into a
-  comment according to the placement guidelines in the licensing rules
-  documentation:
-    SPDX-License-Identifier: MIT
-License-Text:
-
-MIT License
-
-Copyright (c) <year> <copyright holders>
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt
new file mode 100644 (file)
index 0000000..f33a68c
--- /dev/null
@@ -0,0 +1,30 @@
+Valid-License-Identifier: MIT
+SPDX-URL: https://spdx.org/licenses/MIT.html
+Usage-Guide:
+  To use the MIT License put the following SPDX tag/value pair into a
+  comment according to the placement guidelines in the licensing rules
+  documentation:
+    SPDX-License-Identifier: MIT
+License-Text:
+
+MIT License
+
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/LICENSES/PSF-2.0.txt b/LICENSES/PSF-2.0.txt
new file mode 100644 (file)
index 0000000..1df6b3b
--- /dev/null
@@ -0,0 +1,254 @@
+A. HISTORY OF THE SOFTWARE
+==========================
+
+Python was created in the early 1990s by Guido van Rossum at Stichting
+Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
+as a successor of a language called ABC.  Guido remains Python's
+principal author, although it includes many contributions from others.
+
+In 1995, Guido continued his work on Python at the Corporation for
+National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
+in Reston, Virginia where he released several versions of the
+software.
+
+In May 2000, Guido and the Python core development team moved to
+BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
+year, the PythonLabs team moved to Digital Creations, which became
+Zope Corporation.  In 2001, the Python Software Foundation (PSF, see
+https://www.python.org/psf/) was formed, a non-profit organization
+created specifically to own Python-related Intellectual Property.
+Zope Corporation was a sponsoring member of the PSF.
+
+All Python releases are Open Source (see http://www.opensource.org for
+the Open Source Definition).  Historically, most, but not all, Python
+releases have also been GPL-compatible; the table below summarizes
+the various releases.
+
+    Release         Derived     Year        Owner       GPL-
+                    from                                compatible? (1)
+
+    0.9.0 thru 1.2              1991-1995   CWI         yes
+    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
+    1.6             1.5.2       2000        CNRI        no
+    2.0             1.6         2000        BeOpen.com  no
+    1.6.1           1.6         2001        CNRI        yes (2)
+    2.1             2.0+1.6.1   2001        PSF         no
+    2.0.1           2.0+1.6.1   2001        PSF         yes
+    2.1.1           2.1+2.0.1   2001        PSF         yes
+    2.1.2           2.1.1       2002        PSF         yes
+    2.1.3           2.1.2       2002        PSF         yes
+    2.2 and above   2.1.1       2001-now    PSF         yes
+
+Footnotes:
+
+(1) GPL-compatible doesn't mean that we're distributing Python under
+    the GPL.  All Python licenses, unlike the GPL, let you distribute
+    a modified version without making your changes open source.  The
+    GPL-compatible licenses make it possible to combine Python with
+    other software that is released under the GPL; the others don't.
+
+(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
+    because its license has a choice of law clause.  According to
+    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
+    is "not incompatible" with the GPL.
+
+Thanks to the many outside volunteers who have worked under Guido's
+direction to make these releases possible.
+
+
+B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
+===============================================================
+
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation;
+All Rights Reserved" are retained in Python alone or in any derivative version
+prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee.  This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions.  Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee.  This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party.  As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee.  Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement.  This Agreement together with
+Python 1.6.1 may be located on the internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013.  This
+Agreement may also be obtained from a proxy server on the internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee.  This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+        ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands.  All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
index 642466fa21c93dce6d47a34a2ea7c8150eebc55e..0f4493b537c9c6680b7868c172b35e809a3c70d5 100644 (file)
@@ -17,14 +17,21 @@ dist_doc_DATA = \
 dist_noinst_DATA = CodingStyle
 
 EXTRA_DIST = \
-       LICENSES/BSD-2-Clause \
-       LICENSES/BSD-4-Clause \
-       LICENSES/BSL-1.0 \
-       LICENSES/CC-BY-SA-4.0 \
-       LICENSES/GPL-2.0 \
-       LICENSES/GPL-3.0 \
-       LICENSES/LGPL-2.1 \
-       LICENSES/MIT \
+       LICENSES/BSD-2-Clause.txt \
+       LICENSES/BSD-4-Clause.txt \
+       LICENSES/BSL-1.0.txt \
+       LICENSES/CC-BY-SA-4.0.txt \
+       LICENSES/FSFAP.txt \
+       LICENSES/FSFULLR.txt \
+       LICENSES/GPL-2.0-only.txt \
+       LICENSES/GPL-2.0-or-later.txt \
+       LICENSES/GPL-3.0-or-later.txt \
+       LICENSES/LGPL-2.1-only.txt \
+       LICENSES/LicenseRef-Autoconf-exception-macro.txt \
+       LICENSES/MIT.txt \
+       LICENSES/PSF-2.0.txt \
        std-ext-lib.txt \
-       tools/format-cpp \
+       tools/format-cpp.sh \
+       tools/lint-py.sh \
+       tools/shellcheck.sh \
        version
index d43246ae43ddde43fc6d39a60024ec3bf6dff5e6..5c7c48e46b2cce3d6692d77d1c86c01c5ee4e737 100644 (file)
@@ -1,17 +1,22 @@
 // Render with Asciidoctor
 
-= Babeltrace
-13 April 2020
+= Babeltrace 2
+4 March 2024
 :btversion: 2.0
 :bt2: Babeltrace{nbsp}2
-
+ifdef::env-github[]
+:toc: macro
+endif::[]
+ifndef::env-github[]
+:toc: left
+endif::[]
 
 Babeltrace /ˈbæbəltreɪs/, an
 https://efficios.com/[EfficiOS] project, is an open-source
 https://en.wikipedia.org/wiki/Tracing_(software)[trace]
 manipulation framework.
 
-https://ci.lttng.org/job/babeltrace_master_build[image:https://img.shields.io/jenkins/s/https/ci.lttng.org/babeltrace_master_build.svg[]]
+https://ci.lttng.org/job/babeltrace_master_linuxbuild[image:https://img.shields.io/jenkins/s/https/ci.lttng.org/babeltrace_master_linuxbuild.svg[]]
 https://scan.coverity.com/projects/babeltrace[image:https://img.shields.io/coverity/scan/babeltrace.svg[]]
 
 The **_{bt2}_** project offers a library with a
@@ -22,34 +27,45 @@ https://babeltrace.org/docs/v{btversion}/man1/babeltrace2.1/[command-line tool]
 (CLI) which makes it very easy for mere mortals to view, convert,
 transform, and analyze traces.
 
+image::doc/api/libbabeltrace2/images/basic-convert-graph.png[A basic {bt2} conversion graph]
+
 {bt2} is also the reference parser implementation of the
-https://diamon.org/ctf/[Common Trace Format] (CTF), a versatile
-trace format produced by various tracers and tools such as
+https://diamon.org/ctf/[Common Trace Format] (CTF), a flexible
+trace format which various tracers and tools such as
 https://lttng.org/[LTTng] and
-https://barectf.org/[barectf]. The {bt2} library and its Python bindings
-can read and write CTF traces.
+https://barectf.org/[barectf] produce natively.
+The {bt2} library and its Python bindings can read and write CTF traces.
 
-See Babeltrace's https://babeltrace.org[official website], in
+See the {bt2} https://babeltrace.org[official website], in
 particular the
 https://babeltrace.org/docs/v{btversion}/man7/babeltrace2-intro.7[`**babeltrace2-intro**(7)`]
 manual page, to learn more about the project.
 
 [NOTE]
+ifdef::env-github[]
+.**Babeltrace{nbsp}1 vs. {bt2}**
+endif::[]
+ifndef::env-github[]
 .Babeltrace{nbsp}1 vs. {bt2}
+endif::[]
 ====
-The Babeltrace project exists since 2010. In 2020, {bt2} was
-released. {bt2} is a complete rewrite of the library, Python
-bindings, and CLI. It is plugin-based and offers much more features and
-potential than Babeltrace{nbsp}1 while showing comparable performance.
+The Babeltrace project exists since 2010.
 
-Because {bt2} is still a young major release, some
-distributions still provide packages for the Babeltrace{nbsp}1 project.
-Both projects can coexist on the same system as there are no common
-files.
+In 2020, {bt2} was released. {bt2} is a complete rewrite of the library,
+Python bindings, and CLI. It's plugin-based and offers much more
+features and potential than Babeltrace{nbsp}1 while delivering
+comparable performance.
 
-This file documents the **{bt2}** project.
+Some Linux distributions still provide packages for the
+Babeltrace{nbsp}1 project. Both projects can coexist on the same system
+as there are no conflicting files.
+
+This README documents the **{bt2}** project.
 ====
 
+ifdef::env-github[]
+toc::[]
+endif::[]
 
 == Build Babeltrace{nbsp}{btversion} from source
 
@@ -70,11 +86,11 @@ https://clang.llvm.org/[Clang] is one of those.
 Tools::
     * https://www.gnu.org/software/make/[GNU Make]
     * **If you build from a Git clone**:
-    ** https://www.gnu.org/software/automake/[GNU Automake]{nbsp}≥{nbsp}1.12
+    ** https://www.gnu.org/software/automake/[GNU Automake]{nbsp}≥{nbsp}1.13
     ** https://www.gnu.org/software/autoconf/[GNU Autoconf]{nbsp}≥{nbsp}2.69
     ** https://www.gnu.org/software/libtool/[GNU Libtool]{nbsp}≥{nbsp}2.2
     ** https://github.com/westes/flex[flex]{nbsp}≥{nbsp}2.5.35
-    ** https://www.gnu.org/software/bison/bison.html[GNU Bison]{nbsp}≥{nbsp}2.4
+    ** https://www.gnu.org/software/bison/bison.html[GNU Bison]{nbsp}≥{nbsp}2.5
 
 Libraries::
     * A C library (for example,
@@ -106,6 +122,25 @@ _**If you need the `bt2` Python bindings documentation**_::
       Python{nbsp}3
       (Debian/Ubuntu/Fedora: `python3-sphinx`)
 
+[NOTE]
+ifdef::env-github[]
+.**Thanks for the code!**
+endif::[]
+ifndef::env-github[]
+.Thanks for the code!
+endif::[]
+====
+We'd like to thank the authors of the following projects which are
+embedded into the {bt2} source tree:
+
+* https://github.com/fmtlib/fmt[\{fmt}]
+* https://github.com/nlohmann/json[JSON for Modern {cpp}]
+* https://github.com/martinmoene/optional-lite[optional lite]
+* https://github.com/martinmoene/span-lite[span lite]
+* https://github.com/martinmoene/string-view-lite[string_view lite]
+* https://github.com/quicknir/wise_enum[wise_enum]
+* https://github.com/wonder-mice/zf_log[zf_log]
+====
 
 === Procedure
 
@@ -133,6 +168,14 @@ The following options can modify the build:
 `--enable-api-doc`::
     Build the {bt2}{nbsp}C{nbsp}API HTML documentation.
 
+`--enable-built-in-plugins`::
+    Statically link the official plugins into the
+    `babeltrace2` executable.
+
+`--enable-built-in-python-plugin-support`::
+    Statically link the Python plugin provider into the
+    `babeltrace2` executable.
+
 `--enable-debug-info`::
     Build the https://lttng.org/[LTTng] debug information filter
     component class
@@ -158,18 +201,18 @@ The following environment variables can modify the build:
 `BABELTRACE_DEBUG_MODE`::
     Set to `1` to enable the debug mode.
 +
-The debug mode enables more run-time assertions to detect bugs in the
-{bt2} project.
+The debug mode enables more run-time assertions to detect bugs while
+developing the {bt2} project.
 
 `BABELTRACE_DEV_MODE`::
     Set to `1` to enable the <<dev-mode,developer mode>>.
 +
 The {bt2} developer mode enables more precondition and postcondition
-assertions to detect programming errors.
+assertions to detect C{nbsp}API usage errors.
 
 `BABELTRACE_MINIMAL_LOG_LEVEL`::
-    Set the build-time, minimal logging level for all the project's
-    modules.
+    Set the build-time, minimal logging level for all the modules
+    of the project.
 +
 Set to `TRACE`, `DEBUG`, or `INFO`.
 
@@ -177,9 +220,9 @@ Set to `TRACE`, `DEBUG`, or `INFO`.
     Installation directory of {bt2} plugin providers.
 
 `BABELTRACE_PLUGINS_DIR`::
-    Installation directory of {bt2} project plugins.
+    Installation directory of {bt2} official plugins.
 
-See `./configure --help` to list all the available options and
+Run `./configure --help` to list all the available options and
 environment variables.
 --
 
@@ -192,28 +235,32 @@ $ make
 
 To install {bt2}:
 
-* Do:
+* Run:
 +
 [role="term"]
 ----
 # make install
 ----
 
-
 [[dev-mode]]
 === Build {bt2} for plugin or application development
 
-If you are developing a {bt2} plugin or an application which uses
-libbabeltrace2, we recommend that:
+If you're developing a {bt2} plugin or an application which uses
+libbabeltrace2, we recommend to:
 
-* You build {bt2} from source in _developer mode_.
+* Build {bt2} from source in _developer mode_.
 +
 The {bt2} developer mode enables more precondition and postcondition
-assertions to detect programming errors.
+assertions to detect C{nbsp}API usage errors.
++
+The
+https://babeltrace.org/docs/v{btversion}/libbabeltrace2[{bt2}{nbsp}C{nbsp}API documentation]
+always lists the precondition and postconditions of
+functions.
 +
 Set `BABELTRACE_DEV_MODE=1` when you <<conf,configure>> the {bt2} build.
 
-* You use _TRACE_ as the minimal logging level at build time to have
+* Use _TRACE_ as the minimal logging level at build time to have
   access to more logging, should you need it to debug your plugin or
   application.
 +
@@ -236,46 +283,48 @@ $ BABELTRACE_DEV_MODE=1 BABELTRACE_MINIMAL_LOG_LEVEL=TRACE ./configure \
 ----
 
 See the
-https://babeltrace.org/docs/v{btversion}/libbabeltrace2[{bt2}{nbsp}C{nbsp}API]
-documentation for more information.
-
+https://babeltrace.org/docs/v{btversion}/libbabeltrace2[{bt2}{nbsp}C{nbsp}API
+documentation] for more information.
 
 == Use Babeltrace{nbsp}{btversion}
 
-See the https://babeltrace.org[Babeltrace website] to learn how
-to use the different parts of the project.
+See the https://babeltrace.org[{bt2} website] to learn how to use the
+different parts of the project.
 
+If you're new to {bt2}, make sure to read the
+https://babeltrace.org/docs/v{btversion}/man7/babeltrace2-intro.7[`**babeltrace2-intro**(7)`]
+manual page to familiarize yourself with the project.
 
 === Run-time requirements
 
-Libraries::
-    * A C library (for example,
-      https://www.gnu.org/software/libc/[GNU{nbsp}C Library],
-      https://www.musl-libc.org/[musl libc])
-    * https://developer.gnome.org/glib/[GLib]{nbsp}≥{nbsp}2.28
-      (Debian/Ubuntu: `libglib2.0-0`; Fedora: `glib2`)
+Libraries:: {empty}
++
+* A C library (for example,
+  https://www.gnu.org/software/libc/[GNU{nbsp}C Library] or
+  https://www.musl-libc.org/[musl libc])
 
-_**If you need the `bt2` Python bindings**_::
-    * https://www.python.org[Python]{nbsp}≥{nbsp}3.4
-      (Debian/Ubuntu/Fedora: `python3`)
+* https://developer.gnome.org/glib/[GLib]{nbsp}≥{nbsp}2.28
+  (Debian/Ubuntu: `libglib2.0-0`; Fedora: `glib2`)
 
-_**If you need the https://lttng.org/[LTTng] debug information filter component class (https://babeltrace.org/docs/v{btversion}/man7/babeltrace2-filter.lttng-utils.debug-info.7/[`filter.lttng-utils.debug-info`])**_::
-    * https://sourceware.org/elfutils/[elfutils]{nbsp}≥{nbsp}0.154
-      (Debian/Ubuntu: `libelf` and `libdw`; Fedora: `elfutils-libs` and
-      `elfutils-libelf`)
+_**If you need the `bt2` Python bindings**_:: {empty}
++
+* https://www.python.org[Python]{nbsp}≥{nbsp}3.4
+  (Debian/Ubuntu/Fedora: `python3`)
 
+_**If you need the https://lttng.org/[LTTng] debug information filter component class (https://babeltrace.org/docs/v{btversion}/man7/babeltrace2-filter.lttng-utils.debug-info.7/[`filter.lttng-utils.debug-info`])**_:: {empty}
++
+* https://sourceware.org/elfutils/[elfutils]{nbsp}≥{nbsp}0.154
+  (Debian/Ubuntu: `libelf` and `libdw`; Fedora: `elfutils-libs` and
+  `elfutils-libelf`)
 
 == Community
 
-[NOTE]
-====
 Babeltrace was born to parse CTF traces produced by LTTng{nbsp}2.0 and
-pretty-print their events.
+to pretty-print their events.
 
-Even though Babeltrace is independent from the LTTng project today,
-their communities remain very close, which is why they share some
-communication channels and services.
-====
+Even though {bt2} is independent from the LTTng project today, their
+communities remain very close, which is why they share some
+communication channels and services:
 
 Mailing list::
     https://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev[lttng-dev]
@@ -285,15 +334,15 @@ IRC channel::
     irc://irc.oftc.net/lttng[`#lttng`] on the OFTC network
 
 Bug tracker::
-    https://bugs.lttng.org/projects/babeltrace[Babeltrace bug tracker]
+    https://bugs.lttng.org/projects/babeltrace[{bt2} bug tracker]
 
 GitHub project::
     https://github.com/efficios/babeltrace/[efficios/babeltrace]
 
 Continuous integration::
-    https://ci.lttng.org/job/babeltrace_master_build/[Babeltrace's master build]
-    on LTTng's CI
+    https://ci.lttng.org/view/Babeltrace/[{bt2} jobs]
+    on the LTTng CI
 
 Code review::
-    https://review.lttng.org/q/project:babeltrace[_babeltrace_ project]
-    on LTTng Review
+    https://review.lttng.org/q/project:babeltrace[{bt2} project]
+    on LTTng Review (Gerrit)
index 5dcb8bfe79888900c336edf55a148ddc43f4e209..799df2f79857250c8d57775b870bd2d06618d3f1 100644 (file)
@@ -50,7 +50,7 @@ AC_CANONICAL_HOST
 ## Automake base setup ##
 ##                     ##
 
-AM_INIT_AUTOMAKE([1.12 foreign dist-bzip2 no-dist-gzip tar-ustar nostdinc -Wall -Wno-portability -Werror])
+AM_INIT_AUTOMAKE([1.13 foreign dist-bzip2 no-dist-gzip tar-ustar nostdinc subdir-objects -Wall -Wno-portability -Werror])
 AM_MAINTAINER_MODE([enable])
 
 # Enable silent rules by default
@@ -110,7 +110,7 @@ AX_C___ATTRIBUTE__
 AS_IF([test "x$ax_cv___attribute__" != "xyes"],
   [AC_MSG_ERROR([The compiler does not support __attribute__ extensions])])
 
-# Make sur we have pthread support
+# Make sure we have pthread support
 AX_PTHREAD([], [AC_MSG_ERROR([Could not configure pthread support])])
 
 # Checks for typedefs, structures, and compiler characteristics.
@@ -211,12 +211,12 @@ AX_PROG_BISON_VERSION([2.5], [have_bison=yes])
 AS_IF([test "x$have_bison" != "xyes"], [
   AE_IF_IN_GIT_REPO([
     AC_MSG_FAILURE([dnl
-Bison >= 2.4 is required when building from the Git repository. You can
+Bison >= 2.5 is required when building from the Git repository. You can
 set the YACC variable to override automatic detection.
     ])
   ], [
     AC_MSG_WARN([dnl
-Missing Bison >= 2.4. Note that the parser files are already built in
+Missing Bison >= 2.5. Note that the parser files are already built in
 this distribution tarball, so Bison is only needed if you intend to
 modify their sources. You can set the YACC variable to override automatic
 detection.
@@ -250,7 +250,7 @@ AM_CONDITIONAL([HAVE_FLEX], [test "x$have_flex" = "xyes"])
 
 # Always check for python, we will fail later if some features require it and
 # it's unavailable.
-AM_PATH_PYTHON([3.0], [
+AM_PATH_PYTHON([3.4], [
   AE_PATH_PYTHON_MODULES([PYTHON])
 
   # pythondir is the path where extra modules are to be installed
@@ -380,7 +380,7 @@ AS_IF([test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "TRACE" && \
        test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "INFO"],
   [AC_MSG_ERROR([Invalid BABELTRACE_MINIMAL_LOG_LEVEL value ($BABELTRACE_MINIMAL_LOG_LEVEL): use TRACE, DEBUG, or INFO.])]
 )
-AC_DEFINE_UNQUOTED([BT_MINIMAL_LOG_LEVEL], [BT_LOG_$BABELTRACE_MINIMAL_LOG_LEVEL], [Minimal log level])
+AC_DEFINE_UNQUOTED([BT_LOG_MINIMAL_LEVEL], [__BT_LOGGING_LEVEL_$BABELTRACE_MINIMAL_LOG_LEVEL], [Minimal log level])
 
 # BABELTRACE_DEV_MODE:
 AC_ARG_VAR([BABELTRACE_DEV_MODE], [Set to 1 to enable the Babeltrace developer mode (enables run-time checks for plugin developers)])
@@ -396,9 +396,9 @@ AS_IF([test "x$BABELTRACE_DEBUG_MODE" = x1], [
 ], [BABELTRACE_DEBUG_MODE=0])
 
 
-##                              ##
-## Optionnal features selection ##
-##                              ##
+##                             ##
+## Optional features selection ##
+##                             ##
 
 # Python bindings
 # Disabled by default
@@ -448,6 +448,7 @@ AE_FEATURE([Werror],[Treat compiler warnings as errors.])
 # When given, build with AddressSanitizer.
 AE_FEATURE_DEFAULT_DISABLE
 AE_FEATURE([asan],[Build with AddressSanitizer.])
+AE_FEATURE([ubsan],[Build with UndefinedBehaviorSanitizer.])
 
 ##                                          ##
 ## Check for conflicting features selection ##
@@ -513,6 +514,9 @@ AE_IF_FEATURE_ENABLED([built-in-python-plugin-support],
   [AC_DEFINE([BT_BUILT_IN_PYTHON_PLUGIN_SUPPORT], [1], [Define to 1 to register plug-in attributes in static executable sections])]
 )
 
+AE_IF_FEATURE_ENABLED([python-plugins], [ENABLE_PYTHON_PLUGINS=1], [ENABLE_PYTHON_PLUGINS=0])
+AC_SUBST([ENABLE_PYTHON_PLUGINS])
+
 AE_IF_FEATURE_ENABLED([debug-info], [ENABLE_DEBUG_INFO_VAL=1], [ENABLE_DEBUG_INFO_VAL=0])
 AC_SUBST([ENABLE_DEBUG_INFO_VAL])
 
@@ -534,10 +538,28 @@ AS_IF([test -n "$PYTHON_CONFIG"], [have_python_dev=yes], [have_python_dev=no])
 AM_CONDITIONAL([HAVE_PYTHON], [test "x$have_python" = xyes])
 AM_CONDITIONAL([HAVE_PYTHON_DEV], [test "x$have_python_dev" = xyes])
 
+AS_IF([test "x$have_python" = xyes], [
+  AX_COMPARE_VERSION(["$PYTHON_VERSION"], [ge], ["3.12"], [
+    have_python_312_or_greater=yes
+  ])
+])
+
+AM_CONDITIONAL([HAVE_PYTHON_312_OR_GREATER], [test "x$have_python_312_or_greater" = xyes])
+
 AS_IF([AE_IS_FEATURE_ENABLED([python-bindings]) || AE_IS_FEATURE_ENABLED([python-plugins])], [
   AS_IF([test "x$have_python_dev" = xno], [
     AC_MSG_ERROR([Cannot find a suitable python-config. You can override the python-config path with the PYTHON_CONFIG environment variable.])
   ])
+
+  AX_COMPARE_VERSION(["$PYTHON_VERSION"], [ge], ["3.12"], [
+    AC_MSG_CHECKING([for python setuptools])
+    AS_IF(["$PYTHON" -c "import setuptools" 2>/dev/null], [
+      AC_MSG_RESULT([yes])
+    ], [
+      AC_MSG_RESULT([no])
+      AC_MSG_ERROR([Python >= 3.12 removed 'distutils', the 'setuptools' module needs to be installed for the selected interpreter.])
+    ])
+  ])
 ])
 
 AE_IF_FEATURE_ENABLED([python-bindings-doc],
@@ -584,7 +606,7 @@ AE_IF_FEATURE_ENABLED([api-doc],
     DX_XML_FEATURE(OFF)
     DX_PDF_FEATURE(OFF)
     DX_PS_FEATURE(OFF)
-    DX_INIT_DOXYGEN([Babeltrace 2], [$(builddir)/Doxyfile], [output])
+    DX_INIT_DOXYGEN([babeltrace2], [$(builddir)/Doxyfile], [output])
     AS_IF([test -z "$DX_DOXYGEN"],
       [AC_MSG_ERROR([You need doxygen to enable the API documentation])]
     )
@@ -645,13 +667,19 @@ AE_IF_FEATURE_ENABLED([asan], [
   ASAN_LDFLAGS="-fsanitize=address"
 ])
 
-
+# If --enable-ubsan is used...
+AE_IF_FEATURE_ENABLED([ubsan], [
+  # ... add -fsanitize=undefined to the *FLAGS variables.
+  UBSAN_CFLAGS="-fsanitize=undefined"
+  UBSAN_CXXFLAGS="-fsanitize=undefined"
+  UBSAN_LDFLAGS="-fsanitize=undefined"
+])
 
 # CFLAGS from libraries (the glib ones are needed for the following sizeof
 # test).
-AM_CFLAGS="${PTHREAD_CFLAGS} ${GLIB_CFLAGS} ${ASAN_CFLAGS}"
-AM_CXXFLAGS="${PTHREAD_CFLAGS} ${GLIB_CFLAGS} ${ASAN_CXXFLAGS}"
-AM_LDFLAGS="${ASAN_LDFLAGS}"
+AM_CFLAGS="-fvisibility=hidden ${PTHREAD_CFLAGS} ${GLIB_CFLAGS} ${ASAN_CFLAGS} ${UBSAN_CFLAGS}"
+AM_CXXFLAGS="-fvisibility=hidden -fvisibility-inlines-hidden ${PTHREAD_CFLAGS} ${GLIB_CFLAGS} ${ASAN_CXXFLAGS} ${UBSAN_CFLAGS}"
+AM_LDFLAGS="${ASAN_LDFLAGS} ${UBSAN_CFLAGS}"
 
 # Check that the current size_t matches the size that glib thinks it should
 # be. This catches problems on multi-arch where people try to do a 32-bit
@@ -718,7 +746,6 @@ m4_define([WARN_FLAGS_LIST], [ dnl
   -Wduplicated-branches dnl
   -Wlogical-op dnl
   -Wsuggest-override dnl
-  -Wno-unused-parameter dnl
   -Wno-sign-compare dnl
   dnl
   dnl Some versions of SWIG (like 3.0.12) generate code that produces
@@ -733,7 +760,6 @@ m4_define([WARN_FLAGS_LIST], [ dnl
   dnl
   dnl Ref: https://github.com/swig/swig/issues/1259
   -Wno-cast-function-type dnl
-  -Wno-missing-field-initializers dnl
   -Wno-maybe-uninitialized dnl
 ])
 
@@ -772,79 +798,35 @@ AC_SUBST(AM_CPPFLAGS)
 # Add glib to global link libs
 LIBS="$LIBS $GLIB_LIBS"
 
+# Disable exceptions for the {fmt} library.  This means, for example, that a
+# format error will result in an assertion failure (instead of throwing an
+# `fmt::format_error` exception).
+AC_DEFINE_UNQUOTED([FMT_EXCEPTIONS], 0, [Disable {fmt} exceptions.])
+
 # Abuse autoconf's AC_ARG_PROGRAM output variable 'program_transform_name'
 # to rename babeltrace2.bin to babeltrace2 at install time.
 program_transform_name="s&babeltrace2\.bin&babeltrace2&;$program_transform_name"
 AC_SUBST(program_transform_name)
 
 AC_CONFIG_FILES([
-  doc/api/Makefile
   doc/api/libbabeltrace2/Doxyfile
   doc/api/libbabeltrace2/Makefile
-  doc/bindings/Makefile
   doc/bindings/python/Makefile
-  doc/contributing-images/Makefile
   doc/Makefile
   doc/man/asciidoc-attrs.conf
   doc/man/Makefile
   include/Makefile
   Makefile
-  src/argpar/Makefile
-  src/autodisc/Makefile
   src/babeltrace2-ctf-writer.pc
   src/babeltrace2.pc
-  src/bindings/Makefile
   src/bindings/python/bt2/bt2/version.py
   src/bindings/python/bt2/Makefile
   src/bindings/python/bt2/setup.py
-  src/bindings/python/Makefile
   src/cli/Makefile
-  src/common/Makefile
-  src/cpp-common/Makefile
-  src/compat/Makefile
-  src/ctfser/Makefile
-  src/ctf-writer/Makefile
-  src/fd-cache/Makefile
-  src/lib/graph/Makefile
-  src/lib/graph/message/Makefile
-  src/lib/Makefile
-  src/lib/plugin/Makefile
-  src/lib/prio-heap/Makefile
-  src/lib/trace-ir/Makefile
-  src/logging/Makefile
   src/Makefile
-  src/plugins/common/Makefile
-  src/plugins/common/muxing/Makefile
-  src/plugins/common/param-validation/Makefile
-  src/plugins/ctf/common/bfcr/Makefile
-  src/plugins/ctf/common/Makefile
-  src/plugins/ctf/common/metadata/Makefile
-  src/plugins/ctf/common/msg-iter/Makefile
-  src/plugins/ctf/fs-sink/Makefile
-  src/plugins/ctf/fs-src/Makefile
-  src/plugins/ctf/lttng-live/Makefile
-  src/plugins/ctf/Makefile
-  src/plugins/lttng-utils/debug-info/Makefile
-  src/plugins/lttng-utils/Makefile
-  src/plugins/Makefile
-  src/plugins/text/dmesg/Makefile
-  src/plugins/text/Makefile
-  src/plugins/text/pretty/Makefile
-  src/plugins/text/details/Makefile
-  src/plugins/utils/counter/Makefile
-  src/plugins/utils/dummy/Makefile
-  src/plugins/utils/Makefile
-  src/plugins/utils/muxer/Makefile
-  src/plugins/utils/trimmer/Makefile
-  src/py-common/Makefile
-  src/python-plugin-provider/Makefile
-  src/param-parse/Makefile
-  src/string-format/Makefile
   tests/bitfield/Makefile
   tests/ctf-writer/Makefile
   tests/lib/Makefile
-  tests/lib/test-plugin-plugins/Makefile
-  tests/lib/conds/Makefile
   tests/Makefile
   tests/param-validation/Makefile
   tests/plugins/Makefile
@@ -857,12 +839,11 @@ AC_CONFIG_FILES([
   tests/plugins/flt.utils.muxer/succeed/Makefile
   tests/plugins/flt.utils.trimmer/Makefile
   tests/plugins/sink.text.pretty/Makefile
+  tests/utils/env.sh
   tests/utils/Makefile
   tests/utils/tap/Makefile
 ])
 
-AC_CONFIG_FILES([tests/utils/env.sh],[chmod +x tests/utils/env.sh])
-
 AC_OUTPUT
 
 #
index 452c0de654b52c01a7bad7582b1bd0125581fb7b..76abc5d077a187925f55d7f5a07c3a9b7af59c1a 100644 (file)
@@ -1,2 +1,3 @@
-black ~= 22.0
+black ~= 23.9.1
 flake8 >= 3.8
+isort ~= 5.12
index 7047f0e5f24e644cac98c77e4922b50802808203..160cd5a9ca9893452c7629b8d67c53bc235fdaab 100644 (file)
@@ -1,7 +1,26 @@
 # SPDX-License-Identifier: MIT
 
-SUBDIRS = contributing-images man bindings
+SUBDIRS = man
 
 if ENABLE_API_DOC
-SUBDIRS += api
+SUBDIRS += api/libbabeltrace2
 endif
+
+if ENABLE_PYTHON_BINDINGS_DOC
+SUBDIRS += bindings/python
+endif
+
+EXTRA_DIST = \
+       contributing-images/bt-ref01.png \
+       contributing-images/bt-ref02.png \
+       contributing-images/bt-ref03.png \
+       contributing-images/bt-ref04.png \
+       contributing-images/bt-ref05.png \
+       contributing-images/bt-ref06.png \
+       contributing-images/bt-ref07.png \
+       contributing-images/bt-ref08.png \
+       contributing-images/bt-ref09.png \
+       contributing-images/bt-ref10.png \
+       contributing-images/bt-ref11.png \
+       contributing-images/bt-ref12.png \
+       contributing-images/bt-ref13.png
diff --git a/doc/api/Makefile.am b/doc/api/Makefile.am
deleted file mode 100644 (file)
index c5f96ec..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = libbabeltrace2
index cd94819eff482f84e913551e11374734dd8dd26e..07a7c2812d71e5fc990091d0f5b284c3db2004ca 100644 (file)
@@ -28,7 +28,7 @@ ALIASES                += bt_max_mip_version="0"
 ALIASES                += bt_name="Babeltrace&nbsp;2"
 ALIASES                += bt_name_version_min_maj="Babeltrace&nbsp;\bt_version_min_maj"
 ALIASES                += bt_api="Babeltrace&nbsp;2&nbsp;C&nbsp;API"
-ALIASES                += bt_p{1}="<span class="bt-param">\1</span>"
+ALIASES                += bt_p{1}="<span class=\"bt-param\">\1</span>"
 ALIASES                += bt_dt_opt="<strong><em>Optional</em></strong>:"
 ALIASES                += bt_man{2}="<a href=\"https://babeltrace.org/docs/v\bt_version_min_maj/man\2/\1.\2/\"><code><strong>\1</strong>(\2)</code></a>"
 ALIASES                += bt_cli="<a href=\"https://babeltrace.org/docs/v\bt_version_min_maj/man1/babeltrace2.1\"><code>babeltrace2</code></a>"
index 81fe2e82776667a4ddb125c9d753507e94f5c63e..bf92f65de60a59b9a9f80c983b80e660438434b6 100644 (file)
@@ -110,7 +110,7 @@ distill_message_iterator_initialize(
     /* Keep a link to the component's private data */
     distill_iter->distill = distill;
 
-    /* Create the uptream message iterator */
+    /* Create the upstream message iterator */
     bt_message_iterator_create_from_message_iterator(self_message_iterator,
         distill->in_port, &distill_iter->message_iterator);
 
@@ -150,7 +150,6 @@ bool message_passes(struct distill_message_iterator *distill_iter,
 
     /* Move as is if it's not an event message */
     if (bt_message_get_type(message) != BT_MESSAGE_TYPE_EVENT) {
-        passes = false;
         goto end;
     }
 
index 852ca3f70775c13e39aeadbdc37c6c0e71627e16..ab68408ff39768772b8d5103303fd4277d6d94cc 100644 (file)
@@ -81,7 +81,7 @@ epitome_out_graph_is_configured(bt_self_component_sink *self_component_sink)
         bt_self_component_sink_borrow_input_port_by_index(
             self_component_sink, 0);
 
-    /* Create the uptream message iterator */
+    /* Create the upstream message iterator */
     bt_message_iterator_create_from_sink_component(self_component_sink,
         in_port, &epitome_out->message_iterator);
 
diff --git a/doc/bindings/Makefile.am b/doc/bindings/Makefile.am
deleted file mode 100644 (file)
index a9d7ab1..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-if ENABLE_PYTHON_BINDINGS_DOC
-SUBDIRS = python
-endif
index d76cc1c777406f854f8f2bb3469c87db3ddcfaeb..830e0d1e2c4b3da88c61b5eddad38e31707ea564 100644 (file)
 #
 #         :bt2link:`libbabeltrace2 <https://babeltrace.org/docs/v@ver@/libbabeltrace2/>`
 
-import docutils
-import docutils.utils
-import docutils.nodes
 import re
 import functools
 
+import docutils
+import docutils.nodes
+import docutils.utils
+
 
 def _bt2man_role(
     bt2_version, name, rawtext, text, lineno, inliner, options=None, content=None
index 812127ab7027b03761b903b577271084c7473334..cb595145a50bc0304e7463dfd07f31dd795c7a78 100644 (file)
@@ -1,3 +1,7 @@
+..
+   SPDX-FileCopyrightText: 2020 Philippe Proulx <eeppeliteloop@gmail.com>
+   SPDX-License-Identifier: CC-BY-SA-4.0
+
 .. non-breaking space substitution
    (see <https://docutils.sourceforge.io/docs/user/rst/quickref.html#substitution-references-and-definitions>):
 
index 148cc92abca409abca0d4708ce03216b9f8e0d23..99e60af8942fe3535f00e9f924cf3c8c5e7bc288 100644 (file)
@@ -2,9 +2,10 @@
 #
 # Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
 
-import bt2
 import re
 
+import bt2
+
 # project
 project = "Babeltrace 2 Python bindings"
 copyright = "2020, EfficiOS, Inc"
index ab8bd399b1acc28de62baf731a757b3eab911b1d..e2a46cc78626fd2763affed4c2c94ab66732c6ad 100644 (file)
@@ -1,3 +1,7 @@
+..
+   SPDX-FileCopyrightText: 2020 Philippe Proulx <eeppeliteloop@gmail.com>
+   SPDX-License-Identifier: CC-BY-SA-4.0
+
 .. include:: common.rst
 
 .. _examples:
index 2a2c8b16182755efa7399ca49fc839369889fa5c..bf90b63afbc189eb6def34dd856fb253c7218250 100644 (file)
@@ -1,3 +1,7 @@
+..
+   SPDX-FileCopyrightText: 2020 Philippe Proulx <eeppeliteloop@gmail.com>
+   SPDX-License-Identifier: CC-BY-SA-4.0
+
 .. include:: common.rst
 
 Welcome!
index 5b0412dce41979e6a0a8604719cd6ef429d13600..03a7db29f3594a569aa5a4404af143ba0b7c55fb 100644 (file)
@@ -1,3 +1,7 @@
+..
+   SPDX-FileCopyrightText: 2020 Philippe Proulx <eeppeliteloop@gmail.com>
+   SPDX-License-Identifier: CC-BY-SA-4.0
+
 .. include:: common.rst
 
 Installation
diff --git a/doc/contributing-images/Makefile.am b/doc/contributing-images/Makefile.am
deleted file mode 100644 (file)
index 338b7bb..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-EXTRA_DIST = bt-ref01.png bt-ref02.png bt-ref03.png bt-ref04.png \
-       bt-ref05.png bt-ref06.png bt-ref07.png bt-ref08.png \
-       bt-ref09.png bt-ref10.png bt-ref11.png bt-ref12.png \
-       bt-ref13.png
index f98c7d7c29e599581cbe8626056485fd94aeebda..bccd85491c262e72b2b3bf6e63d7f35b52b1ca0c 100644 (file)
@@ -1,6 +1,6 @@
 = babeltrace2-filter.lttng-utils.debug-info(7)
 :manpagetype: component class
-:revdate: 14 September 2019
+:revdate: 1 September 2023
 :compcls: compcls:filter.lttng-utils.debug-info
 :defdebuginfoname: `debug_info`
 
@@ -229,10 +229,12 @@ param:debug-info-field-name='NAME' vtype:[optional string]::
     of the created events 'NAME' instead of the default
     {defdebuginfoname}.
 
-param:full-path=`yes` vtype:[optional boolean]::
-    Use the full path when writing the executable name (`bin`) and
-    source file name (`src`) fields in the {defdebuginfoname} context
-    field of the created events.
+param:full-path='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then use the full path when writing the executable
+    name (`bin`) and source file name (`src`) fields in the
+    {defdebuginfoname} context field of the created events.
++
+Default: false.
 
 param:target-prefix='DIR' vtype:[optional string]::
     Use 'DIR' as the root directory of the target file system instead of
index b1f754e5810c64eb3efcefbbcae4e919beaf2f83..707aac6c4a1dacd8df0060f1f9f5e8aacda4ef85 100644 (file)
@@ -1,6 +1,6 @@
 = babeltrace2-filter.utils.trimmer(7)
 :manpagetype: component class
-:revdate: 14 September 2019
+:revdate: 1 September 2023
 
 
 == NAME
@@ -74,9 +74,11 @@ treats it as such.
 If you don't specify this parameter, the component discards no events
 from the beginning of the trimming time range.
 
-param:gmt=`yes` vtype:[optional boolean]::
-    Set the time zone of the param:begin and param:end parameters
-    to GMT instead of the local time zone.
+param:gmt='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then set the time zone of the param:begin and
+    param:end parameters to GMT instead of the local time zone.
++
+Default: false.
 
 
 == PORTS
index c9e13456a0cb61aec4211e33f3ee8f56399040f3..22dd1f5f7892b765afd3ff7ed2b7098bc6530843 100644 (file)
@@ -85,7 +85,7 @@ $ babeltrace2 query src.ctf.lttng-live sessions \
 [role="term"]
 ----
 $ babeltrace2 query src.ctf.fs metadata-info \
-                    --params='path="/path/to/trace"'
+                    --params='inputs=["/path/to/trace"]'
 ----
 ====
 
@@ -94,7 +94,7 @@ $ babeltrace2 query src.ctf.fs metadata-info \
 [role="term"]
 ----
 $ babeltrace2 query src.ctf.fs babeltrace.trace-infos \
-                    --params='path="/path/to/trace"'
+                    --params='inputs=["/path/to/trace"]'
 ----
 ====
 
index df91341489b2aa484258d19b466a56e15eb02f2f..736602772465acc23c9d045e033a0d26f1448090 100644 (file)
@@ -1,6 +1,6 @@
 = babeltrace2-sink.ctf.fs(7)
 :manpagetype: component class
-:revdate: 14 September 2019
+:revdate: 1 September 2023
 
 
 == NAME
@@ -194,18 +194,24 @@ until the path does not exist.
 
 == INITIALIZATION PARAMETERS
 
-param:assume-single-trace=`yes` vtype:[optional boolean]::
-    Assume that the component only receives messages related to a single
-    input trace.
+param:assume-single-trace='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then assume that the component only receives
+    messages related to a single input trace.
 +
 This parameter affects how the component builds the output trace path
 (see <<output-path,``Output path''>>).
++
+Default: false.
 
-param:ignore-discarded-events=`yes` vtype:[optional boolean]::
-    Ignore discarded events messages.
+param:ignore-discarded-events='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then ignore discarded events messages.
++
+Default: false.
 
-param:ignore-discarded-packets=`yes` vtype:[optional boolean]::
-    Ignore discarded packets messages.
+param:ignore-discarded-packets='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then ignore discarded packets messages.
++
+Default: false.
 
 param:path='PATH' vtype:[string]::
     Base output path.
@@ -213,8 +219,10 @@ param:path='PATH' vtype:[string]::
 See <<output-path,``Output path''>> to learn how the component uses this
 parameter to build the output path for a given input trace.
 
-param:quiet=`yes` vtype:[optional boolean]::
-    Do not write anything to the standard output.
+param:quiet='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then do not write anything to the standard output.
++
+Default: false.
 
 
 == PORTS
index dc8792194ec4669c8bec0c9c4e976925e927dae8..8a23dd4e49fccd475d4c10312902b2f58d52dec7 100644 (file)
@@ -1,6 +1,6 @@
 = babeltrace2-sink.text.details(7)
 :manpagetype: component class
-:revdate: 14 September 2019
+:revdate: 1 September 2023
 
 
 == NAME
@@ -113,8 +113,8 @@ param:color=(`never` | `auto` | `always`) vtype:[optional string]::
 The `BABELTRACE_TERM_COLOR` environment variable overrides this
 parameter.
 
-param:compact=`yes` vtype:[optional boolean]::
-    Enable compact mode.
+param:compact='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then enable compact mode.
 +
 In compact mode, the component prints one line per message, omitting
 many details about messages. This is useful if you only need the time,
@@ -122,24 +122,38 @@ type, and very basic information of messages.
 +
 In compact mode, the component still prints the full metadata blocks.
 You can remove such blocks with the param:with-metadata parameter.
++
+Default: false.
 
-param:with-metadata=`no` vtype:[optional boolean]::
-    Do not print metadata blocks.
+param:with-metadata='VAL' vtype:[optional boolean]::
+    If 'VAL' is false, then do not print metadata blocks.
++
+Default: true.
 
-param:with-stream-class-name=`no` vtype:[optional boolean]::
-    Do not print stream class names.
+param:with-stream-class-name='VAL' vtype:[optional boolean]::
+    If 'VAL' is false, then do not print stream class names.
++
+Default: true.
 
-param:with-stream-name=`no` vtype:[optional boolean]::
-    Do not print stream names.
+param:with-stream-name='VAL' vtype:[optional boolean]::
+    If 'VAL' is false, then do not print stream names.
++
+Default: true.
 
-param:with-time=`no` vtype:[optional boolean]::
-    Do not print timing information.
+param:with-time='VAL' vtype:[optional boolean]::
+    If 'VAL' is false, then do not print timing information.
++
+Default: true.
 
-param:with-trace-name=`no` vtype:[optional boolean]::
-    Do not print trace names.
+param:with-trace-name='VAL' vtype:[optional boolean]::
+    If 'VAL' is false, then do not print trace names.
++
+Default: true.
 
-param:with-uuid=`no` vtype:[optional boolean]::
-    Do not print UUIDs.
+param:with-uuid='VAL' vtype:[optional boolean]::
+    If 'VAL' is false, then do not print UUIDs.
++
+Default: true.
 
 
 == PORTS
index 6d0cd292529542adc789e8f409ad5b88c8756c7f..496879117354bb80bda174c00996560be9321276 100644 (file)
@@ -1,6 +1,6 @@
 = babeltrace2-sink.text.pretty(7)
 :manpagetype: component class
-:revdate: 14 September 2019
+:revdate: 1 September 2023
 
 
 == NAME
@@ -43,20 +43,28 @@ man:babeltrace(1) (Babeltrace~1 command-line tool).
 
 == INITIALIZATION PARAMETERS
 
-param:clock-cycles=`yes` vtype:[optional boolean]::
-    Print event times in clock cycles instead of hours, minutes,
-    seconds, and nanoseconds.
+param:clock-cycles='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then print event times in clock cycles instead of
+    hours, minutes, seconds, and nanoseconds.
++
+Default: false.
 
-param:clock-date=`yes` vtype:[optional boolean]::
-    Print event times _and_ dates.
+param:clock-date='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then print event times _and_ dates.
++
+Default: false.
 
-param:clock-gmt=`yes` vtype:[optional boolean]::
-    Print event times in the GMT time zone instead of the local time
-    zone.
+param:clock-gmt='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then print event times in the GMT time zone
+    instead of the local time zone.
++
+Default: false.
 
-param:clock-seconds=`yes` vtype:[optional boolean]::
-    Print event times in seconds instead of hours, minutes,
-    seconds, and nanoseconds.
+param:clock-seconds='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then print event times in seconds instead of
+    hours, minutes, seconds, and nanoseconds.
++
+Default: false.
 
 param:color=(`never` | `auto` | `always`) vtype:[optional string]::
     Force the terminal color support, one of:
@@ -80,52 +88,200 @@ param:field-default=(`show` | `hide`) vtype:[optional string]::
     By default, show or hide all the fields. This sets the default value
     of all the parameters which start with `field-`.
 
-param:field-emf=(`yes` | `no`) vtype:[optional boolean]::
+param:field-emf='VAL' vtype:[optional boolean]::
     Show or hide the event's Eclipse Modeling Framework URI field.
++
+Default, depending on the param:field-default parameter:
++
+--
+`show`::
+    True
+
+`hide`::
+    False
+
+Not set::
+    False
+--
 
-param:field-loglevel=(`yes` | `no`) vtype:[optional boolean]::
+param:field-loglevel='VAL' vtype:[optional boolean]::
     Show or hide the event's logging level field.
++
+Default, depending on the param:field-default parameter:
++
+--
+`show`::
+    True
 
-param:field-trace=(`yes` | `no`) vtype:[optional boolean]::
+`hide`::
+    False
+
+Not set::
+    False
+--
+
+param:field-trace='VAL' vtype:[optional boolean]::
     Show or hide the trace name field.
++
+Default, depending on the param:field-default parameter:
++
+--
+`show`::
+    True
 
-param:field-trace:domain=(`yes` | `no`) vtype:[optional boolean]::
+`hide`::
+    False
+
+Not set::
+    False
+--
+
+param:field-trace:domain='VAL' vtype:[optional boolean]::
     Show or hide the tracing domain field.
++
+Default, depending on the param:field-default parameter:
++
+--
+`show`::
+    True
+
+`hide`::
+    False
+
+Not set::
+    False
+--
 
-param:field-trace:hostname=(`yes` | `no`) vtype:[optional boolean]::
+param:field-trace:hostname='VAL' vtype:[optional boolean]::
     Show or hide the hostname field.
++
+Default, depending on the param:field-default parameter:
++
+--
+`show`::
+    True
 
-param:field-trace:procname=(`yes` | `no`) vtype:[optional boolean]::
+`hide`::
+    False
+
+Not set::
+    True
+--
+
+param:field-trace:procname='VAL' vtype:[optional boolean]::
     Show or hide the process name field.
++
+Default, depending on the param:field-default parameter:
++
+--
+`show`::
+    True
 
-param:field-trace:vpid=(`yes` | `no`) vtype:[optional boolean]::
+`hide`::
+    False
+
+Not set::
+    True
+--
+
+param:field-trace:vpid='VAL' vtype:[optional boolean]::
     Show or hide the virtual process ID field.
++
+Default, depending on the param:field-default parameter:
++
+--
+`show`::
+    True
 
-param:name-context=(`yes` | `no`) vtype:[optional boolean]::
-    Show or hide the field names in the context scopes.
+`hide`::
+    False
+
+Not set::
+    True
+--
 
 param:name-default=(`show` | `hide`) vtype:[optional string]::
     By default, show or hide all the names. This sets the
     default value of all the parameters which start with `name-`.
 
-param:name-header=(`yes` | `no`) vtype:[optional boolean]::
+param:name-header='VAL' vtype:[optional boolean]::
     Show or hide the field names in the header scopes.
++
+Default, depending on the param:name-default parameter:
++
+--
+`show`::
+    True
+
+`hide`::
+    False
+
+Not set::
+    False
+--
 
-param:name-payload=(`yes` | `no`) vtype:[optional boolean]::
+param:name-context='VAL' vtype:[optional boolean]::
+    Show or hide the field names in the context scopes.
++
+Default, depending on the param:name-default parameter:
++
+--
+`show`::
+    True
+
+`hide`::
+    False
+
+Not set::
+    True
+--
+
+param:name-payload='VAL' vtype:[optional boolean]::
     Show or hide the field names in the event payload scopes.
++
+Default, depending on the param:name-default parameter:
++
+--
+`show`::
+    True
+
+`hide`::
+    False
+
+Not set::
+    True
+--
 
-param:name-scope=(`yes` | `no`) vtype:[optional boolean]::
+param:name-scope='VAL' vtype:[optional boolean]::
     Show or hide the scope names.
++
+Default, depending on the param:name-default parameter:
++
+--
+`show`::
+    True
 
-param:no-delta=`yes` vtype:[optional boolean]::
-    Do not print the time delta between consecutive lines.
+`hide`::
+    False
+
+Not set::
+    False
+--
+
+param:no-delta='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then do not print the time delta between
+    consecutive lines.
++
+Default: false.
 
 param:path='PATH' vtype:[optional string]::
     Print the text output to the file 'PATH' instead of the standard
     output.
 
-param:verbose=`yes` vtype:[optional boolean]::
-    Turn the verbose mode on.
+param:verbose='VAL' vtype:[optional boolean]::
+    If 'VAL' if true, then turn the verbose mode on.
++
+Default: false.
 
 
 == PORTS
index 60f051d50abf176e779854cd01d823a17e139888..082750a0c634ec8804eb92711358cd34494a76e5 100644 (file)
@@ -1,6 +1,6 @@
 = babeltrace2-sink.utils.counter(7)
 :manpagetype: component class
-:revdate: 14 September 2019
+:revdate: 1 September 2023
 
 
 == NAME
@@ -54,8 +54,11 @@ the zero counts with the param:hide-zero parameter.
 
 == INITIALIZATION PARAMETERS
 
-param:hide-zero=`yes` vtype:[optional boolean]::
-    Do not print the statistics lines where the count is zero.
+param:hide-zero='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then do not print the statistics lines where the
+    count is zero.
++
+Default: false.
 
 param:step='STEP' vtype:[optional unsigned integer]::
     Print a new block of statistics every 'STEP' consumed messages
index 3156ee542bccaa204427d301447b433496ba0d61..7aa20b696be10e030aeba5f7dae8a4241457cfbb 100644 (file)
@@ -1,6 +1,6 @@
 = babeltrace2-source.ctf.fs(7)
 :manpagetype: component class
-:revdate: 14 September 2019
+:revdate: 1 September 2023
 
 
 == NAME
@@ -167,9 +167,12 @@ param:clock-class-offset-s='SEC' vtype:[optional signed integer]::
 You can combine this parameter with the param:clock-class-offset-ns
 parameter.
 
-param:force-clock-class-origin-unix-epoch=`yes` vtype:[optional boolean]::
-    Force the origin of all clock classes that the component creates to
-    have a Unix epoch origin, whatever the detected tracer.
+param:force-clock-class-origin-unix-epoch='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then force the origin of all clock classes that
+    the component creates to have a Unix epoch origin, whatever the
+    detected tracer.
++
+Default: false.
 
 param:inputs='DIRS' vtype:[array of strings]::
     Open and read the physical CTF traces located in 'DIRS'.
index 9079fe4b4331890a85c016019187e4a3d84e5e5a..1e75c9d29870da0d5e7ecb04b1e83ca238a78736 100644 (file)
@@ -1,6 +1,6 @@
 = babeltrace2-source.text.dmesg(7)
 :manpagetype: component class
-:revdate: 14 September 2019
+:revdate: 1 September 2023
 
 
 == NAME
@@ -69,10 +69,12 @@ requires that the input kernel ring buffer lines be sorted by timestamp
 
 == INITIALIZATION PARAMETERS
 
-param:no-extract-timestamp=`yes` vtype:[optional boolean]::
-    Do :not: extract timestamps from the kernel ring buffer lines: set
-    the created event's payload's `str` field to the whole line,
-    including any timestamp prefix.
+param:no-extract-timestamp='VAL' vtype:[optional boolean]::
+    If 'VAL' is true, then do :not: extract timestamps from the kernel
+    ring buffer lines: set the created event's payload's `str` field to
+    the whole line, including any timestamp prefix.
++
+Default: false.
 
 param:path='PATH' vtype:[optional string]::
     Read the kernel ring buffer lines from the file 'PATH' instead of
index 82c402adbd2956070eb65c075ce511e7abcdc597..82f625145351f1f5febe9388c75fb8ec0848a0bc 100644 (file)
 #define __BT_ATTR_FORMAT_PRINTF(_string_index, _first_to_check) \
                __attribute__((format(__BT_PRINTF_FORMAT, _string_index, _first_to_check)))
 
+/* Internal: `noexcept` specifier if C++ ≥ 11 */
+#if defined(__cplusplus) && (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
+# define __BT_NOEXCEPT noexcept
+#else
+# define __BT_NOEXCEPT
+#endif
+
 #include <babeltrace2/error-reporting.h>
 #include <babeltrace2/graph/component-class-dev.h>
 #include <babeltrace2/graph/component-class.h>
 #undef __BT_LOGGING_LEVEL_ERROR
 #undef __BT_LOGGING_LEVEL_FATAL
 #undef __BT_LOGGING_LEVEL_NONE
+#undef __BT_NOEXCEPT
 
 #endif /* BABELTRACE2_BABELTRACE_H */
index 43922e23d3fa1fd28643b142cf94b9654bd57b9e..29e53e16b5d3b18fff9973855823d9f1b47bc57d 100644 (file)
@@ -4,6 +4,8 @@
  * Copyright (C) 2010-2019 EfficiOS Inc. and Linux Foundation
  */
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef BABELTRACE2_ERROR_REPORTING_H
 #define BABELTRACE2_ERROR_REPORTING_H
 
@@ -431,7 +433,7 @@ Once you are done with the returned error, do one of:
     Moves an error's ownership to the library.
 */
 extern
-const bt_error *bt_current_thread_take_error(void);
+const bt_error *bt_current_thread_take_error(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -464,7 +466,7 @@ corresponds to catching an exception and discarding it.
     Calls this function and assigns \c NULL to the expression.
 */
 extern
-void bt_current_thread_move_error(const bt_error *error);
+void bt_current_thread_move_error(const bt_error *error) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -511,7 +513,7 @@ bt_error_release(bt_current_thread_take_error());
     library to the caller.
 */
 extern
-void bt_current_thread_clear_error(void);
+void bt_current_thread_clear_error(void) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -616,7 +618,8 @@ extern __BT_ATTR_FORMAT_PRINTF(4, 5)
 bt_current_thread_error_append_cause_status
 bt_current_thread_error_append_cause_from_component(
                bt_self_component *self_component, const char *file_name,
-               uint64_t line_number, const char *message_format, ...);
+               uint64_t line_number,
+               const char *message_format, ...) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -716,7 +719,7 @@ bt_current_thread_error_append_cause_status
 bt_current_thread_error_append_cause_from_message_iterator(
                bt_self_message_iterator *self_message_iterator,
                const char *file_name, uint64_t line_number,
-               const char *message_format, ...);
+               const char *message_format, ...) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -808,7 +811,7 @@ bt_current_thread_error_append_cause_status
 bt_current_thread_error_append_cause_from_component_class(
                bt_self_component_class *self_component_class,
                const char *file_name, uint64_t line_number,
-               const char *message_format, ...);
+               const char *message_format, ...) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -871,7 +874,8 @@ extern __BT_ATTR_FORMAT_PRINTF(4, 5)
 bt_current_thread_error_append_cause_status
 bt_current_thread_error_append_cause_from_unknown(
                const char *module_name, const char *file_name,
-               uint64_t line_number, const char *message_format, ...);
+               uint64_t line_number,
+               const char *message_format, ...) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -913,7 +917,7 @@ with \c __FILE__ and \c __LINE__ as its
 @bt_pre_not_null{error}
 */
 extern
-uint64_t bt_error_get_cause_count(const bt_error *error);
+uint64_t bt_error_get_cause_count(const bt_error *error) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -940,7 +944,7 @@ uint64_t bt_error_get_cause_count(const bt_error *error);
 */
 extern
 const bt_error_cause *bt_error_borrow_cause_by_index(
-               const bt_error *error, uint64_t index);
+               const bt_error *error, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -971,7 +975,7 @@ corresponds to catching an exception and rethrowing it.
     Moves an error's ownership to the library.
 */
 extern
-void bt_error_release(const bt_error *error);
+void bt_error_release(const bt_error *error) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1025,7 +1029,7 @@ typedef enum bt_error_cause_actor_type {
 */
 extern
 bt_error_cause_actor_type bt_error_cause_get_actor_type(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1045,7 +1049,8 @@ bt_error_cause_actor_type bt_error_cause_get_actor_type(
 @bt_pre_not_null{error_cause}
 */
 extern
-const char *bt_error_cause_get_message(const bt_error_cause *error_cause);
+const char *bt_error_cause_get_message(
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1065,7 +1070,8 @@ const char *bt_error_cause_get_message(const bt_error_cause *error_cause);
 @bt_pre_not_null{error_cause}
 */
 extern
-const char *bt_error_cause_get_module_name(const bt_error_cause *error_cause);
+const char *bt_error_cause_get_module_name(
+                       const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1087,7 +1093,8 @@ const char *bt_error_cause_get_module_name(const bt_error_cause *error_cause);
 @bt_pre_not_null{error_cause}
 */
 extern
-const char *bt_error_cause_get_file_name(const bt_error_cause *error_cause);
+const char *bt_error_cause_get_file_name(
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1104,7 +1111,8 @@ const char *bt_error_cause_get_file_name(const bt_error_cause *error_cause);
 @bt_pre_not_null{error_cause}
 */
 extern
-uint64_t bt_error_cause_get_line_number(const bt_error_cause *error_cause);
+uint64_t bt_error_cause_get_line_number(
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1137,7 +1145,7 @@ uint64_t bt_error_cause_get_line_number(const bt_error_cause *error_cause);
 */
 extern
 const char *bt_error_cause_component_actor_get_component_name(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1158,7 +1166,7 @@ const char *bt_error_cause_component_actor_get_component_name(
 */
 extern
 bt_component_class_type bt_error_cause_component_actor_get_component_class_type(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1184,7 +1192,7 @@ bt_component_class_type bt_error_cause_component_actor_get_component_class_type(
 */
 extern
 const char *bt_error_cause_component_actor_get_component_class_name(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1215,7 +1223,7 @@ component class.
 */
 extern
 const char *bt_error_cause_component_actor_get_plugin_name(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1249,7 +1257,7 @@ const char *bt_error_cause_component_actor_get_plugin_name(
 */
 extern
 const char *bt_error_cause_message_iterator_actor_get_component_output_port_name(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1275,7 +1283,7 @@ const char *bt_error_cause_message_iterator_actor_get_component_output_port_name
 */
 extern
 const char *bt_error_cause_message_iterator_actor_get_component_name(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1297,7 +1305,7 @@ const char *bt_error_cause_message_iterator_actor_get_component_name(
 extern
 bt_component_class_type
 bt_error_cause_message_iterator_actor_get_component_class_type(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1323,7 +1331,7 @@ bt_error_cause_message_iterator_actor_get_component_class_type(
 */
 extern
 const char *bt_error_cause_message_iterator_actor_get_component_class_name(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1355,7 +1363,7 @@ component class.
 */
 extern
 const char *bt_error_cause_message_iterator_actor_get_plugin_name(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1384,7 +1392,7 @@ const char *bt_error_cause_message_iterator_actor_get_plugin_name(
 extern
 bt_component_class_type
 bt_error_cause_component_class_actor_get_component_class_type(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1410,7 +1418,7 @@ bt_error_cause_component_class_actor_get_component_class_type(
 */
 extern
 const char *bt_error_cause_component_class_actor_get_component_class_name(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1441,7 +1449,7 @@ component class.
 */
 extern
 const char *bt_error_cause_component_class_actor_get_plugin_name(
-               const bt_error_cause *error_cause);
+               const bt_error_cause *error_cause) __BT_NOEXCEPT;
 
 /*! @} */
 
index bba3882a609b34ab0cf5ea84117223ab344c5ed7..806e4bcba511a1724bdff829ea0432bac564ed6c 100644 (file)
@@ -4,6 +4,8 @@
  * Copyright (C) 2010-2019 EfficiOS Inc. and Linux Foundation
  */
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 /*
  * No include guards here: it is safe to include this file multiple
  * times.
index 20ac581a25273fe558a712861cd4dd0202873bd2..7267e62f1fe95df1ff69f3f7c315083bf2b49570 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_COMPONENT_CLASS_DEV_H
 #define BABELTRACE2_GRAPH_COMPONENT_CLASS_DEV_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -31,7 +33,7 @@ A <strong><em>component class</em></strong> is the class of a \bt_comp:
 
 @attention
     This module (component class development API) offers functions to
-    programatically create component classes. To get the properties of
+    programmatically create component classes. To get the properties of
     an existing component class, see \ref api-comp-cls.
 
 A component class has <em>methods</em>. This module essentially
@@ -1474,7 +1476,8 @@ property values:
 extern
 bt_component_class_source *bt_component_class_source_create(
                const char *name,
-               bt_message_iterator_class *message_iterator_class);
+               bt_message_iterator_class *message_iterator_class)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1518,7 +1521,8 @@ property values:
 extern
 bt_component_class_filter *bt_component_class_filter_create(
                const char *name,
-               bt_message_iterator_class *message_iterator_class);
+               bt_message_iterator_class *message_iterator_class)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1558,7 +1562,8 @@ property values:
 extern
 bt_component_class_sink *bt_component_class_sink_create(
                const char *name,
-               bt_component_class_sink_consume_method consume_method);
+               bt_component_class_sink_consume_method consume_method)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1612,7 +1617,7 @@ See the \ref api-comp-cls-prop-descr "description" property.
 */
 extern bt_component_class_set_description_status
 bt_component_class_set_description(bt_component_class *component_class,
-               const char *description);
+               const char *description) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1659,7 +1664,7 @@ See the \ref api-comp-cls-prop-help "help text" property.
 */
 extern bt_component_class_set_help_status bt_component_class_set_help(
                bt_component_class *component_class,
-               const char *help_text);
+               const char *help_text) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1704,7 +1709,7 @@ See the \ref api-comp-cls-dev-meth-fini "finalize" method.
 extern bt_component_class_set_method_status
 bt_component_class_source_set_finalize_method(
                bt_component_class_source *component_class,
-               bt_component_class_source_finalize_method method);
+               bt_component_class_source_finalize_method method) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1729,7 +1734,7 @@ See the \ref api-comp-cls-dev-meth-fini "finalize" method.
 extern bt_component_class_set_method_status
 bt_component_class_filter_set_finalize_method(
                bt_component_class_filter *component_class,
-               bt_component_class_filter_finalize_method method);
+               bt_component_class_filter_finalize_method method) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1755,7 +1760,7 @@ extern
 bt_component_class_set_method_status
 bt_component_class_sink_set_finalize_method(
                bt_component_class_sink *component_class,
-               bt_component_class_sink_finalize_method method);
+               bt_component_class_sink_finalize_method method) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1781,7 +1786,8 @@ method.
 extern bt_component_class_set_method_status
 bt_component_class_source_set_get_supported_mip_versions_method(
                bt_component_class_source *component_class,
-               bt_component_class_source_get_supported_mip_versions_method method);
+               bt_component_class_source_get_supported_mip_versions_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1807,7 +1813,8 @@ method.
 extern bt_component_class_set_method_status
 bt_component_class_filter_set_get_supported_mip_versions_method(
                bt_component_class_filter *component_class,
-               bt_component_class_filter_get_supported_mip_versions_method method);
+               bt_component_class_filter_get_supported_mip_versions_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1833,7 +1840,8 @@ method.
 extern bt_component_class_set_method_status
 bt_component_class_sink_set_get_supported_mip_versions_method(
                bt_component_class_sink *component_class,
-               bt_component_class_sink_get_supported_mip_versions_method method);
+               bt_component_class_sink_get_supported_mip_versions_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1861,7 +1869,8 @@ extern
 bt_component_class_set_method_status
 bt_component_class_sink_set_graph_is_configured_method(
                bt_component_class_sink *component_class,
-               bt_component_class_sink_graph_is_configured_method method);
+               bt_component_class_sink_graph_is_configured_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1886,7 +1895,8 @@ See the \ref api-comp-cls-dev-meth-init "initialize" method.
 extern bt_component_class_set_method_status
 bt_component_class_source_set_initialize_method(
                bt_component_class_source *component_class,
-               bt_component_class_source_initialize_method method);
+               bt_component_class_source_initialize_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1911,7 +1921,8 @@ See the \ref api-comp-cls-dev-meth-init "initialize" method.
 extern bt_component_class_set_method_status
 bt_component_class_filter_set_initialize_method(
                bt_component_class_filter *component_class,
-               bt_component_class_filter_initialize_method method);
+               bt_component_class_filter_initialize_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1937,7 +1948,7 @@ extern
 bt_component_class_set_method_status
 bt_component_class_sink_set_initialize_method(
                bt_component_class_sink *component_class,
-               bt_component_class_sink_initialize_method method);
+               bt_component_class_sink_initialize_method method) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1964,7 +1975,8 @@ method.
 extern bt_component_class_set_method_status
 bt_component_class_source_set_output_port_connected_method(
                bt_component_class_source *component_class,
-               bt_component_class_source_output_port_connected_method method);
+               bt_component_class_source_output_port_connected_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1991,7 +2003,8 @@ method.
 extern bt_component_class_set_method_status
 bt_component_class_filter_set_input_port_connected_method(
                bt_component_class_filter *component_class,
-               bt_component_class_filter_input_port_connected_method method);
+               bt_component_class_filter_input_port_connected_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2018,7 +2031,8 @@ method.
 extern bt_component_class_set_method_status
 bt_component_class_filter_set_output_port_connected_method(
                bt_component_class_filter *component_class,
-               bt_component_class_filter_output_port_connected_method method);
+               bt_component_class_filter_output_port_connected_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2046,7 +2060,8 @@ extern
 bt_component_class_set_method_status
 bt_component_class_sink_set_input_port_connected_method(
                bt_component_class_sink *component_class,
-               bt_component_class_sink_input_port_connected_method method);
+               bt_component_class_sink_input_port_connected_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2071,7 +2086,7 @@ See the \ref api-comp-cls-dev-meth-query "query" method.
 extern bt_component_class_set_method_status
 bt_component_class_source_set_query_method(
                bt_component_class_source *component_class,
-               bt_component_class_source_query_method method);
+               bt_component_class_source_query_method method) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2096,7 +2111,7 @@ See the \ref api-comp-cls-dev-meth-query "query" method.
 extern bt_component_class_set_method_status
 bt_component_class_filter_set_query_method(
                bt_component_class_filter *component_class,
-               bt_component_class_filter_query_method method);
+               bt_component_class_filter_query_method method) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2122,7 +2137,7 @@ extern
 bt_component_class_set_method_status
 bt_component_class_sink_set_query_method(
                bt_component_class_sink *component_class,
-               bt_component_class_sink_query_method method);
+               bt_component_class_sink_query_method method) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2148,7 +2163,7 @@ bt_component_class_sink_set_query_method(
 */
 static inline
 bt_component_class *bt_component_class_source_as_component_class(
-               bt_component_class_source *component_class)
+               bt_component_class_source *component_class) __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_component_class, component_class);
 }
@@ -2170,7 +2185,7 @@ bt_component_class *bt_component_class_source_as_component_class(
 */
 static inline
 bt_component_class *bt_component_class_filter_as_component_class(
-               bt_component_class_filter *component_class)
+               bt_component_class_filter *component_class) __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_component_class, component_class);
 }
@@ -2192,7 +2207,7 @@ bt_component_class *bt_component_class_filter_as_component_class(
 */
 static inline
 bt_component_class *bt_component_class_sink_as_component_class(
-               bt_component_class_sink *component_class)
+               bt_component_class_sink *component_class) __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_component_class, component_class);
 }
index 66036c22a0629aad34c4347035623ca8ee87a7fb..4fe692f2c88110a112ad9abfe82252253131e0fb 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_COMPONENT_CLASS_H
 #define BABELTRACE2_GRAPH_COMPONENT_CLASS_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -39,7 +41,7 @@ initialization parameters, to create many components with the
 
 There are two ways to obtain a component class:
 
-- Create one programatically: see \ref api-comp-cls-dev.
+- Create one programmatically: see \ref api-comp-cls-dev.
 
 - Borrow one from a \bt_plugin.
 
@@ -236,7 +238,7 @@ typedef enum bt_component_class_type {
     Returns whether or not a component class is a \bt_sink_comp_cls.
 */
 extern bt_component_class_type bt_component_class_get_type(
-               const bt_component_class *component_class);
+               const bt_component_class *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -256,7 +258,7 @@ extern bt_component_class_type bt_component_class_get_type(
 */
 static inline
 bt_bool bt_component_class_is_source(
-               const bt_component_class *component_class)
+               const bt_component_class *component_class) __BT_NOEXCEPT
 {
        return bt_component_class_get_type(component_class) ==
                BT_COMPONENT_CLASS_TYPE_SOURCE;
@@ -280,7 +282,7 @@ bt_bool bt_component_class_is_source(
 */
 static inline
 bt_bool bt_component_class_is_filter(
-               const bt_component_class *component_class)
+               const bt_component_class *component_class) __BT_NOEXCEPT
 {
        return bt_component_class_get_type(component_class) ==
                BT_COMPONENT_CLASS_TYPE_FILTER;
@@ -304,7 +306,7 @@ bt_bool bt_component_class_is_filter(
 */
 static inline
 bt_bool bt_component_class_is_sink(
-               const bt_component_class *component_class)
+               const bt_component_class *component_class) __BT_NOEXCEPT
 {
        return bt_component_class_get_type(component_class) ==
                BT_COMPONENT_CLASS_TYPE_SINK;
@@ -337,7 +339,7 @@ See the \ref api-comp-cls-prop-name "name" property.
 @bt_pre_not_null{component_class}
 */
 extern const char *bt_component_class_get_name(
-               const bt_component_class *component_class);
+               const bt_component_class *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -360,7 +362,7 @@ See the \ref api-comp-cls-prop-descr "description" property.
 @bt_pre_not_null{component_class}
 */
 extern const char *bt_component_class_get_description(
-               const bt_component_class *component_class);
+               const bt_component_class *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -382,7 +384,7 @@ See the \ref api-comp-cls-prop-help "help text" property.
 @bt_pre_not_null{component_class}
 */
 extern const char *bt_component_class_get_help(
-               const bt_component_class *component_class);
+               const bt_component_class *component_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -407,7 +409,7 @@ extern const char *bt_component_class_get_help(
     Decrements the reference count of a component class.
 */
 extern void bt_component_class_get_ref(
-               const bt_component_class *component_class);
+               const bt_component_class *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -425,7 +427,7 @@ extern void bt_component_class_get_ref(
     Increments the reference count of a component class.
 */
 extern void bt_component_class_put_ref(
-               const bt_component_class *component_class);
+               const bt_component_class *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -507,7 +509,7 @@ expression
 static inline
 const bt_component_class *
 bt_component_class_source_as_component_class_const(
-               const bt_component_class_source *component_class)
+               const bt_component_class_source *component_class) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component_class, component_class);
 }
@@ -535,7 +537,7 @@ bt_component_class_source_as_component_class_const(
     Decrements the reference count of a source component class.
 */
 extern void bt_component_class_source_get_ref(
-               const bt_component_class_source *component_class);
+               const bt_component_class_source *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -553,7 +555,7 @@ extern void bt_component_class_source_get_ref(
     Increments the reference count of a source component class.
 */
 extern void bt_component_class_source_put_ref(
-               const bt_component_class_source *component_class);
+               const bt_component_class_source *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -634,7 +636,7 @@ existing \bt_p{_dst} reference.
 static inline
 const bt_component_class *
 bt_component_class_filter_as_component_class_const(
-               const bt_component_class_filter *component_class)
+               const bt_component_class_filter *component_class) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component_class, component_class);
 }
@@ -662,7 +664,7 @@ bt_component_class_filter_as_component_class_const(
     Decrements the reference count of a filter component class.
 */
 extern void bt_component_class_filter_get_ref(
-               const bt_component_class_filter *component_class);
+               const bt_component_class_filter *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -680,7 +682,7 @@ extern void bt_component_class_filter_get_ref(
     Increments the reference count of a filter component class.
 */
 extern void bt_component_class_filter_put_ref(
-               const bt_component_class_filter *component_class);
+               const bt_component_class_filter *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -761,7 +763,7 @@ existing \bt_p{_dst} reference.
 static inline
 const bt_component_class *
 bt_component_class_sink_as_component_class_const(
-               const bt_component_class_sink *component_class)
+               const bt_component_class_sink *component_class) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component_class, component_class);
 }
@@ -789,7 +791,7 @@ bt_component_class_sink_as_component_class_const(
     Decrements the reference count of a sink component class.
 */
 extern void bt_component_class_sink_get_ref(
-               const bt_component_class_sink *component_class);
+               const bt_component_class_sink *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -807,7 +809,7 @@ extern void bt_component_class_sink_get_ref(
     Increments the reference count of a sink component class.
 */
 extern void bt_component_class_sink_put_ref(
-               const bt_component_class_sink *component_class);
+               const bt_component_class_sink *component_class) __BT_NOEXCEPT;
 
 /*!
 @brief
index 41e132bf3391e28ee03f18f392bba0d976c89e31..918bff40cbb3107a2f5a71e8799fa31b7a03da9c 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_COMPONENT_DESCRIPTOR_SET_H
 #define BABELTRACE2_GRAPH_COMPONENT_DESCRIPTOR_SET_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -93,7 +95,8 @@ bt_component_descriptor_set_add_descriptor_with_initialize_method_data().
 @returns
     New component descriptor set reference, or \c NULL on memory error.
 */
-extern bt_component_descriptor_set *bt_component_descriptor_set_create(void);
+extern bt_component_descriptor_set *bt_component_descriptor_set_create(void)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -132,7 +135,7 @@ extern bt_component_descriptor_set_add_descriptor_status
 bt_component_descriptor_set_add_descriptor(
                bt_component_descriptor_set *component_descriptor_set,
                const bt_component_class *component_class,
-               const bt_value *params);
+               const bt_value *params) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -178,7 +181,8 @@ extern bt_component_descriptor_set_add_descriptor_status
 bt_component_descriptor_set_add_descriptor_with_initialize_method_data(
                bt_component_descriptor_set *component_descriptor_set,
                const bt_component_class *component_class,
-               const bt_value *params, void *initialize_method_data);
+               const bt_value *params,
+               void *initialize_method_data) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -203,7 +207,8 @@ bt_component_descriptor_set_add_descriptor_with_initialize_method_data(
     Decrements the reference count of a component descriptor set.
 */
 extern void bt_component_descriptor_set_get_ref(
-               const bt_component_descriptor_set *component_descriptor_set);
+               const bt_component_descriptor_set *component_descriptor_set)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -221,7 +226,8 @@ extern void bt_component_descriptor_set_get_ref(
     Increments the reference count of a component descriptor set.
 */
 extern void bt_component_descriptor_set_put_ref(
-               const bt_component_descriptor_set *component_descriptor_set);
+               const bt_component_descriptor_set *component_descriptor_set)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
index 7999d52402198cdf23c4622486496672b57511ca..1b1925903fc9ae2a83ed6770b258ebaf29a4ee04 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_COMPONENT_H
 #define BABELTRACE2_GRAPH_COMPONENT_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -238,7 +240,7 @@ A component has the following common properties:
     Returns whether or not a component is a \bt_sink_comp.
 */
 extern bt_component_class_type bt_component_get_class_type(
-               const bt_component *component);
+               const bt_component *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -257,7 +259,7 @@ extern bt_component_class_type bt_component_get_class_type(
     Returns the type enumerator of a component's class.
 */
 static inline
-bt_bool bt_component_is_source(const bt_component *component)
+bt_bool bt_component_is_source(const bt_component *component) __BT_NOEXCEPT
 {
        return bt_component_get_class_type(component) ==
                BT_COMPONENT_CLASS_TYPE_SOURCE;
@@ -280,7 +282,7 @@ bt_bool bt_component_is_source(const bt_component *component)
     Returns the type enumerator of a component's class.
 */
 static inline
-bt_bool bt_component_is_filter(const bt_component *component)
+bt_bool bt_component_is_filter(const bt_component *component) __BT_NOEXCEPT
 {
        return bt_component_get_class_type(component) ==
                BT_COMPONENT_CLASS_TYPE_FILTER;
@@ -303,7 +305,7 @@ bt_bool bt_component_is_filter(const bt_component *component)
     Returns the type enumerator of a component's class.
 */
 static inline
-bt_bool bt_component_is_sink(const bt_component *component)
+bt_bool bt_component_is_sink(const bt_component *component) __BT_NOEXCEPT
 {
        return bt_component_get_class_type(component) ==
                BT_COMPONENT_CLASS_TYPE_SINK;
@@ -330,7 +332,7 @@ bt_bool bt_component_is_sink(const bt_component *component)
 @bt_pre_not_null{component}
 */
 extern const bt_component_class *bt_component_borrow_class_const(
-               const bt_component *component);
+               const bt_component *component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -358,7 +360,8 @@ See the \ref api-comp-prop-name "name" property.
 
 @bt_pre_not_null{component}
 */
-extern const char *bt_component_get_name(const bt_component *component);
+extern const char *bt_component_get_name(const bt_component *component)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -376,7 +379,7 @@ See the \ref api-comp-prop-log-lvl "logging level" property.
 @bt_pre_not_null{component}
 */
 extern bt_logging_level bt_component_get_logging_level(
-               const bt_component *component);
+               const bt_component *component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -400,7 +403,7 @@ extern bt_logging_level bt_component_get_logging_level(
 @sa bt_component_put_ref() &mdash;
     Decrements the reference count of a component.
 */
-extern void bt_component_get_ref(const bt_component *component);
+extern void bt_component_get_ref(const bt_component *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -417,7 +420,7 @@ extern void bt_component_get_ref(const bt_component *component);
 @sa bt_component_get_ref() &mdash;
     Increments the reference count of a component.
 */
-extern void bt_component_put_ref(const bt_component *component);
+extern void bt_component_put_ref(const bt_component *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -493,7 +496,7 @@ This macro effectively moves a component reference from the expression
 */
 extern const bt_component_class_source *
 bt_component_source_borrow_class_const(
-               const bt_component_source *component);
+               const bt_component_source *component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -519,7 +522,7 @@ bt_component_source_borrow_class_const(
 */
 static inline
 const bt_component *bt_component_source_as_component_const(
-               const bt_component_source *component)
+               const bt_component_source *component) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component, component);
 }
@@ -545,7 +548,7 @@ const bt_component *bt_component_source_as_component_const(
 @bt_pre_not_null{component}
 */
 extern uint64_t bt_component_source_get_output_port_count(
-               const bt_component_source *component);
+               const bt_component_source *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -578,7 +581,8 @@ extern uint64_t bt_component_source_get_output_port_count(
 */
 extern const bt_port_output *
 bt_component_source_borrow_output_port_by_index_const(
-               const bt_component_source *component, uint64_t index);
+               const bt_component_source *component,
+               uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -608,7 +612,8 @@ returns \c NULL.
 */
 extern const bt_port_output *
 bt_component_source_borrow_output_port_by_name_const(
-               const bt_component_source *component, const char *name);
+               const bt_component_source *component,
+               const char *name) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -633,7 +638,7 @@ bt_component_source_borrow_output_port_by_name_const(
     Decrements the reference count of a source component.
 */
 extern void bt_component_source_get_ref(
-               const bt_component_source *component);
+               const bt_component_source *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -651,7 +656,7 @@ extern void bt_component_source_get_ref(
     Increments the reference count of a source component.
 */
 extern void bt_component_source_put_ref(
-               const bt_component_source *component);
+               const bt_component_source *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -727,7 +732,7 @@ existing \bt_p{_dst} reference.
 */
 extern const bt_component_class_filter *
 bt_component_filter_borrow_class_const(
-               const bt_component_filter *component);
+               const bt_component_filter *component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -753,7 +758,7 @@ bt_component_filter_borrow_class_const(
 */
 static inline
 const bt_component *bt_component_filter_as_component_const(
-               const bt_component_filter *component)
+               const bt_component_filter *component) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component, component);
 }
@@ -779,7 +784,7 @@ const bt_component *bt_component_filter_as_component_const(
 @bt_pre_not_null{component}
 */
 extern uint64_t bt_component_filter_get_input_port_count(
-               const bt_component_filter *component);
+               const bt_component_filter *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -812,7 +817,8 @@ extern uint64_t bt_component_filter_get_input_port_count(
 */
 extern const bt_port_input *
 bt_component_filter_borrow_input_port_by_index_const(
-               const bt_component_filter *component, uint64_t index);
+               const bt_component_filter *component, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -842,7 +848,8 @@ returns \c NULL.
 */
 extern const bt_port_input *
 bt_component_filter_borrow_input_port_by_name_const(
-               const bt_component_filter *component, const char *name);
+               const bt_component_filter *component, const char *name)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -858,7 +865,7 @@ bt_component_filter_borrow_input_port_by_name_const(
 @bt_pre_not_null{component}
 */
 extern uint64_t bt_component_filter_get_output_port_count(
-               const bt_component_filter *component);
+               const bt_component_filter *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -891,7 +898,8 @@ extern uint64_t bt_component_filter_get_output_port_count(
 */
 extern const bt_port_output *
 bt_component_filter_borrow_output_port_by_index_const(
-               const bt_component_filter *component, uint64_t index);
+               const bt_component_filter *component, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -921,7 +929,8 @@ returns \c NULL.
 */
 extern const bt_port_output *
 bt_component_filter_borrow_output_port_by_name_const(
-               const bt_component_filter *component, const char *name);
+               const bt_component_filter *component, const char *name)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -946,7 +955,7 @@ bt_component_filter_borrow_output_port_by_name_const(
     Decrements the reference count of a filter component.
 */
 extern void bt_component_filter_get_ref(
-               const bt_component_filter *component);
+               const bt_component_filter *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -964,7 +973,7 @@ extern void bt_component_filter_get_ref(
     Increments the reference count of a filter component.
 */
 extern void bt_component_filter_put_ref(
-               const bt_component_filter *component);
+               const bt_component_filter *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1040,7 +1049,7 @@ existing \bt_p{_dst} reference.
 */
 extern const bt_component_class_sink *
 bt_component_sink_borrow_class_const(
-               const bt_component_sink *component);
+               const bt_component_sink *component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1066,7 +1075,7 @@ bt_component_sink_borrow_class_const(
 */
 static inline
 const bt_component *bt_component_sink_as_component_const(
-               const bt_component_sink *component)
+               const bt_component_sink *component) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component, component);
 }
@@ -1092,7 +1101,7 @@ const bt_component *bt_component_sink_as_component_const(
 @bt_pre_not_null{component}
 */
 extern uint64_t bt_component_sink_get_input_port_count(
-               const bt_component_sink *component);
+               const bt_component_sink *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1125,7 +1134,8 @@ extern uint64_t bt_component_sink_get_input_port_count(
 */
 extern const bt_port_input *
 bt_component_sink_borrow_input_port_by_index_const(
-               const bt_component_sink *component, uint64_t index);
+               const bt_component_sink *component, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1155,7 +1165,8 @@ returns \c NULL.
 */
 extern const bt_port_input *
 bt_component_sink_borrow_input_port_by_name_const(
-               const bt_component_sink *component, const char *name);
+               const bt_component_sink *component, const char *name)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1180,7 +1191,7 @@ bt_component_sink_borrow_input_port_by_name_const(
     Decrements the reference count of a sink component.
 */
 extern void bt_component_sink_get_ref(
-               const bt_component_sink *component);
+               const bt_component_sink *component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1198,7 +1209,7 @@ extern void bt_component_sink_get_ref(
     Increments the reference count of a sink component.
 */
 extern void bt_component_sink_put_ref(
-               const bt_component_sink *component);
+               const bt_component_sink *component) __BT_NOEXCEPT;
 
 /*!
 @brief
index 3e6d45feb1f8377ed8ed1f31c139c5c7b063e03a..8400522f3ae31c208d2b08dc4facfc54a668624c 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_CONNECTION_H
 #define BABELTRACE2_GRAPH_CONNECTION_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -72,7 +74,7 @@ bt_connection_borrow_downstream_port_const().
 @bt_pre_not_null{connection}
 */
 extern const bt_port_input *bt_connection_borrow_downstream_port_const(
-               const bt_connection *connection);
+               const bt_connection *connection) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -88,7 +90,7 @@ extern const bt_port_input *bt_connection_borrow_downstream_port_const(
 @bt_pre_not_null{connection}
 */
 extern const bt_port_output *bt_connection_borrow_upstream_port_const(
-               const bt_connection *connection);
+               const bt_connection *connection) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -112,7 +114,8 @@ extern const bt_port_output *bt_connection_borrow_upstream_port_const(
 @sa bt_connection_put_ref() &mdash;
     Decrements the reference count of a connection.
 */
-extern void bt_connection_get_ref(const bt_connection *connection);
+extern void bt_connection_get_ref(const bt_connection *connection)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -129,7 +132,8 @@ extern void bt_connection_get_ref(const bt_connection *connection);
 @sa bt_connection_get_ref() &mdash;
     Increments the reference count of a connection.
 */
-extern void bt_connection_put_ref(const bt_connection *connection);
+extern void bt_connection_put_ref(const bt_connection *connection)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
index f3d0ffb5d1be619f57d4a1aa2c26126a0d13ade7..0f80a1e148ff58794ee7cc02cf6fbf69ab48cc68 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_GRAPH_H
 #define BABELTRACE2_GRAPH_GRAPH_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -558,7 +560,7 @@ default interrupter with bt_graph_borrow_default_interrupter().
 @pre
     \bt_p{mip_version} is 0.
 */
-extern bt_graph *bt_graph_create(uint64_t mip_version);
+extern bt_graph *bt_graph_create(uint64_t mip_version) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -602,7 +604,7 @@ bt_graph_add_source_component(bt_graph *graph,
                const bt_component_class_source *component_class,
                const char *name, const bt_value *params,
                bt_logging_level logging_level,
-               const bt_component_source **component);
+               const bt_component_source **component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -683,7 +685,7 @@ bt_graph_add_source_component_with_initialize_method_data(
                const bt_component_class_source *component_class,
                const char *name, const bt_value *params,
                void *initialize_method_data, bt_logging_level logging_level,
-               const bt_component_source **component);
+               const bt_component_source **component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -695,7 +697,7 @@ bt_graph_add_filter_component(bt_graph *graph,
                const bt_component_class_filter *component_class,
                const char *name, const bt_value *params,
                bt_logging_level logging_level,
-               const bt_component_filter **component);
+               const bt_component_filter **component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -776,7 +778,7 @@ bt_graph_add_filter_component_with_initialize_method_data(
                const bt_component_class_filter *component_class,
                const char *name, const bt_value *params,
                void *initialize_method_data, bt_logging_level logging_level,
-               const bt_component_filter **component);
+               const bt_component_filter **component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -788,7 +790,7 @@ bt_graph_add_sink_component(
                bt_graph *graph, const bt_component_class_sink *component_class,
                const char *name, const bt_value *params,
                bt_logging_level logging_level,
-               const bt_component_sink **component);
+               const bt_component_sink **component) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -868,7 +870,7 @@ bt_graph_add_sink_component_with_initialize_method_data(
                bt_graph *graph, const bt_component_class_sink *component_class,
                const char *name, const bt_value *params,
                void *initialize_method_data, bt_logging_level logging_level,
-               const bt_component_sink **component);
+               const bt_component_sink **component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1128,7 +1130,7 @@ bt_graph_add_simple_sink_component(bt_graph *graph, const char *name,
                bt_graph_simple_sink_component_initialize_func initialize_func,
                bt_graph_simple_sink_component_consume_func consume_func,
                bt_graph_simple_sink_component_finalize_func finalize_func,
-               void *user_data, const bt_component_sink **component);
+               void *user_data, const bt_component_sink **component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1217,7 +1219,7 @@ this function.
 extern bt_graph_connect_ports_status bt_graph_connect_ports(bt_graph *graph,
                const bt_port_output *upstream_port,
                const bt_port_input *downstream_port,
-               const bt_connection **connection);
+               const bt_connection **connection) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1333,7 +1335,7 @@ and what you can and cannot do with a configured graph.
     Calls a single trace processing graph's sink component's consuming
     method once.
 */
-extern bt_graph_run_status bt_graph_run(bt_graph *graph);
+extern bt_graph_run_status bt_graph_run(bt_graph *graph) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1441,7 +1443,7 @@ and what you can and cannot do with a configured graph.
     Runs a trace processing graph, making all its sink components
     consume in a round robin fashion.
 */
-extern bt_graph_run_once_status bt_graph_run_once(bt_graph *graph);
+extern bt_graph_run_once_status bt_graph_run_once(bt_graph *graph) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1515,7 +1517,7 @@ graph's interrupters is set. If so, bt_graph_run() returns
     Borrows the default interrupter from a trace processing graph.
 */
 extern bt_graph_add_interrupter_status bt_graph_add_interrupter(bt_graph *graph,
-               const bt_interrupter *interrupter);
+               const bt_interrupter *interrupter) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1537,7 +1539,8 @@ extern bt_graph_add_interrupter_status bt_graph_add_interrupter(bt_graph *graph,
 @sa bt_graph_add_interrupter() &mdash;
     Adds an interrupter to a trace processing graph.
 */
-extern bt_interrupter *bt_graph_borrow_default_interrupter(bt_graph *graph);
+extern bt_interrupter *bt_graph_borrow_default_interrupter(bt_graph *graph)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1666,7 +1669,7 @@ extern bt_graph_add_listener_status
 bt_graph_add_filter_component_input_port_added_listener(
                bt_graph *graph,
                bt_graph_filter_component_input_port_added_listener_func user_func,
-               void *user_data, bt_listener_id *listener_id);
+               void *user_data, bt_listener_id *listener_id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1742,7 +1745,7 @@ extern bt_graph_add_listener_status
 bt_graph_add_sink_component_input_port_added_listener(
                bt_graph *graph,
                bt_graph_sink_component_input_port_added_listener_func user_func,
-               void *user_data, bt_listener_id *listener_id);
+               void *user_data, bt_listener_id *listener_id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1818,7 +1821,7 @@ extern bt_graph_add_listener_status
 bt_graph_add_source_component_output_port_added_listener(
                bt_graph *graph,
                bt_graph_source_component_output_port_added_listener_func user_func,
-               void *user_data, bt_listener_id *listener_id);
+               void *user_data, bt_listener_id *listener_id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1894,7 +1897,7 @@ extern bt_graph_add_listener_status
 bt_graph_add_filter_component_output_port_added_listener(
                bt_graph *graph,
                bt_graph_filter_component_output_port_added_listener_func user_func,
-               void *user_data, bt_listener_id *listener_id);
+               void *user_data, bt_listener_id *listener_id) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1918,7 +1921,7 @@ bt_graph_add_filter_component_output_port_added_listener(
 @sa bt_graph_put_ref() &mdash;
     Decrements the reference count of a trace processing graph.
 */
-extern void bt_graph_get_ref(const bt_graph *graph);
+extern void bt_graph_get_ref(const bt_graph *graph) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1935,7 +1938,7 @@ extern void bt_graph_get_ref(const bt_graph *graph);
 @sa bt_graph_get_ref() &mdash;
     Increments the reference count of a trace processing graph.
 */
-extern void bt_graph_put_ref(const bt_graph *graph);
+extern void bt_graph_put_ref(const bt_graph *graph) __BT_NOEXCEPT;
 
 /*!
 @brief
index 00b9512fba6a046ad4c75b9e1e3542661fdf2158..1c8729b4724dbd80b005b4228c7af93e07251627 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_INTERRUPTER_H
 #define BABELTRACE2_GRAPH_INTERRUPTER_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -106,7 +108,7 @@ On success, the returned interrupter is \em not set
 @returns
     New interrupter reference, or \c NULL on memory error.
 */
-extern bt_interrupter *bt_interrupter_create(void);
+extern bt_interrupter *bt_interrupter_create(void) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -132,7 +134,7 @@ After you call this function, bt_interrupter_is_set() returns
 @sa bt_interrupter_is_set() &mdash;
     Returns whether or not an interrupter is set.
 */
-extern void bt_interrupter_set(bt_interrupter *interrupter);
+extern void bt_interrupter_set(bt_interrupter *interrupter) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -151,7 +153,7 @@ After you call this function, bt_interrupter_is_set() returns
 @sa bt_interrupter_is_set() &mdash;
     Returns whether or not an interrupter is set.
 */
-extern void bt_interrupter_reset(bt_interrupter *interrupter);
+extern void bt_interrupter_reset(bt_interrupter *interrupter) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -170,7 +172,8 @@ extern void bt_interrupter_reset(bt_interrupter *interrupter);
 @sa bt_interrupter_reset() &mdash;
     Resets an interrupter.
 */
-extern bt_bool bt_interrupter_is_set(const bt_interrupter *interrupter);
+extern bt_bool bt_interrupter_is_set(const bt_interrupter *interrupter)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -194,7 +197,8 @@ extern bt_bool bt_interrupter_is_set(const bt_interrupter *interrupter);
 @sa bt_interrupter_put_ref() &mdash;
     Decrements the reference count of an interrupter.
 */
-extern void bt_interrupter_get_ref(const bt_interrupter *interrupter);
+extern void bt_interrupter_get_ref(const bt_interrupter *interrupter)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -211,7 +215,8 @@ extern void bt_interrupter_get_ref(const bt_interrupter *interrupter);
 @sa bt_interrupter_get_ref() &mdash;
     Increments the reference count of an interrupter.
 */
-extern void bt_interrupter_put_ref(const bt_interrupter *interrupter);
+extern void bt_interrupter_put_ref(const bt_interrupter *interrupter)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
index 05c58987f49209d9fbf047dd6da62c7dc2e22dcf..82ee1f2d6e4cdb579f1859d1c3977526fe06a146 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_MESSAGE_ITERATOR_CLASS_H
 #define BABELTRACE2_GRAPH_MESSAGE_ITERATOR_CLASS_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -417,21 +419,21 @@ The available message iterator class methods to implement are:
     this method, then the next time your
     \link api-msg-iter-cls-meth-next "next" method\endlink is called:
 
-    - For each "active" \bt_stream at the seeked time point, you must
+    - For each "active" \bt_stream at the sought time point, you must
       emit a \bt_sb_msg for this stream before you emit any other
       message for this stream.
 
       The stream beginning message must have a
       \ref api-msg-sb-prop-cs "default clock snapshot" which corresponds
-      to the seeked time point.
+      to the sought time point.
 
-    - For each "active" \bt_pkt at the seeked time point, you must
+    - For each "active" \bt_pkt at the sought time point, you must
       emit a \bt_pb_msg for this packet before you emit any other
       message for this packet.
 
       The packet beginning message must have a
       \ref api-msg-pb-prop-cs "default clock snapshot" which corresponds
-      to the seeked time point.
+      to the sought time point.
 
     The \bt_name project recommends that this method executes fast
     enough so as not to block an interactive application running on the
@@ -975,7 +977,8 @@ typedef bt_message_iterator_class_seek_ns_from_origin_method_status
 */
 extern bt_message_iterator_class *
 bt_message_iterator_class_create(
-               bt_message_iterator_class_next_method next_method);
+               bt_message_iterator_class_next_method next_method)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1020,7 +1023,7 @@ See the \ref api-msg-iter-cls-meth-fini "finalize" method.
 extern bt_message_iterator_class_set_method_status
 bt_message_iterator_class_set_finalize_method(
                bt_message_iterator_class *message_iterator_class,
-               bt_message_iterator_class_finalize_method method);
+               bt_message_iterator_class_finalize_method method) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1045,7 +1048,8 @@ See the \ref api-msg-iter-cls-meth-init "initialize" method.
 extern bt_message_iterator_class_set_method_status
 bt_message_iterator_class_set_initialize_method(
                bt_message_iterator_class *message_iterator_class,
-               bt_message_iterator_class_initialize_method method);
+               bt_message_iterator_class_initialize_method method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1082,7 +1086,8 @@ extern bt_message_iterator_class_set_method_status
 bt_message_iterator_class_set_seek_beginning_methods(
                bt_message_iterator_class *message_iterator_class,
                bt_message_iterator_class_seek_beginning_method seek_method,
-               bt_message_iterator_class_can_seek_beginning_method can_seek_method);
+               bt_message_iterator_class_can_seek_beginning_method can_seek_method)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1121,7 +1126,8 @@ extern bt_message_iterator_class_set_method_status
 bt_message_iterator_class_set_seek_ns_from_origin_methods(
                bt_message_iterator_class *message_iterator_class,
                bt_message_iterator_class_seek_ns_from_origin_method seek_method,
-               bt_message_iterator_class_can_seek_ns_from_origin_method can_seek_method);
+               bt_message_iterator_class_can_seek_ns_from_origin_method can_seek_method)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1146,7 +1152,8 @@ bt_message_iterator_class_set_seek_ns_from_origin_methods(
     Decrements the reference count of a message iterator class.
 */
 extern void bt_message_iterator_class_get_ref(
-               const bt_message_iterator_class *message_iterator_class);
+               const bt_message_iterator_class *message_iterator_class)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1164,7 +1171,8 @@ extern void bt_message_iterator_class_get_ref(
     Increments the reference count of a message iterator class.
 */
 extern void bt_message_iterator_class_put_ref(
-               const bt_message_iterator_class *message_iterator_class);
+               const bt_message_iterator_class *message_iterator_class)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
index 123e4771261b4d537607b483d18c0e3b2994f72c..1a39dfc56284512fb5f84b628e20f24df7237ca0 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_MESSAGE_ITERATOR_H
 #define BABELTRACE2_GRAPH_MESSAGE_ITERATOR_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -262,7 +264,7 @@ extern bt_message_iterator_create_from_message_iterator_status
 bt_message_iterator_create_from_message_iterator(
                bt_self_message_iterator *self_message_iterator,
                bt_self_component_port_input *port,
-               bt_message_iterator **message_iterator);
+               bt_message_iterator **message_iterator) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -326,7 +328,7 @@ extern bt_message_iterator_create_from_sink_component_status
 bt_message_iterator_create_from_sink_component(
                bt_self_component_sink *self_component_sink,
                bt_self_component_port_input *port,
-               bt_message_iterator **message_iterator);
+               bt_message_iterator **message_iterator) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -351,7 +353,7 @@ bt_message_iterator_create_from_sink_component(
 */
 extern bt_component *
 bt_message_iterator_borrow_component(
-               bt_message_iterator *message_iterator);
+               bt_message_iterator *message_iterator) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -452,7 +454,8 @@ messages.
 */
 extern bt_message_iterator_next_status
 bt_message_iterator_next(bt_message_iterator *message_iterator,
-               bt_message_array_const *messages, uint64_t *count);
+               bt_message_array_const *messages, uint64_t *count)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -528,7 +531,7 @@ call bt_message_iterator_seek_beginning().
 extern bt_message_iterator_can_seek_beginning_status
 bt_message_iterator_can_seek_beginning(
                bt_message_iterator *message_iterator,
-               bt_bool *can_seek_beginning);
+               bt_bool *can_seek_beginning) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -594,7 +597,7 @@ without performing any other \ref api-msg-iter-ops "operation" on
 */
 extern bt_message_iterator_seek_beginning_status
 bt_message_iterator_seek_beginning(
-               bt_message_iterator *message_iterator);
+               bt_message_iterator *message_iterator) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -671,7 +674,8 @@ call bt_message_iterator_seek_ns_from_origin().
 extern bt_message_iterator_can_seek_ns_from_origin_status
 bt_message_iterator_can_seek_ns_from_origin(
                bt_message_iterator *message_iterator,
-               int64_t ns_from_origin, bt_bool *can_seek_ns_from_origin);
+               int64_t ns_from_origin, bt_bool *can_seek_ns_from_origin)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -743,7 +747,7 @@ without performing any other \ref api-msg-iter-ops "operation" on
 extern bt_message_iterator_seek_ns_from_origin_status
 bt_message_iterator_seek_ns_from_origin(
                bt_message_iterator *message_iterator,
-               int64_t ns_from_origin);
+               int64_t ns_from_origin) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -771,7 +775,7 @@ message sequence have some \bt_cs.
 */
 extern bt_bool
 bt_message_iterator_can_seek_forward(
-               bt_message_iterator *message_iterator);
+               bt_message_iterator *message_iterator) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -796,7 +800,7 @@ bt_message_iterator_can_seek_forward(
     Decrements the reference count of a message iterator.
 */
 extern void bt_message_iterator_get_ref(
-               const bt_message_iterator *message_iterator);
+               const bt_message_iterator *message_iterator) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -814,7 +818,7 @@ extern void bt_message_iterator_get_ref(
     Increments the reference count of a message iterator.
 */
 extern void bt_message_iterator_put_ref(
-               const bt_message_iterator *message_iterator);
+               const bt_message_iterator *message_iterator) __BT_NOEXCEPT;
 
 /*!
 @brief
index c5a75bda2eff7233b0f98c2410e4674e8cebbb53..a73d6e1cc240d7053e0af421d32d9d902e992c9f 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_MESSAGE_H
 #define BABELTRACE2_GRAPH_MESSAGE_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -790,7 +792,7 @@ A message iterator inactivity message has the following property:
 <h1>\anchor api-msg-mip Message Interchange Protocol</h1>
 
 The <em>Message Interchange Protocol</em> (MIP) is the system of rules
-used by \bt_p_comp and \bt_p_msg_iter to exchance messages within a
+used by \bt_p_comp and \bt_p_msg_iter to exchange messages within a
 trace processing graph.
 
 The MIP covers everything related to messages and what they contain, as
@@ -1023,7 +1025,8 @@ typedef enum bt_message_type {
 
 @bt_pre_not_null{message}
 */
-extern bt_message_type bt_message_get_type(const bt_message *message);
+extern bt_message_type bt_message_get_type(const bt_message *message)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1097,7 +1100,7 @@ property values:
 extern
 bt_message *bt_message_stream_beginning_create(
                bt_self_message_iterator *self_message_iterator,
-               const bt_stream *stream);
+               const bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1122,7 +1125,7 @@ See the \ref api-msg-sb-prop-stream "stream" property.
     \c const version of this function.
 */
 extern bt_stream *bt_message_stream_beginning_borrow_stream(
-               bt_message *message);
+               bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1132,7 +1135,7 @@ extern bt_stream *bt_message_stream_beginning_borrow_stream(
 See bt_message_stream_beginning_borrow_stream().
 */
 extern const bt_stream *bt_message_stream_beginning_borrow_stream_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1160,7 +1163,7 @@ See the \ref api-msg-sb-prop-cs "default clock snapshot" property.
 */
 extern
 void bt_message_stream_beginning_set_default_clock_snapshot(
-               bt_message *message, uint64_t value);
+               bt_message *message, uint64_t value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1196,7 +1199,7 @@ See the \ref api-msg-sb-prop-cs "default clock snapshot" property.
 extern bt_message_stream_clock_snapshot_state
 bt_message_stream_beginning_borrow_default_clock_snapshot_const(
                const bt_message *message,
-               const bt_clock_snapshot **clock_snapshot);
+               const bt_clock_snapshot **clock_snapshot) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1228,7 +1231,7 @@ bt_stream_class_borrow_default_clock_class_const(
 */
 extern const bt_clock_class *
 bt_message_stream_beginning_borrow_stream_class_default_clock_class_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1274,7 +1277,7 @@ property values:
 extern
 bt_message *bt_message_stream_end_create(
                bt_self_message_iterator *self_message_iterator,
-               const bt_stream *stream);
+               const bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1299,7 +1302,7 @@ See the \ref api-msg-se-prop-stream "stream" property.
     \c const version of this function.
 */
 extern bt_stream *bt_message_stream_end_borrow_stream(
-               bt_message *message);
+               bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1309,7 +1312,7 @@ extern bt_stream *bt_message_stream_end_borrow_stream(
 See bt_message_stream_end_borrow_stream().
 */
 extern const bt_stream *bt_message_stream_end_borrow_stream_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1337,7 +1340,7 @@ See the \ref api-msg-se-prop-cs "default clock snapshot" property.
 */
 extern
 void bt_message_stream_end_set_default_clock_snapshot(
-               bt_message *message, uint64_t value);
+               bt_message *message, uint64_t value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1373,7 +1376,7 @@ See the \ref api-msg-se-prop-cs "default clock snapshot" property.
 extern bt_message_stream_clock_snapshot_state
 bt_message_stream_end_borrow_default_clock_snapshot_const(
                const bt_message *message,
-               const bt_clock_snapshot **clock_snapshot);
+               const bt_clock_snapshot **clock_snapshot) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1405,7 +1408,7 @@ bt_stream_class_borrow_default_clock_class_const(
 */
 extern const bt_clock_class *
 bt_message_stream_end_borrow_stream_class_default_clock_class_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1492,7 +1495,7 @@ extern
 bt_message *bt_message_event_create(
                bt_self_message_iterator *self_message_iterator,
                const bt_event_class *event_class,
-               const bt_stream *stream);
+               const bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1576,7 +1579,8 @@ extern
 bt_message *bt_message_event_create_with_default_clock_snapshot(
                bt_self_message_iterator *self_message_iterator,
                const bt_event_class *event_class,
-               const bt_stream *stream, uint64_t clock_snapshot_value);
+               const bt_stream *stream, uint64_t clock_snapshot_value)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1661,7 +1665,7 @@ extern
 bt_message *bt_message_event_create_with_packet(
                bt_self_message_iterator *self_message_iterator,
                const bt_event_class *event_class,
-               const bt_packet *packet);
+               const bt_packet *packet) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1750,7 +1754,8 @@ extern
 bt_message *bt_message_event_create_with_packet_and_default_clock_snapshot(
                bt_self_message_iterator *self_message_iterator,
                const bt_event_class *event_class,
-               const bt_packet *packet, uint64_t clock_snapshot_value);
+               const bt_packet *packet, uint64_t clock_snapshot_value)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1774,8 +1779,8 @@ See the \ref api-msg-ev-prop-ev "event" property.
 @sa bt_message_event_borrow_event_const() &mdash;
     \c const version of this function.
 */
-extern bt_event *bt_message_event_borrow_event(
-               bt_message *message);
+extern bt_event *bt_message_event_borrow_event(bt_message *message)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1785,7 +1790,7 @@ extern bt_event *bt_message_event_borrow_event(
 See bt_message_event_borrow_event().
 */
 extern const bt_event *bt_message_event_borrow_event_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1806,7 +1811,8 @@ See the \ref api-msg-ev-prop-cs "default clock snapshot" property.
     \ref api-tir-stream-cls-prop-def-clock-cls "default clock class".
 */
 extern const bt_clock_snapshot *
-bt_message_event_borrow_default_clock_snapshot_const(const bt_message *message);
+bt_message_event_borrow_default_clock_snapshot_const(const bt_message *message)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1839,7 +1845,7 @@ bt_stream_class_borrow_default_clock_class_const(
 */
 extern const bt_clock_class *
 bt_message_event_borrow_stream_class_default_clock_class_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1907,7 +1913,7 @@ property values:
 extern
 bt_message *bt_message_packet_beginning_create(
                bt_self_message_iterator *self_message_iterator,
-               const bt_packet *packet);
+               const bt_packet *packet) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1972,7 +1978,8 @@ property values:
 extern
 bt_message *bt_message_packet_beginning_create_with_default_clock_snapshot(
                bt_self_message_iterator *self_message_iterator,
-               const bt_packet *packet, uint64_t clock_snapshot_value);
+               const bt_packet *packet, uint64_t clock_snapshot_value)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1997,7 +2004,7 @@ See the \ref api-msg-pb-prop-pkt "packet" property.
     \c const version of this function.
 */
 extern bt_packet *bt_message_packet_beginning_borrow_packet(
-               bt_message *message);
+               bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2007,7 +2014,7 @@ extern bt_packet *bt_message_packet_beginning_borrow_packet(
 See bt_message_packet_beginning_borrow_packet().
 */
 extern const bt_packet *bt_message_packet_beginning_borrow_packet_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2030,7 +2037,7 @@ See the \ref api-msg-pb-prop-cs "default clock snapshot" property.
 */
 extern const bt_clock_snapshot *
 bt_message_packet_beginning_borrow_default_clock_snapshot_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2063,7 +2070,7 @@ bt_stream_class_borrow_default_clock_class_const(
 */
 extern const bt_clock_class *
 bt_message_packet_beginning_borrow_stream_class_default_clock_class_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2130,7 +2137,7 @@ property values:
 extern
 bt_message *bt_message_packet_end_create(
                bt_self_message_iterator *self_message_iterator,
-               const bt_packet *packet);
+               const bt_packet *packet) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2195,7 +2202,8 @@ property values:
 extern
 bt_message *bt_message_packet_end_create_with_default_clock_snapshot(
                bt_self_message_iterator *self_message_iterator,
-               const bt_packet *packet, uint64_t clock_snapshot_value);
+               const bt_packet *packet, uint64_t clock_snapshot_value)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2220,7 +2228,7 @@ See the \ref api-msg-pe-prop-pkt "packet" property.
     \c const version of this function.
 */
 extern bt_packet *bt_message_packet_end_borrow_packet(
-               bt_message *message);
+               bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2230,7 +2238,7 @@ extern bt_packet *bt_message_packet_end_borrow_packet(
 See bt_message_packet_end_borrow_packet().
 */
 extern const bt_packet *bt_message_packet_end_borrow_packet_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2253,7 +2261,7 @@ See the \ref api-msg-pe-prop-cs "default clock snapshot" property.
 */
 extern const bt_clock_snapshot *
 bt_message_packet_end_borrow_default_clock_snapshot_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2286,7 +2294,7 @@ bt_stream_class_borrow_default_clock_class_const(
 */
 extern const bt_clock_class *
 bt_message_packet_end_borrow_stream_class_default_clock_class_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2355,7 +2363,7 @@ property values:
 */
 extern bt_message *bt_message_discarded_events_create(
                bt_self_message_iterator *self_message_iterator,
-               const bt_stream *stream);
+               const bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2428,7 +2436,8 @@ extern bt_message *bt_message_discarded_events_create_with_default_clock_snapsho
                bt_self_message_iterator *self_message_iterator,
                const bt_stream *stream,
                uint64_t beginning_clock_snapshot_value,
-               uint64_t end_clock_snapshot_value);
+               uint64_t end_clock_snapshot_value)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2453,7 +2462,7 @@ See the \ref api-msg-disc-ev-prop-stream "stream" property.
     \c const version of this function.
 */
 extern bt_stream *bt_message_discarded_events_borrow_stream(
-               bt_message *message);
+               bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2463,7 +2472,8 @@ extern bt_stream *bt_message_discarded_events_borrow_stream(
 See bt_message_discarded_events_borrow_stream().
 */
 extern const bt_stream *
-bt_message_discarded_events_borrow_stream_const(const bt_message *message);
+bt_message_discarded_events_borrow_stream_const(const bt_message *message)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2490,7 +2500,7 @@ property.
 */
 extern const bt_clock_snapshot *
 bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2517,7 +2527,7 @@ property.
 */
 extern const bt_clock_snapshot *
 bt_message_discarded_events_borrow_end_default_clock_snapshot_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2549,7 +2559,7 @@ bt_stream_class_borrow_default_clock_class_const(
 */
 extern const bt_clock_class *
 bt_message_discarded_events_borrow_stream_class_default_clock_class_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2576,7 +2586,7 @@ property.
     message.
 */
 extern void bt_message_discarded_events_set_count(bt_message *message,
-               uint64_t count);
+               uint64_t count) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2607,7 +2617,7 @@ property.
     Sets the number of discarded events of a discarded events message.
 */
 extern bt_property_availability bt_message_discarded_events_get_count(
-               const bt_message *message, uint64_t *count);
+               const bt_message *message, uint64_t *count) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2676,7 +2686,7 @@ property values:
 */
 extern bt_message *bt_message_discarded_packets_create(
                bt_self_message_iterator *self_message_iterator,
-               const bt_stream *stream);
+               const bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2748,7 +2758,7 @@ property values:
 extern bt_message *bt_message_discarded_packets_create_with_default_clock_snapshots(
                bt_self_message_iterator *self_message_iterator,
                const bt_stream *stream, uint64_t beginning_clock_snapshot_value,
-               uint64_t end_clock_snapshot_value);
+               uint64_t end_clock_snapshot_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2773,7 +2783,7 @@ See the \ref api-msg-disc-ev-prop-stream "stream" property.
     \c const version of this function.
 */
 extern bt_stream *bt_message_discarded_packets_borrow_stream(
-               bt_message *message);
+               bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2783,7 +2793,8 @@ extern bt_stream *bt_message_discarded_packets_borrow_stream(
 See bt_message_discarded_packets_borrow_stream().
 */
 extern const bt_stream *
-bt_message_discarded_packets_borrow_stream_const(const bt_message *message);
+bt_message_discarded_packets_borrow_stream_const(const bt_message *message)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2810,7 +2821,7 @@ property.
 */
 extern const bt_clock_snapshot *
 bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2837,7 +2848,7 @@ property.
 */
 extern const bt_clock_snapshot *
 bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2869,7 +2880,7 @@ bt_stream_class_borrow_default_clock_class_const(
 */
 extern const bt_clock_class *
 bt_message_discarded_packets_borrow_stream_class_default_clock_class_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2896,7 +2907,7 @@ property.
     message.
 */
 extern void bt_message_discarded_packets_set_count(bt_message *message,
-               uint64_t count);
+               uint64_t count) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2927,7 +2938,7 @@ property.
     Sets the number of discarded packets of a discarded packets message.
 */
 extern bt_property_availability bt_message_discarded_packets_get_count(
-               const bt_message *message, uint64_t *count);
+               const bt_message *message, uint64_t *count) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2979,7 +2990,7 @@ extern
 bt_message *bt_message_message_iterator_inactivity_create(
                bt_self_message_iterator *self_message_iterator,
                const bt_clock_class *clock_class,
-               uint64_t clock_snapshot_value);
+               uint64_t clock_snapshot_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2999,7 +3010,7 @@ See the \ref api-msg-inac-prop-cs "clock snapshot" property.
 */
 extern const bt_clock_snapshot *
 bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(
-               const bt_message *message);
+               const bt_message *message) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3023,7 +3034,7 @@ bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(
 @sa bt_message_put_ref() &mdash;
     Decrements the reference count of a message.
 */
-extern void bt_message_get_ref(const bt_message *message);
+extern void bt_message_get_ref(const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3040,7 +3051,7 @@ extern void bt_message_get_ref(const bt_message *message);
 @sa bt_message_get_ref() &mdash;
     Increments the reference count of a message.
 */
-extern void bt_message_put_ref(const bt_message *message);
+extern void bt_message_put_ref(const bt_message *message) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3186,7 +3197,8 @@ component descriptors of \bt_p{component_descriptors}, it returns
 extern bt_get_greatest_operative_mip_version_status
 bt_get_greatest_operative_mip_version(
                const bt_component_descriptor_set *component_descriptors,
-               bt_logging_level logging_level, uint64_t *mip_version);
+               bt_logging_level logging_level, uint64_t *mip_version)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3199,7 +3211,7 @@ As of \bt_name_version_min_maj, this function returns
 @returns
     Maximal available MIP version (\bt_max_mip_version).
 */
-extern uint64_t bt_get_maximal_mip_version(void);
+extern uint64_t bt_get_maximal_mip_version(void) __BT_NOEXCEPT;
 
 /*! @} */
 
index 3b210f2dce4050eb996700b46e41a59581ce074d..b25395eefae9c63eef83aacc30fc2fb1e93d31ab 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_PORT_H
 #define BABELTRACE2_GRAPH_PORT_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -191,7 +193,7 @@ typedef enum bt_port_type {
 @sa bt_port_is_output() &mdash;
     Returns whether or not a port is an \bt_oport.
 */
-extern bt_port_type bt_port_get_type(const bt_port *port);
+extern bt_port_type bt_port_get_type(const bt_port *port) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -209,7 +211,7 @@ extern bt_port_type bt_port_get_type(const bt_port *port);
     Returns the type enumerator of a port.
 */
 static inline
-bt_bool bt_port_is_input(const bt_port *port)
+bt_bool bt_port_is_input(const bt_port *port) __BT_NOEXCEPT
 {
        return bt_port_get_type(port) == BT_PORT_TYPE_INPUT;
 }
@@ -230,7 +232,7 @@ bt_bool bt_port_is_input(const bt_port *port)
     Returns the type enumerator of a port.
 */
 static inline
-bt_bool bt_port_is_output(const bt_port *port)
+bt_bool bt_port_is_output(const bt_port *port) __BT_NOEXCEPT
 {
        return bt_port_get_type(port) == BT_PORT_TYPE_OUTPUT;
 }
@@ -258,7 +260,7 @@ This function returns \c NULL if \bt_p{port} is unconnected
 @bt_pre_not_null{port}
 */
 extern const bt_connection *bt_port_borrow_connection_const(
-               const bt_port *port);
+               const bt_port *port) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -280,7 +282,7 @@ extern const bt_connection *bt_port_borrow_connection_const(
 @bt_pre_not_null{port}
 */
 extern const bt_component *bt_port_borrow_component_const(
-               const bt_port *port);
+               const bt_port *port) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -307,7 +309,7 @@ See the \ref api-port-prop-name "name" property.
 
 @bt_pre_not_null{port}
 */
-extern const char *bt_port_get_name(const bt_port *port);
+extern const char *bt_port_get_name(const bt_port *port) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -323,7 +325,7 @@ See the \ref api-port-prop-is-connected "is connected?" property.
 
 @bt_pre_not_null{port}
 */
-extern bt_bool bt_port_is_connected(const bt_port *port);
+extern bt_bool bt_port_is_connected(const bt_port *port) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -347,7 +349,7 @@ extern bt_bool bt_port_is_connected(const bt_port *port);
 @sa bt_port_put_ref() &mdash;
     Decrements the reference count of a port.
 */
-extern void bt_port_get_ref(const bt_port *port);
+extern void bt_port_get_ref(const bt_port *port) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -364,7 +366,7 @@ extern void bt_port_get_ref(const bt_port *port);
 @sa bt_port_get_ref() &mdash;
     Increments the reference count of a port.
 */
-extern void bt_port_put_ref(const bt_port *port);
+extern void bt_port_put_ref(const bt_port *port) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -441,7 +443,8 @@ existing \bt_p{_dst} reference.
     \bt_p{port} as a common port.
 */
 static inline
-const bt_port *bt_port_input_as_port_const(const bt_port_input *port)
+const bt_port *bt_port_input_as_port_const(
+               const bt_port_input *port) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_port, port);
 }
@@ -461,7 +464,7 @@ const bt_port *bt_port_input_as_port_const(const bt_port_input *port)
 @sa bt_port_input_put_ref() &mdash;
     Decrements the reference count of an input port.
 */
-extern void bt_port_input_get_ref(const bt_port_input *port);
+extern void bt_port_input_get_ref(const bt_port_input *port) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -478,7 +481,7 @@ extern void bt_port_input_get_ref(const bt_port_input *port);
 @sa bt_port_input_get_ref() &mdash;
     Increments the reference count of an input port.
 */
-extern void bt_port_input_put_ref(const bt_port_input *port);
+extern void bt_port_input_put_ref(const bt_port_input *port) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -555,7 +558,8 @@ existing \bt_p{_dst} reference.
     \bt_p{port} as a common port.
 */
 static inline
-const bt_port *bt_port_output_as_port_const(const bt_port_output *port)
+const bt_port *bt_port_output_as_port_const(
+               const bt_port_output *port) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_port, port);
 }
@@ -575,7 +579,7 @@ const bt_port *bt_port_output_as_port_const(const bt_port_output *port)
 @sa bt_port_output_put_ref() &mdash;
     Decrements the reference count of a \bt_oport.
 */
-extern void bt_port_output_get_ref(const bt_port_output *port);
+extern void bt_port_output_get_ref(const bt_port_output *port) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -592,7 +596,7 @@ extern void bt_port_output_get_ref(const bt_port_output *port);
 @sa bt_port_output_get_ref() &mdash;
     Increments the reference count of a \bt_oport.
 */
-extern void bt_port_output_put_ref(const bt_port_output *port);
+extern void bt_port_output_put_ref(const bt_port_output *port) __BT_NOEXCEPT;
 
 /*!
 @brief
index 9c6e97c4717bcdf19e6c483c7410d40f9e1b6883..81a0daeb0cdf863d9db8a60a3752bf5556e7d669 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_PRIVATE_QUERY_EXECUTOR_H
 #define BABELTRACE2_GRAPH_PRIVATE_QUERY_EXECUTOR_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -77,7 +79,7 @@ bt_private_query_executor_as_query_executor_const() function to
 static inline
 const bt_query_executor *
 bt_private_query_executor_as_query_executor_const(
-               bt_private_query_executor *query_executor)
+               bt_private_query_executor *query_executor) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_query_executor, query_executor);
 }
index d4ef58f97ab2a6c6b2b6c7682f5afebd45bc328c..0d4757a5fe83335f0bdb1fe37f933aa9f7c86d11 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_QUERY_EXECUTOR_H
 #define BABELTRACE2_GRAPH_QUERY_EXECUTOR_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -143,7 +145,8 @@ A query executor has the following property:
 extern
 bt_query_executor *bt_query_executor_create(
                const bt_component_class *component_class,
-               const char *object_name, const bt_value *params);
+               const char *object_name, const bt_value *params)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -173,7 +176,7 @@ executor, the query method of \bt_p{component_class} receives:
     to create.
 
     Unlike the \bt_p{params} parameter of
-    the <code>bt_graph_add_*_component_*_port_added_listener()</code>
+    the <code>bt_graph_add_*_component*()</code>
     functions (see \ref api-graph), this parameter does not need to
     be a \bt_map_val.
 
@@ -196,7 +199,7 @@ extern
 bt_query_executor *bt_query_executor_create_with_method_data(
                const bt_component_class *component_class,
                const char *object_name, const bt_value *params,
-               void *method_data);
+               void *method_data) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -285,7 +288,8 @@ bt_query_executor_create_with_method_data().
 */
 extern
 bt_query_executor_query_status bt_query_executor_query(
-               bt_query_executor *query_executor, const bt_value **result);
+               bt_query_executor *query_executor, const bt_value **result)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -329,7 +333,7 @@ See the \ref api-qexec-prop-log-lvl "logging level" property.
 */
 extern bt_query_executor_set_logging_level_status
 bt_query_executor_set_logging_level(bt_query_executor *query_executor,
-               bt_logging_level logging_level);
+               bt_logging_level logging_level) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -350,7 +354,7 @@ See the \ref api-qexec-prop-log-lvl "logging level" property.
     Sets the logging level of a query executor.
 */
 extern bt_logging_level bt_query_executor_get_logging_level(
-               const bt_query_executor *query_executor);
+               const bt_query_executor *query_executor) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -418,7 +422,7 @@ is set) with bt_query_executor_is_interrupted().
 */
 extern bt_query_executor_add_interrupter_status
 bt_query_executor_add_interrupter(bt_query_executor *query_executor,
-               const bt_interrupter *interrupter);
+               const bt_interrupter *interrupter) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -443,7 +447,7 @@ bt_query_executor_add_interrupter(bt_query_executor *query_executor,
     Adds an interrupter to a query executor.
 */
 extern bt_interrupter *bt_query_executor_borrow_default_interrupter(
-               bt_query_executor *query_executor);
+               bt_query_executor *query_executor) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -461,7 +465,7 @@ extern bt_interrupter *bt_query_executor_borrow_default_interrupter(
 @bt_pre_not_null{query_executor}
 */
 extern bt_bool bt_query_executor_is_interrupted(
-               const bt_query_executor *query_executor);
+               const bt_query_executor *query_executor) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -485,7 +489,8 @@ extern bt_bool bt_query_executor_is_interrupted(
 @sa bt_query_executor_put_ref() &mdash;
     Decrements the reference count of a query executor.
 */
-extern void bt_query_executor_get_ref(const bt_query_executor *query_executor);
+extern void bt_query_executor_get_ref(const bt_query_executor *query_executor)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -502,7 +507,8 @@ extern void bt_query_executor_get_ref(const bt_query_executor *query_executor);
 @sa bt_query_executor_get_ref() &mdash;
     Increments the reference count of a query executor.
 */
-extern void bt_query_executor_put_ref(const bt_query_executor *query_executor);
+extern void bt_query_executor_put_ref(const bt_query_executor *query_executor)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
index c5de3037a59f9500c9cc5527e1ee27833a5699ee..366f7146f26388a26c578588ddbac6f9a8b1a949 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_SELF_COMPONENT_CLASS_H
 #define BABELTRACE2_GRAPH_SELF_COMPONENT_CLASS_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -86,7 +88,7 @@ public #bt_component_class, #bt_component_class_source,
 */
 static inline
 const bt_component_class *bt_self_component_class_as_component_class(
-               bt_self_component_class *self_component_class)
+               bt_self_component_class *self_component_class) __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_component_class, self_component_class);
 }
@@ -111,6 +113,7 @@ static inline
 const bt_component_class_source *
 bt_self_component_class_source_as_component_class_source(
                bt_self_component_class_source *self_component_class)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component_class_source,
                self_component_class);
@@ -136,6 +139,7 @@ static inline
 const bt_component_class_filter *
 bt_self_component_class_filter_as_component_class_filter(
                bt_self_component_class_filter *self_component_class)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component_class_filter,
                self_component_class);
@@ -161,6 +165,7 @@ static inline
 const bt_component_class_sink *
 bt_self_component_class_sink_as_component_class_sink(
                bt_self_component_class_sink *self_component_class)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component_class_sink, self_component_class);
 }
@@ -192,6 +197,7 @@ static inline
 bt_self_component_class*
 bt_self_component_class_source_as_self_component_class(
                bt_self_component_class_source *self_component_class)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_self_component_class, self_component_class);
 }
@@ -216,6 +222,7 @@ static inline
 bt_self_component_class*
 bt_self_component_class_filter_as_self_component_class(
                bt_self_component_class_filter *self_component_class)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_self_component_class, self_component_class);
 }
@@ -240,6 +247,7 @@ static inline
 bt_self_component_class*
 bt_self_component_class_sink_as_self_component_class(
                bt_self_component_class_sink *self_component_class)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_self_component_class, self_component_class);
 }
index 8532d270c83d687f54a67d02bcdb86203c86b5e3..5cb500b2fb3074a41af2d33bb90d24ca36dca6b0 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_SELF_COMPONENT_PORT_H
 #define BABELTRACE2_GRAPH_SELF_COMPONENT_PORT_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -84,7 +86,7 @@ functions.
 @bt_pre_not_null{self_component_port}
 */
 extern bt_self_component *bt_self_component_port_borrow_component(
-               bt_self_component_port *self_component_port);
+               bt_self_component_port *self_component_port) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -112,7 +114,8 @@ bt_self_component_sink_add_input_port().
 @bt_pre_not_null{self_component_port}
 */
 extern void *bt_self_component_port_get_data(
-               const bt_self_component_port *self_component_port);
+               const bt_self_component_port *self_component_port)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -138,7 +141,7 @@ extern void *bt_self_component_port_get_data(
 */
 static inline
 const bt_port *bt_self_component_port_as_port(
-               bt_self_component_port *self_component_port)
+               bt_self_component_port *self_component_port) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_port, self_component_port);
 }
@@ -161,6 +164,7 @@ const bt_port *bt_self_component_port_as_port(
 static inline
 const bt_port_input *bt_self_component_port_input_as_port_input(
                const bt_self_component_port_input *self_component_port)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_port_input, self_component_port);
 }
@@ -183,6 +187,7 @@ const bt_port_input *bt_self_component_port_input_as_port_input(
 static inline
 const bt_port_output *bt_self_component_port_output_as_port_output(
                bt_self_component_port_output *self_component_port)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_port_output, self_component_port);
 }
@@ -214,6 +219,7 @@ static inline
 bt_self_component_port *
 bt_self_component_port_input_as_self_component_port(
                bt_self_component_port_input *self_component_port)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_self_component_port, self_component_port);
 }
@@ -238,6 +244,7 @@ static inline
 bt_self_component_port *
 bt_self_component_port_output_as_self_component_port(
                bt_self_component_port_output *self_component_port)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_self_component_port, self_component_port);
 }
index f37346d1538e0ce6c888ab29b1b055b20f27c997..77e299af185632056868e8e49cd7f8a604452065 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_SELF_COMPONENT_H
 #define BABELTRACE2_GRAPH_SELF_COMPONENT_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -184,7 +186,8 @@ extern bt_self_component_add_port_status
 bt_self_component_source_add_output_port(
                bt_self_component_source *self_component,
                const char *name, void *user_data,
-               bt_self_component_port_output **self_component_port);
+               bt_self_component_port_output **self_component_port)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -228,7 +231,8 @@ extern bt_self_component_add_port_status
 bt_self_component_filter_add_input_port(
                bt_self_component_filter *self_component,
                const char *name, void *user_data,
-               bt_self_component_port_input **self_component_port);
+               bt_self_component_port_input **self_component_port)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -272,7 +276,8 @@ extern bt_self_component_add_port_status
 bt_self_component_filter_add_output_port(
                bt_self_component_filter *self_component,
                const char *name, void *user_data,
-               bt_self_component_port_output **self_component_port);
+               bt_self_component_port_output **self_component_port)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -317,7 +322,8 @@ extern bt_self_component_add_port_status
 bt_self_component_sink_add_input_port(
                bt_self_component_sink *self_component,
                const char *name, void *user_data,
-               bt_self_component_port_input **self_component_port);
+               bt_self_component_port_input **self_component_port)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -357,7 +363,7 @@ bt_self_component_sink_add_input_port(
 extern bt_self_component_port_output *
 bt_self_component_source_borrow_output_port_by_index(
                bt_self_component_source *self_component,
-               uint64_t index);
+               uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -390,7 +396,7 @@ bt_self_component_source_borrow_output_port_by_index(
 extern bt_self_component_port_input *
 bt_self_component_filter_borrow_input_port_by_index(
                bt_self_component_filter *self_component,
-               uint64_t index);
+               uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -423,7 +429,7 @@ bt_self_component_filter_borrow_input_port_by_index(
 extern bt_self_component_port_output *
 bt_self_component_filter_borrow_output_port_by_index(
                bt_self_component_filter *self_component,
-               uint64_t index);
+               uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -455,7 +461,8 @@ bt_self_component_filter_borrow_output_port_by_index(
 */
 extern bt_self_component_port_input *
 bt_self_component_sink_borrow_input_port_by_index(
-               bt_self_component_sink *self_component, uint64_t index);
+               bt_self_component_sink *self_component, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -485,7 +492,7 @@ function returns \c NULL.
 extern bt_self_component_port_output *
 bt_self_component_source_borrow_output_port_by_name(
                bt_self_component_source *self_component,
-               const char *name);
+               const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -515,7 +522,7 @@ function returns \c NULL.
 extern bt_self_component_port_input *
 bt_self_component_filter_borrow_input_port_by_name(
                bt_self_component_filter *self_component,
-               const char *name);
+               const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -545,7 +552,7 @@ function returns \c NULL.
 extern bt_self_component_port_output *
 bt_self_component_filter_borrow_output_port_by_name(
                bt_self_component_filter *self_component,
-               const char *name);
+               const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -575,7 +582,7 @@ function returns \c NULL.
 extern bt_self_component_port_input *
 bt_self_component_sink_borrow_input_port_by_name(
                bt_self_component_sink *self_component,
-               const char *name);
+               const char *name) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -600,7 +607,8 @@ bt_self_component_sink_borrow_input_port_by_name(
     Returns the user data of a component.
 */
 extern void bt_self_component_set_data(
-               bt_self_component *self_component, void *user_data);
+               bt_self_component *self_component, void *user_data)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -618,7 +626,7 @@ extern void bt_self_component_set_data(
     Sets the user data of a component.
 */
 extern void *bt_self_component_get_data(
-               const bt_self_component *self_component);
+               const bt_self_component *self_component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -647,7 +655,7 @@ extern void *bt_self_component_get_data(
 */
 extern
 uint64_t bt_self_component_get_graph_mip_version(
-               bt_self_component *self_component);
+               bt_self_component *self_component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -677,7 +685,7 @@ uint64_t bt_self_component_get_graph_mip_version(
     Adds an interrupter to a graph.
 */
 extern bt_bool bt_self_component_sink_is_interrupted(
-               const bt_self_component_sink *self_component);
+               const bt_self_component_sink *self_component) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -703,7 +711,7 @@ extern bt_bool bt_self_component_sink_is_interrupted(
 */
 static inline
 const bt_component *bt_self_component_as_component(
-               bt_self_component *self_component)
+               bt_self_component *self_component) __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_component, self_component);
 }
@@ -727,7 +735,7 @@ const bt_component *bt_self_component_as_component(
 static inline
 const bt_component_source *
 bt_self_component_source_as_component_source(
-               bt_self_component_source *self_component)
+               bt_self_component_source *self_component) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component_source, self_component);
 }
@@ -751,7 +759,7 @@ bt_self_component_source_as_component_source(
 static inline
 const bt_component_filter *
 bt_self_component_filter_as_component_filter(
-               bt_self_component_filter *self_component)
+               bt_self_component_filter *self_component) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component_filter, self_component);
 }
@@ -775,7 +783,7 @@ bt_self_component_filter_as_component_filter(
 static inline
 const bt_component_sink *
 bt_self_component_sink_as_component_sink(
-               bt_self_component_sink *self_component)
+               bt_self_component_sink *self_component) __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_component_sink, self_component);
 }
@@ -805,7 +813,7 @@ bt_self_component_sink_as_component_sink(
 */
 static inline
 bt_self_component *bt_self_component_source_as_self_component(
-               bt_self_component_source *self_component)
+               bt_self_component_source *self_component) __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_self_component, self_component);
 }
@@ -828,7 +836,7 @@ bt_self_component *bt_self_component_source_as_self_component(
 */
 static inline
 bt_self_component *bt_self_component_filter_as_self_component(
-               bt_self_component_filter *self_component)
+               bt_self_component_filter *self_component) __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_self_component, self_component);
 }
@@ -851,7 +859,7 @@ bt_self_component *bt_self_component_filter_as_self_component(
 */
 static inline
 bt_self_component *bt_self_component_sink_as_self_component(
-               bt_self_component_sink *self_component)
+               bt_self_component_sink *self_component) __BT_NOEXCEPT
 {
        return __BT_UPCAST(bt_self_component, self_component);
 }
index 452f33a9dc540a40c60e0b9aa1e25857f5ca709a..556a172f669959f3ff70739efc3b2012a613c78f 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_GRAPH_SELF_MESSAGE_ITERATOR_H
 #define BABELTRACE2_GRAPH_SELF_MESSAGE_ITERATOR_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -83,7 +85,7 @@ bt_self_message_iterator_configuration_set_can_seek_forward().
 */
 extern bt_self_component *
 bt_self_message_iterator_borrow_component(
-               bt_self_message_iterator *self_message_iterator);
+               bt_self_message_iterator *self_message_iterator) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -107,7 +109,8 @@ bt_self_message_iterator_borrow_component(
 */
 extern bt_self_component_port_output *
 bt_self_message_iterator_borrow_port(
-               bt_self_message_iterator *self_message_iterator);
+               bt_self_message_iterator *self_message_iterator)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -133,7 +136,7 @@ bt_self_message_iterator_borrow_port(
 */
 extern void bt_self_message_iterator_set_data(
                bt_self_message_iterator *self_message_iterator,
-               void *user_data);
+               void *user_data) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -153,7 +156,8 @@ extern void bt_self_message_iterator_set_data(
 */
 extern
 void *bt_self_message_iterator_get_data(
-               const bt_self_message_iterator *self_message_iterator);
+               const bt_self_message_iterator *self_message_iterator)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -183,7 +187,8 @@ void *bt_self_message_iterator_get_data(
     Adds an interrupter to a graph.
 */
 extern bt_bool bt_self_message_iterator_is_interrupted(
-               const bt_self_message_iterator *self_message_iterator);
+               const bt_self_message_iterator *self_message_iterator)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -219,7 +224,7 @@ message sequence have some \bt_cs.
 */
 extern void bt_self_message_iterator_configuration_set_can_seek_forward(
                bt_self_message_iterator_configuration *configuration,
-               bt_bool can_seek_forward);
+               bt_bool can_seek_forward) __BT_NOEXCEPT;
 
 /*! @} */
 
index 7e87fe9df5663fadc94bad680acdf3fcb59e485c..d3aff20ee6360663834647bae73a6ee94385a1e3 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_INTEGER_RANGE_SET_H
 #define BABELTRACE2_INTEGER_RANGE_SET_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -147,7 +149,7 @@ The returned lower value is included in \bt_p{int_range}.
 @bt_pre_is_bool_val{int_range}
 */
 extern uint64_t bt_integer_range_unsigned_get_lower(
-               const bt_integer_range_unsigned *int_range);
+               const bt_integer_range_unsigned *int_range) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -166,7 +168,7 @@ The returned upper value is included in \bt_p{int_range}.
 @bt_pre_is_bool_val{int_range}
 */
 extern uint64_t bt_integer_range_unsigned_get_upper(
-               const bt_integer_range_unsigned *int_range);
+               const bt_integer_range_unsigned *int_range) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -190,7 +192,7 @@ lower and upper values.
 */
 extern bt_bool bt_integer_range_unsigned_is_equal(
                const bt_integer_range_unsigned *a_int_range,
-               const bt_integer_range_unsigned *b_int_range);
+               const bt_integer_range_unsigned *b_int_range) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -216,7 +218,7 @@ The returned lower value is included in \bt_p{int_range}.
 @bt_pre_is_bool_val{int_range}
 */
 extern int64_t bt_integer_range_signed_get_lower(
-               const bt_integer_range_signed *int_range);
+               const bt_integer_range_signed *int_range) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -235,7 +237,7 @@ The returned upper value is included in \bt_p{int_range}.
 @bt_pre_is_bool_val{int_range}
 */
 extern int64_t bt_integer_range_signed_get_upper(
-               const bt_integer_range_signed *int_range);
+               const bt_integer_range_signed *int_range) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -259,7 +261,7 @@ lower and upper values.
 */
 extern bt_bool bt_integer_range_signed_is_equal(
                const bt_integer_range_signed *a_int_range,
-               const bt_integer_range_signed *b_int_range);
+               const bt_integer_range_signed *b_int_range) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -309,7 +311,7 @@ typedef enum bt_integer_range_set_add_range_status {
 @bt_pre_not_null{int_range_set}
 */
 extern uint64_t bt_integer_range_set_get_range_count(
-               const bt_integer_range_set *int_range_set);
+               const bt_integer_range_set *int_range_set) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -325,7 +327,8 @@ extern uint64_t bt_integer_range_set_get_range_count(
 @returns
     New unsigned integer range set, or \c NULL on memory error.
 */
-extern bt_integer_range_set_unsigned *bt_integer_range_set_unsigned_create(void);
+extern bt_integer_range_set_unsigned *bt_integer_range_set_unsigned_create(
+               void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -360,7 +363,7 @@ integer range to add to \bt_p{int_range_set}.
 extern bt_integer_range_set_add_range_status
 bt_integer_range_set_unsigned_add_range(
                bt_integer_range_set_unsigned *int_range_set,
-               uint64_t lower, uint64_t upper);
+               uint64_t lower, uint64_t upper) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -392,7 +395,7 @@ bt_integer_range_set_unsigned_add_range(
 extern const bt_integer_range_unsigned *
 bt_integer_range_set_unsigned_borrow_range_by_index_const(
                const bt_integer_range_set_unsigned *int_range_set,
-               uint64_t index);
+               uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -418,7 +421,8 @@ is \em not equal to an unsigned integer range containing [2,&nbsp;15].
 */
 extern bt_bool bt_integer_range_set_unsigned_is_equal(
                const bt_integer_range_set_unsigned *int_range_set_a,
-               const bt_integer_range_set_unsigned *int_range_set_b);
+               const bt_integer_range_set_unsigned *int_range_set_b)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -438,6 +442,7 @@ extern bt_bool bt_integer_range_set_unsigned_is_equal(
 static inline
 const bt_integer_range_set *bt_integer_range_set_unsigned_as_range_set_const(
                const bt_integer_range_set_unsigned *int_range_set)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_integer_range_set, int_range_set);
 }
@@ -459,7 +464,8 @@ const bt_integer_range_set *bt_integer_range_set_unsigned_as_range_set_const(
     Decrements the reference count of an unsigned integer range set.
 */
 extern void bt_integer_range_set_unsigned_get_ref(
-               const bt_integer_range_set_unsigned *int_range_set);
+               const bt_integer_range_set_unsigned *int_range_set)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -478,7 +484,8 @@ extern void bt_integer_range_set_unsigned_get_ref(
     Increments the reference count of an unsigned integer range set.
 */
 extern void bt_integer_range_set_unsigned_put_ref(
-               const bt_integer_range_set_unsigned *int_range_set);
+               const bt_integer_range_set_unsigned *int_range_set)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -549,7 +556,8 @@ the existing \bt_p{_dst} reference.
 @returns
     New signed integer range set, or \c NULL on memory error.
 */
-extern bt_integer_range_set_signed *bt_integer_range_set_signed_create(void);
+extern bt_integer_range_set_signed *bt_integer_range_set_signed_create(
+               void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -584,7 +592,7 @@ integer range to add to \bt_p{int_range_set}.
 extern bt_integer_range_set_add_range_status
 bt_integer_range_set_signed_add_range(
                bt_integer_range_set_signed *int_range_set,
-               int64_t lower, int64_t upper);
+               int64_t lower, int64_t upper) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -615,7 +623,8 @@ bt_integer_range_set_signed_add_range(
 */
 extern const bt_integer_range_signed *
 bt_integer_range_set_signed_borrow_range_by_index_const(
-               const bt_integer_range_set_signed *int_range_set, uint64_t index);
+               const bt_integer_range_set_signed *int_range_set,
+               uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -641,7 +650,8 @@ is \em not equal to a signed integer range containing [−57,&nbsp;42].
 */
 extern bt_bool bt_integer_range_set_signed_is_equal(
                const bt_integer_range_set_signed *int_range_set_a,
-               const bt_integer_range_set_signed *int_range_set_b);
+               const bt_integer_range_set_signed *int_range_set_b)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -661,6 +671,7 @@ extern bt_bool bt_integer_range_set_signed_is_equal(
 static inline
 const bt_integer_range_set *bt_integer_range_set_signed_as_range_set_const(
                const bt_integer_range_set_signed *int_range_set)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_integer_range_set, int_range_set);
 }
@@ -682,7 +693,7 @@ const bt_integer_range_set *bt_integer_range_set_signed_as_range_set_const(
     Decrements the reference count of a signed integer range set.
 */
 extern void bt_integer_range_set_signed_get_ref(
-               const bt_integer_range_set_signed *int_range_set);
+               const bt_integer_range_set_signed *int_range_set) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -701,7 +712,7 @@ extern void bt_integer_range_set_signed_get_ref(
     Increments the reference count of a signed integer range set.
 */
 extern void bt_integer_range_set_signed_put_ref(
-               const bt_integer_range_set_signed *int_range_set);
+               const bt_integer_range_set_signed *int_range_set) __BT_NOEXCEPT;
 
 /*!
 @brief
index acb75303306ac1ea30b1c8aca22f63edb86fcd28..884957efe48c2daa381f154f5490aa77e07f13bf 100644 (file)
@@ -9,6 +9,8 @@
  * times.
  */
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
index 2b5da45cecd584aa0e5bf9470c46c8bfee92a47b..146601867494b209495621091fc502d0cd71a93a 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_LOGGING_H
 #define BABELTRACE2_LOGGING_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -187,7 +189,8 @@ individual components and query operations.
 @sa bt_logging_get_global_level() &mdash;
     Returns the current library's global logging level.
 */
-extern void bt_logging_set_global_level(bt_logging_level logging_level);
+extern void bt_logging_set_global_level(bt_logging_level logging_level)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -199,7 +202,7 @@ extern void bt_logging_set_global_level(bt_logging_level logging_level);
 @sa bt_logging_set_global_level() &mdash;
     Sets the current library's global logging level.
 */
-extern bt_logging_level bt_logging_get_global_level(void);
+extern bt_logging_level bt_logging_get_global_level(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -218,7 +221,7 @@ are not built.
 @sa bt_logging_get_global_level() &mdash;
     Returns the current library's global logging level.
 */
-extern bt_logging_level bt_logging_get_minimal_level(void);
+extern bt_logging_level bt_logging_get_minimal_level(void) __BT_NOEXCEPT;
 
 /*! @} */
 
index 1c8d0154097c849201cab9bb1108ce12d7538d17..42420d8e2da5da937ccb3415471900a5c666aff5 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_PLUGIN_PLUGIN_DEV_H
 #define BABELTRACE2_PLUGIN_PLUGIN_DEV_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
 #define _BT_HIDDEN __attribute__((visibility("hidden")))
 #endif
 
+/*
+ * _BT_EXPORT: set the visibility for exported functions.
+ */
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define _BT_EXPORT
+#else
+#define _BT_EXPORT __attribute__((visibility("default")))
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -316,8 +327,8 @@ BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(my_plugin_id,
 @brief
     Defines a plugin module.
 
-In a plugin define C file, you must use this macro before you use any
-other <code>BT_PLUGIN*()</code> macro.
+In a plugin definition C file, you must use this macro before you use
+any other <code>BT_PLUGIN*()</code> macro.
 */
 #define BT_PLUGIN_MODULE() \
        static struct __bt_plugin_descriptor const * const __bt_plugin_descriptor_dummy __BT_PLUGIN_DESCRIPTOR_ATTRS = NULL; \
@@ -336,35 +347,35 @@ other <code>BT_PLUGIN*()</code> macro.
        _BT_HIDDEN extern struct __bt_plugin_component_class_descriptor_attribute const *__BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTES_BEGIN_SYMBOL __BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTES_BEGIN_EXTRA; \
        _BT_HIDDEN extern struct __bt_plugin_component_class_descriptor_attribute const *__BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTES_END_SYMBOL __BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTES_END_EXTRA; \
        \
-       struct __bt_plugin_descriptor const * const *__bt_get_begin_section_plugin_descriptors(void) \
+       _BT_EXPORT struct __bt_plugin_descriptor const * const *__bt_get_begin_section_plugin_descriptors(void) \
        { \
                return &__BT_PLUGIN_DESCRIPTOR_BEGIN_SYMBOL; \
        } \
-       struct __bt_plugin_descriptor const * const *__bt_get_end_section_plugin_descriptors(void) \
+       _BT_EXPORT struct __bt_plugin_descriptor const * const *__bt_get_end_section_plugin_descriptors(void) \
        { \
                return &__BT_PLUGIN_DESCRIPTOR_END_SYMBOL; \
        } \
-       struct __bt_plugin_descriptor_attribute const * const *__bt_get_begin_section_plugin_descriptor_attributes(void) \
+       _BT_EXPORT struct __bt_plugin_descriptor_attribute const * const *__bt_get_begin_section_plugin_descriptor_attributes(void) \
        { \
                return &__BT_PLUGIN_DESCRIPTOR_ATTRIBUTES_BEGIN_SYMBOL; \
        } \
-       struct __bt_plugin_descriptor_attribute const * const *__bt_get_end_section_plugin_descriptor_attributes(void) \
+       _BT_EXPORT struct __bt_plugin_descriptor_attribute const * const *__bt_get_end_section_plugin_descriptor_attributes(void) \
        { \
                return &__BT_PLUGIN_DESCRIPTOR_ATTRIBUTES_END_SYMBOL; \
        } \
-       struct __bt_plugin_component_class_descriptor const * const *__bt_get_begin_section_component_class_descriptors(void) \
+       _BT_EXPORT struct __bt_plugin_component_class_descriptor const * const *__bt_get_begin_section_component_class_descriptors(void) \
        { \
                return &__BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_BEGIN_SYMBOL; \
        } \
-       struct __bt_plugin_component_class_descriptor const * const *__bt_get_end_section_component_class_descriptors(void) \
+       _BT_EXPORT struct __bt_plugin_component_class_descriptor const * const *__bt_get_end_section_component_class_descriptors(void) \
        { \
                return &__BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_END_SYMBOL; \
        } \
-       struct __bt_plugin_component_class_descriptor_attribute const * const *__bt_get_begin_section_component_class_descriptor_attributes(void) \
+       _BT_EXPORT struct __bt_plugin_component_class_descriptor_attribute const * const *__bt_get_begin_section_component_class_descriptor_attributes(void) \
        { \
                return &__BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTES_BEGIN_SYMBOL; \
        } \
-       struct __bt_plugin_component_class_descriptor_attribute const * const *__bt_get_end_section_component_class_descriptor_attributes(void) \
+       _BT_EXPORT struct __bt_plugin_component_class_descriptor_attribute const * const *__bt_get_end_section_component_class_descriptor_attributes(void) \
        { \
                return &__BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTES_END_SYMBOL; \
        }
index b38364363e4b812e0da82a0b9e0cd0d973b9ffcd..9bb598a09325f22a18118e86c057f775c7469551 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_PLUGIN_PLUGIN_LOADING_H
 #define BABELTRACE2_PLUGIN_PLUGIN_LOADING_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -375,7 +377,8 @@ If this function doesn't find any plugin, it returns
 extern bt_plugin_find_status bt_plugin_find(const char *plugin_name,
                bt_bool find_in_std_env_var, bt_bool find_in_user_dir,
                bt_bool find_in_sys_dir, bt_bool find_in_static,
-               bt_bool fail_on_load_error, const bt_plugin **plugin);
+               bt_bool fail_on_load_error, const bt_plugin **plugin)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -499,7 +502,7 @@ If this function doesn't find any plugin, it returns
 bt_plugin_find_all_status bt_plugin_find_all(bt_bool find_in_std_env_var,
                bt_bool find_in_user_dir, bt_bool find_in_sys_dir,
                bt_bool find_in_static, bt_bool fail_on_load_error,
-               const bt_plugin_set **plugins);
+               const bt_plugin_set **plugins) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -586,7 +589,7 @@ If this function doesn't find any plugin, it returns
 */
 extern bt_plugin_find_all_from_file_status bt_plugin_find_all_from_file(
                const char *path, bt_bool fail_on_load_error,
-               const bt_plugin_set **plugins);
+               const bt_plugin_set **plugins) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -681,7 +684,7 @@ If this function doesn't find any plugin, it returns
 */
 extern bt_plugin_find_all_from_dir_status bt_plugin_find_all_from_dir(
                const char *path, bt_bool recurse, bt_bool fail_on_load_error,
-               const bt_plugin_set **plugins);
+               const bt_plugin_set **plugins) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -757,7 +760,8 @@ If this function doesn't find any plugin, it returns
 @bt_pre_not_null{plugins}
 */
 extern bt_plugin_find_all_from_static_status bt_plugin_find_all_from_static(
-               bt_bool fail_on_load_error, const bt_plugin_set **plugins);
+               bt_bool fail_on_load_error, const bt_plugin_set **plugins)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -784,7 +788,7 @@ See the \ref api-plugin-prop-name "name" property.
 
 @bt_pre_not_null{plugin}
 */
-extern const char *bt_plugin_get_name(const bt_plugin *plugin);
+extern const char *bt_plugin_get_name(const bt_plugin *plugin) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -804,7 +808,8 @@ See the \ref api-plugin-prop-descr "description" property.
 
 @bt_pre_not_null{plugin}
 */
-extern const char *bt_plugin_get_description(const bt_plugin *plugin);
+extern const char *bt_plugin_get_description(const bt_plugin *plugin)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -824,7 +829,7 @@ See the \ref api-plugin-prop-author "author name(s)" property.
 
 @bt_pre_not_null{plugin}
 */
-extern const char *bt_plugin_get_author(const bt_plugin *plugin);
+extern const char *bt_plugin_get_author(const bt_plugin *plugin) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -845,7 +850,7 @@ See the \ref api-plugin-prop-license "license" property.
 
 @bt_pre_not_null{plugin}
 */
-extern const char *bt_plugin_get_license(const bt_plugin *plugin);
+extern const char *bt_plugin_get_license(const bt_plugin *plugin) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -870,7 +875,7 @@ because a static plugin has no path property.
 
 @bt_pre_not_null{plugin}
 */
-extern const char *bt_plugin_get_path(const bt_plugin *plugin);
+extern const char *bt_plugin_get_path(const bt_plugin *plugin) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -913,7 +918,8 @@ See the \ref api-plugin-prop-version "version" property.
 */
 extern bt_property_availability bt_plugin_get_version(
                const bt_plugin *plugin, unsigned int *major,
-               unsigned int *minor, unsigned int *patch, const char **extra);
+               unsigned int *minor, unsigned int *patch, const char **extra)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -937,7 +943,7 @@ extern bt_property_availability bt_plugin_get_version(
 @bt_pre_not_null{plugin}
 */
 extern uint64_t bt_plugin_get_source_component_class_count(
-               const bt_plugin *plugin);
+               const bt_plugin *plugin) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -954,7 +960,7 @@ extern uint64_t bt_plugin_get_source_component_class_count(
 @bt_pre_not_null{plugin}
 */
 extern uint64_t bt_plugin_get_filter_component_class_count(
-               const bt_plugin *plugin);
+               const bt_plugin *plugin) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -971,7 +977,7 @@ extern uint64_t bt_plugin_get_filter_component_class_count(
 @bt_pre_not_null{plugin}
 */
 extern uint64_t bt_plugin_get_sink_component_class_count(
-               const bt_plugin *plugin);
+               const bt_plugin *plugin) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1003,7 +1009,7 @@ extern uint64_t bt_plugin_get_sink_component_class_count(
 */
 extern const bt_component_class_source *
 bt_plugin_borrow_source_component_class_by_index_const(
-               const bt_plugin *plugin, uint64_t index);
+               const bt_plugin *plugin, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1035,7 +1041,7 @@ bt_plugin_borrow_source_component_class_by_index_const(
 */
 extern const bt_component_class_filter *
 bt_plugin_borrow_filter_component_class_by_index_const(
-               const bt_plugin *plugin, uint64_t index);
+               const bt_plugin *plugin, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1067,7 +1073,7 @@ bt_plugin_borrow_filter_component_class_by_index_const(
 */
 extern const bt_component_class_sink *
 bt_plugin_borrow_sink_component_class_by_index_const(
-               const bt_plugin *plugin, uint64_t index);
+               const bt_plugin *plugin, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1100,7 +1106,7 @@ If no source component class has the name \bt_p{name} within
 */
 extern const bt_component_class_source *
 bt_plugin_borrow_source_component_class_by_name_const(
-               const bt_plugin *plugin, const char *name);
+               const bt_plugin *plugin, const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1133,7 +1139,7 @@ If no filter component class has the name \bt_p{name} within
 */
 extern const bt_component_class_filter *
 bt_plugin_borrow_filter_component_class_by_name_const(
-               const bt_plugin *plugin, const char *name);
+               const bt_plugin *plugin, const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1166,7 +1172,7 @@ If no sink component class has the name \bt_p{name} within
 */
 extern const bt_component_class_sink *
 bt_plugin_borrow_sink_component_class_by_name_const(
-               const bt_plugin *plugin, const char *name);
+               const bt_plugin *plugin, const char *name) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1190,7 +1196,7 @@ bt_plugin_borrow_sink_component_class_by_name_const(
 @sa bt_plugin_put_ref() &mdash;
     Decrements the reference count of a plugin.
 */
-extern void bt_plugin_get_ref(const bt_plugin *plugin);
+extern void bt_plugin_get_ref(const bt_plugin *plugin) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1207,7 +1213,7 @@ extern void bt_plugin_get_ref(const bt_plugin *plugin);
 @sa bt_plugin_get_ref() &mdash;
     Increments the reference count of a plugin.
 */
-extern void bt_plugin_put_ref(const bt_plugin *plugin);
+extern void bt_plugin_put_ref(const bt_plugin *plugin) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1282,7 +1288,7 @@ This macro effectively moves a plugin reference from the expression
 @bt_pre_not_null{plugin}
 */
 extern uint64_t bt_plugin_set_get_plugin_count(
-               const bt_plugin_set *plugin_set);
+               const bt_plugin_set *plugin_set) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1309,7 +1315,7 @@ extern uint64_t bt_plugin_set_get_plugin_count(
     \bt_p{plugin_set} (as returned by bt_plugin_set_get_plugin_count()).
 */
 extern const bt_plugin *bt_plugin_set_borrow_plugin_by_index_const(
-               const bt_plugin_set *plugin_set, uint64_t index);
+               const bt_plugin_set *plugin_set, uint64_t index) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1333,7 +1339,8 @@ extern const bt_plugin *bt_plugin_set_borrow_plugin_by_index_const(
 @sa bt_plugin_set_put_ref() &mdash;
     Decrements the reference count of a plugin set.
 */
-extern void bt_plugin_set_get_ref(const bt_plugin_set *plugin_set);
+extern void bt_plugin_set_get_ref(const bt_plugin_set *plugin_set)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1350,7 +1357,8 @@ extern void bt_plugin_set_get_ref(const bt_plugin_set *plugin_set);
 @sa bt_plugin_set_get_ref() &mdash;
     Increments the reference count of a plugin set.
 */
-extern void bt_plugin_set_put_ref(const bt_plugin_set *plugin_set);
+extern void bt_plugin_set_put_ref(const bt_plugin_set *plugin_set)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
index b28a6c5e8295352ec0622e1fb16bade342688661..a54e8b8986ec35d8c81efc551762ba67c14b8c32 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_CLOCK_CLASS_H
 #define BABELTRACE2_TRACE_IR_CLOCK_CLASS_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -324,7 +326,8 @@ On success, the returned clock class has the following property values:
 
 @bt_pre_not_null{self_component}
 */
-extern bt_clock_class *bt_clock_class_create(bt_self_component *self_component);
+extern bt_clock_class *bt_clock_class_create(bt_self_component *self_component)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -359,7 +362,7 @@ See the \ref api-tir-clock-cls-prop-freq "frequency" property.
     Returns the frequency of a clock class.
 */
 extern void bt_clock_class_set_frequency(bt_clock_class *clock_class,
-               uint64_t frequency);
+               uint64_t frequency) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -379,7 +382,7 @@ See the \ref api-tir-clock-cls-prop-freq "frequency" property.
     Sets the frequency of a clock class.
 */
 extern uint64_t bt_clock_class_get_frequency(
-               const bt_clock_class *clock_class);
+               const bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -407,7 +410,7 @@ See the \ref api-tir-clock-cls-prop-offset "offset" property.
     Returns the offset of a clock class.
 */
 extern void bt_clock_class_set_offset(bt_clock_class *clock_class,
-               int64_t offset_seconds, uint64_t offset_cycles);
+               int64_t offset_seconds, uint64_t offset_cycles) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -434,7 +437,7 @@ See the \ref api-tir-clock-cls-prop-offset "offset" property.
     Sets the offset of a clock class.
 */
 extern void bt_clock_class_get_offset(const bt_clock_class *clock_class,
-               int64_t *offset_seconds, uint64_t *offset_cycles);
+               int64_t *offset_seconds, uint64_t *offset_cycles) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -455,7 +458,7 @@ See the \ref api-tir-clock-cls-prop-precision "precision" property.
     Returns the precision of a clock class.
 */
 extern void bt_clock_class_set_precision(bt_clock_class *clock_class,
-               uint64_t precision);
+               uint64_t precision) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -476,7 +479,7 @@ See the \ref api-tir-clock-cls-prop-precision "precision" property.
     Sets the precision of a clock class.
 */
 extern uint64_t bt_clock_class_get_precision(
-               const bt_clock_class *clock_class);
+               const bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -501,7 +504,7 @@ property.
     Unix epoch.
 */
 extern void bt_clock_class_set_origin_is_unix_epoch(bt_clock_class *clock_class,
-               bt_bool origin_is_unix_epoch);
+               bt_bool origin_is_unix_epoch) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -525,7 +528,7 @@ property.
     Sets whether or not the origin of a clock class is the Unix epoch.
 */
 extern bt_bool bt_clock_class_origin_is_unix_epoch(
-               const bt_clock_class *clock_class);
+               const bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -570,7 +573,7 @@ See the \ref api-tir-clock-cls-prop-name "name" property.
     Returns the name of a clock class.
 */
 extern bt_clock_class_set_name_status bt_clock_class_set_name(
-               bt_clock_class *clock_class, const char *name);
+               bt_clock_class *clock_class, const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -597,7 +600,7 @@ If \bt_p{clock_class} has no name, this function returns \c NULL.
     Sets the name of a clock class.
 */
 extern const char *bt_clock_class_get_name(
-               const bt_clock_class *clock_class);
+               const bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -642,7 +645,8 @@ See the \ref api-tir-clock-cls-prop-descr "description" property.
     Returns the description of a clock class.
 */
 extern bt_clock_class_set_description_status bt_clock_class_set_description(
-               bt_clock_class *clock_class, const char *description);
+               bt_clock_class *clock_class, const char *description)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -669,7 +673,7 @@ If \bt_p{clock_class} has no description, this function returns \c NULL.
     Sets the description of a clock class.
 */
 extern const char *bt_clock_class_get_description(
-               const bt_clock_class *clock_class);
+               const bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -692,7 +696,7 @@ See the \ref api-tir-clock-cls-prop-uuid "UUID" property.
     Returns the UUID of a clock class.
 */
 extern void bt_clock_class_set_uuid(bt_clock_class *clock_class,
-               bt_uuid uuid);
+               bt_uuid uuid) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -719,7 +723,7 @@ If \bt_p{clock_class} has no UUID, this function returns \c NULL.
     Sets the UUID of a clock class.
 */
 extern bt_uuid bt_clock_class_get_uuid(
-               const bt_clock_class *clock_class);
+               const bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -751,7 +755,8 @@ property.
     Borrows the user attributes of a clock class.
 */
 extern void bt_clock_class_set_user_attributes(
-               bt_clock_class *clock_class, const bt_value *user_attributes);
+               bt_clock_class *clock_class, const bt_value *user_attributes)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -778,7 +783,7 @@ property.
     \c const version of this function.
 */
 extern bt_value *bt_clock_class_borrow_user_attributes(
-               bt_clock_class *clock_class);
+               bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -788,7 +793,7 @@ extern bt_value *bt_clock_class_borrow_user_attributes(
 See bt_clock_class_borrow_user_attributes().
 */
 extern const bt_value *bt_clock_class_borrow_user_attributes_const(
-               const bt_clock_class *clock_class);
+               const bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -868,7 +873,7 @@ code if any step of the computation process causes an integer overflow.
 extern bt_clock_class_cycles_to_ns_from_origin_status
 bt_clock_class_cycles_to_ns_from_origin(
                const bt_clock_class *clock_class,
-               uint64_t value, int64_t *ns_from_origin);
+               uint64_t value, int64_t *ns_from_origin) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -892,7 +897,8 @@ bt_clock_class_cycles_to_ns_from_origin(
 @sa bt_clock_class_put_ref() &mdash;
     Decrements the reference count of a clock class.
 */
-extern void bt_clock_class_get_ref(const bt_clock_class *clock_class);
+extern void bt_clock_class_get_ref(
+               const bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -909,7 +915,8 @@ extern void bt_clock_class_get_ref(const bt_clock_class *clock_class);
 @sa bt_clock_class_get_ref() &mdash;
     Increments the reference count of a clock class.
 */
-extern void bt_clock_class_put_ref(const bt_clock_class *clock_class);
+extern void bt_clock_class_put_ref(
+               const bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
index 554131c05773045622a93b27b9347f7a6bde9c6c..79bf17fc5811c24c4c34e5d0ac6d7245cc0ee94e 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_CLOCK_SNAPSHOT_H
 #define BABELTRACE2_TRACE_IR_CLOCK_SNAPSHOT_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -105,7 +107,7 @@ properties of its class.
 @bt_pre_not_null{clock_snapshot}
 */
 extern const bt_clock_class *bt_clock_snapshot_borrow_clock_class_const(
-               const bt_clock_snapshot *clock_snapshot);
+               const bt_clock_snapshot *clock_snapshot) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -125,7 +127,7 @@ extern const bt_clock_class *bt_clock_snapshot_borrow_clock_class_const(
     clock snapshot's value.
 */
 extern uint64_t bt_clock_snapshot_get_value(
-               const bt_clock_snapshot *clock_snapshot);
+               const bt_clock_snapshot *clock_snapshot) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -201,7 +203,7 @@ code if any step of the computation process causes an integer overflow.
 extern bt_clock_snapshot_get_ns_from_origin_status
 bt_clock_snapshot_get_ns_from_origin(
                const bt_clock_snapshot *clock_snapshot,
-               int64_t *ns_from_origin);
+               int64_t *ns_from_origin) __BT_NOEXCEPT;
 
 /*! @} */
 
index 845b64f1ca1237c7d195f4d85157f7f03fe3e0d3..f4e815d5e6847e4c1ddbecf97bcd89b78f5a9d40 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_EVENT_CLASS_H
 #define BABELTRACE2_TRACE_IR_EVENT_CLASS_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -257,7 +259,7 @@ On success, the returned event class has the following property values:
     stream class.
 */
 extern bt_event_class *bt_event_class_create(
-               bt_stream_class *stream_class);
+               bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -330,7 +332,7 @@ On success, the returned event class has the following property values:
     stream class.
 */
 extern bt_event_class *bt_event_class_create_with_id(
-               bt_stream_class *stream_class, uint64_t id);
+               bt_stream_class *stream_class, uint64_t id) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -356,7 +358,7 @@ extern bt_event_class *bt_event_class_create_with_id(
     \c const version of this function.
 */
 extern bt_stream_class *bt_event_class_borrow_stream_class(
-               bt_event_class *event_class);
+               bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -366,7 +368,7 @@ extern bt_stream_class *bt_event_class_borrow_stream_class(
 See bt_event_class_borrow_stream_class().
 */
 extern const bt_stream_class *bt_event_class_borrow_stream_class_const(
-               const bt_event_class *event_class);
+               const bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -393,7 +395,8 @@ See the \ref api-tir-ev-cls-prop-id "numeric ID" property.
     Creates an event class with a specific numeric ID and adds it to a
     stream class.
 */
-extern uint64_t bt_event_class_get_id(const bt_event_class *event_class);
+extern uint64_t bt_event_class_get_id(
+               const bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -438,7 +441,7 @@ See the \ref api-tir-ev-cls-prop-name "name" property.
     Returns the name of an event class.
 */
 extern bt_event_class_set_name_status bt_event_class_set_name(
-               bt_event_class *event_class, const char *name);
+               bt_event_class *event_class, const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -464,7 +467,8 @@ If \bt_p{event_class} has no name, this function returns \c NULL.
 @sa bt_event_class_set_name() &mdash;
     Sets the name of an event class.
 */
-extern const char *bt_event_class_get_name(const bt_event_class *event_class);
+extern const char *bt_event_class_get_name(
+               const bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -586,7 +590,7 @@ See the \ref api-tir-ev-cls-prop-log-lvl "log level" property.
     Returns the log level of an event class.
 */
 extern void bt_event_class_set_log_level(bt_event_class *event_class,
-               bt_event_class_log_level log_level);
+               bt_event_class_log_level log_level) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -614,7 +618,7 @@ See the \ref api-tir-ev-cls-prop-log-lvl "log level" property.
 */
 extern bt_property_availability bt_event_class_get_log_level(
                const bt_event_class *event_class,
-               bt_event_class_log_level *log_level);
+               bt_event_class_log_level *log_level) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -659,7 +663,7 @@ See the \ref api-tir-ev-cls-prop-emf-uri "EMF URI" property.
     Returns the EMF URI of an event class.
 */
 extern bt_event_class_set_emf_uri_status bt_event_class_set_emf_uri(
-               bt_event_class *event_class, const char *emf_uri);
+               bt_event_class *event_class, const char *emf_uri) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -687,7 +691,7 @@ If \bt_p{event_class} has no EMF URI, this function returns \c NULL.
     Sets the EMF URI of an event class.
 */
 extern const char *bt_event_class_get_emf_uri(
-               const bt_event_class *event_class);
+               const bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -749,7 +753,7 @@ See the \ref api-tir-ev-cls-prop-p-fc "payload field class" property.
 */
 extern bt_event_class_set_field_class_status
 bt_event_class_set_payload_field_class(bt_event_class *event_class,
-               bt_field_class *field_class);
+               bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -775,7 +779,7 @@ returns \c NULL.
     \c const version of this function.
 */
 extern bt_field_class *bt_event_class_borrow_payload_field_class(
-               bt_event_class *event_class);
+               bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -785,7 +789,7 @@ extern bt_field_class *bt_event_class_borrow_payload_field_class(
 See bt_event_class_borrow_payload_field_class().
 */
 extern const bt_field_class *bt_event_class_borrow_payload_field_class_const(
-               const bt_event_class *event_class);
+               const bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -829,7 +833,7 @@ property.
 */
 extern bt_event_class_set_field_class_status
 bt_event_class_set_specific_context_field_class(bt_event_class *event_class,
-               bt_field_class *field_class);
+               bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -857,7 +861,8 @@ returns \c NULL.
     \c const version of this function.
 */
 extern bt_field_class *
-bt_event_class_borrow_specific_context_field_class(bt_event_class *event_class);
+bt_event_class_borrow_specific_context_field_class(
+               bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -868,7 +873,7 @@ See bt_event_class_borrow_specific_context_field_class().
 */
 extern const bt_field_class *
 bt_event_class_borrow_specific_context_field_class_const(
-               const bt_event_class *event_class);
+               const bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -899,7 +904,8 @@ See the \ref api-tir-ev-cls-prop-user-attrs "user attributes" property.
     Borrows the user attributes of an event class.
 */
 extern void bt_event_class_set_user_attributes(
-               bt_event_class *event_class, const bt_value *user_attributes);
+               bt_event_class *event_class, const bt_value *user_attributes)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -926,7 +932,7 @@ See the \ref api-tir-ev-cls-prop-user-attrs "user attributes" property.
     \c const version of this function.
 */
 extern bt_value *bt_event_class_borrow_user_attributes(
-               bt_event_class *event_class);
+               bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -936,7 +942,7 @@ extern bt_value *bt_event_class_borrow_user_attributes(
 See bt_event_class_borrow_user_attributes().
 */
 extern const bt_value *bt_event_class_borrow_user_attributes_const(
-               const bt_event_class *event_class);
+               const bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -960,7 +966,8 @@ extern const bt_value *bt_event_class_borrow_user_attributes_const(
 @sa bt_event_class_put_ref() &mdash;
     Decrements the reference count of an event class.
 */
-extern void bt_event_class_get_ref(const bt_event_class *event_class);
+extern void bt_event_class_get_ref(
+               const bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -977,7 +984,8 @@ extern void bt_event_class_get_ref(const bt_event_class *event_class);
 @sa bt_event_class_get_ref() &mdash;
     Increments the reference count of an event class.
 */
-extern void bt_event_class_put_ref(const bt_event_class *event_class);
+extern void bt_event_class_put_ref(
+               const bt_event_class *event_class) __BT_NOEXCEPT;
 
 /*!
 @brief
index 474f0c9cb5b76ee3ca7dc0446d6d4615abd510ed..bcb2af3deca25384ab3d4b72b082c30c4cd127a7 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_EVENT_H
 #define BABELTRACE2_TRACE_IR_EVENT_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -167,7 +169,7 @@ An event has the following properties:
 @sa bt_event_borrow_class_const() &mdash;
     \c const version of this function.
 */
-extern bt_event_class *bt_event_borrow_class(bt_event *event);
+extern bt_event_class *bt_event_borrow_class(bt_event *event) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -177,7 +179,7 @@ extern bt_event_class *bt_event_borrow_class(bt_event *event);
 See bt_event_borrow_class().
 */
 extern const bt_event_class *bt_event_borrow_class_const(
-               const bt_event *event);
+               const bt_event *event) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -203,7 +205,7 @@ extern const bt_event_class *bt_event_borrow_class_const(
 @sa bt_event_borrow_stream_const() &mdash;
     \c const version of this function.
 */
-extern bt_stream *bt_event_borrow_stream(bt_event *event);
+extern bt_stream *bt_event_borrow_stream(bt_event *event) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -213,7 +215,7 @@ extern bt_stream *bt_event_borrow_stream(bt_event *event);
 See bt_event_borrow_stream().
 */
 extern const bt_stream *bt_event_borrow_stream_const(
-               const bt_event *event);
+               const bt_event *event) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -243,7 +245,7 @@ extern const bt_stream *bt_event_borrow_stream_const(
 @sa bt_event_borrow_packet_const() &mdash;
     \c const version of this function.
 */
-extern bt_packet *bt_event_borrow_packet(bt_event *event);
+extern bt_packet *bt_event_borrow_packet(bt_event *event) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -253,7 +255,7 @@ extern bt_packet *bt_event_borrow_packet(bt_event *event);
 See bt_event_borrow_packet().
 */
 extern const bt_packet *bt_event_borrow_packet_const(
-               const bt_event *event);
+               const bt_event *event) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -280,7 +282,7 @@ See the \ref api-tir-ev-prop-payload "payload field" property.
 @sa bt_event_borrow_payload_field_const() &mdash;
     \c const version of this function.
 */
-extern bt_field *bt_event_borrow_payload_field(bt_event *event);
+extern bt_field *bt_event_borrow_payload_field(bt_event *event) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -290,7 +292,7 @@ extern bt_field *bt_event_borrow_payload_field(bt_event *event);
 See bt_event_borrow_payload_field().
 */
 extern const bt_field *bt_event_borrow_payload_field_const(
-               const bt_event *event);
+               const bt_event *event) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -311,7 +313,7 @@ See the \ref api-tir-ev-prop-spec-ctx "specific context field" property.
     \c const version of this function.
 */
 extern bt_field *
-bt_event_borrow_specific_context_field(bt_event *event);
+bt_event_borrow_specific_context_field(bt_event *event) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -321,7 +323,7 @@ bt_event_borrow_specific_context_field(bt_event *event);
 See bt_event_borrow_specific_context_field().
 */
 extern const bt_field *bt_event_borrow_specific_context_field_const(
-               const bt_event *event);
+               const bt_event *event) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -342,7 +344,7 @@ See the \ref api-tir-ev-prop-common-ctx "common context field" property.
     \c const version of this function.
 */
 extern bt_field *
-bt_event_borrow_common_context_field(bt_event *event);
+bt_event_borrow_common_context_field(bt_event *event) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -352,7 +354,7 @@ bt_event_borrow_common_context_field(bt_event *event);
 See bt_event_borrow_common_context_field().
 */
 extern const bt_field *bt_event_borrow_common_context_field_const(
-               const bt_event *event);
+               const bt_event *event) __BT_NOEXCEPT;
 
 /*! @} */
 
index 4d8624828e598eac1916d85102214cae5c93cc68..fdd2099502c4c1878cd57783a20b0be8081d3be7 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_FIELD_CLASS_H
 #define BABELTRACE2_TRACE_IR_FIELD_CLASS_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -1495,7 +1497,7 @@ typedef enum bt_field_class_type {
     given type.
 */
 extern bt_field_class_type bt_field_class_get_type(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1524,7 +1526,7 @@ returns #BT_TRUE.
 */
 static inline
 bt_bool bt_field_class_type_is(const bt_field_class_type type,
-               const bt_field_class_type other_type)
+               const bt_field_class_type other_type) __BT_NOEXCEPT
 {
        return (type & other_type) == other_type;
 }
@@ -1565,7 +1567,8 @@ See the \ref api-tir-fc-prop-user-attrs "user attributes" property.
     Borrows the user attributes of a field class.
 */
 extern void bt_field_class_set_user_attributes(
-               bt_field_class *field_class, const bt_value *user_attributes);
+               bt_field_class *field_class,
+               const bt_value *user_attributes) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1592,7 +1595,7 @@ See the \ref api-tir-fc-prop-user-attrs "user attributes" property.
     \c const version of this function.
 */
 extern bt_value *bt_field_class_borrow_user_attributes(
-               bt_field_class *field_class);
+               bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1602,7 +1605,7 @@ extern bt_value *bt_field_class_borrow_user_attributes(
 See bt_field_class_borrow_user_attributes().
 */
 extern const bt_value *bt_field_class_borrow_user_attributes_const(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1636,7 +1639,7 @@ property value:
 @bt_pre_not_null{trace_class}
 */
 extern bt_field_class *bt_field_class_bool_create(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @}
@@ -1681,7 +1684,7 @@ property values:
     0 < \bt_p{length} ≤ 64.
 */
 extern bt_field_class *bt_field_class_bit_array_create(
-               bt_trace_class *trace_class, uint64_t length);
+               bt_trace_class *trace_class, uint64_t length) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1699,7 +1702,7 @@ See the \ref api-tir-fc-ba-prop-len "length" property.
 @bt_pre_is_ba_fc{field_class}
 */
 extern uint64_t bt_field_class_bit_array_get_length(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @}
@@ -1743,7 +1746,7 @@ See the \ref api-tir-fc-int-prop-size "field value range" property.
     Returns the field value range of an integer field class.
 */
 extern void bt_field_class_integer_set_field_value_range(
-               bt_field_class *field_class, uint64_t n);
+               bt_field_class *field_class, uint64_t n) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1774,7 +1777,7 @@ See the \ref api-tir-fc-int-prop-size "field value range" property.
     Sets the field value range of an integer field class.
 */
 extern uint64_t bt_field_class_integer_get_field_value_range(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1828,7 +1831,8 @@ See the \ref api-tir-fc-int-prop-base "preferred display base" property.
 */
 extern void bt_field_class_integer_set_preferred_display_base(
                bt_field_class *field_class,
-               bt_field_class_integer_preferred_display_base preferred_display_base);
+               bt_field_class_integer_preferred_display_base preferred_display_base)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1857,7 +1861,7 @@ See the \ref api-tir-fc-int-prop-base "preferred display base" property.
 */
 extern bt_field_class_integer_preferred_display_base
 bt_field_class_integer_get_preferred_display_base(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1897,7 +1901,7 @@ property values:
 @bt_pre_not_null{trace_class}
 */
 extern bt_field_class *bt_field_class_integer_unsigned_create(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1937,7 +1941,7 @@ property values:
 @bt_pre_not_null{trace_class}
 */
 extern bt_field_class *bt_field_class_integer_signed_create(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1974,7 +1978,7 @@ following property value:
 @bt_pre_not_null{trace_class}
 */
 extern bt_field_class *bt_field_class_real_single_precision_create(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2011,7 +2015,7 @@ following property value:
 @bt_pre_not_null{trace_class}
 */
 extern bt_field_class *bt_field_class_real_double_precision_create(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2101,7 +2105,7 @@ See the \ref api-tir-fc-enum-prop-mappings "mappings" property.
 @bt_pre_is_enum_fc{field_class}
 */
 extern uint64_t bt_field_class_enumeration_get_mapping_count(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2122,7 +2126,8 @@ See the \ref api-tir-fc-enum-prop-mappings "mappings" property.
 @bt_pre_not_null{mapping}
 */
 extern const char *bt_field_class_enumeration_mapping_get_label(
-               const bt_field_class_enumeration_mapping *mapping);
+               const bt_field_class_enumeration_mapping *mapping)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2167,7 +2172,7 @@ following property values:
 @bt_pre_not_null{trace_class}
 */
 extern bt_field_class *bt_field_class_enumeration_unsigned_create(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2203,7 +2208,7 @@ See the \ref api-tir-fc-enum-prop-mappings "mappings" property.
 extern bt_field_class_enumeration_add_mapping_status
 bt_field_class_enumeration_unsigned_add_mapping(
                bt_field_class *field_class, const char *label,
-               const bt_integer_range_set_unsigned *ranges);
+               const bt_integer_range_set_unsigned *ranges) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2240,7 +2245,8 @@ See the \ref api-tir-fc-enum-prop-mappings "mappings" property.
 */
 extern const bt_field_class_enumeration_unsigned_mapping *
 bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
-               const bt_field_class *field_class, uint64_t index);
+               const bt_field_class *field_class, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2274,7 +2280,8 @@ If there's no mapping having the label \bt_p{label} in
 */
 extern const bt_field_class_enumeration_unsigned_mapping *
 bt_field_class_enumeration_unsigned_borrow_mapping_by_label_const(
-               const bt_field_class *field_class, const char *label);
+               const bt_field_class *field_class, const char *label)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2327,7 +2334,7 @@ extern bt_field_class_enumeration_get_mapping_labels_for_value_status
 bt_field_class_enumeration_unsigned_get_mapping_labels_for_value(
                const bt_field_class *field_class, uint64_t value,
                bt_field_class_enumeration_mapping_label_array *labels,
-               uint64_t *count);
+               uint64_t *count) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2361,7 +2368,8 @@ See the \ref api-tir-fc-enum-prop-mappings "mappings" property.
 */
 extern const bt_integer_range_set_unsigned *
 bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
-               const bt_field_class_enumeration_unsigned_mapping *mapping);
+               const bt_field_class_enumeration_unsigned_mapping *mapping)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2385,6 +2393,7 @@ static inline
 const bt_field_class_enumeration_mapping *
 bt_field_class_enumeration_unsigned_mapping_as_mapping_const(
                const bt_field_class_enumeration_unsigned_mapping *mapping)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_field_class_enumeration_mapping, mapping);
 }
@@ -2432,7 +2441,7 @@ following property values:
 @bt_pre_not_null{trace_class}
 */
 extern bt_field_class *bt_field_class_enumeration_signed_create(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2468,7 +2477,7 @@ See the \ref api-tir-fc-enum-prop-mappings "mappings" property.
 extern bt_field_class_enumeration_add_mapping_status
 bt_field_class_enumeration_signed_add_mapping(
                bt_field_class *field_class, const char *label,
-               const bt_integer_range_set_signed *ranges);
+               const bt_integer_range_set_signed *ranges) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2505,7 +2514,8 @@ See the \ref api-tir-fc-enum-prop-mappings "mappings" property.
 */
 extern const bt_field_class_enumeration_signed_mapping *
 bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
-               const bt_field_class *field_class, uint64_t index);
+               const bt_field_class *field_class, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2539,7 +2549,8 @@ If there's no mapping having the label \bt_p{label} in
 */
 extern const bt_field_class_enumeration_signed_mapping *
 bt_field_class_enumeration_signed_borrow_mapping_by_label_const(
-               const bt_field_class *field_class, const char *label);
+               const bt_field_class *field_class, const char *label)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2592,7 +2603,7 @@ extern bt_field_class_enumeration_get_mapping_labels_for_value_status
 bt_field_class_enumeration_signed_get_mapping_labels_for_value(
                const bt_field_class *field_class, int64_t value,
                bt_field_class_enumeration_mapping_label_array *labels,
-               uint64_t *count);
+               uint64_t *count) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2626,7 +2637,8 @@ See the \ref api-tir-fc-enum-prop-mappings "mappings" property.
 */
 extern const bt_integer_range_set_signed *
 bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
-               const bt_field_class_enumeration_signed_mapping *mapping);
+               const bt_field_class_enumeration_signed_mapping *mapping)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2650,6 +2662,7 @@ static inline
 const bt_field_class_enumeration_mapping *
 bt_field_class_enumeration_signed_mapping_as_mapping_const(
                const bt_field_class_enumeration_signed_mapping *mapping)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_field_class_enumeration_mapping, mapping);
 }
@@ -2686,7 +2699,7 @@ value:
 @bt_pre_not_null{trace_class}
 */
 extern bt_field_class *bt_field_class_string_create(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2716,7 +2729,7 @@ property.
     \c const version of this function.
 */
 extern bt_field_class *bt_field_class_array_borrow_element_field_class(
-               bt_field_class *field_class);
+               bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2727,7 +2740,7 @@ See bt_field_class_array_borrow_element_field_class().
 */
 extern const bt_field_class *
 bt_field_class_array_borrow_element_field_class_const(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2779,7 +2792,8 @@ bt_pre_fc_not_in_tc{element_field_class}
 */
 extern bt_field_class *bt_field_class_array_static_create(
                bt_trace_class *trace_class,
-               bt_field_class *element_field_class, uint64_t length);
+               bt_field_class *element_field_class, uint64_t length)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2797,7 +2811,7 @@ See the \ref api-tir-fc-sarray-prop-len "length" property.
 @bt_pre_is_sarray_fc{field_class}
 */
 extern uint64_t bt_field_class_array_static_get_length(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2867,7 +2881,7 @@ property values:
 extern bt_field_class *bt_field_class_array_dynamic_create(
                bt_trace_class *trace_class,
                bt_field_class *element_field_class,
-               bt_field_class *length_field_class);
+               bt_field_class *length_field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2905,7 +2919,7 @@ In the meantime, this function returns \c NULL.
 */
 extern const bt_field_path *
 bt_field_class_array_dynamic_with_length_field_borrow_length_field_path_const(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2942,7 +2956,7 @@ property values:
 @bt_pre_not_null{trace_class}
 */
 extern bt_field_class *bt_field_class_structure_create(
-    bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2995,8 +3009,9 @@ See the \ref api-tir-fc-struct-prop-members "members" property.
 */
 extern bt_field_class_structure_append_member_status
 bt_field_class_structure_append_member(
-    bt_field_class *field_class,
-    const char *name, bt_field_class *member_field_class);
+               bt_field_class *field_class,
+               const char *name, bt_field_class *member_field_class)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3016,7 +3031,7 @@ See the \ref api-tir-fc-struct-prop-members "members" property.
 @bt_pre_is_struct_fc{field_class}
 */
 extern uint64_t bt_field_class_structure_get_member_count(
-    const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3054,7 +3069,7 @@ See the \ref api-tir-fc-struct-prop-members "members" property.
 */
 extern bt_field_class_structure_member *
 bt_field_class_structure_borrow_member_by_index(
-    bt_field_class *field_class, uint64_t index);
+               bt_field_class *field_class, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3065,7 +3080,8 @@ See bt_field_class_structure_borrow_member_by_index().
 */
 extern const bt_field_class_structure_member *
 bt_field_class_structure_borrow_member_by_index_const(
-    const bt_field_class *field_class, uint64_t index);
+               const bt_field_class *field_class, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3102,7 +3118,7 @@ If there's no member having the name \bt_p{name} in
 */
 extern bt_field_class_structure_member *
 bt_field_class_structure_borrow_member_by_name(
-    bt_field_class *field_class, const char *name);
+               bt_field_class *field_class, const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3113,7 +3129,8 @@ See bt_field_class_structure_borrow_member_by_name().
 */
 extern const bt_field_class_structure_member *
 bt_field_class_structure_borrow_member_by_name_const(
-    const bt_field_class *field_class, const char *name);
+               const bt_field_class *field_class, const char *name)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3148,7 +3165,7 @@ See the \ref api-tir-fc-struct-prop-members "members" property.
 @bt_pre_not_null{member}
 */
 extern const char *bt_field_class_structure_member_get_name(
-    const bt_field_class_structure_member *member);
+               const bt_field_class_structure_member *member) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3170,7 +3187,7 @@ See the \ref api-tir-fc-struct-prop-members "members" property.
 */
 extern bt_field_class *
 bt_field_class_structure_member_borrow_field_class(
-    bt_field_class_structure_member *member);
+               bt_field_class_structure_member *member) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3181,7 +3198,7 @@ See bt_field_class_structure_member_borrow_field_class().
 */
 extern const bt_field_class *
 bt_field_class_structure_member_borrow_field_class_const(
-    const bt_field_class_structure_member *member);
+               const bt_field_class_structure_member *member) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3213,8 +3230,8 @@ See the \ref api-tir-fc-struct-prop-members "members" property.
     Borrows the user attributes of a structure field class member.
 */
 extern void bt_field_class_structure_member_set_user_attributes(
-    bt_field_class_structure_member *member,
-    const bt_value *user_attributes);
+               bt_field_class_structure_member *member,
+               const bt_value *user_attributes) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3244,7 +3261,7 @@ See the \ref api-tir-fc-struct-prop-members "members" property.
 */
 extern bt_value *
 bt_field_class_structure_member_borrow_user_attributes(
-    bt_field_class_structure_member *member);
+               bt_field_class_structure_member *member) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3255,7 +3272,7 @@ See bt_field_class_structure_member_borrow_user_attributes().
 */
 extern const bt_value *
 bt_field_class_structure_member_borrow_user_attributes_const(
-    const bt_field_class_structure_member *member);
+               const bt_field_class_structure_member *member) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3284,7 +3301,7 @@ See the \ref api-tir-fc-opt-prop-fc "optional field class" property.
     \c const version of this function.
 */
 extern bt_field_class *bt_field_class_option_borrow_field_class(
-               bt_field_class *field_class);
+               bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3295,7 +3312,7 @@ See bt_field_class_option_borrow_field_class().
 */
 extern const bt_field_class *
 bt_field_class_option_borrow_field_class_const(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3342,7 +3359,7 @@ values:
 */
 extern bt_field_class *bt_field_class_option_without_selector_create(
                bt_trace_class *trace_class,
-               bt_field_class *optional_field_class);
+               bt_field_class *optional_field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3379,7 +3396,7 @@ In the meantime, this function returns \c NULL.
 */
 extern const bt_field_path *
 bt_field_class_option_with_selector_field_borrow_selector_field_path_const(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3441,7 +3458,7 @@ values:
 extern bt_field_class *bt_field_class_option_with_selector_field_bool_create(
                bt_trace_class *trace_class,
                bt_field_class *optional_field_class,
-               bt_field_class *selector_field_class);
+               bt_field_class *selector_field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3467,7 +3484,8 @@ property.
 */
 extern void
 bt_field_class_option_with_selector_field_bool_set_selector_is_reversed(
-               bt_field_class *field_class, bt_bool selector_is_reversed);
+               bt_field_class *field_class, bt_bool selector_is_reversed)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3493,7 +3511,7 @@ property.
 */
 extern bt_bool
 bt_field_class_option_with_selector_field_bool_selector_is_reversed(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3564,7 +3582,8 @@ bt_field_class_option_with_selector_field_integer_unsigned_create(
                bt_trace_class *trace_class,
                bt_field_class *optional_field_class,
                bt_field_class *selector_field_class,
-               const bt_integer_range_set_unsigned *ranges);
+               const bt_integer_range_set_unsigned *ranges)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3586,7 +3605,7 @@ property.
 */
 extern const bt_integer_range_set_unsigned *
 bt_field_class_option_with_selector_field_integer_unsigned_borrow_selector_ranges_const(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3657,7 +3676,7 @@ bt_field_class_option_with_selector_field_integer_signed_create(
                bt_trace_class *trace_class,
                bt_field_class *optional_field_class,
                bt_field_class *selector_field_class,
-               const bt_integer_range_set_signed *ranges);
+               const bt_integer_range_set_signed *ranges) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3679,7 +3698,7 @@ property.
 */
 extern const bt_integer_range_set_signed *
 bt_field_class_option_with_selector_field_integer_signed_borrow_selector_ranges_const(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3741,7 +3760,7 @@ property values:
 */
 extern bt_field_class *bt_field_class_variant_create(
                bt_trace_class *trace_class,
-               bt_field_class *selector_field_class);
+               bt_field_class *selector_field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3761,7 +3780,7 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
 @bt_pre_is_var_fc{field_class}
 */
 extern uint64_t bt_field_class_variant_get_option_count(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3799,7 +3818,7 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
 */
 extern bt_field_class_variant_option *
 bt_field_class_variant_borrow_option_by_index(
-               bt_field_class *field_class, uint64_t index);
+               bt_field_class *field_class, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3810,7 +3829,8 @@ See bt_field_class_variant_borrow_option_by_index().
 */
 extern const bt_field_class_variant_option *
 bt_field_class_variant_borrow_option_by_index_const(
-               const bt_field_class *field_class, uint64_t index);
+               const bt_field_class *field_class, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3847,7 +3867,7 @@ If there's no option having the name \bt_p{name} in
 */
 extern bt_field_class_variant_option *
 bt_field_class_variant_borrow_option_by_name(
-               bt_field_class *field_class, const char *name);
+               bt_field_class *field_class, const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3858,7 +3878,8 @@ See bt_field_class_variant_borrow_option_by_name().
 */
 extern const bt_field_class_variant_option *
 bt_field_class_variant_borrow_option_by_name_const(
-               const bt_field_class *field_class, const char *name);
+               const bt_field_class *field_class, const char *name)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -3893,7 +3914,7 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
 @bt_pre_not_null{option}
 */
 extern const char *bt_field_class_variant_option_get_name(
-               const bt_field_class_variant_option *option);
+               const bt_field_class_variant_option *option) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3914,7 +3935,7 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
     \c const version of this function.
 */
 extern bt_field_class *bt_field_class_variant_option_borrow_field_class(
-               bt_field_class_variant_option *option);
+               bt_field_class_variant_option *option) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3925,7 +3946,7 @@ See bt_field_class_variant_option_borrow_field_class().
 */
 extern const bt_field_class *
 bt_field_class_variant_option_borrow_field_class_const(
-               const bt_field_class_variant_option *option);
+               const bt_field_class_variant_option *option) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3961,7 +3982,7 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
 */
 extern void bt_field_class_variant_option_set_user_attributes(
                bt_field_class_variant_option *option,
-               const bt_value *user_attributes);
+               const bt_value *user_attributes) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -3992,7 +4013,7 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
     \c const version of this function.
 */
 extern bt_value *bt_field_class_variant_option_borrow_user_attributes(
-               bt_field_class_variant_option *option);
+               bt_field_class_variant_option *option) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -4002,7 +4023,7 @@ extern bt_value *bt_field_class_variant_option_borrow_user_attributes(
 See bt_field_class_variant_option_borrow_user_attributes().
 */
 extern const bt_value *bt_field_class_variant_option_borrow_user_attributes_const(
-               const bt_field_class_variant_option *option);
+               const bt_field_class_variant_option *option) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -4065,7 +4086,7 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
 extern bt_field_class_variant_without_selector_append_option_status
 bt_field_class_variant_without_selector_append_option(
                bt_field_class *field_class, const char *name,
-               bt_field_class *option_field_class);
+               bt_field_class *option_field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -4123,7 +4144,7 @@ In the meantime, this function returns \c NULL.
 */
 extern const bt_field_path *
 bt_field_class_variant_with_selector_field_borrow_selector_field_path_const(
-               const bt_field_class *field_class);
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -4180,7 +4201,7 @@ extern bt_field_class_variant_with_selector_field_integer_append_option_status
 bt_field_class_variant_with_selector_field_integer_unsigned_append_option(
                bt_field_class *field_class, const char *name,
                bt_field_class *option_field_class,
-               const bt_integer_range_set_unsigned *ranges);
+               const bt_integer_range_set_unsigned *ranges) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -4217,7 +4238,8 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
 */
 extern const bt_field_class_variant_with_selector_field_integer_unsigned_option *
 bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_index_const(
-               const bt_field_class *field_class, uint64_t index);
+               const bt_field_class *field_class, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -4255,7 +4277,8 @@ If there's no option having the name \bt_p{name} in
 */
 extern const bt_field_class_variant_with_selector_field_integer_unsigned_option *
 bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_name_const(
-               const bt_field_class *field_class, const char *name);
+               const bt_field_class *field_class, const char *name)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -4289,7 +4312,8 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
 */
 extern const bt_integer_range_set_unsigned *
 bt_field_class_variant_with_selector_field_integer_unsigned_option_borrow_ranges_const(
-               const bt_field_class_variant_with_selector_field_integer_unsigned_option *option);
+               const bt_field_class_variant_with_selector_field_integer_unsigned_option *option)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -4313,6 +4337,7 @@ static inline
 const bt_field_class_variant_option *
 bt_field_class_variant_with_selector_field_integer_unsigned_option_as_option_const(
                const bt_field_class_variant_with_selector_field_integer_unsigned_option *option)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_field_class_variant_option, option);
 }
@@ -4372,7 +4397,8 @@ extern bt_field_class_variant_with_selector_field_integer_append_option_status
 bt_field_class_variant_with_selector_field_integer_signed_append_option(
                bt_field_class *field_class, const char *name,
                bt_field_class *option_field_class,
-               const bt_integer_range_set_signed *ranges);
+               const bt_integer_range_set_signed *ranges)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -4409,7 +4435,8 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
 */
 extern const bt_field_class_variant_with_selector_field_integer_signed_option *
 bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_index_const(
-               const bt_field_class *field_class, uint64_t index);
+               const bt_field_class *field_class, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -4447,7 +4474,8 @@ If there's no option having the name \bt_p{name} in
 */
 extern const bt_field_class_variant_with_selector_field_integer_signed_option *
 bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_name_const(
-               const bt_field_class *field_class, const char *name);
+               const bt_field_class *field_class, const char *name)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -4481,7 +4509,8 @@ See the \ref api-tir-fc-var-prop-opts "options" property.
 */
 extern const bt_integer_range_set_signed *
 bt_field_class_variant_with_selector_field_integer_signed_option_borrow_ranges_const(
-               const bt_field_class_variant_with_selector_field_integer_signed_option *option);
+               const bt_field_class_variant_with_selector_field_integer_signed_option *option)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -4505,6 +4534,7 @@ static inline
 const bt_field_class_variant_option *
 bt_field_class_variant_with_selector_field_integer_signed_option_as_option_const(
                const bt_field_class_variant_with_selector_field_integer_signed_option *option)
+               __BT_NOEXCEPT
 {
        return __BT_UPCAST_CONST(bt_field_class_variant_option, option);
 }
@@ -4531,7 +4561,8 @@ bt_field_class_variant_with_selector_field_integer_signed_option_as_option_const
 @sa bt_field_class_put_ref() &mdash;
     Decrements the reference count of a field class.
 */
-extern void bt_field_class_get_ref(const bt_field_class *field_class);
+extern void bt_field_class_get_ref(
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -4548,7 +4579,8 @@ extern void bt_field_class_get_ref(const bt_field_class *field_class);
 @sa bt_field_class_get_ref() &mdash;
     Increments the reference count of a field class.
 */
-extern void bt_field_class_put_ref(const bt_field_class *field_class);
+extern void bt_field_class_put_ref(
+               const bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
index f58a454c6ae3292765f6c802fa70be0996d90237..039ceb5095fcc1f0a02a99b000ca1c7217687dd4 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_FIELD_PATH_H
 #define BABELTRACE2_TRACE_IR_FIELD_PATH_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -228,7 +230,7 @@ See the \ref api-tir-field-path-prop-root "root scope" property.
 @bt_pre_not_null{field_path}
 */
 extern bt_field_path_scope bt_field_path_get_root_scope(
-               const bt_field_path *field_path);
+               const bt_field_path *field_path) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -246,7 +248,7 @@ See the \ref api-tir-field-path-prop-items "items" property.
 @bt_pre_not_null{field_path}
 */
 extern uint64_t bt_field_path_get_item_count(
-               const bt_field_path *field_path);
+               const bt_field_path *field_path) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -278,7 +280,7 @@ See the \ref api-tir-field-path-prop-items "items" property.
     Returns the number of items contained in a field path.
 */
 extern const bt_field_path_item *bt_field_path_borrow_item_by_index_const(
-               const bt_field_path *field_path, uint64_t index);
+               const bt_field_path *field_path, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -295,7 +297,8 @@ extern const bt_field_path_item *bt_field_path_borrow_item_by_index_const(
 @sa bt_field_path_put_ref() &mdash;
     Decrements the reference count of a field path.
 */
-extern void bt_field_path_get_ref(const bt_field_path *field_path);
+extern void bt_field_path_get_ref(const bt_field_path *field_path)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -312,7 +315,8 @@ extern void bt_field_path_get_ref(const bt_field_path *field_path);
 @sa bt_field_path_get_ref() &mdash;
     Increments the reference count of a field path.
 */
-extern void bt_field_path_put_ref(const bt_field_path *field_path);
+extern void bt_field_path_put_ref(const bt_field_path *field_path)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -420,7 +424,7 @@ See the \ref api-tir-field-path-prop-items "items" property.
 @bt_pre_not_null{item}
 */
 extern bt_field_path_item_type bt_field_path_item_get_type(
-               const bt_field_path_item *item);
+               const bt_field_path_item *item) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -442,7 +446,7 @@ See the \ref api-tir-field-path-prop-items "items" property.
     #BT_FIELD_PATH_ITEM_TYPE_INDEX).
 */
 extern uint64_t bt_field_path_item_index_get_index(
-               const bt_field_path_item *item);
+               const bt_field_path_item *item) __BT_NOEXCEPT;
 
 /*! @} */
 
index 1f7847d56a1b9a259c8e8944ecb8d71cd0f0c322..dc3934392e08b980d8ea7c084acf2be4ff8bc4ce 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_FIELD_H
 #define BABELTRACE2_TRACE_IR_FIELD_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -442,7 +444,7 @@ bt_field_class_get_type(bt_field_borrow_class(field))
     Returns the type enumerator of a \bt_fc.
 */
 extern bt_field_class_type bt_field_get_class_type(
-               const bt_field *field);
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -466,7 +468,7 @@ extern bt_field_class_type bt_field_get_class_type(
 @sa bt_field_borrow_class_const() &mdash;
     \c const version of this function.
 */
-extern bt_field_class *bt_field_borrow_class(bt_field *field);
+extern bt_field_class *bt_field_borrow_class(bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -476,7 +478,7 @@ extern bt_field_class *bt_field_borrow_class(bt_field *field);
 See bt_field_borrow_class().
 */
 extern const bt_field_class *bt_field_borrow_class_const(
-               const bt_field *field);
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -502,7 +504,8 @@ extern const bt_field_class *bt_field_borrow_class_const(
 @sa bt_field_bool_get_value() &mdash;
     Returns the value of a boolean field.
 */
-extern void bt_field_bool_set_value(bt_field *field, bt_bool value);
+extern void bt_field_bool_set_value(bt_field *field, bt_bool value)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -520,7 +523,7 @@ extern void bt_field_bool_set_value(bt_field *field, bt_bool value);
 @sa bt_field_bool_set_value() &mdash;
     Sets the value of a boolean field.
 */
-extern bt_bool bt_field_bool_get_value(const bt_field *field);
+extern bt_bool bt_field_bool_get_value(const bt_field *field) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -551,7 +554,7 @@ See \bt_c_ba_field to learn more.
     Returns the bits of a bit array field as an integer.
 */
 extern void bt_field_bit_array_set_value_as_integer(bt_field *field,
-               uint64_t bits);
+               uint64_t bits) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -575,7 +578,7 @@ See \bt_c_ba_field to learn more.
     Sets the bits of a bit array field from an integer.
 */
 extern uint64_t bt_field_bit_array_get_value_as_integer(
-               const bt_field *field);
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -606,7 +609,7 @@ extern uint64_t bt_field_bit_array_get_value_as_integer(
     Returns the value of an unsigned integer field.
 */
 extern void bt_field_integer_unsigned_set_value(bt_field *field,
-               uint64_t value);
+               uint64_t value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -625,7 +628,7 @@ extern void bt_field_integer_unsigned_set_value(bt_field *field,
     Sets the value of an unsigned integer field.
 */
 extern uint64_t bt_field_integer_unsigned_get_value(
-               const bt_field *field);
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -649,7 +652,7 @@ extern uint64_t bt_field_integer_unsigned_get_value(
     Returns the value of an signed integer field.
 */
 extern void bt_field_integer_signed_set_value(bt_field *field,
-               int64_t value);
+               int64_t value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -667,7 +670,8 @@ extern void bt_field_integer_signed_set_value(bt_field *field,
 @sa bt_field_integer_signed_set_value() &mdash;
     Sets the value of an signed integer field.
 */
-extern int64_t bt_field_integer_signed_get_value(const bt_field *field);
+extern int64_t bt_field_integer_signed_get_value(
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -737,7 +741,7 @@ bt_field_class_enumeration_unsigned_get_mapping_labels_for_value(
 extern bt_field_enumeration_get_mapping_labels_status
 bt_field_enumeration_unsigned_get_mapping_labels(const bt_field *field,
                bt_field_class_enumeration_mapping_label_array *labels,
-               uint64_t *count);
+               uint64_t *count) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -780,7 +784,7 @@ bt_field_class_enumeration_signed_get_mapping_labels_for_value(
 extern bt_field_enumeration_get_mapping_labels_status
 bt_field_enumeration_signed_get_mapping_labels(const bt_field *field,
                bt_field_class_enumeration_mapping_label_array *labels,
-               uint64_t *count);
+               uint64_t *count) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -808,7 +812,7 @@ bt_field_enumeration_signed_get_mapping_labels(const bt_field *field,
     Returns the value of a single-precision real field.
 */
 extern void bt_field_real_single_precision_set_value(bt_field *field,
-               float value);
+               float value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -826,7 +830,8 @@ extern void bt_field_real_single_precision_set_value(bt_field *field,
 @sa bt_field_real_single_precision_set_value() &mdash;
     Sets the value of a single-precision real field.
 */
-extern float bt_field_real_single_precision_get_value(const bt_field *field);
+extern float bt_field_real_single_precision_get_value(
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -847,7 +852,7 @@ extern float bt_field_real_single_precision_get_value(const bt_field *field);
     Returns the value of a double-precision real field.
 */
 extern void bt_field_real_double_precision_set_value(bt_field *field,
-               double value);
+               double value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -865,7 +870,8 @@ extern void bt_field_real_double_precision_set_value(bt_field *field,
 @sa bt_field_real_double_precision_set_value() &mdash;
     Sets the value of a double-precision real field.
 */
-extern double bt_field_real_double_precision_get_value(const bt_field *field);
+extern double bt_field_real_double_precision_get_value(
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -920,7 +926,7 @@ typedef enum bt_field_string_set_value_status {
     Clears a string field.
 */
 extern bt_field_string_set_value_status bt_field_string_set_value(
-               bt_field *field, const char *value);
+               bt_field *field, const char *value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -935,7 +941,7 @@ extern bt_field_string_set_value_status bt_field_string_set_value(
 @bt_pre_not_null{field}
 @bt_pre_is_string_field{field}
 */
-extern uint64_t bt_field_string_get_length(const bt_field *field);
+extern uint64_t bt_field_string_get_length(const bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -957,7 +963,8 @@ extern uint64_t bt_field_string_get_length(const bt_field *field);
 @sa bt_field_string_set_value() &mdash;
     Sets the value of a string field.
 */
-extern const char *bt_field_string_get_value(const bt_field *field);
+extern const char *bt_field_string_get_value(
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1008,7 +1015,7 @@ typedef enum bt_field_string_append_status {
     Sets the value of a string field.
 */
 extern bt_field_string_append_status bt_field_string_append(
-               bt_field *field, const char *value);
+               bt_field *field, const char *value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1045,7 +1052,8 @@ extern bt_field_string_append_status bt_field_string_append(
     Sets the value of a string field.
 */
 extern bt_field_string_append_status bt_field_string_append_with_length(
-               bt_field *field, const char *value, uint64_t length);
+               bt_field *field, const char *value, uint64_t length)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1062,7 +1070,7 @@ extern bt_field_string_append_status bt_field_string_append_with_length(
 @sa bt_field_string_set_value() &mdash;
     Sets the value of a string field.
 */
-extern void bt_field_string_clear(bt_field *field);
+extern void bt_field_string_clear(bt_field *field) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1084,7 +1092,7 @@ extern void bt_field_string_clear(bt_field *field);
 @bt_pre_not_null{field}
 @bt_pre_is_array_field{field}
 */
-extern uint64_t bt_field_array_get_length(const bt_field *field);
+extern uint64_t bt_field_array_get_length(const bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1119,7 +1127,7 @@ extern uint64_t bt_field_array_get_length(const bt_field *field);
     \c const version of this function.
 */
 extern bt_field *bt_field_array_borrow_element_field_by_index(
-               bt_field *field, uint64_t index);
+               bt_field *field, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1130,7 +1138,7 @@ See bt_field_array_borrow_element_field_by_index().
 */
 extern const bt_field *
 bt_field_array_borrow_element_field_by_index_const(
-               const bt_field *field, uint64_t index);
+               const bt_field *field, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1169,7 +1177,8 @@ typedef enum bt_field_array_dynamic_set_length_status {
 @bt_pre_hot{field}
 */
 extern bt_field_array_dynamic_set_length_status
-bt_field_array_dynamic_set_length(bt_field *field, uint64_t length);
+bt_field_array_dynamic_set_length(bt_field *field, uint64_t length)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1209,7 +1218,7 @@ bt_field_array_dynamic_set_length(bt_field *field, uint64_t length);
     \c const version of this function.
 */
 extern bt_field *bt_field_structure_borrow_member_field_by_index(
-               bt_field *field, uint64_t index);
+               bt_field *field, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1220,7 +1229,7 @@ See bt_field_structure_borrow_member_field_by_index().
 */
 extern const bt_field *
 bt_field_structure_borrow_member_field_by_index_const(
-               const bt_field *field, uint64_t index);
+               const bt_field *field, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1253,7 +1262,7 @@ returns \c NULL.
     \c const version of this function.
 */
 extern bt_field *bt_field_structure_borrow_member_field_by_name(
-               bt_field *field, const char *name);
+               bt_field *field, const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1264,7 +1273,7 @@ See bt_field_structure_borrow_member_field_by_name().
 */
 extern const bt_field *
 bt_field_structure_borrow_member_field_by_name_const(
-               const bt_field *field, const char *name);
+               const bt_field *field, const char *name) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1286,7 +1295,8 @@ bt_field_structure_borrow_member_field_by_name_const(
 @bt_pre_not_null{field}
 @bt_pre_is_opt_field{field}
 */
-extern void bt_field_option_set_has_field(bt_field *field, bt_bool has_field);
+extern void bt_field_option_set_has_field(bt_field *field, bt_bool has_field)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1317,7 +1327,7 @@ If \bt_p{field} has no field, this function returns \c NULL.
 @sa bt_field_option_borrow_field_const() &mdash;
     \c const version of this function.
 */
-extern bt_field *bt_field_option_borrow_field(bt_field *field);
+extern bt_field *bt_field_option_borrow_field(bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1327,7 +1337,7 @@ extern bt_field *bt_field_option_borrow_field(bt_field *field);
 See bt_field_option_borrow_field().
 */
 extern const bt_field *
-bt_field_option_borrow_field_const(const bt_field *field);
+bt_field_option_borrow_field_const(const bt_field *field) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1370,7 +1380,7 @@ typedef enum bt_field_variant_select_option_by_index_status {
 */
 extern bt_field_variant_select_option_by_index_status
 bt_field_variant_select_option_by_index(
-               bt_field *field, uint64_t index);
+               bt_field *field, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1403,7 +1413,7 @@ bt_field_variant_select_option_by_index(
     \c const version of this function.
 */
 extern bt_field *bt_field_variant_borrow_selected_option_field(
-               bt_field *field);
+               bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1414,7 +1424,7 @@ See bt_field_variant_borrow_selected_option_field().
 */
 extern const bt_field *
 bt_field_variant_borrow_selected_option_field_const(
-               const bt_field *field);
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1434,7 +1444,7 @@ bt_field_variant_borrow_selected_option_field_const(
     Borrows the field of a variant field's selected option.
 */
 extern uint64_t bt_field_variant_get_selected_option_index(
-               const bt_field *field);
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1459,7 +1469,7 @@ bt_field_class_variant_borrow_option_by_index(
 */
 extern const bt_field_class_variant_option *
 bt_field_variant_borrow_selected_option_class_const(
-               const bt_field *field);
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1484,7 +1494,7 @@ bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_ind
 */
 extern const bt_field_class_variant_with_selector_field_integer_unsigned_option *
 bt_field_variant_with_selector_field_integer_unsigned_borrow_selected_option_class_const(
-               const bt_field *field);
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1509,7 +1519,7 @@ bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_index
 */
 extern const bt_field_class_variant_with_selector_field_integer_signed_option *
 bt_field_variant_with_selector_field_integer_signed_borrow_selected_option_class_const(
-               const bt_field *field);
+               const bt_field *field) __BT_NOEXCEPT;
 
 /*! @} */
 
index 813d75efaf4756d5626ae0c14c70e461265cb31a..80fd0de678028c6617ed572a7864716facfacb6e 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_PACKET_H
 #define BABELTRACE2_TRACE_IR_PACKET_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -145,7 +147,7 @@ On success, the returned packet has the following property value:
     <code>bt_stream_class_supports_packets(bt_stream_borrow_class_const(stream))</code>
     returns #BT_TRUE.
 */
-extern bt_packet *bt_packet_create(const bt_stream *stream);
+extern bt_packet *bt_packet_create(const bt_stream *stream) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -171,7 +173,7 @@ extern bt_packet *bt_packet_create(const bt_stream *stream);
 @sa bt_packet_borrow_stream_const() &mdash;
     \c const version of this function.
 */
-extern bt_stream *bt_packet_borrow_stream(bt_packet *packet);
+extern bt_stream *bt_packet_borrow_stream(bt_packet *packet) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -181,7 +183,7 @@ extern bt_stream *bt_packet_borrow_stream(bt_packet *packet);
 See bt_packet_borrow_stream().
 */
 extern const bt_stream *bt_packet_borrow_stream_const(
-               const bt_packet *packet);
+               const bt_packet *packet) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -209,7 +211,7 @@ See the \ref api-tir-pkt-prop-ctx "context field" property.
     \c const version of this function.
 */
 extern
-bt_field *bt_packet_borrow_context_field(bt_packet *packet);
+bt_field *bt_packet_borrow_context_field(bt_packet *packet) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -220,7 +222,7 @@ See bt_packet_borrow_context_field().
 */
 extern
 const bt_field *bt_packet_borrow_context_field_const(
-               const bt_packet *packet);
+               const bt_packet *packet) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -244,7 +246,7 @@ const bt_field *bt_packet_borrow_context_field_const(
 @sa bt_packet_put_ref() &mdash;
     Decrements the reference count of a packet.
 */
-extern void bt_packet_get_ref(const bt_packet *packet);
+extern void bt_packet_get_ref(const bt_packet *packet) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -261,7 +263,7 @@ extern void bt_packet_get_ref(const bt_packet *packet);
 @sa bt_packet_get_ref() &mdash;
     Increments the reference count of a packet.
 */
-extern void bt_packet_put_ref(const bt_packet *packet);
+extern void bt_packet_put_ref(const bt_packet *packet) __BT_NOEXCEPT;
 
 /*!
 @brief
index 92df5159e323346ea5fdca17ac62dfbf82b7f5d4..e74d3cc3593996ede96465f67b7b9dc5433ee487 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_STREAM_CLASS_H
 #define BABELTRACE2_TRACE_IR_STREAM_CLASS_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -559,7 +561,7 @@ On success, the returned stream class has the following property values:
     trace class.
 */
 extern bt_stream_class *bt_stream_class_create(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -656,7 +658,7 @@ On success, the returned stream class has the following property values:
     trace class.
 */
 extern bt_stream_class *bt_stream_class_create_with_id(
-               bt_trace_class *trace_class, uint64_t id);
+               bt_trace_class *trace_class, uint64_t id) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -682,7 +684,7 @@ extern bt_stream_class *bt_stream_class_create_with_id(
     \c const version of this function.
 */
 extern bt_trace_class *bt_stream_class_borrow_trace_class(
-               bt_stream_class *stream_class);
+               bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -692,7 +694,7 @@ extern bt_trace_class *bt_stream_class_borrow_trace_class(
 See bt_stream_class_borrow_trace_class().
 */
 extern const bt_trace_class *bt_stream_class_borrow_trace_class_const(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -715,7 +717,7 @@ extern const bt_trace_class *bt_stream_class_borrow_trace_class_const(
 @bt_pre_not_null{stream_class}
 */
 extern uint64_t bt_stream_class_get_event_class_count(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -750,7 +752,7 @@ extern uint64_t bt_stream_class_get_event_class_count(
 */
 extern bt_event_class *
 bt_stream_class_borrow_event_class_by_index(
-               bt_stream_class *stream_class, uint64_t index);
+               bt_stream_class *stream_class, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -761,7 +763,8 @@ See bt_stream_class_borrow_event_class_by_index().
 */
 extern const bt_event_class *
 bt_stream_class_borrow_event_class_by_index_const(
-               const bt_stream_class *stream_class, uint64_t index);
+               const bt_stream_class *stream_class, uint64_t index)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -794,7 +797,7 @@ If there's no event class having the numeric ID \bt_p{id} in
 */
 extern bt_event_class *
 bt_stream_class_borrow_event_class_by_id(
-               bt_stream_class *stream_class, uint64_t id);
+               bt_stream_class *stream_class, uint64_t id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -805,7 +808,7 @@ See bt_stream_class_borrow_event_class_by_id().
 */
 extern const bt_event_class *
 bt_stream_class_borrow_event_class_by_id_const(
-               const bt_stream_class *stream_class, uint64_t id);
+               const bt_stream_class *stream_class, uint64_t id) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -833,7 +836,7 @@ See the \ref api-tir-stream-cls-prop-id "numeric ID" property.
     trace class.
 */
 extern uint64_t bt_stream_class_get_id(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -878,7 +881,7 @@ See the \ref api-tir-stream-cls-prop-name "name" property.
     Returns the name of a stream class.
 */
 extern bt_stream_class_set_name_status bt_stream_class_set_name(
-               bt_stream_class *stream_class, const char *name);
+               bt_stream_class *stream_class, const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -905,7 +908,7 @@ If \bt_p{stream_class} has no name, this function returns \c NULL.
     Sets the name of a stream class.
 */
 extern const char *bt_stream_class_get_name(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -948,7 +951,7 @@ property.
 extern bt_stream_class_set_default_clock_class_status
 bt_stream_class_set_default_clock_class(
                bt_stream_class *stream_class,
-               bt_clock_class *clock_class);
+               bt_clock_class *clock_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -976,7 +979,7 @@ returns \c NULL.
     \c const version of this function.
 */
 extern bt_clock_class *bt_stream_class_borrow_default_clock_class(
-               bt_stream_class *stream_class);
+               bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -987,7 +990,7 @@ See bt_stream_class_borrow_default_clock_class().
 */
 extern const bt_clock_class *
 bt_stream_class_borrow_default_clock_class_const(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1062,7 +1065,7 @@ bt_stream_class_set_supports_packets()).
 extern bt_stream_class_set_field_class_status
 bt_stream_class_set_packet_context_field_class(
                bt_stream_class *stream_class,
-               bt_field_class *field_class);
+               bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1091,7 +1094,7 @@ returns \c NULL.
 */
 extern bt_field_class *
 bt_stream_class_borrow_packet_context_field_class(
-               bt_stream_class *stream_class);
+               bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1102,7 +1105,7 @@ See bt_stream_class_borrow_packet_context_field_class().
 */
 extern const bt_field_class *
 bt_stream_class_borrow_packet_context_field_class_const(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1147,7 +1150,7 @@ property.
 extern bt_stream_class_set_field_class_status
 bt_stream_class_set_event_common_context_field_class(
                bt_stream_class *stream_class,
-               bt_field_class *field_class);
+               bt_field_class *field_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1178,7 +1181,7 @@ function returns \c NULL.
 
 extern bt_field_class *
 bt_stream_class_borrow_event_common_context_field_class(
-               bt_stream_class *stream_class);
+               bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1189,7 +1192,7 @@ See bt_stream_class_borrow_event_common_context_field_class().
 */
 extern const bt_field_class *
 bt_stream_class_borrow_event_common_context_field_class_const(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1216,7 +1219,7 @@ property.
 */
 extern void bt_stream_class_set_assigns_automatic_event_class_id(
                bt_stream_class *stream_class,
-    bt_bool assigns_automatic_event_class_id);
+               bt_bool assigns_automatic_event_class_id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1242,7 +1245,7 @@ property.
     event class IDs.
 */
 extern bt_bool bt_stream_class_assigns_automatic_event_class_id(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1268,7 +1271,8 @@ property.
     stream IDs.
 */
 extern void bt_stream_class_set_assigns_automatic_stream_id(
-               bt_stream_class *stream_class, bt_bool assigns_automatic_stream_id);
+               bt_stream_class *stream_class,
+               bt_bool assigns_automatic_stream_id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1293,7 +1297,7 @@ property.
     stream IDs.
 */
 extern bt_bool bt_stream_class_assigns_automatic_stream_id(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1349,7 +1353,7 @@ properties.
 extern void bt_stream_class_set_supports_packets(
                bt_stream_class *stream_class, bt_bool supports_packets,
                bt_bool with_beginning_default_clock_snapshot,
-               bt_bool with_end_default_clock_snapshot);
+               bt_bool with_end_default_clock_snapshot) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1372,7 +1376,7 @@ property.
     Sets whether or not a stream class's streams have packets.
 */
 extern bt_bool bt_stream_class_supports_packets(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1401,7 +1405,7 @@ property.
     have an end default clock snapshot.
 */
 extern bt_bool bt_stream_class_packets_have_beginning_default_clock_snapshot(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1430,7 +1434,7 @@ property.
     have a beginning default clock snapshot.
 */
 extern bt_bool bt_stream_class_packets_have_end_default_clock_snapshot(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1476,7 +1480,7 @@ properties.
 extern void bt_stream_class_set_supports_discarded_events(
                bt_stream_class *stream_class,
                bt_bool supports_discarded_events,
-               bt_bool with_default_clock_snapshots);
+               bt_bool with_default_clock_snapshots) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1502,7 +1506,7 @@ property.
     events.
 */
 extern bt_bool bt_stream_class_supports_discarded_events(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1529,7 +1533,7 @@ property.
     events.
 */
 extern bt_bool bt_stream_class_discarded_events_have_default_clock_snapshots(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1581,7 +1585,7 @@ bt_stream_class_set_supports_packets()).
 extern void bt_stream_class_set_supports_discarded_packets(
                bt_stream_class *stream_class,
                bt_bool supports_discarded_packets,
-               bt_bool with_default_clock_snapshots);
+               bt_bool with_default_clock_snapshots) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1607,7 +1611,7 @@ property.
     packets.
 */
 extern bt_bool bt_stream_class_supports_discarded_packets(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1634,7 +1638,7 @@ property.
     packets.
 */
 extern bt_bool bt_stream_class_discarded_packets_have_default_clock_snapshots(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1666,7 +1670,8 @@ property.
     Borrows the user attributes of a stream class.
 */
 extern void bt_stream_class_set_user_attributes(
-               bt_stream_class *stream_class, const bt_value *user_attributes);
+               bt_stream_class *stream_class,
+               const bt_value *user_attributes) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1694,7 +1699,7 @@ property.
     \c const version of this function.
 */
 extern bt_value *bt_stream_class_borrow_user_attributes(
-               bt_stream_class *stream_class);
+               bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1704,7 +1709,7 @@ extern bt_value *bt_stream_class_borrow_user_attributes(
 See bt_stream_class_borrow_user_attributes().
 */
 extern const bt_value *bt_stream_class_borrow_user_attributes_const(
-               const bt_stream_class *stream_class);
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1728,7 +1733,8 @@ extern const bt_value *bt_stream_class_borrow_user_attributes_const(
 @sa bt_stream_class_put_ref() &mdash;
     Decrements the reference count of a stream class.
 */
-extern void bt_stream_class_get_ref(const bt_stream_class *stream_class);
+extern void bt_stream_class_get_ref(
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1745,7 +1751,8 @@ extern void bt_stream_class_get_ref(const bt_stream_class *stream_class);
 @sa bt_stream_class_get_ref() &mdash;
     Increments the reference count of a stream class.
 */
-extern void bt_stream_class_put_ref(const bt_stream_class *stream_class);
+extern void bt_stream_class_put_ref(
+               const bt_stream_class *stream_class) __BT_NOEXCEPT;
 
 /*!
 @brief
index 95f17a7f118a7d17dfa2973fd0a6fd624d17a1c4..70628359015f97d5aa4394813d2cc5932ee9549f 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_STREAM_H
 #define BABELTRACE2_TRACE_IR_STREAM_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -241,7 +243,7 @@ On success, the returned stream has the following property values:
     trace.
 */
 extern bt_stream *bt_stream_create(bt_stream_class *stream_class,
-               bt_trace *trace);
+               bt_trace *trace) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -309,7 +311,7 @@ On success, the returned stream has the following property values:
 */
 extern bt_stream *bt_stream_create_with_id(
                bt_stream_class *stream_class,
-               bt_trace *trace, uint64_t id);
+               bt_trace *trace, uint64_t id) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -334,7 +336,7 @@ extern bt_stream *bt_stream_create_with_id(
 @sa bt_stream_borrow_class_const() &mdash;
     \c const version of this function.
 */
-extern bt_stream_class *bt_stream_borrow_class(bt_stream *stream);
+extern bt_stream_class *bt_stream_borrow_class(bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -344,7 +346,7 @@ extern bt_stream_class *bt_stream_borrow_class(bt_stream *stream);
 See bt_stream_borrow_class().
 */
 extern const bt_stream_class *bt_stream_borrow_class_const(
-               const bt_stream *stream);
+               const bt_stream *stream) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -368,7 +370,7 @@ extern const bt_stream_class *bt_stream_borrow_class_const(
 @sa bt_stream_borrow_trace_const() &mdash;
     \c const version of this function.
 */
-extern bt_trace *bt_stream_borrow_trace(bt_stream *stream);
+extern bt_trace *bt_stream_borrow_trace(bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -378,7 +380,7 @@ extern bt_trace *bt_stream_borrow_trace(bt_stream *stream);
 See bt_stream_borrow_trace().
 */
 extern const bt_trace *bt_stream_borrow_trace_const(
-               const bt_stream *stream);
+               const bt_stream *stream) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -405,7 +407,7 @@ See the \ref api-tir-stream-prop-id "numeric ID" property.
     Creates a stream with a specific numeric ID and adds it to a
     trace.
 */
-extern uint64_t bt_stream_get_id(const bt_stream *stream);
+extern uint64_t bt_stream_get_id(const bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -450,7 +452,7 @@ See the \ref api-tir-stream-prop-name "name" property.
     Returns the name of a stream.
 */
 extern bt_stream_set_name_status bt_stream_set_name(bt_stream *stream,
-               const char *name);
+               const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -476,7 +478,7 @@ If \bt_p{stream} has no name, this function returns \c NULL.
 @sa bt_stream_class_set_name() &mdash;
     Sets the name of a stream.
 */
-extern const char *bt_stream_get_name(const bt_stream *stream);
+extern const char *bt_stream_get_name(const bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -508,7 +510,8 @@ property.
     Borrows the user attributes of a stream.
 */
 extern void bt_stream_set_user_attributes(
-               bt_stream *stream, const bt_value *user_attributes);
+               bt_stream *stream, const bt_value *user_attributes)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -535,7 +538,8 @@ property.
 @sa bt_stream_borrow_user_attributes_const() &mdash;
     \c const version of this function.
 */
-extern bt_value *bt_stream_borrow_user_attributes(bt_stream *stream);
+extern bt_value *bt_stream_borrow_user_attributes(bt_stream *stream)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -545,7 +549,7 @@ extern bt_value *bt_stream_borrow_user_attributes(bt_stream *stream);
 See bt_stream_borrow_user_attributes().
 */
 extern const bt_value *bt_stream_borrow_user_attributes_const(
-               const bt_stream *stream);
+               const bt_stream *stream) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -569,7 +573,7 @@ extern const bt_value *bt_stream_borrow_user_attributes_const(
 @sa bt_stream_put_ref() &mdash;
     Decrements the reference count of a stream.
 */
-extern void bt_stream_get_ref(const bt_stream *stream);
+extern void bt_stream_get_ref(const bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -586,7 +590,7 @@ extern void bt_stream_get_ref(const bt_stream *stream);
 @sa bt_stream_get_ref() &mdash;
     Increments the reference count of a stream.
 */
-extern void bt_stream_put_ref(const bt_stream *stream);
+extern void bt_stream_put_ref(const bt_stream *stream) __BT_NOEXCEPT;
 
 /*!
 @brief
index fd01d32b81f9ec89cf92580288a68e5480e568c1..4a50b3115152bfaecc4fd988ca23f28849004fe7 100644 (file)
@@ -4,9 +4,11 @@
  * Copyright (C) 2010-2019 EfficiOS Inc. and Linux Foundation
  */
 
- #ifndef BABELTRACE2_TRACE_IR_TRACE_CLASS_H
+#ifndef BABELTRACE2_TRACE_IR_TRACE_CLASS_H
 #define BABELTRACE2_TRACE_IR_TRACE_CLASS_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -164,7 +166,8 @@ On success, the returned trace class has the following property values:
 @returns
     New trace class reference, or \c NULL on memory error.
 */
-extern bt_trace_class *bt_trace_class_create(bt_self_component *self_component);
+extern bt_trace_class *bt_trace_class_create(bt_self_component *self_component)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -187,7 +190,7 @@ extern bt_trace_class *bt_trace_class_create(bt_self_component *self_component);
 @bt_pre_not_null{trace_class}
 */
 extern uint64_t bt_trace_class_get_stream_class_count(
-               const bt_trace_class *trace_class);
+               const bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -221,7 +224,7 @@ extern uint64_t bt_trace_class_get_stream_class_count(
     \c const version of this function.
 */
 extern bt_stream_class *bt_trace_class_borrow_stream_class_by_index(
-               bt_trace_class *trace_class, uint64_t index);
+               bt_trace_class *trace_class, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -232,7 +235,8 @@ See bt_trace_class_borrow_stream_class_by_index().
 */
 extern const bt_stream_class *
 bt_trace_class_borrow_stream_class_by_index_const(
-               const bt_trace_class *trace_class, uint64_t index);
+               const bt_trace_class *trace_class,
+               uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -264,7 +268,7 @@ If there's no stream class having the numeric ID \bt_p{id} in
     \c const version of this function.
 */
 extern bt_stream_class *bt_trace_class_borrow_stream_class_by_id(
-               bt_trace_class *trace_class, uint64_t id);
+               bt_trace_class *trace_class, uint64_t id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -274,7 +278,7 @@ extern bt_stream_class *bt_trace_class_borrow_stream_class_by_id(
 See bt_trace_class_borrow_stream_class_by_id().
 */
 extern const bt_stream_class *bt_trace_class_borrow_stream_class_by_id_const(
-               const bt_trace_class *trace_class, uint64_t id);
+               const bt_trace_class *trace_class, uint64_t id) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -307,7 +311,7 @@ property.
 */
 extern void bt_trace_class_set_assigns_automatic_stream_class_id(
                bt_trace_class *trace_class,
-               bt_bool assigns_automatic_stream_class_id);
+               bt_bool assigns_automatic_stream_class_id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -333,7 +337,7 @@ property.
     stream class IDs.
 */
 extern bt_bool bt_trace_class_assigns_automatic_stream_class_id(
-               const bt_trace_class *trace_class);
+               const bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -365,7 +369,8 @@ property.
     Borrows the user attributes of a trace class.
 */
 extern void bt_trace_class_set_user_attributes(
-               bt_trace_class *trace_class, const bt_value *user_attributes);
+               bt_trace_class *trace_class,
+               const bt_value *user_attributes) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -393,7 +398,7 @@ property.
     \c const version of this function.
 */
 extern bt_value *bt_trace_class_borrow_user_attributes(
-               bt_trace_class *trace_class);
+               bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -403,7 +408,7 @@ extern bt_value *bt_trace_class_borrow_user_attributes(
 See bt_trace_class_borrow_user_attributes().
 */
 extern const bt_value *bt_trace_class_borrow_user_attributes_const(
-               const bt_trace_class *trace_class);
+               const bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -496,7 +501,7 @@ extern bt_trace_class_add_listener_status
 bt_trace_class_add_destruction_listener(
         const bt_trace_class *trace_class,
         bt_trace_class_destruction_listener_func user_func,
-        void *user_data, bt_listener_id *listener_id);
+        void *user_data, bt_listener_id *listener_id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -548,7 +553,8 @@ You can call this function when \bt_p{trace_class} is
 */
 extern bt_trace_class_remove_listener_status
 bt_trace_class_remove_destruction_listener(
-        const bt_trace_class *trace_class, bt_listener_id listener_id);
+               const bt_trace_class *trace_class, bt_listener_id listener_id)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -572,7 +578,8 @@ bt_trace_class_remove_destruction_listener(
 @sa bt_trace_class_put_ref() &mdash;
     Decrements the reference count of a trace class.
 */
-extern void bt_trace_class_get_ref(const bt_trace_class *trace_class);
+extern void bt_trace_class_get_ref(
+               const bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -589,7 +596,8 @@ extern void bt_trace_class_get_ref(const bt_trace_class *trace_class);
 @sa bt_trace_class_get_ref() &mdash;
     Increments the reference count of a trace class.
 */
-extern void bt_trace_class_put_ref(const bt_trace_class *trace_class);
+extern void bt_trace_class_put_ref(
+               const bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*!
 @brief
index e36a5fd3fe869c00d20f3e6edea9c0374b0f5c88..954a43870e2863317a0108a7822aa02e8550541e 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TRACE_IR_TRACE_H
 #define BABELTRACE2_TRACE_IR_TRACE_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -186,7 +188,7 @@ On success, the returned trace has the following property values:
 @returns
     New trace reference, or \c NULL on memory error.
 */
-extern bt_trace *bt_trace_create(bt_trace_class *trace_class);
+extern bt_trace *bt_trace_create(bt_trace_class *trace_class) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -211,7 +213,7 @@ extern bt_trace *bt_trace_create(bt_trace_class *trace_class);
 @sa bt_trace_borrow_class_const() &mdash;
     \c const version of this function.
 */
-extern bt_trace_class *bt_trace_borrow_class(bt_trace *trace);
+extern bt_trace_class *bt_trace_borrow_class(bt_trace *trace) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -221,7 +223,7 @@ extern bt_trace_class *bt_trace_borrow_class(bt_trace *trace);
 See bt_trace_borrow_class().
 */
 extern const bt_trace_class *bt_trace_borrow_class_const(
-               const bt_trace *trace);
+               const bt_trace *trace) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -243,7 +245,7 @@ extern const bt_trace_class *bt_trace_borrow_class_const(
 
 @bt_pre_not_null{trace}
 */
-extern uint64_t bt_trace_get_stream_count(const bt_trace *trace);
+extern uint64_t bt_trace_get_stream_count(const bt_trace *trace) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -277,7 +279,7 @@ extern uint64_t bt_trace_get_stream_count(const bt_trace *trace);
     \c const version of this function.
 */
 extern bt_stream *bt_trace_borrow_stream_by_index(bt_trace *trace,
-               uint64_t index);
+               uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -287,7 +289,7 @@ extern bt_stream *bt_trace_borrow_stream_by_index(bt_trace *trace,
 See bt_trace_borrow_stream_by_index().
 */
 extern const bt_stream *bt_trace_borrow_stream_by_index_const(
-               const bt_trace *trace, uint64_t index);
+               const bt_trace *trace, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -319,7 +321,7 @@ If there's no stream having the numeric ID \bt_p{id} in
     \c const version of this function.
 */
 extern bt_stream *bt_trace_borrow_stream_by_id(bt_trace *trace,
-               uint64_t id);
+               uint64_t id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -329,7 +331,7 @@ extern bt_stream *bt_trace_borrow_stream_by_id(bt_trace *trace,
 See bt_trace_borrow_stream_by_id().
 */
 extern const bt_stream *bt_trace_borrow_stream_by_id_const(
-               const bt_trace *trace, uint64_t id);
+               const bt_trace *trace, uint64_t id) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -380,7 +382,7 @@ See the \ref api-tir-trace-prop-name "name" property.
     Returns the name of a trace.
 */
 extern bt_trace_set_name_status bt_trace_set_name(bt_trace *trace,
-               const char *name);
+               const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -406,7 +408,7 @@ If \bt_p{trace} has no name, this function returns \c NULL.
 @sa bt_trace_set_name() &mdash;
     Sets the name of a trace.
 */
-extern const char *bt_trace_get_name(const bt_trace *trace);
+extern const char *bt_trace_get_name(const bt_trace *trace) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -428,7 +430,7 @@ See the \ref api-tir-trace-prop-uuid "UUID" property.
 @sa bt_trace_get_uuid() &mdash;
     Returns the UUID of a trace.
 */
-extern void bt_trace_set_uuid(bt_trace *trace, bt_uuid uuid);
+extern void bt_trace_set_uuid(bt_trace *trace, bt_uuid uuid) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -454,7 +456,7 @@ If \bt_p{trace} has no UUID, this function returns \c NULL.
 @sa bt_trace_set_uuid() &mdash;
     Sets the UUID of a trace.
 */
-extern bt_uuid bt_trace_get_uuid(const bt_trace *trace);
+extern bt_uuid bt_trace_get_uuid(const bt_trace *trace) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -507,7 +509,7 @@ On success, if \bt_p{trace} already contains an environment entry named
 */
 extern bt_trace_set_environment_entry_status
 bt_trace_set_environment_entry_integer(bt_trace *trace, const char *name,
-               int64_t value);
+               int64_t value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -543,7 +545,7 @@ On success, if \bt_p{trace} already contains an environment entry named
 */
 extern bt_trace_set_environment_entry_status
 bt_trace_set_environment_entry_string(bt_trace *trace, const char *name,
-               const char *value);
+               const char *value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -560,7 +562,8 @@ See the \ref api-tir-trace-prop-env "environment" property.
 
 @bt_pre_not_null{trace}
 */
-extern uint64_t bt_trace_get_environment_entry_count(const bt_trace *trace);
+extern uint64_t bt_trace_get_environment_entry_count(
+               const bt_trace *trace) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -610,7 +613,7 @@ See the \ref api-tir-trace-prop-env "environment" property.
 */
 extern void bt_trace_borrow_environment_entry_by_index_const(
                const bt_trace *trace, uint64_t index,
-               const char **name, const bt_value **value);
+               const char **name, const bt_value **value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -645,7 +648,7 @@ function returns \c NULL.
 @bt_pre_not_null{name}
 */
 extern const bt_value *bt_trace_borrow_environment_entry_value_by_name_const(
-               const bt_trace *trace, const char *name);
+               const bt_trace *trace, const char *name) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -675,7 +678,7 @@ property.
     Borrows the user attributes of a trace.
 */
 extern void bt_trace_set_user_attributes(
-               bt_trace *trace, const bt_value *user_attributes);
+               bt_trace *trace, const bt_value *user_attributes) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -701,7 +704,7 @@ property.
 @sa bt_trace_borrow_user_attributes_const() &mdash;
     \c const version of this function.
 */
-extern bt_value *bt_trace_borrow_user_attributes(bt_trace *trace);
+extern bt_value *bt_trace_borrow_user_attributes(bt_trace *trace) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -711,7 +714,7 @@ extern bt_value *bt_trace_borrow_user_attributes(bt_trace *trace);
 See bt_trace_borrow_user_attributes().
 */
 extern const bt_value *bt_trace_borrow_user_attributes_const(
-               const bt_trace *trace);
+               const bt_trace *trace) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -802,7 +805,7 @@ added destruction listener with bt_trace_remove_destruction_listener().
 extern bt_trace_add_listener_status bt_trace_add_destruction_listener(
                const bt_trace *trace,
                bt_trace_destruction_listener_func user_func,
-               void *user_data, bt_listener_id *listener_id);
+               void *user_data, bt_listener_id *listener_id) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -853,7 +856,8 @@ You can call this function when \bt_p{trace} is
     Adds a destruction listener to a trace.
 */
 extern bt_trace_remove_listener_status bt_trace_remove_destruction_listener(
-               const bt_trace *trace, bt_listener_id listener_id);
+               const bt_trace *trace, bt_listener_id listener_id)
+               __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -877,7 +881,7 @@ extern bt_trace_remove_listener_status bt_trace_remove_destruction_listener(
 @sa bt_trace_put_ref() &mdash;
     Decrements the reference count of a trace.
 */
-extern void bt_trace_get_ref(const bt_trace *trace);
+extern void bt_trace_get_ref(const bt_trace *trace) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -894,7 +898,7 @@ extern void bt_trace_get_ref(const bt_trace *trace);
 @sa bt_trace_get_ref() &mdash;
     Increments the reference count of a trace.
 */
-extern void bt_trace_put_ref(const bt_trace *trace);
+extern void bt_trace_put_ref(const bt_trace *trace) __BT_NOEXCEPT;
 
 /*!
 @brief
index 2f9e6496bc9424ddc3f0b781f4b931d89579eff5..42e6bd48f286eb1783dc00f8a233a9c4a5756a09 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_TYPES_H
 #define BABELTRACE2_TYPES_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -33,7 +35,6 @@ typedef struct bt_error bt_error;
 typedef struct bt_error_cause bt_error_cause;
 typedef struct bt_event bt_event;
 typedef struct bt_event_class bt_event_class;
-typedef struct bt_event_header_field bt_event_header_field;
 typedef struct bt_field bt_field;
 typedef struct bt_field_class bt_field_class;
 typedef struct bt_field_class_enumeration_mapping bt_field_class_enumeration_mapping;
@@ -59,7 +60,6 @@ typedef struct bt_object bt_object;
 typedef struct bt_packet bt_packet;
 typedef struct bt_plugin bt_plugin;
 typedef struct bt_plugin_set bt_plugin_set;
-typedef struct bt_plugin_so_shared_lib_handle bt_plugin_so_shared_lib_handle;
 typedef struct bt_port bt_port;
 typedef struct bt_port_input bt_port_input;
 typedef struct bt_port_output bt_port_output;
index 8426a0adeff4c0729c2d01ee001a7adb531226d0..80c0e66bde5e4bf5820310c072dff3a8485b79c9 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_UTIL_H
 #define BABELTRACE2_UTIL_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -121,7 +123,7 @@ code if any step of the computation process causes an integer overflow.
 bt_util_clock_cycles_to_ns_from_origin_status
 bt_util_clock_cycles_to_ns_from_origin(uint64_t cycles,
                uint64_t frequency, int64_t offset_seconds,
-               uint64_t offset_cycles, int64_t *ns_from_origin);
+               uint64_t offset_cycles, int64_t *ns_from_origin) __BT_NOEXCEPT;
 
 /*! @} */
 
index ae60e652fa59b620e287f2cad54c971c54398b01..10638bd6c0209a213eacaf90e51739543e2eba3a 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_VALUE_H
 #define BABELTRACE2_VALUE_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -323,7 +325,7 @@ typedef enum bt_value_type {
 @sa bt_value_is_map() &mdash;
     Returns whether or not a value is a map value.
 */
-extern bt_value_type bt_value_get_type(const bt_value *value);
+extern bt_value_type bt_value_get_type(const bt_value *value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -368,7 +370,7 @@ returns #BT_TRUE.
 */
 static inline
 bt_bool bt_value_type_is(const bt_value_type type,
-               const bt_value_type other_type)
+               const bt_value_type other_type) __BT_NOEXCEPT
 {
        return (type & other_type) == other_type;
 }
@@ -399,7 +401,7 @@ bt_bool bt_value_type_is(const bt_value_type type,
     The null value singleton.
 */
 static inline
-bt_bool bt_value_is_null(const bt_value *value)
+bt_bool bt_value_is_null(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_get_type(value) == BT_VALUE_TYPE_NULL;
 }
@@ -423,7 +425,7 @@ bt_bool bt_value_is_null(const bt_value *value)
     type.
 */
 static inline
-bt_bool bt_value_is_bool(const bt_value *value)
+bt_bool bt_value_is_bool(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_get_type(value) == BT_VALUE_TYPE_BOOL;
 }
@@ -448,7 +450,7 @@ bt_bool bt_value_is_bool(const bt_value *value)
     type.
 */
 static inline
-bt_bool bt_value_is_unsigned_integer(const bt_value *value)
+bt_bool bt_value_is_unsigned_integer(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_get_type(value) == BT_VALUE_TYPE_UNSIGNED_INTEGER;
 }
@@ -473,7 +475,7 @@ bt_bool bt_value_is_unsigned_integer(const bt_value *value)
     type.
 */
 static inline
-bt_bool bt_value_is_signed_integer(const bt_value *value)
+bt_bool bt_value_is_signed_integer(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_get_type(value) == BT_VALUE_TYPE_SIGNED_INTEGER;
 }
@@ -497,7 +499,7 @@ bt_bool bt_value_is_signed_integer(const bt_value *value)
     type.
 */
 static inline
-bt_bool bt_value_is_real(const bt_value *value)
+bt_bool bt_value_is_real(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_get_type(value) == BT_VALUE_TYPE_REAL;
 }
@@ -521,7 +523,7 @@ bt_bool bt_value_is_real(const bt_value *value)
     type.
 */
 static inline
-bt_bool bt_value_is_string(const bt_value *value)
+bt_bool bt_value_is_string(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_get_type(value) == BT_VALUE_TYPE_STRING;
 }
@@ -545,7 +547,7 @@ bt_bool bt_value_is_string(const bt_value *value)
     type.
 */
 static inline
-bt_bool bt_value_is_array(const bt_value *value)
+bt_bool bt_value_is_array(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_get_type(value) == BT_VALUE_TYPE_ARRAY;
 }
@@ -569,7 +571,7 @@ bt_bool bt_value_is_array(const bt_value *value)
     type.
 */
 static inline
-bt_bool bt_value_is_map(const bt_value *value)
+bt_bool bt_value_is_map(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_get_type(value) == BT_VALUE_TYPE_MAP;
 }
@@ -631,7 +633,7 @@ The returned value has the type #BT_VALUE_TYPE_BOOL.
 @sa bt_value_bool_create_init() &mdash;
     Creates a boolean value with a given initial raw value.
 */
-extern bt_value *bt_value_bool_create(void);
+extern bt_value *bt_value_bool_create(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -648,7 +650,7 @@ The returned value has the type #BT_VALUE_TYPE_BOOL.
 @sa bt_value_bool_create() &mdash;
     Creates a boolean value initialized to #BT_FALSE.
 */
-extern bt_value *bt_value_bool_create_init(bt_bool raw_value);
+extern bt_value *bt_value_bool_create_init(bt_bool raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -667,7 +669,7 @@ extern bt_value *bt_value_bool_create_init(bt_bool raw_value);
 @sa bt_value_bool_get() &mdash;
     Returns the raw value of a boolean value.
 */
-extern void bt_value_bool_set(bt_value *value, bt_bool raw_value);
+extern void bt_value_bool_set(bt_value *value, bt_bool raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -685,7 +687,7 @@ extern void bt_value_bool_set(bt_value *value, bt_bool raw_value);
 @sa bt_value_bool_set() &mdash;
     Sets the raw value of a boolean value.
 */
-extern bt_bool bt_value_bool_get(const bt_value *value);
+extern bt_bool bt_value_bool_get(const bt_value *value) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -706,7 +708,7 @@ The returned value has the type #BT_VALUE_TYPE_UNSIGNED_INTEGER.
 @sa bt_value_integer_unsigned_create_init() &mdash;
     Creates an unsigned integer value with a given initial raw value.
 */
-extern bt_value *bt_value_integer_unsigned_create(void);
+extern bt_value *bt_value_integer_unsigned_create(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -724,7 +726,7 @@ The returned value has the type #BT_VALUE_TYPE_UNSIGNED_INTEGER.
 @sa bt_value_bool_create() &mdash;
     Creates an unsigned integer value initialized to 0.
 */
-extern bt_value *bt_value_integer_unsigned_create_init(uint64_t raw_value);
+extern bt_value *bt_value_integer_unsigned_create_init(uint64_t raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -745,7 +747,7 @@ extern bt_value *bt_value_integer_unsigned_create_init(uint64_t raw_value);
     Returns the raw value of an unsigned integer value.
 */
 extern void bt_value_integer_unsigned_set(bt_value *value,
-               uint64_t raw_value);
+               uint64_t raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -763,7 +765,7 @@ extern void bt_value_integer_unsigned_set(bt_value *value,
 @sa bt_value_integer_unsigned_set() &mdash;
     Sets the raw value of an unsigned integer value.
 */
-extern uint64_t bt_value_integer_unsigned_get(const bt_value *value);
+extern uint64_t bt_value_integer_unsigned_get(const bt_value *value) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -784,7 +786,7 @@ The returned value has the type #BT_VALUE_TYPE_SIGNED_INTEGER.
 @sa bt_value_integer_signed_create_init() &mdash;
     Creates a signed integer value with a given initial raw value.
 */
-extern bt_value *bt_value_integer_signed_create(void);
+extern bt_value *bt_value_integer_signed_create(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -802,7 +804,7 @@ The returned value has the type #BT_VALUE_TYPE_SIGNED_INTEGER.
 @sa bt_value_bool_create() &mdash;
     Creates a signed integer value initialized to 0.
 */
-extern bt_value *bt_value_integer_signed_create_init(int64_t raw_value);
+extern bt_value *bt_value_integer_signed_create_init(int64_t raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -822,7 +824,7 @@ extern bt_value *bt_value_integer_signed_create_init(int64_t raw_value);
 @sa bt_value_integer_signed_get() &mdash;
     Returns the raw value of a signed integer value.
 */
-extern void bt_value_integer_signed_set(bt_value *value, int64_t raw_value);
+extern void bt_value_integer_signed_set(bt_value *value, int64_t raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -840,7 +842,7 @@ extern void bt_value_integer_signed_set(bt_value *value, int64_t raw_value);
 @sa bt_value_integer_signed_set() &mdash;
     Sets the raw value of a signed integer value.
 */
-extern int64_t bt_value_integer_signed_get(const bt_value *value);
+extern int64_t bt_value_integer_signed_get(const bt_value *value) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -861,7 +863,7 @@ The returned value has the type #BT_VALUE_TYPE_REAL.
 @sa bt_value_real_create_init() &mdash;
     Creates a real value with a given initial raw value.
 */
-extern bt_value *bt_value_real_create(void);
+extern bt_value *bt_value_real_create(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -878,7 +880,7 @@ The returned value has the type #BT_VALUE_TYPE_REAL.
 @sa bt_value_real_create() &mdash;
     Creates a real value initialized to 0.
 */
-extern bt_value *bt_value_real_create_init(double raw_value);
+extern bt_value *bt_value_real_create_init(double raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -897,7 +899,7 @@ extern bt_value *bt_value_real_create_init(double raw_value);
 @sa bt_value_real_get() &mdash;
     Returns the raw value of a real value.
 */
-extern void bt_value_real_set(bt_value *value, double raw_value);
+extern void bt_value_real_set(bt_value *value, double raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -915,7 +917,7 @@ extern void bt_value_real_set(bt_value *value, double raw_value);
 @sa bt_value_real_set() &mdash;
     Sets the raw value of a real value.
 */
-extern double bt_value_real_get(const bt_value *value);
+extern double bt_value_real_get(const bt_value *value) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -936,7 +938,7 @@ The returned value has the type #BT_VALUE_TYPE_STRING.
 @sa bt_value_string_create_init() &mdash;
     Creates a string value with a given initial raw value.
 */
-extern bt_value *bt_value_string_create(void);
+extern bt_value *bt_value_string_create(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -956,7 +958,7 @@ The returned value has the type #BT_VALUE_TYPE_STRING.
 @sa bt_value_string_create() &mdash;
     Creates an empty string value.
 */
-extern bt_value *bt_value_string_create_init(const char *raw_value);
+extern bt_value *bt_value_string_create_init(const char *raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1001,7 +1003,7 @@ typedef enum bt_value_string_set_status {
     Returns the raw value of a string value.
 */
 extern bt_value_string_set_status bt_value_string_set(bt_value *value,
-               const char *raw_value);
+               const char *raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1023,7 +1025,7 @@ extern bt_value_string_set_status bt_value_string_set(bt_value *value,
 @sa bt_value_string_set() &mdash;
     Sets the raw value of a string value.
 */
-extern const char *bt_value_string_get(const bt_value *value);
+extern const char *bt_value_string_get(const bt_value *value) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -1041,7 +1043,7 @@ The returned value has the type #BT_VALUE_TYPE_ARRAY.
 @returns
     New array value reference, or \c NULL on memory error.
 */
-extern bt_value *bt_value_array_create(void);
+extern bt_value *bt_value_array_create(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1105,7 +1107,7 @@ To append a null value, pass #bt_value_null as \bt_p{element_value}.
     Creates and appends an empty map value to an array value.
 */
 extern bt_value_array_append_element_status bt_value_array_append_element(
-               bt_value *value, bt_value *element_value);
+               bt_value *value, bt_value *element_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1134,7 +1136,7 @@ extern bt_value_array_append_element_status bt_value_array_append_element(
     Appends an existing value to an array value.
 */
 extern bt_value_array_append_element_status
-bt_value_array_append_bool_element(bt_value *value, bt_bool raw_value);
+bt_value_array_append_bool_element(bt_value *value, bt_bool raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1165,7 +1167,7 @@ bt_value_array_append_bool_element(bt_value *value, bt_bool raw_value);
 */
 extern bt_value_array_append_element_status
 bt_value_array_append_unsigned_integer_element(bt_value *value,
-               uint64_t raw_value);
+               uint64_t raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1196,7 +1198,7 @@ bt_value_array_append_unsigned_integer_element(bt_value *value,
 */
 extern bt_value_array_append_element_status
 bt_value_array_append_signed_integer_element(bt_value *value,
-               int64_t raw_value);
+               int64_t raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1225,7 +1227,7 @@ bt_value_array_append_signed_integer_element(bt_value *value,
     Appends an existing value to an array value.
 */
 extern bt_value_array_append_element_status
-bt_value_array_append_real_element(bt_value *value, double raw_value);
+bt_value_array_append_real_element(bt_value *value, double raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1255,7 +1257,7 @@ bt_value_array_append_real_element(bt_value *value, double raw_value);
     Appends an existing value to an array value.
 */
 extern bt_value_array_append_element_status
-bt_value_array_append_string_element(bt_value *value, const char *raw_value);
+bt_value_array_append_string_element(bt_value *value, const char *raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1290,7 +1292,7 @@ array value.
 */
 extern bt_value_array_append_element_status
 bt_value_array_append_empty_array_element(bt_value *value,
-               bt_value **element_value);
+               bt_value **element_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1325,7 +1327,7 @@ map value.
 */
 extern bt_value_array_append_element_status
 bt_value_array_append_empty_map_element(bt_value *value,
-               bt_value **element_value);
+               bt_value **element_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1384,7 +1386,7 @@ at index \bt_p{index}.
 */
 extern bt_value_array_set_element_by_index_status
 bt_value_array_set_element_by_index(bt_value *value, uint64_t index,
-               bt_value *element_value);
+               bt_value *element_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1414,7 +1416,7 @@ bt_value_array_set_element_by_index(bt_value *value, uint64_t index,
     \c const version of this function.
 */
 extern bt_value *bt_value_array_borrow_element_by_index(bt_value *value,
-               uint64_t index);
+               uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1424,7 +1426,7 @@ extern bt_value *bt_value_array_borrow_element_by_index(bt_value *value,
 See bt_value_array_borrow_element_by_index().
 */
 extern const bt_value *bt_value_array_borrow_element_by_index_const(
-               const bt_value *value, uint64_t index);
+               const bt_value *value, uint64_t index) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1442,7 +1444,7 @@ extern const bt_value *bt_value_array_borrow_element_by_index_const(
 @sa bt_value_array_is_empty() &mdash;
     Returns whether or not an array value is empty.
 */
-extern uint64_t bt_value_array_get_length(const bt_value *value);
+extern uint64_t bt_value_array_get_length(const bt_value *value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1461,7 +1463,7 @@ extern uint64_t bt_value_array_get_length(const bt_value *value);
     Returns the length of an array value.
 */
 static inline
-bt_bool bt_value_array_is_empty(const bt_value *value)
+bt_bool bt_value_array_is_empty(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_array_get_length(value) == 0;
 }
@@ -1482,7 +1484,7 @@ The returned value has the type #BT_VALUE_TYPE_MAP.
 @returns
     New map value reference, or \c NULL on memory error.
 */
-extern bt_value *bt_value_map_create(void);
+extern bt_value *bt_value_map_create(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1556,7 +1558,8 @@ On success, if \bt_p{value} already contains an entry with key
     Creates a map value and uses it to insert an entry in a map value.
 */
 extern bt_value_map_insert_entry_status bt_value_map_insert_entry(
-               bt_value *value, const char *key, bt_value *entry_value);
+               bt_value *value, const char *key,
+               bt_value *entry_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1590,7 +1593,8 @@ created boolean value.
     Inserts an entry with an existing value in a map value.
 */
 extern bt_value_map_insert_entry_status bt_value_map_insert_bool_entry(
-               bt_value *value, const char *key, bt_bool raw_value);
+               bt_value *value, const char *key, bt_bool raw_value)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1625,7 +1629,7 @@ created unsigned integer value.
 */
 extern bt_value_map_insert_entry_status
 bt_value_map_insert_unsigned_integer_entry(bt_value *value, const char *key,
-               uint64_t raw_value);
+               uint64_t raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1660,7 +1664,7 @@ created signed integer value.
 */
 extern bt_value_map_insert_entry_status
 bt_value_map_insert_signed_integer_entry(bt_value *value, const char *key,
-               int64_t raw_value);
+               int64_t raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1694,7 +1698,8 @@ created real value.
     Inserts an entry with an existing value in a map value.
 */
 extern bt_value_map_insert_entry_status bt_value_map_insert_real_entry(
-               bt_value *value, const char *key, double raw_value);
+               bt_value *value, const char *key, double raw_value)
+               __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1729,7 +1734,7 @@ created string value.
 */
 extern bt_value_map_insert_entry_status
 bt_value_map_insert_string_entry(bt_value *value, const char *key,
-               const char *raw_value);
+               const char *raw_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1768,7 +1773,7 @@ created empty array value.
 */
 extern bt_value_map_insert_entry_status
 bt_value_map_insert_empty_array_entry(bt_value *value, const char *key,
-               bt_value **entry_value);
+               bt_value **entry_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1807,7 +1812,7 @@ created empty map value.
 */
 extern bt_value_map_insert_entry_status
 bt_value_map_insert_empty_map_entry(bt_value *value, const char *key,
-               bt_value **entry_value);
+               bt_value **entry_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1832,7 +1837,7 @@ returns \c NULL.
     @endparblock
 
 @bt_pre_not_null{value}
-@bt_pre_is_array_val{value}
+@bt_pre_is_map_val{value}
 @bt_pre_not_null{key}
 
 @sa bt_value_map_borrow_entry_value_const() &mdash;
@@ -1841,7 +1846,7 @@ returns \c NULL.
     Returns whether or not a map value has an entry with a given key.
 */
 extern bt_value *bt_value_map_borrow_entry_value(
-               bt_value *value, const char *key);
+               bt_value *value, const char *key) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -1851,7 +1856,7 @@ extern bt_value *bt_value_map_borrow_entry_value(
 See bt_value_map_borrow_entry_value().
 */
 extern const bt_value *bt_value_map_borrow_entry_value_const(
-               const bt_value *value, const char *key);
+               const bt_value *value, const char *key) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2006,7 +2011,7 @@ The iteration process stops when one of:
 */
 extern bt_value_map_foreach_entry_status bt_value_map_foreach_entry(
                bt_value *value, bt_value_map_foreach_entry_func user_func,
-               void *user_data);
+               void *user_data) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2121,7 +2126,7 @@ See bt_value_map_foreach_entry().
 extern bt_value_map_foreach_entry_const_status bt_value_map_foreach_entry_const(
                const bt_value *value,
                bt_value_map_foreach_entry_const_func user_func,
-               void *user_data);
+               void *user_data) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2139,7 +2144,7 @@ extern bt_value_map_foreach_entry_const_status bt_value_map_foreach_entry_const(
 @sa bt_value_map_is_empty() &mdash;
     Returns whether or not a map value is empty.
 */
-extern uint64_t bt_value_map_get_size(const bt_value *value);
+extern uint64_t bt_value_map_get_size(const bt_value *value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2158,7 +2163,7 @@ extern uint64_t bt_value_map_get_size(const bt_value *value);
     Returns the size of a map value.
 */
 static inline
-bt_bool bt_value_map_is_empty(const bt_value *value)
+bt_bool bt_value_map_is_empty(const bt_value *value) __BT_NOEXCEPT
 {
        return bt_value_map_get_size(value) == 0;
 }
@@ -2184,7 +2189,7 @@ bt_bool bt_value_map_is_empty(const bt_value *value)
     Borrows the value of a specific map value entry.
 */
 extern bt_bool bt_value_map_has_entry(const bt_value *value,
-               const char *key);
+               const char *key) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2270,7 +2275,7 @@ The map value \bt_p{value} becomes:
     Deep-copies a value.
 */
 extern bt_value_map_extend_status bt_value_map_extend(
-               bt_value *value, const bt_value *extension_value);
+               bt_value *value, const bt_value *extension_value) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2323,7 +2328,7 @@ identical.
 @bt_pre_not_null{copy_value}
 */
 extern bt_value_copy_status bt_value_copy(const bt_value *value,
-               bt_value **copy_value);
+               bt_value **copy_value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2346,7 +2351,7 @@ extern bt_value_copy_status bt_value_copy(const bt_value *value,
 @bt_pre_not_null{b_value}
 */
 extern bt_bool bt_value_is_equal(const bt_value *a_value,
-               const bt_value *b_value);
+               const bt_value *b_value) __BT_NOEXCEPT;
 
 /*! @} */
 
@@ -2370,7 +2375,7 @@ extern bt_bool bt_value_is_equal(const bt_value *a_value,
 @sa bt_value_put_ref() &mdash;
     Decrements the reference count of a value.
 */
-extern void bt_value_get_ref(const bt_value *value);
+extern void bt_value_get_ref(const bt_value *value) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -2387,7 +2392,7 @@ extern void bt_value_get_ref(const bt_value *value);
 @sa bt_value_get_ref() &mdash;
     Increments the reference count of a value.
 */
-extern void bt_value_put_ref(const bt_value *value);
+extern void bt_value_put_ref(const bt_value *value) __BT_NOEXCEPT;
 
 /*!
 @brief
index 067427123926638896754555c8a3527cd8eb6c20..66ad5415d07fd5c7b3d55ae6561649dc9a22873d 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef BABELTRACE2_VERSION_H
 #define BABELTRACE2_VERSION_H
 
+/* IWYU pragma: private, include <babeltrace2/babeltrace.h> */
+
 #ifndef __BT_IN_BABELTRACE_H
 # error "Please include <babeltrace2/babeltrace.h> instead."
 #endif
@@ -66,7 +68,7 @@ version:
 @returns
     Major version of the library.
 */
-extern unsigned int bt_version_get_major(void);
+extern unsigned int bt_version_get_major(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -75,7 +77,7 @@ extern unsigned int bt_version_get_major(void);
 @returns
     Minor version of the library.
 */
-extern unsigned int bt_version_get_minor(void);
+extern unsigned int bt_version_get_minor(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -84,7 +86,7 @@ extern unsigned int bt_version_get_minor(void);
 @returns
     Patch version of the library.
 */
-extern unsigned int bt_version_get_patch(void);
+extern unsigned int bt_version_get_patch(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -96,7 +98,7 @@ The development stage \em can contain a version suffix such as
 @returns
     Development stage of the library's version, or \c NULL if none.
 */
-extern const char *bt_version_get_development_stage(void);
+extern const char *bt_version_get_development_stage(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -110,7 +112,7 @@ of the library.
     Version control system revision's description of the library's
     version, or \c NULL if none.
 */
-extern const char *bt_version_get_vcs_revision_description(void);
+extern const char *bt_version_get_vcs_revision_description(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -125,7 +127,7 @@ development build, this function returns \c NULL.
 @sa bt_version_get_name_description() &mdash;
     Returns the description of libbabeltrace2's release name.
 */
-extern const char *bt_version_get_name(void);
+extern const char *bt_version_get_name(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -140,7 +142,7 @@ case for a development build, this function returns \c NULL.
 @sa bt_version_get_name() &mdash;
     Returns libbabeltrace2's release name.
 */
-extern const char *bt_version_get_name_description(void);
+extern const char *bt_version_get_name_description(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -152,7 +154,7 @@ custom build.
 @returns
     Library's version extra name, or \c NULL if not available.
 */
-extern const char *bt_version_get_extra_name(void);
+extern const char *bt_version_get_extra_name(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -168,7 +170,7 @@ for a custom build.
     Can contain newlines.
     @endparblock
 */
-extern const char *bt_version_get_extra_description(void);
+extern const char *bt_version_get_extra_description(void) __BT_NOEXCEPT;
 
 /*!
 @brief
@@ -185,7 +187,7 @@ for a custom build.
     applied to Babeltrace's source tree for a custom build.
     @endparblock
 */
-extern const char *bt_version_get_extra_patch_names(void);
+extern const char *bt_version_get_extra_patch_names(void) __BT_NOEXCEPT;
 
 /*! @} */
 
index 9c856356c0cda65b4df891e05314cbc74e719bcf..29d86e0fe876dc12d18b09d9fd8945fb6c74ab93 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: FSFAP
 # ============================================================================
 #  https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html
 # ============================================================================
index dd6d8b61406c32a1822760b8835062a7d6b1a156..e1ea0fc46b6da517e04bb3c3fa40b891aebd88f0 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: FSFAP
 # ===========================================================================
 #      https://www.gnu.org/software/autoconf-archive/ax_append_flag.html
 # ===========================================================================
index 6a1ede15e6315876712dc0007100ea298028b593..98999e11f63d90d671631316db9e47bde4bb7738 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-Autoconf-exception-macro
 # ===========================================================================
 #    https://www.gnu.org/software/autoconf-archive/ax_c___attribute__.html
 # ===========================================================================
index bd753b34d7dc57063b0b65f1441dde0889e1cd26..8f62565e9a60f9511bf8ead4671b6f3b1bd97996 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: FSFAP
 # ===========================================================================
 #  https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
 # ===========================================================================
index 43087b2e6889ec6f8ebd2f8ba77f4a9a716f8ac2..c081ffacc03ace7b740b4a33a8b68dab4cd8dae7 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: FSFAP
 # ===========================================================================
 #  https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
 # ===========================================================================
 #
 #   Check for baseline language coverage in the compiler for the specified
 #   version of the C++ standard.  If necessary, add switches to CXX and
-#   CXXCPP to enable support.  VERSION may be '11' (for the C++11 standard)
-#   or '14' (for the C++14 standard).
+#   CXXCPP to enable support.  VERSION may be '11', '14', '17', or '20' for
+#   the respective C++ standard version.
 #
 #   The second argument, if specified, indicates whether you insist on an
 #   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
 #   -std=c++11).  If neither is specified, you get whatever works, with
-#   preference for an extended mode.
+#   preference for no added switch, and then for an extended mode.
 #
 #   The third argument, if specified 'mandatory' or if left unspecified,
 #   indicates that baseline support for the specified C++ standard is
 #   Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
 #   Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
 #   Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
+#   Copyright (c) 2020 Jason Merrill <jason@redhat.com>
+#   Copyright (c) 2021 Jörn Heusipp <osmanx@problemloesungsmaschine.de>
 #
 #   Copying and distribution of this file, with or without modification, are
 #   permitted in any medium without royalty provided the copyright notice
 #   and this notice are preserved.  This file is offered as-is, without any
 #   warranty.
 
-#serial 11
+#serial 18
 
 dnl  This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
 dnl  (serial version number 13).
@@ -50,6 +53,7 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
   m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
         [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
         [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
+        [$1], [20], [ax_cxx_compile_alternatives="20"],
         [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
   m4_if([$2], [], [],
         [$2], [ext], [],
@@ -62,6 +66,16 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
   AC_LANG_PUSH([C++])dnl
   ac_success=no
 
+  m4_if([$2], [], [dnl
+    AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
+                  ax_cv_cxx_compile_cxx$1,
+      [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+        [ax_cv_cxx_compile_cxx$1=yes],
+        [ax_cv_cxx_compile_cxx$1=no])])
+    if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
+      ac_success=yes
+    fi])
+
   m4_if([$2], [noext], [], [dnl
   if test x$ac_success = xno; then
     for alternative in ${ax_cxx_compile_alternatives}; do
@@ -91,9 +105,18 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
     dnl HP's aCC needs +std=c++11 according to:
     dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
     dnl Cray's crayCC needs "-h std=c++11"
+    dnl MSVC needs -std:c++NN for C++17 and later (default is C++14)
     for alternative in ${ax_cxx_compile_alternatives}; do
-      for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
-        cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+      for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do
+        if test x"$switch" = xMSVC; then
+          dnl AS_TR_SH maps both `:` and `=` to `_` so -std:c++17 would collide
+          dnl with -std=c++17.  We suffix the cache variable name with _MSVC to
+          dnl avoid this.
+          switch=-std:c++${alternative}
+          cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_${switch}_MSVC])
+        else
+          cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+        fi
         AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
                        $cachevar,
           [ac_save_CXX="$CXX"
@@ -140,7 +163,6 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
   _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
 )
 
-
 dnl  Test body for checking C++14 support
 
 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
@@ -148,12 +170,24 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
   _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
 )
 
+dnl  Test body for checking C++17 support
+
 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
   _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
   _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
   _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
 )
 
+dnl  Test body for checking C++20 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20],
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+  _AX_CXX_COMPILE_STDCXX_testbody_new_in_20
+)
+
+
 dnl  Tests for new features in C++11
 
 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
@@ -165,7 +199,11 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
 
 #error "This is not a C++ compiler"
 
-#elif __cplusplus < 201103L
+// MSVC always sets __cplusplus to 199711L in older versions; newer versions
+// only set it correctly if /Zc:__cplusplus is specified as well as a
+// /std:c++NN switch:
+// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
+#elif __cplusplus < 201103L && !defined _MSC_VER
 
 #error "This is not a C++11 compiler"
 
@@ -456,7 +494,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
 
 #error "This is not a C++ compiler"
 
-#elif __cplusplus < 201402L
+#elif __cplusplus < 201402L && !defined _MSC_VER
 
 #error "This is not a C++14 compiler"
 
@@ -580,7 +618,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
 
 #error "This is not a C++ compiler"
 
-#elif __cplusplus < 201703L
+#elif __cplusplus < 201703L && !defined _MSC_VER
 
 #error "This is not a C++17 compiler"
 
@@ -946,6 +984,36 @@ namespace cxx17
 
 }  // namespace cxx17
 
-#endif  // __cplusplus < 201703L
+#endif  // __cplusplus < 201703L && !defined _MSC_VER
+
+]])
+
+
+dnl  Tests for new features in C++20
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 202002L && !defined _MSC_VER
+
+#error "This is not a C++20 compiler"
+
+#else
+
+#include <version>
+
+namespace cxx20
+{
+
+// As C++20 supports feature test macros in the standard, there is no
+// immediate need to actually test for feature availability on the
+// Autoconf side.
+
+}  // namespace cxx20
+
+#endif  // __cplusplus < 202002L && !defined _MSC_VER
 
 ]])
index 1d467de3d51f5f0b180a39c5627203a307797d5b..7a4196ff5dffdac5f47af65351efd5042c06a439 100644 (file)
@@ -36,6 +36,8 @@
 #   Copyright (c) 2008 Rafael Laboissiere <rafael@laboissiere.net>
 #   Copyright (c) 2008 Andrew Collier
 #   Copyright (c) 2011 Murray Cumming <murrayc@openismus.com>
+#   Copyright (c) 2018 Reini Urban <rurban@cpan.org>
+#   Copyright (c) 2021 Vincent Danjean <Vincent.Danjean@ens-lyon.org>
 #
 #   This program is free software; you can redistribute it and/or modify it
 #   under the terms of the GNU General Public License as published by the
 #   modified version of the Autoconf Macro, you may extend this special
 #   exception to the GPL to apply to your modified version as well.
 
-#serial 13
+#serial 15
 
 AC_DEFUN([AX_PKG_SWIG],[
         # Find path to the "swig" executable.
         AC_PATH_PROGS([SWIG],[swig swig3.0 swig2.0])
         if test -z "$SWIG" ; then
                 m4_ifval([$3],[$3],[:])
-        elif test -n "$1" ; then
+        elif test -z "$1" ; then
+                m4_ifval([$2],[$2],[:])
+       else
                 AC_MSG_CHECKING([SWIG version])
                 [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`]
                 AC_MSG_RESULT([$swig_version])
@@ -81,12 +85,12 @@ AC_DEFUN([AX_PKG_SWIG],[
                         if test -z "$required_major" ; then
                                 [required_major=0]
                         fi
-                        [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+                        [required=`echo $required. | sed 's/[0-9]*[^0-9]//'`]
                         [required_minor=`echo $required | sed 's/[^0-9].*//'`]
                         if test -z "$required_minor" ; then
                                 [required_minor=0]
                         fi
-                        [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+                        [required=`echo $required. | sed 's/[0-9]*[^0-9]//'`]
                         [required_patch=`echo $required | sed 's/[^0-9].*//'`]
                         if test -z "$required_patch" ; then
                                 [required_patch=0]
@@ -121,7 +125,7 @@ AC_DEFUN([AX_PKG_SWIG],[
                                 m4_ifval([$3],[$3],[])
                         else
                                 AC_MSG_CHECKING([for SWIG library])
-                                SWIG_LIB=`$SWIG -swiglib`
+                                SWIG_LIB=`$SWIG -swiglib | tr '\r\n' '  '`
                                 AC_MSG_RESULT([$SWIG_LIB])
                                 m4_ifval([$2],[$2],[])
                         fi
index 507f182a8745f2d1327a74f80376136ec8aab7bd..db15ab983a1c3f32dd94a5d393ef001f8761d031 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-Autoconf-exception-macro
 # ===========================================================================
 #        https://www.gnu.org/software/autoconf-archive/ax_pthread.html
 # ===========================================================================
 #   flags that are needed. (The user can also force certain compiler
 #   flags/libs to be tested by setting these environment variables.)
 #
-#   Also sets PTHREAD_CC to any special C compiler that is needed for
-#   multi-threaded programs (defaults to the value of CC otherwise). (This
-#   is necessary on AIX to use the special cc_r compiler alias.)
+#   Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
+#   needed for multi-threaded programs (defaults to the value of CC
+#   respectively CXX otherwise). (This is necessary on e.g. AIX to use the
+#   special cc_r/CC_r compiler alias.)
 #
 #   NOTE: You are assumed to not only compile your program with these flags,
 #   but also to link with them as well. For example, you might link with
 #   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#   $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
 #
 #   If you are only building threaded programs, you may wish to use these
 #   variables in your default LIBS, CFLAGS, and CC:
 #
 #     LIBS="$PTHREAD_LIBS $LIBS"
 #     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
 #     CC="$PTHREAD_CC"
+#     CXX="$PTHREAD_CXX"
 #
 #   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
 #   has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
@@ -83,7 +88,7 @@
 #   modified version of the Autoconf Macro, you may extend this special
 #   exception to the GPL to apply to your modified version as well.
 
-#serial 27
+#serial 31
 
 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
 AC_DEFUN([AX_PTHREAD], [
@@ -105,6 +110,7 @@ if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
         ax_pthread_save_CFLAGS="$CFLAGS"
         ax_pthread_save_LIBS="$LIBS"
         AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
+        AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
         CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
         LIBS="$PTHREAD_LIBS $LIBS"
         AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
@@ -482,18 +488,28 @@ if test "x$ax_pthread_ok" = "xyes"; then
                     [#handle absolute path differently from PATH based program lookup
                      AS_CASE(["x$CC"],
                          [x/*],
-                         [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
-                         [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+                         [
+                          AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])
+                          AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])])
+                        ],
+                         [
+                          AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])
+                          AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])])
+                        ]
+                     )
+                    ])
                 ;;
             esac
         fi
 fi
 
 test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
 
 AC_SUBST([PTHREAD_LIBS])
 AC_SUBST([PTHREAD_CFLAGS])
 AC_SUBST([PTHREAD_CC])
+AC_SUBST([PTHREAD_CXX])
 
 # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
 if test "x$ax_pthread_ok" = "xyes"; then
index 17c3eab7dafde6c1358c7c189bbe44a6e14995a7..6de1b2a5a2f068825b69b1f4940aff49a6947989 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: FSFAP
 # ===========================================================================
 #    https://www.gnu.org/software/autoconf-archive/ax_require_defined.html
 # ===========================================================================
index 39d5b4a34258a46003a3cea6e0b308d7ef7e1ae7..fb033ec7f387586b225e0f14c63fa4215dbc7c53 100644 (file)
      | src/bindings/python/bt2/setup\.py$
      | src/bindings/python/bt2/bt2/native_bt\.py$
      | src/bindings/python/bt2/bt2/version\.py$
+     | tests/utils/python/normand\.py$
+     | tests/utils/python/typing/typing\.py$
      | tests/utils/python/tap
 
   )
   '''
+
+[tool.isort]
+profile = "black"
+extend_skip_glob = [
+    "tests/utils/python/tap",
+    "tests/utils/python/normand.py",
+    "tests/utils/python/typing/typing.py",
+]
+length_sort = true
index 9a58dd3cf6812ae271139dc0e78da87c4c627f1e..02702a12cc992342aa272e0ec0cd19ca95f01630 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,10 +2,16 @@
 # E501: line too long
 # W503: line break before binary operator (conflicts with black's way of
 #       formatting)
-ignore = E501,W503
+# E203: Whitespace before ':' (conclicts with black's way of formatting)
+ignore = E501,W503,E203
 
-# __init__.py has a bunch of (expectedly) unused imports, so disable that
-# warning for this file.
-per-file-ignores = src/bindings/python/bt2/bt2/__init__.py:F401
+# Disabled warnings for `bt2/__init__.py`:
+#
+# F401:
+#     Has a bunch of (expectedly) unused imports.
+#
+# E402:
+#     Has code to set up the DLL search path before imports.
+per-file-ignores = src/bindings/python/bt2/bt2/__init__.py:F401,E402
 
-exclude = tests/utils/python/tap
+exclude = tests/utils/python/normand.py tests/utils/python/tap tests/utils/python/typing/typing.py
index 6e01a12d7efbb59199fcd8f5973ae11fa85cdffc..05feb3e741b026bfec2a82591f0dabee1bf8894e 100644 (file)
+# SPDX-FileCopyrightText: 2019-2023 EfficiOS, Inc.
 # SPDX-License-Identifier: MIT
 
+# Build the current dir containing the library and plugins before the cli and
+# bindings.
 SUBDIRS = \
-       common \
-       py-common \
-       cpp-common \
-       autodisc \
-       argpar \
-       ctfser \
-       fd-cache \
-       compat \
-       logging \
-       ctf-writer \
-       lib \
-       string-format \
-       python-plugin-provider \
-       plugins \
-       param-parse \
-       cli \
-       bindings
+       . \
+       cli
+
+if ENABLE_PYTHON_BINDINGS
+SUBDIRS += bindings/python/bt2
+endif
+
+## This target generates an include file that contains the git version
+## string of the current branch, it must be continuously updated when
+## we build in the git repo and shipped in dist tarballs to reflect the
+## status of the tree when it was generated. If the tree is clean and
+## the current commit is a tag starting with "v", consider this a
+## release version and set an empty git version.
+
+version_verbose = $(version_verbose_@AM_V@)
+version_verbose_ = $(version_verbose_@AM_DEFAULT_V@)
+version_verbose_0 = @echo "  GEN     " $@;
+
+common/version.i:
+       $(version_verbose)GREP=$(GREP) SED=$(SED) TOP_SRCDIR="$(top_srcdir)" $(SHELL) $(srcdir)/gen-version-i.sh
+
+dist_noinst_SCRIPTS = gen-version-i.sh
+
+# Ensure version.i is generated before any code is built.
+BUILT_SOURCES = common/version.i
+
+##
+## version.i is defined as a .PHONY target even if it's a real file,
+## we want the target to be re-run on every make.
+##
+.PHONY: common/version.i
+
+CLEANFILES = common/version.i.tmp
+
+##
+## Only clean "version.i" on dist-clean, we need to keep it on regular
+## clean when it's part of a dist tarball.
+##
+DISTCLEANFILES = common/version.i
+
+#
+# Convenience libraries
+#
+
+noinst_LTLIBRARIES = \
+       argpar/libargpar.la \
+       autodisc/libautodisc.la \
+       clock-correlation-validator/libclock-correlation-validator.la \
+       common/libcommon.la \
+       compat/libcompat.la \
+       cpp-common/libcpp-common.la \
+       cpp-common/vendor/fmt/libfmt.la \
+       ctfser/libctfser.la \
+       fd-cache/libfd-cache.la \
+       logging/liblogging.la \
+       param-parse/libparam-parse.la \
+       plugins/common/muxing/libmuxing.la \
+       plugins/common/param-validation/libparam-validation.la \
+       plugins/ctf/common/metadata/libctf-ast.la \
+       plugins/ctf/common/metadata/libctf-parser.la \
+       string-format/libstring-format.la
+
+
+argpar_libargpar_la_SOURCES = \
+       argpar/argpar.c \
+       argpar/argpar.h
+
+autodisc_libautodisc_la_SOURCES = \
+       autodisc/autodisc.c \
+       autodisc/autodisc.h
+
+clock_correlation_validator_libclock_correlation_validator_la_SOURCES = \
+       clock-correlation-validator/clock-correlation-validator.cpp \
+       clock-correlation-validator/clock-correlation-validator.h \
+       clock-correlation-validator/clock-correlation-validator.hpp
+
+common_libcommon_la_SOURCES = \
+       common/align.h \
+       common/assert.c \
+       common/assert.h \
+       common/common.c \
+       common/common.h \
+       common/list.h \
+       common/macros.h \
+       common/mmap-align.h \
+       common/safe.h \
+       common/uuid.c \
+       common/uuid.h \
+       common/version.h \
+       common/version.i
+
+common_libcommon_la_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       -DBABELTRACE_PLUGINS_DIR=\"$(BABELTRACE_PLUGINS_DIR)\"
+
+compat_libcompat_la_SOURCES = \
+       compat/bitfield.h \
+       compat/compiler.h \
+       compat/endian.h \
+       compat/fcntl.h \
+       compat/glib.h \
+       compat/limits.h \
+       compat/memstream.h \
+       compat/mman.c \
+       compat/mman.h \
+       compat/socket.hpp \
+       compat/stdio.h \
+       compat/stdlib.h \
+       compat/string.h \
+       compat/time.h \
+       compat/unistd.h \
+       compat/utc.h
+
+cpp_common_libcpp_common_la_SOURCES = \
+       cpp-common/bt2/borrowed-object-iterator.hpp \
+       cpp-common/bt2/borrowed-object-proxy.hpp \
+       cpp-common/bt2/borrowed-object.hpp \
+       cpp-common/bt2/clock-class.hpp \
+       cpp-common/bt2/clock-snapshot.hpp \
+       cpp-common/bt2/component-class-dev.hpp \
+       cpp-common/bt2/component-class.hpp \
+       cpp-common/bt2/component-port.hpp \
+       cpp-common/bt2/graph.hpp \
+       cpp-common/bt2/error.hpp \
+       cpp-common/bt2/exc.hpp \
+       cpp-common/bt2/field-class.hpp \
+       cpp-common/bt2/field-path.hpp \
+       cpp-common/bt2/field.hpp \
+       cpp-common/bt2/integer-range-set.hpp \
+       cpp-common/bt2/integer-range.hpp \
+       cpp-common/bt2/internal/comp-cls-bridge.hpp \
+       cpp-common/bt2/internal/utils.hpp \
+       cpp-common/bt2/logging.hpp \
+       cpp-common/bt2/message-array.hpp \
+       cpp-common/bt2/message-iterator.hpp \
+       cpp-common/bt2/message.hpp \
+       cpp-common/bt2/optional-borrowed-object.hpp \
+       cpp-common/bt2/plugin-dev.hpp \
+       cpp-common/bt2/plugin-load.hpp \
+       cpp-common/bt2/plugin-set.hpp \
+       cpp-common/bt2/plugin.hpp \
+       cpp-common/bt2/private-query-executor.hpp \
+       cpp-common/bt2/query-executor.hpp \
+       cpp-common/bt2/raw-value-proxy.hpp \
+       cpp-common/bt2/self-component-class.hpp \
+       cpp-common/bt2/self-component-port.hpp \
+       cpp-common/bt2/self-message-iterator-configuration.hpp \
+       cpp-common/bt2/self-message-iterator.hpp \
+       cpp-common/bt2/shared-object.hpp \
+       cpp-common/bt2/trace-ir.hpp \
+       cpp-common/bt2/type-traits.hpp \
+       cpp-common/bt2/value.hpp \
+       cpp-common/bt2/wrap.hpp \
+       cpp-common/bt2c/align.hpp \
+       cpp-common/bt2c/c-string-view.hpp \
+       cpp-common/bt2c/call.hpp \
+       cpp-common/bt2c/contains.hpp \
+       cpp-common/bt2c/data-len.hpp \
+       cpp-common/bt2c/dummy.cpp \
+       cpp-common/bt2c/endian.hpp \
+       cpp-common/bt2c/exc.hpp \
+       cpp-common/bt2c/file-utils.cpp \
+       cpp-common/bt2c/file-utils.hpp \
+       cpp-common/bt2c/fmt.hpp \
+       cpp-common/bt2c/glib-up.hpp \
+       cpp-common/bt2c/libc-up.hpp \
+       cpp-common/bt2c/logging.hpp \
+       cpp-common/bt2c/prio-heap.hpp \
+       cpp-common/bt2c/read-fixed-len-int.hpp \
+       cpp-common/bt2c/safe-ops.hpp \
+       cpp-common/bt2c/span.hpp \
+       cpp-common/bt2c/std-int.hpp \
+       cpp-common/bt2c/type-traits.hpp \
+       cpp-common/bt2c/uuid.hpp \
+       cpp-common/bt2c/vector.hpp \
+       cpp-common/bt2s/make-unique.hpp \
+       cpp-common/bt2s/optional.hpp \
+       cpp-common/bt2s/span.hpp \
+       cpp-common/bt2s/string-view.hpp \
+       cpp-common/vendor/nlohmann/json.hpp \
+       cpp-common/vendor/optional-lite/optional.hpp \
+       cpp-common/vendor/span-lite/span.hpp \
+       cpp-common/vendor/string-view-lite/string_view.hpp \
+       cpp-common/vendor/wise-enum/optional.h \
+       cpp-common/vendor/wise-enum/optional_common.h \
+       cpp-common/vendor/wise-enum/wise_enum.h \
+       cpp-common/vendor/wise-enum/wise_enum_detail.h \
+       cpp-common/vendor/wise-enum/wise_enum_generated.h
+
+cpp_common_vendor_fmt_libfmt_la_SOURCES = \
+       cpp-common/vendor/fmt/args.h \
+       cpp-common/vendor/fmt/chrono.h \
+       cpp-common/vendor/fmt/color.h \
+       cpp-common/vendor/fmt/compile.h \
+       cpp-common/vendor/fmt/core.h \
+       cpp-common/vendor/fmt/format-inl.h \
+       cpp-common/vendor/fmt/format.cc \
+       cpp-common/vendor/fmt/format.h \
+       cpp-common/vendor/fmt/os.cc \
+       cpp-common/vendor/fmt/os.h \
+       cpp-common/vendor/fmt/ostream.h \
+       cpp-common/vendor/fmt/printf.h \
+       cpp-common/vendor/fmt/ranges.h \
+       cpp-common/vendor/fmt/std.h \
+       cpp-common/vendor/fmt/xchar.h
+
+cpp_common_vendor_fmt_libfmt_la_CXXFLAGS = \
+       $(AM_CXXFLAGS) -Wno-missing-noreturn
+
+ctfser_libctfser_la_SOURCES = \
+       ctfser/ctfser.c \
+       ctfser/ctfser.h
+
+fd_cache_libfd_cache_la_SOURCES = \
+       fd-cache/fd-cache.c \
+       fd-cache/fd-cache.h
+
+logging_liblogging_la_SOURCES = \
+       logging/comp-logging.h \
+       logging/log-api.c \
+       logging/log-api.h \
+       logging/log.h
+
+param_parse_libparam_parse_la_SOURCES = \
+       param-parse/param-parse.c \
+       param-parse/param-parse.h
+
+string_format_libstring_format_la_SOURCES = \
+       string-format/format-plugin-comp-cls-name.c \
+       string-format/format-plugin-comp-cls-name.h \
+       string-format/format-error.c \
+       string-format/format-error.h
+
+plugins_common_muxing_libmuxing_la_SOURCES = \
+       plugins/common/muxing/muxing.c \
+       plugins/common/muxing/muxing.h
+
+plugins_common_param_validation_libparam_validation_la_SOURCES = \
+       plugins/common/param-validation/param-validation.c \
+       plugins/common/param-validation/param-validation.h
+
+# Set flags for the Bison based metadata parser
+#  -t : instrument the parser
+#  -d : produce a header
+#  -v : verbose
+#  -Wno-yacc : disable POSIX Yacc incompatibilities warnings
+AM_YFLAGS = \
+       -t -d -v -Wno-yacc
+
+plugins_ctf_common_metadata_libctf_parser_la_SOURCES = \
+       plugins/ctf/common/src/metadata/tsdl/lexer.lpp \
+       plugins/ctf/common/src/metadata/tsdl/parser.ypp \
+       plugins/ctf/common/src/metadata/tsdl/objstack.cpp
+
+# scanner-symbols.h is included to prefix generated yy_* symbols with bt_.
+plugins_ctf_common_metadata_libctf_parser_la_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       -include $(srcdir)/plugins/ctf/common/src/metadata/tsdl/scanner-symbols.hpp
+
+# This library contains (mostly) generated code, silence some warnings that it
+# produces.
+plugins_ctf_common_metadata_libctf_parser_la_CXXFLAGS = \
+       $(AM_CXXFLAGS) \
+       -Wno-unused-function \
+       -Wno-null-dereference \
+       -Wno-missing-field-initializers \
+       -Wno-unused-parameter
+
+plugins_ctf_common_metadata_libctf_ast_la_SOURCES = \
+       plugins/ctf/common/src/metadata/tsdl/visitor-generate-ir.cpp \
+       plugins/ctf/common/src/metadata/tsdl/visitor-semantic-validator.cpp \
+       plugins/ctf/common/src/metadata/tsdl/visitor-parent-links.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ast.hpp \
+       plugins/ctf/common/src/metadata/tsdl/objstack.hpp \
+       plugins/ctf/common/src/metadata/tsdl/parser.hpp \
+       plugins/ctf/common/src/metadata/tsdl/parser-wrap.hpp \
+       plugins/ctf/common/src/metadata/tsdl/scanner.hpp \
+       plugins/ctf/common/src/metadata/tsdl/scanner-symbols.hpp \
+       plugins/ctf/common/src/metadata/tsdl/decoder.cpp \
+       plugins/ctf/common/src/metadata/tsdl/decoder.hpp \
+       plugins/ctf/common/src/metadata/tsdl/decoder-packetized-file-stream-to-buf.cpp \
+       plugins/ctf/common/src/metadata/tsdl/decoder-packetized-file-stream-to-buf.hpp \
+       plugins/ctf/common/src/metadata/tsdl/logging.hpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta.hpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-visitors.hpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-validate.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-meanings.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-in-ir.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-default-clock-classes.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-text-array-sequence.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-alignments.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-value-storing-indexes.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-stream-class-config.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-warn-meaningless-header-fields.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-translate.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-resolve.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-configure-ir-trace.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-meta-configure-ir-trace.hpp
+
+if BABELTRACE_BUILD_WITH_MINGW
+plugins_ctf_common_metadata_libctf_ast_la_LIBADD = -lintl -liconv -lole32
+endif
+
+BUILT_SOURCES += \
+       plugins/ctf/common/src/metadata/tsdl/parser.hpp
+
+ALL_LOCAL =
+
+if HAVE_BISON
+# We have bison: we can clean the generated parser files
+CLEANFILES += \
+       plugins/ctf/common/src/metadata/tsdl/parser.cpp \
+       plugins/ctf/common/src/metadata/tsdl/parser.hpp \
+       plugins/ctf/common/src/metadata/tsdl/parser.output
+else # HAVE_BISON
+# Create target used to stop the build if we want to build the parser,
+# but we don't have the necessary tool to do so
+plugins/ctf/common/metadata/parser.cpp plugins/ctf/common/metadata/parser.hpp: plugins/ctf/common/metadata/parser.ypp
+       @echo "Error: Cannot build target because bison is missing."
+       @echo "Make sure bison is installed and run the configure script again."
+       @false
+
+ALL_LOCAL += \
+       plugins/ctf/common/metadata/parser.cpp \
+       plugins/ctf/common/metadata/parser.hpp
+endif # HAVE_BISON
+
+if HAVE_FLEX
+# We have flex: we can clean the generated lexer files
+CLEANFILES += plugins/ctf/common/metadata/lexer.cpp
+else # HAVE_FLEX
+# Create target used to stop the build if we want to build the lexer,
+# but we don't have the necessary tool to do so
+plugins/ctf/common/metadata/lexer.cpp: plugins/ctf/common/metadata/lexer.lpp
+       @echo "Error: Cannot build target because flex is missing."
+       @echo "Make sure flex is installed and run the configure script again."
+       @false
+
+ALL_LOCAL += plugins/ctf/common/metadata/lexer.cpp
+endif # HAVE_FLEX
+
+all-local: $(ALL_LOCAL)
+
+if ENABLE_PYTHON_COMMON_DEPS
+noinst_LTLIBRARIES += py-common/libpy-common.la
+
+py_common_libpy_common_la_SOURCES = \
+       py-common/py-common.c \
+       py-common/py-common.h
+
+py_common_libpy_common_la_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       $(PYTHON_INCLUDE)
+
+endif # ENABLE_PYTHON_COMMON_DEPS
+
+if ENABLE_DEBUG_INFO
+noinst_LTLIBRARIES += plugins/lttng-utils/debug-info/libdebug-info.la
+
+plugins_lttng_utils_debug_info_libdebug_info_la_SOURCES = \
+       plugins/lttng-utils/debug-info/bin-info.c \
+       plugins/lttng-utils/debug-info/bin-info.h \
+       plugins/lttng-utils/debug-info/crc32.c \
+       plugins/lttng-utils/debug-info/crc32.h \
+       plugins/lttng-utils/debug-info/debug-info.c \
+       plugins/lttng-utils/debug-info/debug-info.h \
+       plugins/lttng-utils/debug-info/dwarf.c \
+       plugins/lttng-utils/debug-info/dwarf.h \
+       plugins/lttng-utils/debug-info/trace-ir-data-copy.c \
+       plugins/lttng-utils/debug-info/trace-ir-data-copy.h \
+       plugins/lttng-utils/debug-info/trace-ir-mapping.c \
+       plugins/lttng-utils/debug-info/trace-ir-mapping.h \
+       plugins/lttng-utils/debug-info/trace-ir-metadata-copy.c \
+       plugins/lttng-utils/debug-info/trace-ir-metadata-copy.h \
+       plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.c \
+       plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.h \
+       plugins/lttng-utils/debug-info/utils.c \
+       plugins/lttng-utils/debug-info/utils.h
+
+plugins_lttng_utils_debug_info_libdebug_info_la_LIBADD = \
+       fd-cache/libfd-cache.la
+
+endif # ENABLE_DEBUG_INFO
+
+
+#
+# Public libraries
+#
+
+lib_LTLIBRARIES = \
+       ctf-writer/libbabeltrace2-ctf-writer.la \
+       lib/libbabeltrace2.la
+
+lib_libbabeltrace2_la_SOURCES = \
+       lib/graph/message/discarded-items.c \
+       lib/graph/message/discarded-items.h \
+       lib/graph/message/event.c \
+       lib/graph/message/event.h \
+       lib/graph/message-iterator-class.c \
+       lib/graph/message-iterator-class.h \
+       lib/graph/message/message.c \
+       lib/graph/message/message.h \
+       lib/graph/message/message-iterator-inactivity.c \
+       lib/graph/message/message-iterator-inactivity.h \
+       lib/graph/message/packet.c \
+       lib/graph/message/packet.h \
+       lib/graph/message/stream.c \
+       lib/graph/message/stream.h \
+       lib/graph/component.c \
+       lib/graph/component-class.c \
+       lib/graph/component-class.h \
+       lib/graph/component-class-sink-simple.c \
+       lib/graph/component-class-sink-simple.h \
+       lib/graph/component-descriptor-set.c \
+       lib/graph/component-descriptor-set.h \
+       lib/graph/component-filter.c \
+       lib/graph/component-filter.h \
+       lib/graph/component.h \
+       lib/graph/component-sink.c \
+       lib/graph/component-sink.h \
+       lib/graph/component-source.c \
+       lib/graph/component-source.h \
+       lib/graph/connection.c \
+       lib/graph/connection.h \
+       lib/graph/graph.c \
+       lib/graph/graph.h \
+       lib/graph/interrupter.c \
+       lib/graph/interrupter.h \
+       lib/graph/iterator.c \
+       lib/graph/iterator.h \
+       lib/graph/mip.c \
+       lib/graph/port.c \
+       lib/graph/port.h \
+       lib/graph/query-executor.c \
+       lib/graph/query-executor.h \
+       lib/plugin/plugin.c \
+       lib/plugin/plugin.h \
+       lib/plugin/plugin-so.c \
+       lib/plugin/plugin-so.h \
+       lib/trace-ir/attributes.c \
+       lib/trace-ir/attributes.h \
+       lib/trace-ir/clock-class.c \
+       lib/trace-ir/clock-class.h \
+       lib/trace-ir/clock-snapshot.c \
+       lib/trace-ir/clock-snapshot.h \
+       lib/trace-ir/event.c \
+       lib/trace-ir/event-class.c \
+       lib/trace-ir/event-class.h \
+       lib/trace-ir/event.h \
+       lib/trace-ir/field.c \
+       lib/trace-ir/field-class.c \
+       lib/trace-ir/field-class.h \
+       lib/trace-ir/field.h \
+       lib/trace-ir/field-path.c \
+       lib/trace-ir/field-path.h \
+       lib/trace-ir/field-wrapper.c \
+       lib/trace-ir/field-wrapper.h \
+       lib/trace-ir/packet.c \
+       lib/trace-ir/packet.h \
+       lib/trace-ir/resolve-field-path.c \
+       lib/trace-ir/resolve-field-path.h \
+       lib/trace-ir/stream.c \
+       lib/trace-ir/stream-class.c \
+       lib/trace-ir/stream-class.h \
+       lib/trace-ir/stream.h \
+       lib/trace-ir/trace.c \
+       lib/trace-ir/trace-class.c \
+       lib/trace-ir/trace-class.h \
+       lib/trace-ir/trace.h \
+       lib/trace-ir/utils.c \
+       lib/trace-ir/utils.h \
+       lib/assert-cond-base.h \
+       lib/assert-cond.h \
+       lib/assert-cond.c \
+       lib/babeltrace2.c \
+       lib/current-thread.c \
+       lib/error.c \
+       lib/error.h \
+       lib/func-status.h \
+       lib/integer-range-set.c \
+       lib/integer-range-set.h \
+       lib/lib-logging.c \
+       lib/logging.c \
+       lib/logging.h \
+       lib/object-pool.c \
+       lib/object-pool.h \
+       lib/object.h \
+       lib/property.h \
+       lib/util.c \
+       lib/value.c \
+       lib/value.h
+
+lib_libbabeltrace2_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -version-info $(BABELTRACE_LIBRARY_VERSION)
+
+lib_libbabeltrace2_la_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       '-DBABELTRACE_PLUGIN_PROVIDERS_DIR="$(BABELTRACE_PLUGIN_PROVIDERS_DIR)"'
+
+lib_libbabeltrace2_la_LIBADD = \
+       logging/liblogging.la \
+       common/libcommon.la \
+       compat/libcompat.la \
+       clock-correlation-validator/libclock-correlation-validator.la
+
+nodist_EXTRA_lib_libbabeltrace2_la_SOURCES = dummy.cpp
+
+ctf_writer_libbabeltrace2_ctf_writer_la_SOURCES = \
+       ctf-writer/assert-pre.h \
+       ctf-writer/attributes.c \
+       ctf-writer/attributes.h \
+       ctf-writer/clock.c \
+       ctf-writer/clock-class.c \
+       ctf-writer/clock-class.h \
+       ctf-writer/clock.h \
+       ctf-writer/event.c \
+       ctf-writer/event-class.c \
+       ctf-writer/event-class.h \
+       ctf-writer/event.h \
+       ctf-writer/field-path.c \
+       ctf-writer/field-path.h \
+       ctf-writer/fields.c \
+       ctf-writer/fields.h \
+       ctf-writer/field-types.c \
+       ctf-writer/field-types.h \
+       ctf-writer/field-wrapper.c \
+       ctf-writer/field-wrapper.h \
+       ctf-writer/functor.c \
+       ctf-writer/functor.h \
+       ctf-writer/logging.c \
+       ctf-writer/logging.h \
+       ctf-writer/object.c \
+       ctf-writer/object.h \
+       ctf-writer/object-pool.c \
+       ctf-writer/object-pool.h \
+       ctf-writer/resolve.c \
+       ctf-writer/resolve.h \
+       ctf-writer/stream.c \
+       ctf-writer/stream-class.c \
+       ctf-writer/stream-class.h \
+       ctf-writer/stream.h \
+       ctf-writer/trace.c \
+       ctf-writer/trace.h \
+       ctf-writer/utils.c \
+       ctf-writer/utils.h \
+       ctf-writer/validation.c \
+       ctf-writer/validation.h \
+       ctf-writer/values.c \
+       ctf-writer/values.h \
+       ctf-writer/visitor.c \
+       ctf-writer/visitor.h \
+       ctf-writer/writer.c \
+       ctf-writer/writer.h
+
+ctf_writer_libbabeltrace2_ctf_writer_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -version-info $(BABELTRACE_LIBRARY_VERSION)
+
+ctf_writer_libbabeltrace2_ctf_writer_la_LIBADD = \
+       logging/liblogging.la \
+       common/libcommon.la \
+       ctfser/libctfser.la \
+       compat/libcompat.la
 
 pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = babeltrace2.pc babeltrace2-ctf-writer.pc
+pkgconfig_DATA = \
+       babeltrace2-ctf-writer.pc \
+       babeltrace2.pc
+
+#
+# Python plugin provider
+#
+
+if ENABLE_PYTHON_PLUGINS
+pluginproviderdir = "$(BABELTRACE_PLUGIN_PROVIDERS_DIR)"
+pluginprovider_LTLIBRARIES = python-plugin-provider/babeltrace2-python-plugin-provider.la
+
+python_plugin_provider_babeltrace2_python_plugin_provider_la_SOURCES = \
+       python-plugin-provider/python-plugin-provider.c \
+       python-plugin-provider/python-plugin-provider.h
+
+python_plugin_provider_babeltrace2_python_plugin_provider_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -avoid-version -module \
+       $(PYTHON_LDFLAGS)
+
+python_plugin_provider_babeltrace2_python_plugin_provider_la_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       $(PYTHON_INCLUDE)
+
+python_plugin_provider_babeltrace2_python_plugin_provider_la_LIBADD =
+
+# Link the Python plugin provider library with libbabeltrace2
+# when it's not built-in the babeltrace2 executable.
+if !ENABLE_BUILT_IN_PLUGINS
+python_plugin_provider_babeltrace2_python_plugin_provider_la_LIBADD += \
+       logging/liblogging.la \
+       common/libcommon.la \
+       py-common/libpy-common.la \
+       lib/libbabeltrace2.la
+endif
+endif # ENABLE_PYTHON_PLUGINS
+
+#
+# Plugins
+#
+
+plugindir = "$(BABELTRACE_PLUGINS_DIR)"
+plugin_LTLIBRARIES = \
+       plugins/ctf/babeltrace-plugin-ctf.la \
+       plugins/text/babeltrace-plugin-text.la \
+       plugins/utils/babeltrace-plugin-utils.la
+
+
+# utils plugin
+plugins_utils_babeltrace_plugin_utils_la_SOURCES = \
+       plugins/utils/counter/counter.c \
+       plugins/utils/counter/counter.h \
+       plugins/utils/dummy/dummy.c \
+       plugins/utils/dummy/dummy.h \
+       plugins/utils/muxer/comp.cpp \
+       plugins/utils/muxer/comp.hpp \
+       plugins/utils/muxer/msg-iter.cpp \
+       plugins/utils/muxer/msg-iter.hpp \
+       plugins/utils/muxer/upstream-msg-iter.cpp \
+       plugins/utils/muxer/upstream-msg-iter.hpp \
+       plugins/utils/trimmer/trimmer.c \
+       plugins/utils/trimmer/trimmer.h \
+       plugins/utils/plugin.cpp
+
+plugins_utils_babeltrace_plugin_utils_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -avoid-version -module $(LD_NOTEXT)
+
+plugins_utils_babeltrace_plugin_utils_la_LIBADD = \
+       plugins/common/muxing/libmuxing.la
+
+if !ENABLE_BUILT_IN_PLUGINS
+plugins_utils_babeltrace_plugin_utils_la_LIBADD += \
+       lib/libbabeltrace2.la \
+       common/libcommon.la \
+       cpp-common/vendor/fmt/libfmt.la \
+       logging/liblogging.la \
+       plugins/common/param-validation/libparam-validation.la \
+       clock-correlation-validator/libclock-correlation-validator.la
+endif
+
+# ctf plugin
+plugins_ctf_babeltrace_plugin_ctf_la_SOURCES = \
+       plugins/ctf/common/src/bfcr/bfcr.cpp \
+       plugins/ctf/common/src/bfcr/bfcr.hpp \
+       plugins/ctf/common/src/clk-cls-cfg.hpp \
+       plugins/ctf/common/src/msg-iter/msg-iter.cpp \
+       plugins/ctf/common/src/msg-iter/msg-iter.hpp \
+       plugins/ctf/fs-sink/fs-sink.cpp \
+       plugins/ctf/fs-sink/fs-sink-ctf-meta.hpp \
+       plugins/ctf/fs-sink/fs-sink.hpp \
+       plugins/ctf/fs-sink/fs-sink-stream.cpp \
+       plugins/ctf/fs-sink/fs-sink-stream.hpp \
+       plugins/ctf/fs-sink/fs-sink-trace.cpp \
+       plugins/ctf/fs-sink/fs-sink-trace.hpp \
+       plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.cpp \
+       plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.hpp \
+       plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.cpp \
+       plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.hpp \
+       plugins/ctf/fs-src/data-stream-file.cpp \
+       plugins/ctf/fs-src/data-stream-file.hpp \
+       plugins/ctf/fs-src/file.cpp \
+       plugins/ctf/fs-src/file.hpp \
+       plugins/ctf/fs-src/fs.cpp \
+       plugins/ctf/fs-src/fs.hpp \
+       plugins/ctf/fs-src/lttng-index.hpp \
+       plugins/ctf/fs-src/metadata.cpp \
+       plugins/ctf/fs-src/metadata.hpp \
+       plugins/ctf/fs-src/query.cpp \
+       plugins/ctf/fs-src/query.hpp \
+       plugins/ctf/lttng-live/data-stream.cpp \
+       plugins/ctf/lttng-live/data-stream.hpp \
+       plugins/ctf/lttng-live/lttng-live.cpp \
+       plugins/ctf/lttng-live/lttng-live.hpp \
+       plugins/ctf/lttng-live/lttng-viewer-abi.hpp \
+       plugins/ctf/lttng-live/metadata.cpp \
+       plugins/ctf/lttng-live/metadata.hpp \
+       plugins/ctf/lttng-live/viewer-connection.cpp \
+       plugins/ctf/lttng-live/viewer-connection.hpp \
+       plugins/ctf/plugin.cpp
+
+plugins_ctf_babeltrace_plugin_ctf_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -avoid-version -module $(LD_NOTEXT)
+
+plugins_ctf_babeltrace_plugin_ctf_la_LIBADD = \
+       plugins/ctf/common/metadata/libctf-parser.la \
+       plugins/ctf/common/metadata/libctf-ast.la \
+       plugins/common/param-validation/libparam-validation.la
+
+if BABELTRACE_BUILD_WITH_MINGW
+plugins_ctf_babeltrace_plugin_ctf_la_LIBADD += -lws2_32
+endif
+
+if !ENABLE_BUILT_IN_PLUGINS
+plugins_ctf_babeltrace_plugin_ctf_la_LIBADD += \
+       lib/libbabeltrace2.la \
+       logging/liblogging.la \
+       plugins/common/muxing/libmuxing.la \
+       common/libcommon.la \
+       ctfser/libctfser.la \
+       cpp-common/vendor/fmt/libfmt.la
+endif
+
+# text plugin
+plugins_text_babeltrace_plugin_text_la_SOURCES = \
+       plugins/text/details/colors.h \
+       plugins/text/details/details.c \
+       plugins/text/details/details.h \
+       plugins/text/details/obj-lifetime-mgmt.c \
+       plugins/text/details/obj-lifetime-mgmt.h \
+       plugins/text/details/write.c \
+       plugins/text/details/write.h \
+       plugins/text/dmesg/dmesg.c \
+       plugins/text/dmesg/dmesg.h \
+       plugins/text/pretty/pretty.c \
+       plugins/text/pretty/pretty.h \
+       plugins/text/pretty/print.c \
+       plugins/text/plugin.c
+
+plugins_text_babeltrace_plugin_text_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -avoid-version -module $(LD_NOTEXT)
+
+plugins_text_babeltrace_plugin_text_la_LIBADD =
+
+if !ENABLE_BUILT_IN_PLUGINS
+plugins_text_babeltrace_plugin_text_la_LIBADD += \
+       lib/libbabeltrace2.la \
+       common/libcommon.la \
+       logging/liblogging.la \
+       compat/libcompat.la \
+       plugins/common/param-validation/libparam-validation.la
+endif
+
+# lttng-utils plugin
+if ENABLE_DEBUG_INFO
+plugin_LTLIBRARIES += \
+       plugins/lttng-utils/babeltrace-plugin-lttng-utils.la
+
+plugins_lttng_utils_babeltrace_plugin_lttng_utils_la_SOURCES = \
+       plugins/lttng-utils/plugin.c
+
+plugins_lttng_utils_babeltrace_plugin_lttng_utils_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -avoid-version -module $(LD_NOTEXT) \
+       $(ELFUTILS_LIBS)
+
+plugins_lttng_utils_babeltrace_plugin_lttng_utils_la_LIBADD = \
+       plugins/lttng-utils/debug-info/libdebug-info.la
+
+if !ENABLE_BUILT_IN_PLUGINS
+plugins_lttng_utils_babeltrace_plugin_lttng_utils_la_LIBADD += \
+       lib/libbabeltrace2.la \
+       common/libcommon.la \
+       logging/liblogging.la \
+       plugins/common/param-validation/libparam-validation.la
+endif # !ENABLE_BUILT_IN_PLUGINS
+endif # ENABLE_DEBUG_INFO
+
+EXTRA_DIST = \
+       cpp-common/vendor/optional-lite/optional.hpp.license \
+       cpp-common/vendor/span-lite/span.hpp.license \
+       cpp-common/vendor/string-view-lite/string_view.hpp.license
diff --git a/src/Makefile.common.inc b/src/Makefile.common.inc
new file mode 100644 (file)
index 0000000..0d71e95
--- /dev/null
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: 2017-2024 EfficiOS, Inc.
+# SPDX-License-Identifier: MIT
+
+# Path to the plugins build directory.
+PLUGINS_PATH = $(abs_top_builddir)/src/plugins
+
+# Takes a plugin name and outputs the needed LDFLAGS to embed it.
+#
+# The --whole-archive option is important here. From the GNU linker's
+# documentation:
+#
+#     For each archive mentioned on the command line after the
+#     --whole-archive option, include every object file in the archive in
+#     the link, rather than searching the archive for the required object
+#     files.
+#
+# In our case, we find the plugins thanks to special sections in the
+# binary that are filled by plugin objects. If the linker discards those
+# symbols because the CLI does not use them directly, the CLI reports
+# no plugins found (plugins are effectively not embedded).
+pluginarchive = $(LD_WHOLE_ARCHIVE)$(PLUGINS_PATH)/$(1)/.libs/babeltrace-plugin-$(1).a$(LD_NO_WHOLE_ARCHIVE)
diff --git a/src/argpar/Makefile.am b/src/argpar/Makefile.am
deleted file mode 100644 (file)
index 385d19b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libargpar.la
-
-libargpar_la_SOURCES = argpar.c argpar.h
index 27503c56fd56944207b4cf60869196bf1ae51c64..e34b209a30b42f8aa334839cd27a96e024eed65e 100644 (file)
@@ -243,7 +243,7 @@ For example, with the following command line (all options have no
 argument):
 
 @code{.unparsed}
--f -m meow --jus mix --kilo
+-f -m meow --just mix --kilo
 @endcode
 
 The original argument index of \c meow is&nbsp;2 while the original
@@ -278,7 +278,7 @@ For example, with the following command line (all options have no
 argument):
 
 @code{.unparsed}
--f -m meow --jus mix --kilo
+-f -m meow --just mix --kilo
 @endcode
 
 The non-option index of \c meow is&nbsp;0 while the original
diff --git a/src/autodisc/Makefile.am b/src/autodisc/Makefile.am
deleted file mode 100644 (file)
index addff74..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-autodisc.la
-
-libbabeltrace2_autodisc_la_SOURCES = \
-       autodisc.c \
-       autodisc.h
index 0ae53817866924f16e74b0da9b471ccb715f1c01..d171a33dba2edf790e3a443e630608a2add6c951 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 #define BT_LOG_TAG "CLI-CFG-SRC-AUTO-DISC"
-#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_OUTPUT_LEVEL ((enum bt_log_level) log_level)
 #include "logging/log.h"
 
 #include <stdbool.h>
@@ -14,9 +14,9 @@
 #include "common/common.h"
 
 #define BT_AUTODISC_LOG_AND_APPEND(_lvl, _fmt, ...)                            \
-       do {                                                            \
-               BT_LOG_WRITE(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__);    \
-               (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( \
+       do {                                                                    \
+               BT_LOG_WRITE_PRINTF(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__);     \
+               (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(       \
                        "Source auto-discovery", _fmt, ##__VA_ARGS__);          \
        } while (0)
 
@@ -347,7 +347,7 @@ auto_source_discovery_internal_status support_info_query_all_sources(
                const bt_component_class_source *source;
                const bt_plugin *plugin;
                const bt_value *group;
-               double weigth;
+               double weight;
        } winner = { NULL, NULL, NULL, 0 };
 
        if (interrupter && bt_interrupter_is_set(interrupter)) {
@@ -479,7 +479,7 @@ auto_source_discovery_internal_status support_info_query_all_sources(
                                        bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
                                        input_type, weight, group_value ? bt_value_string_get(group_value) : "(none)");
 
-                               if (weight > winner.weigth) {
+                               if (weight > winner.weight) {
                                        winner.source = source_cc;
                                        winner.plugin = plugin;
 
@@ -487,7 +487,7 @@ auto_source_discovery_internal_status support_info_query_all_sources(
                                        winner.group = group_value;
                                        bt_value_get_ref(winner.group);
 
-                                       winner.weigth = weight;
+                                       winner.weight = weight;
                                }
                        } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_ERROR) {
                                BT_AUTODISC_LOGE_APPEND_CAUSE("babeltrace.support-info query failed.");
@@ -516,7 +516,7 @@ auto_source_discovery_internal_status support_info_query_all_sources(
                group = winner.group ? bt_value_string_get(winner.group) : NULL;
 
                BT_LOGI("Input awarded: input=%s, type=%s, component-class-name=source.%s.%s, weight=%f, group=%s",
-                       input, input_type, plugin_name, source_name, winner.weigth, group ? group : "(none)");
+                       input, input_type, plugin_name, source_name, winner.weight, group ? group : "(none)");
 
                status = auto_source_discovery_add(auto_disc, plugin_name,
                        source_name, group, input, original_input_index, log_level);
index 960f5b47ae239f70d9927ae940535845a79a0666..f3514f7af290ae4c3949cb2cf25bbbe1aa7512f7 100644 (file)
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: 2019 EfficiOS, Inc.
+# SPDX-License-Identifier: MIT
+
 prefix=@prefix@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
index 8d812a5b0ad6672bc8da1aa2feeab992718ae477..0cdf11f849340126561089bb9ae4700de9c683ae 100644 (file)
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: 2019 EfficiOS, Inc.
+# SPDX-License-Identifier: MIT
+
 prefix=@prefix@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
diff --git a/src/bindings/Makefile.am b/src/bindings/Makefile.am
deleted file mode 100644 (file)
index d8ee190..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-if ENABLE_PYTHON_BINDINGS
-SUBDIRS = python
-endif
diff --git a/src/bindings/python/Makefile.am b/src/bindings/python/Makefile.am
deleted file mode 100644 (file)
index cc6c233..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = bt2
index ab27d64ea9e342a0efade928c838cb11b01f9330..fc7a23f158c10b3cda2590be643d0def06d205a3 100644 (file)
@@ -1,3 +1,6 @@
+# SPDX-FileCopyrightText: 2019-2020 EfficiOS, Inc.
+# SPDX-License-Identifier: MIT
+
 bt2/native_bt.py
 bt2/native_bt.c
 bt2/native_bt.d
index c8e3eb44ae99e391fba19fccd46b1d7048a00a9a..77b3bec5d48af470a452216194ed07229db31b5d 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-FileCopyrightText: 2019-2023 EfficiOS, Inc.
 # SPDX-License-Identifier: MIT
 
 # Since the shared object used by the python bindings is not built with
@@ -94,11 +95,11 @@ STATIC_BINDINGS_DEPS =                                      \
 # Convenience static libraries on which the Python bindings library depends.
 # These are listed in the setup.py(.in) file.
 STATIC_LIBRARIES_DEPS = \
-       $(top_builddir)/src/autodisc/libbabeltrace2-autodisc.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/py-common/libbabeltrace2-py-common.la \
-       $(top_builddir)/src/string-format/libbabeltrace2-string-format.la
+       $(top_builddir)/src/autodisc/libautodisc.la \
+       $(top_builddir)/src/logging/liblogging.la \
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/py-common/libpy-common.la \
+       $(top_builddir)/src/string-format/libstring-format.la
 
 GENERATED_BINDINGS_DEPS =      \
        bt2/native_bt.c         \
@@ -119,6 +120,21 @@ WARN_CFLAGS += -Wno-null-dereference
 # code.
 WARN_CFLAGS += -Wno-deprecated-declarations
 
+# Python 3.11 + gcc 12.2.0 gave warnings of this kind in Python.h.
+WARN_CFLAGS += -Wno-redundant-decls
+
+# For SWIG generated code
+WARN_CFLAGS += -Wno-missing-field-initializers
+WARN_CFLAGS += -Wno-unused-parameter
+
+# SWIG 4.2.0 generates:
+#
+#   #if __cplusplus >=201103L
+#
+# ... leading to some `-Wundef` warnings when building the extension as C, where
+# `__cplusplus` is not defined.
+WARN_CFLAGS += -Wno-undef
+
 BUILD_FLAGS=CC="$(CC)" \
                CFLAGS="$(GLIB_CFLAGS) $(AM_CFLAGS) $(CFLAGS) $(WARN_CFLAGS)" \
                CPPFLAGS="$(AM_CPPFLAGS) $(CPPFLAGS) -I$(srcdir)/bt2" \
@@ -148,12 +164,25 @@ $(builddir)/bt2/native_bt.c: $(SWIG_INTERFACE_FILES)
 
 -include bt2/native_bt.d
 
+pyinstall_verbose = $(pyinstall_verbose_@AM_V@)
+pyinstall_verbose_ = $(pyinstall_verbose_@AM_DEFAULT_V@)
+pyinstall_verbose_0 = @
+
+# For Python < 3.12, force the use of distutils even if setuptools is
+# installed. For Python >= 3.12, set the externally managed option to allow
+# installation in a directory which isn't in the current PYTHONPATH.
+if HAVE_PYTHON_312_OR_GREATER
+PY_INSTALL_OPTS = --single-version-externally-managed
+else
+export SETUPTOOLS_USE_DISTUTILS=stdlib
+endif
+
 install-exec-local: build-python-bindings.stamp
-       @opts="--prefix=$(prefix) --record $(INSTALLED_FILES) --verbose --no-compile $(DISTSETUPOPTS)"; \
+       $(pyinstall_verbose)opts="--prefix=$(prefix) --exec-prefix=$(exec_prefix) --record $(INSTALLED_FILES) --verbose --no-compile $(DISTSETUPOPTS)"; \
        if [ "$(DESTDIR)" != "" ]; then \
                opts="$$opts --root=$(DESTDIR)"; \
        fi; \
-       $(PYTHON) $(builddir)/setup.py install $$opts;
+       $(PYTHON) $(builddir)/setup.py install $(PY_INSTALL_OPTS) $$opts;
 
 clean-local:
        rm -rf $(builddir)/build
index 38bb9b17dad280e7ead2a481ff204ae0a9dd6f02..2dd7dc36b69ae1cace2ad37fd62ab5d6ce5a07ab 100644 (file)
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
+import os
 import sys
 
+# With Python ≥ 3.8 on Windows, the DLL lookup mechanism to load native
+# modules doesn't search the `PATH` environment variable like everything
+# else on this platform.
+#
+# See <https://docs.python.org/3/whatsnew/3.8.html#bpo-36085-whatsnew>.
+#
+# Restore this behaviour by doing it manually.
+if os.name == "nt" and sys.version_info >= (3, 8):
+    for path in os.getenv("PATH", "").split(os.pathsep):
+        if os.path.exists(path) and path != ".":
+            os.add_dll_directory(path)
+
+del os
+
+
+from bt2.mip import get_maximal_mip_version, get_greatest_operative_mip_version
+from bt2.error import (
+    ComponentClassType,
+    _Error,
+    _ErrorCause,
+    _MemoryError,
+    _ComponentErrorCause,
+    _ComponentClassErrorCause,
+    _MessageIteratorErrorCause,
+)
+from bt2.field import (
+    _BoolField,
+    _RealField,
+    _ArrayField,
+    _OptionField,
+    _StringField,
+    _IntegerField,
+    _VariantField,
+    _BitArrayField,
+    _BoolFieldConst,
+    _RealFieldConst,
+    _StructureField,
+    _ArrayFieldConst,
+    _EnumerationField,
+    _OptionFieldConst,
+    _StaticArrayField,
+    _StringFieldConst,
+    _DynamicArrayField,
+    _IntegerFieldConst,
+    _VariantFieldConst,
+    _BitArrayFieldConst,
+    _SignedIntegerField,
+    _StructureFieldConst,
+    _UnsignedIntegerField,
+    _EnumerationFieldConst,
+    _StaticArrayFieldConst,
+    _DynamicArrayFieldConst,
+    _SignedEnumerationField,
+    _SignedIntegerFieldConst,
+    _DoublePrecisionRealField,
+    _SinglePrecisionRealField,
+    _UnsignedEnumerationField,
+    _UnsignedIntegerFieldConst,
+    _SignedEnumerationFieldConst,
+    _DoublePrecisionRealFieldConst,
+    _SinglePrecisionRealFieldConst,
+    _UnsignedEnumerationFieldConst,
+)
+from bt2.graph import Graph
+from bt2.utils import Stop, TryAgain, UnknownObject, _OverflowError
+from bt2.value import (
+    MapValue,
+    BoolValue,
+    RealValue,
+    ArrayValue,
+    StringValue,
+    SignedIntegerValue,
+    UnsignedIntegerValue,
+    create_value,
+    _IntegerValue,
+    _MapValueConst,
+    _BoolValueConst,
+    _RealValueConst,
+    _ArrayValueConst,
+    _StringValueConst,
+    _IntegerValueConst,
+    _SignedIntegerValueConst,
+    _UnsignedIntegerValueConst,
+)
+from bt2.plugin import find_plugin, find_plugins, find_plugins_in_path
+from bt2.logging import (
+    LoggingLevel,
+    get_global_logging_level,
+    set_global_logging_level,
+    get_minimal_logging_level,
+)
+from bt2.message import (
+    _EventMessage,
+    _PacketEndMessage,
+    _StreamEndMessage,
+    _EventMessageConst,
+    _PacketEndMessageConst,
+    _StreamEndMessageConst,
+    _DiscardedEventsMessage,
+    _PacketBeginningMessage,
+    _StreamBeginningMessage,
+    _DiscardedPacketsMessage,
+    _DiscardedEventsMessageConst,
+    _PacketBeginningMessageConst,
+    _StreamBeginningMessageConst,
+    _DiscardedPacketsMessageConst,
+    _MessageIteratorInactivityMessage,
+    _MessageIteratorInactivityMessageConst,
+)
+from bt2.version import __version__
+from bt2.component import (
+    _UserSinkComponent,
+    _SinkComponentConst,
+    _IncompleteUserClass,
+    _UserFilterComponent,
+    _UserSourceComponent,
+    _FilterComponentConst,
+    _SourceComponentConst,
+    _SinkComponentClassConst,
+    _FilterComponentClassConst,
+    _SourceComponentClassConst,
+)
+from bt2.py_plugin import register_plugin, plugin_component_class
+from bt2.field_path import (
+    FieldPathScope,
+    _IndexFieldPathItem,
+    _CurrentArrayElementFieldPathItem,
+    _CurrentOptionContentFieldPathItem,
+)
+
 # import all public names
 from bt2.clock_class import ClockClassOffset
-from bt2.clock_snapshot import _ClockSnapshotConst
-from bt2.clock_snapshot import _UnknownClockSnapshot
-from bt2.component import _SourceComponentClassConst
-from bt2.component import _FilterComponentClassConst
-from bt2.component import _SinkComponentClassConst
-from bt2.component import _SourceComponentConst
-from bt2.component import _FilterComponentConst
-from bt2.component import _SinkComponentConst
-from bt2.component import _UserSourceComponent
-from bt2.component import _UserFilterComponent
-from bt2.component import _UserSinkComponent
-from bt2.component_descriptor import ComponentDescriptor
-from bt2.error import ComponentClassType
-from bt2.error import _ErrorCause
-from bt2.error import _ComponentErrorCause
-from bt2.error import _ComponentClassErrorCause
-from bt2.error import _MessageIteratorErrorCause
-from bt2.error import _Error
 from bt2.event_class import EventClassLogLevel
-from bt2.field import _BoolField
-from bt2.field import _BitArrayField
-from bt2.field import _IntegerField
-from bt2.field import _UnsignedIntegerField
-from bt2.field import _SignedIntegerField
-from bt2.field import _RealField
-from bt2.field import _SinglePrecisionRealField
-from bt2.field import _DoublePrecisionRealField
-from bt2.field import _EnumerationField
-from bt2.field import _UnsignedEnumerationField
-from bt2.field import _SignedEnumerationField
-from bt2.field import _StringField
-from bt2.field import _StructureField
-from bt2.field import _OptionField
-from bt2.field import _VariantField
-from bt2.field import _ArrayField
-from bt2.field import _StaticArrayField
-from bt2.field import _DynamicArrayField
-from bt2.field import _BoolFieldConst
-from bt2.field import _BitArrayFieldConst
-from bt2.field import _IntegerFieldConst
-from bt2.field import _UnsignedIntegerFieldConst
-from bt2.field import _SignedIntegerFieldConst
-from bt2.field import _RealFieldConst
-from bt2.field import _SinglePrecisionRealFieldConst
-from bt2.field import _DoublePrecisionRealFieldConst
-from bt2.field import _EnumerationFieldConst
-from bt2.field import _UnsignedEnumerationFieldConst
-from bt2.field import _SignedEnumerationFieldConst
-from bt2.field import _StringFieldConst
-from bt2.field import _StructureFieldConst
-from bt2.field import _OptionFieldConst
-from bt2.field import _VariantFieldConst
-from bt2.field import _ArrayFieldConst
-from bt2.field import _StaticArrayFieldConst
-from bt2.field import _DynamicArrayFieldConst
-from bt2.field_class import IntegerDisplayBase
-from bt2.field_class import _BoolFieldClass
-from bt2.field_class import _BitArrayFieldClass
-from bt2.field_class import _IntegerFieldClass
-from bt2.field_class import _UnsignedIntegerFieldClass
-from bt2.field_class import _SignedIntegerFieldClass
-from bt2.field_class import _RealFieldClass
-from bt2.field_class import _EnumerationFieldClass
-from bt2.field_class import _UnsignedEnumerationFieldClass
-from bt2.field_class import _SignedEnumerationFieldClass
-from bt2.field_class import _StringFieldClass
-from bt2.field_class import _StructureFieldClass
-from bt2.field_class import _OptionFieldClass
-from bt2.field_class import _OptionWithSelectorFieldClass
-from bt2.field_class import _OptionWithBoolSelectorFieldClass
-from bt2.field_class import _OptionWithIntegerSelectorFieldClass
-from bt2.field_class import _OptionWithUnsignedIntegerSelectorFieldClass
-from bt2.field_class import _OptionWithSignedIntegerSelectorFieldClass
-from bt2.field_class import _VariantFieldClass
-from bt2.field_class import _VariantFieldClassWithoutSelector
-from bt2.field_class import _VariantFieldClassWithIntegerSelector
-from bt2.field_class import _VariantFieldClassWithUnsignedIntegerSelector
-from bt2.field_class import _VariantFieldClassWithSignedIntegerSelector
-from bt2.field_class import _ArrayFieldClass
-from bt2.field_class import _StaticArrayFieldClass
-from bt2.field_class import _DynamicArrayFieldClass
-from bt2.field_class import _DynamicArrayWithLengthFieldFieldClass
-from bt2.field_class import _BoolFieldClassConst
-from bt2.field_class import _BitArrayFieldClassConst
-from bt2.field_class import _IntegerFieldClassConst
-from bt2.field_class import _UnsignedIntegerFieldClassConst
-from bt2.field_class import _SignedIntegerFieldClassConst
-from bt2.field_class import _RealFieldClassConst
-from bt2.field_class import _EnumerationFieldClassConst
-from bt2.field_class import _UnsignedEnumerationFieldClassConst
-from bt2.field_class import _SignedEnumerationFieldClassConst
-from bt2.field_class import _StringFieldClassConst
-from bt2.field_class import _StructureFieldClassConst
-from bt2.field_class import _OptionFieldClassConst
-from bt2.field_class import _OptionWithSelectorFieldClassConst
-from bt2.field_class import _OptionWithBoolSelectorFieldClassConst
-from bt2.field_class import _OptionWithIntegerSelectorFieldClassConst
-from bt2.field_class import _OptionWithUnsignedIntegerSelectorFieldClassConst
-from bt2.field_class import _OptionWithSignedIntegerSelectorFieldClassConst
-from bt2.field_class import _VariantFieldClassConst
-from bt2.field_class import _VariantFieldClassWithoutSelectorConst
-from bt2.field_class import _VariantFieldClassWithIntegerSelectorConst
-from bt2.field_class import _VariantFieldClassWithUnsignedIntegerSelectorConst
-from bt2.field_class import _VariantFieldClassWithSignedIntegerSelectorConst
-from bt2.field_class import _ArrayFieldClassConst
-from bt2.field_class import _StaticArrayFieldClassConst
-from bt2.field_class import _DynamicArrayFieldClassConst
-from bt2.field_class import _DynamicArrayWithLengthFieldFieldClassConst
-from bt2.field_path import FieldPathScope
-from bt2.field_path import _IndexFieldPathItem
-from bt2.field_path import _CurrentArrayElementFieldPathItem
-from bt2.field_path import _CurrentOptionContentFieldPathItem
-from bt2.graph import Graph
-from bt2.integer_range_set import SignedIntegerRange
-from bt2.integer_range_set import UnsignedIntegerRange
-from bt2.integer_range_set import SignedIntegerRangeSet
-from bt2.integer_range_set import UnsignedIntegerRangeSet
-from bt2.integer_range_set import _SignedIntegerRangeConst
-from bt2.integer_range_set import _UnsignedIntegerRangeConst
-from bt2.integer_range_set import _SignedIntegerRangeSetConst
-from bt2.integer_range_set import _UnsignedIntegerRangeSetConst
+from bt2.field_class import (
+    IntegerDisplayBase,
+    _BoolFieldClass,
+    _RealFieldClass,
+    _ArrayFieldClass,
+    _OptionFieldClass,
+    _StringFieldClass,
+    _IntegerFieldClass,
+    _VariantFieldClass,
+    _BitArrayFieldClass,
+    _BoolFieldClassConst,
+    _RealFieldClassConst,
+    _StructureFieldClass,
+    _ArrayFieldClassConst,
+    _EnumerationFieldClass,
+    _OptionFieldClassConst,
+    _StaticArrayFieldClass,
+    _StringFieldClassConst,
+    _DynamicArrayFieldClass,
+    _IntegerFieldClassConst,
+    _VariantFieldClassConst,
+    _BitArrayFieldClassConst,
+    _SignedIntegerFieldClass,
+    _StructureFieldClassConst,
+    _UnsignedIntegerFieldClass,
+    _EnumerationFieldClassConst,
+    _StaticArrayFieldClassConst,
+    _DynamicArrayFieldClassConst,
+    _SignedEnumerationFieldClass,
+    _OptionWithSelectorFieldClass,
+    _SignedIntegerFieldClassConst,
+    _UnsignedEnumerationFieldClass,
+    _UnsignedIntegerFieldClassConst,
+    _OptionWithBoolSelectorFieldClass,
+    _SignedEnumerationFieldClassConst,
+    _VariantFieldClassWithoutSelector,
+    _OptionWithSelectorFieldClassConst,
+    _UnsignedEnumerationFieldClassConst,
+    _OptionWithIntegerSelectorFieldClass,
+    _VariantFieldClassWithIntegerSelector,
+    _DynamicArrayWithLengthFieldFieldClass,
+    _OptionWithBoolSelectorFieldClassConst,
+    _VariantFieldClassWithoutSelectorConst,
+    _OptionWithIntegerSelectorFieldClassConst,
+    _OptionWithSignedIntegerSelectorFieldClass,
+    _VariantFieldClassWithIntegerSelectorConst,
+    _DynamicArrayWithLengthFieldFieldClassConst,
+    _VariantFieldClassWithSignedIntegerSelector,
+    _OptionWithUnsignedIntegerSelectorFieldClass,
+    _VariantFieldClassWithUnsignedIntegerSelector,
+    _OptionWithSignedIntegerSelectorFieldClassConst,
+    _VariantFieldClassWithSignedIntegerSelectorConst,
+    _OptionWithUnsignedIntegerSelectorFieldClassConst,
+    _VariantFieldClassWithUnsignedIntegerSelectorConst,
+)
 from bt2.interrupter import Interrupter
-from bt2.logging import LoggingLevel
-from bt2.logging import get_minimal_logging_level
-from bt2.logging import get_global_logging_level
-from bt2.logging import set_global_logging_level
-from bt2.message import _EventMessage
-from bt2.message import _PacketBeginningMessage
-from bt2.message import _PacketEndMessage
-from bt2.message import _StreamBeginningMessage
-from bt2.message import _StreamEndMessage
-from bt2.message import _MessageIteratorInactivityMessage
-from bt2.message import _DiscardedEventsMessage
-from bt2.message import _DiscardedPacketsMessage
-from bt2.message import _EventMessageConst
-from bt2.message import _PacketBeginningMessageConst
-from bt2.message import _PacketEndMessageConst
-from bt2.message import _StreamBeginningMessageConst
-from bt2.message import _StreamEndMessageConst
-from bt2.message import _MessageIteratorInactivityMessageConst
-from bt2.message import _DiscardedEventsMessageConst
-from bt2.message import _DiscardedPacketsMessageConst
-from bt2.message_iterator import _UserMessageIterator
-from bt2.mip import get_greatest_operative_mip_version
-from bt2.mip import get_maximal_mip_version
-from bt2.plugin import find_plugins_in_path
-from bt2.plugin import find_plugins
-from bt2.plugin import find_plugin
-from bt2.py_plugin import plugin_component_class
-from bt2.py_plugin import register_plugin
+from bt2.clock_snapshot import _ClockSnapshotConst, _UnknownClockSnapshot
 from bt2.query_executor import QueryExecutor
-from bt2.trace_collection_message_iterator import AutoSourceComponentSpec
-from bt2.trace_collection_message_iterator import ComponentSpec
-from bt2.trace_collection_message_iterator import TraceCollectionMessageIterator
-from bt2.value import create_value
-from bt2.value import BoolValue
-from bt2.value import _IntegerValue
-from bt2.value import UnsignedIntegerValue
-from bt2.value import SignedIntegerValue
-from bt2.value import RealValue
-from bt2.value import StringValue
-from bt2.value import ArrayValue
-from bt2.value import MapValue
-from bt2.value import _BoolValueConst
-from bt2.value import _IntegerValueConst
-from bt2.value import _UnsignedIntegerValueConst
-from bt2.value import _SignedIntegerValueConst
-from bt2.value import _RealValueConst
-from bt2.value import _StringValueConst
-from bt2.value import _ArrayValueConst
-from bt2.value import _MapValueConst
-from bt2.version import __version__
+from bt2.message_iterator import _UserMessageIterator
+from bt2.integer_range_set import (
+    SignedIntegerRange,
+    UnsignedIntegerRange,
+    SignedIntegerRangeSet,
+    UnsignedIntegerRangeSet,
+    _SignedIntegerRangeConst,
+    _UnsignedIntegerRangeConst,
+    _SignedIntegerRangeSetConst,
+    _UnsignedIntegerRangeSetConst,
+)
+from bt2.component_descriptor import ComponentDescriptor
+from bt2.trace_collection_message_iterator import (
+    ComponentSpec,
+    AutoSourceComponentSpec,
+    TraceCollectionMessageIterator,
+)
 
 if (sys.version_info.major, sys.version_info.minor) != (3, 4):
 
@@ -227,39 +262,11 @@ if (sys.version_info.major, sys.version_info.minor) != (3, 4):
 del sys
 
 
-class _MemoryError(_Error):
-    """Raised when an operation fails due to memory issues."""
-
-
-class UnknownObject(Exception):
-    """
-    Raised when a component class handles a query for an object it doesn't
-    know about.
-    """
-
-    pass
-
-
-class _OverflowError(_Error, OverflowError):
-    pass
-
-
-class TryAgain(Exception):
-    pass
-
-
-class Stop(StopIteration):
-    pass
-
-
-class _IncompleteUserClass(Exception):
-    pass
-
-
 def _init_and_register_exit():
-    from bt2 import native_bt
     import atexit
 
+    from bt2 import native_bt
+
     atexit.register(native_bt.bt2_exit_handler)
     native_bt.bt2_init_from_bt2()
 
index 4cdea396be2b77939470090b2308fa5d7c47568c..c7527c7722d91bc447b70807df3d24c870484270 100644 (file)
@@ -2,15 +2,18 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import value as bt2_value
 import uuid as uuidp
 
+from bt2 import utils as bt2_utils
+from bt2 import value as bt2_value
+from bt2 import object as bt2_object
+from bt2 import native_bt
+
 
 class ClockClassOffset:
     def __init__(self, seconds=0, cycles=0):
-        utils._check_int64(seconds)
-        utils._check_int64(cycles)
+        bt2_utils._check_int64(seconds)
+        bt2_utils._check_int64(cycles)
         self._seconds = seconds
         self._cycles = cycles
 
@@ -30,9 +33,15 @@ class ClockClassOffset:
         return (self.seconds, self.cycles) == (other.seconds, other.cycles)
 
 
-class _ClockClassConst(object._SharedObject):
-    _get_ref = staticmethod(native_bt.clock_class_get_ref)
-    _put_ref = staticmethod(native_bt.clock_class_put_ref)
+class _ClockClassConst(bt2_object._SharedObject):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.clock_class_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.clock_class_put_ref(ptr)
+
     _create_value_from_ptr_and_get_ref = staticmethod(
         bt2_value._create_from_const_ptr_and_get_ref
     )
@@ -82,10 +91,10 @@ class _ClockClassConst(object._SharedObject):
         return uuidp.UUID(bytes=uuid_bytes)
 
     def cycles_to_ns_from_origin(self, cycles):
-        utils._check_uint64(cycles)
+        bt2_utils._check_uint64(cycles)
         status, ns = native_bt.clock_class_cycles_to_ns_from_origin(self._ptr, cycles)
         error_msg = "cannot convert clock value to nanoseconds from origin for given clock class"
-        utils._handle_func_status(status, error_msg)
+        bt2_utils._handle_func_status(status, error_msg)
         return ns
 
 
@@ -99,45 +108,47 @@ class _ClockClass(_ClockClassConst):
 
     def _user_attributes(self, user_attributes):
         value = bt2_value.create_value(user_attributes)
-        utils._check_type(value, bt2_value.MapValue)
+        bt2_utils._check_type(value, bt2_value.MapValue)
         native_bt.clock_class_set_user_attributes(self._ptr, value._ptr)
 
     _user_attributes = property(fset=_user_attributes)
 
     def _name(self, name):
-        utils._check_str(name)
+        bt2_utils._check_str(name)
         status = native_bt.clock_class_set_name(self._ptr, name)
-        utils._handle_func_status(status, "cannot set clock class object's name")
+        bt2_utils._handle_func_status(status, "cannot set clock class object's name")
 
     _name = property(fset=_name)
 
     def _description(self, description):
-        utils._check_str(description)
+        bt2_utils._check_str(description)
         status = native_bt.clock_class_set_description(self._ptr, description)
-        utils._handle_func_status(status, "cannot set clock class object's description")
+        bt2_utils._handle_func_status(
+            status, "cannot set clock class object's description"
+        )
 
     _description = property(fset=_description)
 
     def _frequency(self, frequency):
-        utils._check_uint64(frequency)
+        bt2_utils._check_uint64(frequency)
         native_bt.clock_class_set_frequency(self._ptr, frequency)
 
     _frequency = property(fset=_frequency)
 
     def _precision(self, precision):
-        utils._check_uint64(precision)
+        bt2_utils._check_uint64(precision)
         native_bt.clock_class_set_precision(self._ptr, precision)
 
     _precision = property(fset=_precision)
 
     def _offset(self, offset):
-        utils._check_type(offset, ClockClassOffset)
+        bt2_utils._check_type(offset, ClockClassOffset)
         native_bt.clock_class_set_offset(self._ptr, offset.seconds, offset.cycles)
 
     _offset = property(fset=_offset)
 
     def _origin_is_unix_epoch(self, origin_is_unix_epoch):
-        utils._check_bool(origin_is_unix_epoch)
+        bt2_utils._check_bool(origin_is_unix_epoch)
         native_bt.clock_class_set_origin_is_unix_epoch(
             self._ptr, int(origin_is_unix_epoch)
         )
@@ -145,7 +156,7 @@ class _ClockClass(_ClockClassConst):
     _origin_is_unix_epoch = property(fset=_origin_is_unix_epoch)
 
     def _uuid(self, uuid):
-        utils._check_type(uuid, uuidp.UUID)
+        bt2_utils._check_type(uuid, uuidp.UUID)
         native_bt.clock_class_set_uuid(self._ptr, uuid.bytes)
 
     _uuid = property(fset=_uuid)
index 6c2e3285e04ce13679dbcd817982a4384531a291..82591463de7fb99373f77b9bcd7fd7c8bc3f01e1 100644 (file)
@@ -2,14 +2,17 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
 import numbers
-from bt2 import clock_class as bt2_clock_class
 import functools
 
+from bt2 import utils as bt2_utils
+from bt2 import object as bt2_object
+from bt2 import native_bt
+from bt2 import clock_class as bt2_clock_class
+
 
 @functools.total_ordering
-class _ClockSnapshotConst(object._UniqueObject):
+class _ClockSnapshotConst(bt2_object._UniqueObject):
     @property
     def clock_class(self):
         cc_ptr = native_bt.clock_snapshot_borrow_clock_class_const(self._ptr)
@@ -23,7 +26,7 @@ class _ClockSnapshotConst(object._UniqueObject):
     @property
     def ns_from_origin(self):
         status, ns = native_bt.clock_snapshot_get_ns_from_origin(self._ptr)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot get clock snapshot's nanoseconds from origin"
         )
         return ns
index 7ae6fe25cee9124a017fc72485009d1a87e38254..30f0849c7c4d4ce066eb25cd60f2c651a44678ea 100644 (file)
@@ -2,16 +2,24 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import message_iterator as bt2_message_iterator
+import sys
 import collections.abc
+
+from bt2 import port as bt2_port
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
 from bt2 import value as bt2_value
-from bt2 import trace_class as bt2_trace_class
+from bt2 import object as bt2_object
+from bt2 import native_bt
 from bt2 import clock_class as bt2_clock_class
+from bt2 import trace_class as bt2_trace_class
 from bt2 import query_executor as bt2_query_executor
-from bt2 import port as bt2_port
-import sys
-import bt2
+from bt2 import message_iterator as bt2_message_iterator
+from bt2 import integer_range_set as bt2_integer_range_set
+
+
+class _IncompleteUserClass(Exception):
+    pass
 
 
 # This class wraps a component class pointer. This component class could
@@ -25,7 +33,7 @@ import bt2
 #     pointer to a 'bt_component_class *'.
 
 
-class _ComponentClassConst(object._SharedObject):
+class _ComponentClassConst(bt2_object._SharedObject):
     @property
     def name(self):
         ptr = self._bt_as_component_class_ptr(self._ptr)
@@ -58,24 +66,42 @@ class _ComponentClassConst(object._SharedObject):
 
 
 class _SourceComponentClassConst(_ComponentClassConst):
-    _get_ref = staticmethod(native_bt.component_class_source_get_ref)
-    _put_ref = staticmethod(native_bt.component_class_source_put_ref)
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.component_class_source_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.component_class_source_put_ref(ptr)
+
     _bt_as_component_class_ptr = staticmethod(
         native_bt.component_class_source_as_component_class
     )
 
 
 class _FilterComponentClassConst(_ComponentClassConst):
-    _get_ref = staticmethod(native_bt.component_class_filter_get_ref)
-    _put_ref = staticmethod(native_bt.component_class_filter_put_ref)
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.component_class_filter_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.component_class_filter_put_ref(ptr)
+
     _bt_as_component_class_ptr = staticmethod(
         native_bt.component_class_filter_as_component_class
     )
 
 
 class _SinkComponentClassConst(_ComponentClassConst):
-    _get_ref = staticmethod(native_bt.component_class_sink_get_ref)
-    _put_ref = staticmethod(native_bt.component_class_sink_put_ref)
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.component_class_sink_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.component_class_sink_put_ref(ptr)
+
     _bt_as_component_class_ptr = staticmethod(
         native_bt.component_class_sink_as_component_class
     )
@@ -104,7 +130,6 @@ class _PortIterator(collections.abc.Iterator):
 
 
 class _ComponentPorts(collections.abc.Mapping):
-
     # component_ptr is a bt_component_source *, bt_component_filter * or
     # bt_component_sink *.  Its type must match the type expected by the
     # functions passed as arguments.
@@ -124,7 +149,7 @@ class _ComponentPorts(collections.abc.Mapping):
         self._port_pycls = port_pycls
 
     def __getitem__(self, key):
-        utils._check_str(key)
+        bt2_utils._check_str(key)
         port_ptr = self._borrow_port_ptr_by_name(self._component_ptr, key)
 
         if port_ptr is None:
@@ -218,9 +243,14 @@ class _SinkComponentConst(_ComponentConst):
 
 # This is analogous to _SourceComponentClassConst, but for source
 # component objects.
-class _GenericSourceComponentConst(object._SharedObject, _SourceComponentConst):
-    _get_ref = staticmethod(native_bt.component_source_get_ref)
-    _put_ref = staticmethod(native_bt.component_source_put_ref)
+class _GenericSourceComponentConst(bt2_object._SharedObject, _SourceComponentConst):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.component_source_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.component_source_put_ref(ptr)
 
     @property
     def output_ports(self):
@@ -235,9 +265,14 @@ class _GenericSourceComponentConst(object._SharedObject, _SourceComponentConst):
 
 # This is analogous to _FilterComponentClassConst, but for filter
 # component objects.
-class _GenericFilterComponentConst(object._SharedObject, _FilterComponentConst):
-    _get_ref = staticmethod(native_bt.component_filter_get_ref)
-    _put_ref = staticmethod(native_bt.component_filter_put_ref)
+class _GenericFilterComponentConst(bt2_object._SharedObject, _FilterComponentConst):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.component_filter_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.component_filter_put_ref(ptr)
 
     @property
     def output_ports(self):
@@ -262,9 +297,14 @@ class _GenericFilterComponentConst(object._SharedObject, _FilterComponentConst):
 
 # This is analogous to _SinkComponentClassConst, but for sink
 # component objects.
-class _GenericSinkComponentConst(object._SharedObject, _SinkComponentConst):
-    _get_ref = staticmethod(native_bt.component_sink_get_ref)
-    _put_ref = staticmethod(native_bt.component_sink_put_ref)
+class _GenericSinkComponentConst(bt2_object._SharedObject, _SinkComponentConst):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.component_sink_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.component_sink_put_ref(ptr)
 
     @property
     def input_ports(self):
@@ -448,12 +488,12 @@ class _UserComponentType(type):
             return
 
         comp_cls_name = kwargs.get("name", class_name)
-        utils._check_str(comp_cls_name)
+        bt2_utils._check_str(comp_cls_name)
         comp_cls_descr = None
         comp_cls_help = None
 
         if hasattr(cls, "__doc__") and cls.__doc__ is not None:
-            utils._check_str(cls.__doc__)
+            bt2_utils._check_str(cls.__doc__)
             docstring = _trim_docstring(cls.__doc__)
             lines = docstring.splitlines()
 
@@ -477,7 +517,7 @@ class _UserComponentType(type):
             )
         elif _UserSinkComponent in bases:
             if not hasattr(cls, "_user_consume"):
-                raise bt2._IncompleteUserClass(
+                raise _IncompleteUserClass(
                     "cannot create component class '{}': missing a _user_consume() method".format(
                         class_name
                     )
@@ -487,14 +527,14 @@ class _UserComponentType(type):
                 cls, comp_cls_name, comp_cls_descr, comp_cls_help
             )
         else:
-            raise bt2._IncompleteUserClass(
+            raise _IncompleteUserClass(
                 "cannot find a known component class base in the bases of '{}'".format(
                     class_name
                 )
             )
 
         if cc_ptr is None:
-            raise bt2._MemoryError(
+            raise bt2_error._MemoryError(
                 "cannot create component class '{}'".format(class_name)
             )
 
@@ -527,21 +567,21 @@ class _UserComponentType(type):
     @staticmethod
     def _bt_set_iterator_class(cls, iter_cls):
         if iter_cls is None:
-            raise bt2._IncompleteUserClass(
+            raise _IncompleteUserClass(
                 "cannot create component class '{}': missing message iterator class".format(
                     cls.__name__
                 )
             )
 
         if not issubclass(iter_cls, bt2_message_iterator._UserMessageIterator):
-            raise bt2._IncompleteUserClass(
+            raise _IncompleteUserClass(
                 "cannot create component class '{}': message iterator class does not inherit bt2._UserMessageIterator".format(
                     cls.__name__
                 )
             )
 
         if not hasattr(iter_cls, "__next__"):
-            raise bt2._IncompleteUserClass(
+            raise _IncompleteUserClass(
                 "cannot create component class '{}': message iterator class is missing a __next__() method".format(
                     cls.__name__
                 )
@@ -550,7 +590,7 @@ class _UserComponentType(type):
         if hasattr(iter_cls, "_user_can_seek_ns_from_origin") and not hasattr(
             iter_cls, "_user_seek_ns_from_origin"
         ):
-            raise bt2._IncompleteUserClass(
+            raise _IncompleteUserClass(
                 "cannot create component class '{}': message iterator class implements _user_can_seek_ns_from_origin but not _user_seek_ns_from_origin".format(
                     cls.__name__
                 )
@@ -559,7 +599,7 @@ class _UserComponentType(type):
         if hasattr(iter_cls, "_user_can_seek_beginning") and not hasattr(
             iter_cls, "_user_seek_beginning"
         ):
-            raise bt2._IncompleteUserClass(
+            raise _IncompleteUserClass(
                 "cannot create component class '{}': message iterator class implements _user_can_seek_beginning but not _user_seek_beginning".format(
                     cls.__name__
                 )
@@ -596,9 +636,9 @@ class _UserComponentType(type):
         # this can raise, but the native side checks the exception
         range_set = cls._user_get_supported_mip_versions(params, obj, log_level)
 
-        if type(range_set) is not bt2.UnsignedIntegerRangeSet:
+        if type(range_set) is not bt2_integer_range_set.UnsignedIntegerRangeSet:
             # this can raise, but the native side checks the exception
-            range_set = bt2.UnsignedIntegerRangeSet(range_set)
+            range_set = bt2_integer_range_set.UnsignedIntegerRangeSet(range_set)
 
         # return new reference
         range_set._get_ref(range_set._ptr)
@@ -631,7 +671,7 @@ class _UserComponentType(type):
             priv_query_exec._invalidate()
 
         # this can raise, but the native side checks the exception
-        results = bt2.create_value(results)
+        results = bt2_value.create_value(results)
 
         if results is None:
             results_ptr = native_bt.value_null
@@ -643,7 +683,7 @@ class _UserComponentType(type):
         return int(results_ptr)
 
     def _user_query(cls, priv_query_executor, object_name, params, method_obj):
-        raise bt2.UnknownObject
+        raise bt2_utils.UnknownObject
 
     def _bt_component_class_ptr(self):
         return self._bt_as_component_class_ptr(self._bt_cc_ptr)
@@ -753,7 +793,7 @@ class _UserComponent(metaclass=_UserComponentType):
         tc_ptr = native_bt.trace_class_create(ptr)
 
         if tc_ptr is None:
-            raise bt2._MemoryError("could not create trace class")
+            raise bt2_error._MemoryError("could not create trace class")
 
         tc = bt2_trace_class._TraceClass._create_from_ptr(tc_ptr)
         tc._assigns_automatic_stream_class_id = assigns_automatic_stream_class_id
@@ -778,7 +818,7 @@ class _UserComponent(metaclass=_UserComponentType):
         cc_ptr = native_bt.clock_class_create(ptr)
 
         if cc_ptr is None:
-            raise bt2._MemoryError("could not create clock class")
+            raise bt2_error._MemoryError("could not create clock class")
 
         cc = bt2_clock_class._ClockClass._create_from_ptr(cc_ptr)
 
@@ -832,7 +872,7 @@ class _UserSourceComponent(_UserComponent, _SourceComponentConst):
         )
 
     def _add_output_port(self, name, user_data=None):
-        utils._check_str(name)
+        bt2_utils._check_str(name)
 
         if name in self._output_ports:
             raise ValueError(
@@ -843,7 +883,7 @@ class _UserSourceComponent(_UserComponent, _SourceComponentConst):
 
         fn = native_bt.self_component_source_add_output_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             comp_status, "cannot add output port to source component object"
         )
         assert self_port_ptr is not None
@@ -890,7 +930,7 @@ class _UserFilterComponent(_UserComponent, _FilterComponentConst):
         )
 
     def _add_output_port(self, name, user_data=None):
-        utils._check_str(name)
+        bt2_utils._check_str(name)
 
         if name in self._output_ports:
             raise ValueError(
@@ -901,7 +941,7 @@ class _UserFilterComponent(_UserComponent, _FilterComponentConst):
 
         fn = native_bt.self_component_filter_add_output_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             comp_status, "cannot add output port to filter component object"
         )
         assert self_port_ptr
@@ -910,7 +950,7 @@ class _UserFilterComponent(_UserComponent, _FilterComponentConst):
         )
 
     def _add_input_port(self, name, user_data=None):
-        utils._check_str(name)
+        bt2_utils._check_str(name)
 
         if name in self._input_ports:
             raise ValueError(
@@ -921,7 +961,7 @@ class _UserFilterComponent(_UserComponent, _FilterComponentConst):
 
         fn = native_bt.self_component_filter_add_input_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             comp_status, "cannot add input port to filter component object"
         )
         assert self_port_ptr
@@ -960,7 +1000,7 @@ class _UserSinkComponent(_UserComponent, _SinkComponentConst):
         )
 
     def _add_input_port(self, name, user_data=None):
-        utils._check_str(name)
+        bt2_utils._check_str(name)
 
         if name in self._input_ports:
             raise ValueError(
@@ -971,7 +1011,7 @@ class _UserSinkComponent(_UserComponent, _SinkComponentConst):
 
         fn = native_bt.self_component_sink_add_input_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             comp_status, "cannot add input port to sink component object"
         )
         assert self_port_ptr
@@ -980,7 +1020,7 @@ class _UserSinkComponent(_UserComponent, _SinkComponentConst):
         )
 
     def _create_message_iterator(self, input_port):
-        utils._check_type(input_port, bt2_port._UserComponentInputPort)
+        bt2_utils._check_type(input_port, bt2_port._UserComponentInputPort)
 
         if not input_port.is_connected:
             raise ValueError("input port is not connected")
@@ -991,7 +1031,7 @@ class _UserSinkComponent(_UserComponent, _SinkComponentConst):
         ) = native_bt.bt2_message_iterator_create_from_sink_component(
             self._bt_ptr, input_port._ptr
         )
-        utils._handle_func_status(status, "cannot create message iterator object")
+        bt2_utils._handle_func_status(status, "cannot create message iterator object")
         assert msg_iter_ptr is not None
 
         return bt2_message_iterator._UserComponentInputPortMessageIterator(msg_iter_ptr)
index c09c98ef521eca5341cf971f6c2dd83ae0fe2051..35179ab7bcdab5f2724411471d5fa900236d1fff 100644 (file)
@@ -2,9 +2,9 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt
+from bt2 import value as bt2_value
 from bt2 import component as bt2_component
-import bt2
+from bt2 import native_bt
 
 
 def _is_source_comp_cls(comp_cls):
@@ -56,7 +56,7 @@ class ComponentDescriptor:
             raise ValueError("cannot pass a Python object to a non-Python component")
 
         self._comp_cls = component_class
-        self._params = bt2.create_value(params)
+        self._params = bt2_value.create_value(params)
         self._obj = obj
 
     @property
index 0f1fce65c8d5c2529b9bfd066b6fb494ce1dbdf6..cdbe271ec109e4f0a05f6f4db05ab6a45c3adf0f 100644 (file)
@@ -2,14 +2,19 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt
 from bt2 import port as bt2_port
 from bt2 import object as bt2_object
+from bt2 import native_bt
 
 
 class _ConnectionConst(bt2_object._SharedObject):
-    _get_ref = staticmethod(native_bt.connection_get_ref)
-    _put_ref = staticmethod(native_bt.connection_put_ref)
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.connection_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.connection_put_ref(ptr)
 
     @property
     def downstream_port(self):
index e07a3f9002d1cb8c9a5fc32660c70e294e3d51ae..b6d0e9e11edbbcf8eadd9e024f6ab8e1520dec67 100644 (file)
@@ -2,9 +2,10 @@
 #
 # Copyright (c) 2019 Simon Marchi <simon.marchi@efficios.com>
 
-from bt2 import native_bt
 from collections import abc
 
+from bt2 import native_bt
+
 
 class ComponentClassType:
     SOURCE = native_bt.COMPONENT_CLASS_TYPE_SOURCE
@@ -219,3 +220,7 @@ class _Error(Exception, abc.Sequence):
 
     def __str__(self):
         return self._str
+
+
+class _MemoryError(_Error):
+    """Raised when an operation fails due to memory issues."""
index 81215a2069a1d398f0c61932a8d1f3a3a9868241..3ff934fda65803a9fd871fc178451382ec8ea3e0 100644 (file)
@@ -2,15 +2,18 @@
 #
 # Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import event_class as bt2_event_class
+import collections.abc
+
+from bt2 import field as bt2_field
+from bt2 import utils as bt2_utils
+from bt2 import object as bt2_object
 from bt2 import packet as bt2_packet
 from bt2 import stream as bt2_stream
-from bt2 import field as bt2_field
-import collections.abc
+from bt2 import native_bt
+from bt2 import event_class as bt2_event_class
 
 
-class _EventConst(object._UniqueObject, collections.abc.Mapping):
+class _EventConst(bt2_object._UniqueObject, collections.abc.Mapping):
     _borrow_class_ptr = staticmethod(native_bt.event_borrow_class_const)
     _borrow_packet_ptr = staticmethod(native_bt.event_borrow_packet_const)
     _borrow_stream_ptr = staticmethod(native_bt.event_borrow_stream_const)
@@ -90,7 +93,7 @@ class _EventConst(object._UniqueObject, collections.abc.Mapping):
         )
 
     def __getitem__(self, key):
-        utils._check_str(key)
+        bt2_utils._check_str(key)
         payload_field = self.payload_field
 
         if payload_field is not None and key in payload_field:
index cbbd807cb4ab78941c6d854cc2709014c3b87eaf..54ca10a6a4bc93336f592ecaddcd4746620d2df8 100644 (file)
@@ -2,9 +2,11 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import field_class as bt2_field_class
+from bt2 import utils as bt2_utils
 from bt2 import value as bt2_value
+from bt2 import object as bt2_object
+from bt2 import native_bt
+from bt2 import field_class as bt2_field_class
 
 
 def _bt2_stream_class():
@@ -31,9 +33,15 @@ class EventClassLogLevel:
     DEBUG = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG
 
 
-class _EventClassConst(object._SharedObject):
-    _get_ref = staticmethod(native_bt.event_class_get_ref)
-    _put_ref = staticmethod(native_bt.event_class_put_ref)
+class _EventClassConst(bt2_object._SharedObject):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.event_class_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.event_class_put_ref(ptr)
+
     _borrow_stream_class_ptr = staticmethod(
         native_bt.event_class_borrow_stream_class_const
     )
@@ -145,7 +153,7 @@ class _EventClass(_EventClassConst):
 
     def _emf_uri(self, emf_uri):
         status = native_bt.event_class_set_emf_uri(self._ptr, emf_uri)
-        utils._handle_func_status(status, "cannot set event class object's EMF URI")
+        bt2_utils._handle_func_status(status, "cannot set event class object's EMF URI")
 
     _emf_uri = property(fset=_emf_uri)
 
@@ -153,7 +161,7 @@ class _EventClass(_EventClassConst):
         status = native_bt.event_class_set_specific_context_field_class(
             self._ptr, context_field_class._ptr
         )
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot set event class object's context field class"
         )
 
@@ -163,7 +171,7 @@ class _EventClass(_EventClassConst):
         status = native_bt.event_class_set_payload_field_class(
             self._ptr, payload_field_class._ptr
         )
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot set event class object's payload field class"
         )
 
@@ -179,11 +187,11 @@ class _EventClass(_EventClassConst):
         payload_field_class,
     ):
         if name is not None:
-            utils._check_str(name)
+            bt2_utils._check_str(name)
 
         if user_attributes is not None:
             value = bt2_value.create_value(user_attributes)
-            utils._check_type(value, bt2_value.MapValue)
+            bt2_utils._check_type(value, bt2_value.MapValue)
 
         if log_level is not None:
             log_levels = (
@@ -208,15 +216,17 @@ class _EventClass(_EventClassConst):
                 raise ValueError("'{}' is not a valid log level".format(log_level))
 
         if emf_uri is not None:
-            utils._check_str(emf_uri)
+            bt2_utils._check_str(emf_uri)
 
         if specific_context_field_class is not None:
-            utils._check_type(
+            bt2_utils._check_type(
                 specific_context_field_class, bt2_field_class._StructureFieldClass
             )
 
         if payload_field_class is not None:
-            utils._check_type(payload_field_class, bt2_field_class._StructureFieldClass)
+            bt2_utils._check_type(
+                payload_field_class, bt2_field_class._StructureFieldClass
+            )
 
 
 _EVENT_CLASS_LOG_LEVEL_TO_OBJ = {
index 40ce52c52adc7b47e0dd5e34f67ae525267508dc..b3bdcd54a3ad7984ec13c6bce8ccb52f813fc603 100644 (file)
@@ -2,18 +2,20 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import field_class as bt2_field_class
-import collections.abc
-import functools
-import numbers
 import math
+import numbers
+import functools
+import collections.abc
+
+from bt2 import utils as bt2_utils
+from bt2 import object as bt2_object
+from bt2 import native_bt
+from bt2 import field_class as bt2_field_class
 
 
 def _create_field_from_ptr_template(
     object_map, ptr, owner_ptr, owner_get_ref, owner_put_ref
 ):
-
     field_class_ptr = native_bt.field_borrow_class_const(ptr)
     typeid = native_bt.field_class_get_type(field_class_ptr)
     field = object_map[typeid]._create_from_ptr_and_get_ref(
@@ -50,7 +52,7 @@ def _get_leaf_field(field):
     return field
 
 
-class _FieldConst(object._UniqueObject):
+class _FieldConst(bt2_object._UniqueObject):
     _create_field_from_ptr = staticmethod(_create_field_from_const_ptr)
     _create_field_class_from_ptr_and_get_ref = staticmethod(
         bt2_field_class._create_field_class_from_const_ptr_and_get_ref
@@ -109,7 +111,7 @@ class _BitArrayField(_BitArrayFieldConst, _Field):
     _NAME = "Bit array"
 
     def _value_as_integer(self, value):
-        utils._check_uint64(value)
+        bt2_utils._check_uint64(value)
         native_bt.field_bit_array_set_value_as_integer(self._ptr, value)
 
     value_as_integer = property(
@@ -451,7 +453,7 @@ class _EnumerationFieldConst(_IntegerFieldConst):
     @property
     def labels(self):
         status, labels = self._get_mapping_labels(self._ptr)
-        utils._handle_func_status(status, "cannot get label for enumeration field")
+        bt2_utils._handle_func_status(status, "cannot get label for enumeration field")
 
         assert labels is not None
         return labels
@@ -547,7 +549,7 @@ class _StringField(_StringFieldConst, _Field):
     def __iadd__(self, value):
         value = self._value_to_str(value)
         status = native_bt.field_string_append(self._ptr, value)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot append to string field object's value"
         )
         return self
@@ -621,7 +623,7 @@ class _StructureFieldConst(_ContainerFieldConst, collections.abc.Mapping):
         return "{{{}}}".format(", ".join(items))
 
     def __getitem__(self, key):
-        utils._check_str(key)
+        bt2_utils._check_str(key)
         field_ptr = self._borrow_member_field_ptr_by_name(self._ptr, key)
 
         if field_ptr is None:
@@ -632,7 +634,7 @@ class _StructureFieldConst(_ContainerFieldConst, collections.abc.Mapping):
         )
 
     def member_at_index(self, index):
-        utils._check_uint64(index)
+        bt2_utils._check_uint64(index)
 
         if index >= len(self):
             raise IndexError
@@ -709,7 +711,7 @@ class _OptionField(_OptionFieldConst, _Field):
     _borrow_field_ptr = staticmethod(native_bt.field_option_borrow_field)
 
     def _has_field(self, value):
-        utils._check_bool(value)
+        bt2_utils._check_bool(value)
         native_bt.field_option_set_has_field(self._ptr, value)
 
     has_field = property(fget=_OptionFieldConst.has_field.fget, fset=_has_field)
@@ -887,9 +889,9 @@ class _DynamicArrayField(_DynamicArrayFieldConst, _ArrayField, _Field):
     _NAME = "Dynamic array"
 
     def _set_length(self, length):
-        utils._check_uint64(length)
+        bt2_utils._check_uint64(length)
         status = native_bt.field_array_dynamic_set_length(self._ptr, length)
-        utils._handle_func_status(status, "cannot set dynamic array length")
+        bt2_utils._handle_func_status(status, "cannot set dynamic array length")
 
     length = property(fget=_ArrayField._get_length, fset=_set_length)
 
index eed50140ea3edd878b7efc4205b1a9078263a0bb..8594b496d08609f1e4ea31d7a6bc0bb116572ebc 100644 (file)
@@ -2,12 +2,15 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
 import collections.abc
+
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
+from bt2 import value as bt2_value
+from bt2 import object as bt2_object
+from bt2 import native_bt
 from bt2 import field_path as bt2_field_path
 from bt2 import integer_range_set as bt2_integer_range_set
-from bt2 import value as bt2_value
-import bt2
 
 
 def _obj_type_from_field_class_ptr_template(type_map, ptr):
@@ -44,9 +47,15 @@ class IntegerDisplayBase:
     HEXADECIMAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL
 
 
-class _FieldClassConst(object._SharedObject):
-    _get_ref = staticmethod(native_bt.field_class_get_ref)
-    _put_ref = staticmethod(native_bt.field_class_put_ref)
+class _FieldClassConst(bt2_object._SharedObject):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.field_class_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.field_class_put_ref(ptr)
+
     _borrow_user_attributes_ptr = staticmethod(
         native_bt.field_class_borrow_user_attributes_const
     )
@@ -56,7 +65,7 @@ class _FieldClassConst(object._SharedObject):
 
     def _check_create_status(self, ptr):
         if ptr is None:
-            raise bt2._MemoryError(
+            raise bt2_error._MemoryError(
                 "cannot create {} field class object".format(self._NAME.lower())
             )
 
@@ -77,7 +86,7 @@ class _FieldClass(_FieldClassConst):
 
     def _user_attributes(self, user_attributes):
         value = bt2_value.create_value(user_attributes)
-        utils._check_type(value, bt2_value.MapValue)
+        bt2_utils._check_type(value, bt2_value.MapValue)
         native_bt.field_class_set_user_attributes(self._ptr, value._ptr)
 
     _user_attributes = property(fset=_user_attributes)
@@ -128,7 +137,7 @@ class _IntegerFieldClass(_FieldClass, _IntegerFieldClassConst):
     _field_value_range = property(fset=_field_value_range)
 
     def _preferred_display_base(self, base):
-        utils._check_uint64(base)
+        bt2_utils._check_uint64(base)
 
         if base not in (
             IntegerDisplayBase.BINARY,
@@ -239,7 +248,7 @@ class _EnumerationFieldClassConst(_IntegerFieldClassConst, collections.abc.Mappi
         self._check_int_type(value)
 
         status, labels = self._get_mapping_labels_for_value(self._ptr, value)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot get mapping labels for value {}".format(value)
         )
         return [self[label] for label in labels]
@@ -250,7 +259,7 @@ class _EnumerationFieldClassConst(_IntegerFieldClassConst, collections.abc.Mappi
             yield self._mapping_pycls(mapping_ptr).label
 
     def __getitem__(self, label):
-        utils._check_str(label)
+        bt2_utils._check_str(label)
         mapping_ptr = self._borrow_mapping_ptr_by_label(self._ptr, label)
 
         if mapping_ptr is None:
@@ -261,14 +270,14 @@ class _EnumerationFieldClassConst(_IntegerFieldClassConst, collections.abc.Mappi
 
 class _EnumerationFieldClass(_EnumerationFieldClassConst, _IntegerFieldClass):
     def add_mapping(self, label, ranges):
-        utils._check_str(label)
-        utils._check_type(ranges, self._range_set_pycls)
+        bt2_utils._check_str(label)
+        bt2_utils._check_type(ranges, self._range_set_pycls)
 
         if label in self:
             raise ValueError("duplicate mapping label '{}'".format(label))
 
         status = self._add_mapping(self._ptr, label, ranges._ptr)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot add mapping to enumeration field class object"
         )
 
@@ -293,7 +302,7 @@ class _UnsignedEnumerationFieldClassConst(
     _get_mapping_labels_for_value = staticmethod(
         native_bt.field_class_enumeration_unsigned_get_mapping_labels_for_value
     )
-    _check_int_type = staticmethod(utils._check_uint64)
+    _check_int_type = staticmethod(bt2_utils._check_uint64)
 
 
 class _UnsignedEnumerationFieldClass(
@@ -320,7 +329,7 @@ class _SignedEnumerationFieldClassConst(
     _get_mapping_labels_for_value = staticmethod(
         native_bt.field_class_enumeration_signed_get_mapping_labels_for_value
     )
-    _check_int_type = staticmethod(utils._check_int64)
+    _check_int_type = staticmethod(bt2_utils._check_int64)
 
 
 class _SignedEnumerationFieldClass(
@@ -394,7 +403,7 @@ class _StructureFieldClassMember(_StructureFieldClassMemberConst):
 
     def _user_attributes(self, user_attributes):
         value = bt2_value.create_value(user_attributes)
-        utils._check_type(value, bt2_value.MapValue)
+        bt2_utils._check_type(value, bt2_value.MapValue)
         native_bt.field_class_structure_member_set_user_attributes(
             self._ptr, value._ptr
         )
@@ -439,7 +448,7 @@ class _StructureFieldClassConst(_FieldClassConst, collections.abc.Mapping):
             yield native_bt.field_class_structure_member_get_name(member_ptr)
 
     def member_at_index(self, index):
-        utils._check_uint64(index)
+        bt2_utils._check_uint64(index)
 
         if index >= len(self):
             raise IndexError
@@ -460,8 +469,8 @@ class _StructureFieldClass(_StructureFieldClassConst, _FieldClass):
     _structure_member_field_class_pycls = property(lambda _: _StructureFieldClassMember)
 
     def append_member(self, name, field_class, user_attributes=None):
-        utils._check_str(name)
-        utils._check_type(field_class, _FieldClass)
+        bt2_utils._check_str(name)
+        bt2_utils._check_type(field_class, _FieldClass)
 
         if name in self:
             raise ValueError("duplicate member name '{}'".format(name))
@@ -470,12 +479,12 @@ class _StructureFieldClass(_StructureFieldClassConst, _FieldClass):
 
         if user_attributes is not None:
             # check now that user attributes are valid
-            user_attributes_value = bt2.create_value(user_attributes)
+            user_attributes_value = bt2_value.create_value(user_attributes)
 
         status = native_bt.field_class_structure_append_member(
             self._ptr, name, field_class._ptr
         )
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot append member to structure field class object"
         )
 
@@ -582,7 +591,7 @@ class _OptionWithBoolSelectorFieldClass(
     _NAME = "Option (with boolean selector)"
 
     def _selector_is_reversed(self, selector_is_reversed):
-        utils._check_bool(selector_is_reversed)
+        bt2_utils._check_bool(selector_is_reversed)
         native_bt.field_class_option_with_selector_field_bool_set_selector_is_reversed(
             self._ptr, selector_is_reversed
         )
@@ -665,7 +674,7 @@ class _VariantFieldClassOption(_VariantFieldClassOptionConst):
 
     def _user_attributes(self, user_attributes):
         value = bt2_value.create_value(user_attributes)
-        utils._check_type(value, bt2_value.MapValue)
+        bt2_utils._check_type(value, bt2_value.MapValue)
         native_bt.field_class_variant_option_set_user_attributes(self._ptr, value._ptr)
 
     _user_attributes = property(fset=_user_attributes)
@@ -770,7 +779,7 @@ class _VariantFieldClassConst(_FieldClassConst, collections.abc.Mapping):
             yield native_bt.field_class_variant_option_get_name(base_opt_ptr)
 
     def option_at_index(self, index):
-        utils._check_uint64(index)
+        bt2_utils._check_uint64(index)
 
         if index >= len(self):
             raise IndexError
@@ -801,8 +810,8 @@ class _VariantFieldClassWithoutSelector(
     _NAME = "Variant (without selector)"
 
     def append_option(self, name, field_class, user_attributes=None):
-        utils._check_str(name)
-        utils._check_type(field_class, _FieldClass)
+        bt2_utils._check_str(name)
+        bt2_utils._check_type(field_class, _FieldClass)
 
         if name in self:
             raise ValueError("duplicate option name '{}'".format(name))
@@ -811,12 +820,12 @@ class _VariantFieldClassWithoutSelector(
 
         if user_attributes is not None:
             # check now that user attributes are valid
-            user_attributes_value = bt2.create_value(user_attributes)
+            user_attributes_value = bt2_value.create_value(user_attributes)
 
         status = native_bt.field_class_variant_without_selector_append_option(
             self._ptr, name, field_class._ptr
         )
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot append option to variant field class object"
         )
 
@@ -851,9 +860,9 @@ class _VariantFieldClassWithIntegerSelector(
     _NAME = "Variant (with selector)"
 
     def append_option(self, name, field_class, ranges, user_attributes=None):
-        utils._check_str(name)
-        utils._check_type(field_class, _FieldClass)
-        utils._check_type(ranges, self._variant_option_pycls._range_set_pycls)
+        bt2_utils._check_str(name)
+        bt2_utils._check_type(field_class, _FieldClass)
+        bt2_utils._check_type(ranges, self._variant_option_pycls._range_set_pycls)
 
         if name in self:
             raise ValueError("duplicate option name '{}'".format(name))
@@ -865,12 +874,12 @@ class _VariantFieldClassWithIntegerSelector(
 
         if user_attributes is not None:
             # check now that user attributes are valid
-            user_attributes_value = bt2.create_value(user_attributes)
+            user_attributes_value = bt2_value.create_value(user_attributes)
 
         # TODO: check overlaps (precondition of self._append_option())
 
         status = self._append_option(self._ptr, name, field_class._ptr, ranges._ptr)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot append option to variant field class object"
         )
 
index a2d39629bf38ac7edc9a97b2e3f01fdfd9be3cc0..d64d22861e4b7524f98a184f834a6f0df21fb9dd 100644 (file)
@@ -3,7 +3,9 @@
 # Copyright (c) 2018 Francis Deslauriers <francis.deslauriers@efficios.com>
 
 import collections
-from bt2 import native_bt, object
+
+from bt2 import object as bt2_object
+from bt2 import native_bt
 
 
 class FieldPathScope:
@@ -34,9 +36,14 @@ class _CurrentOptionContentFieldPathItem(_FieldPathItem):
     pass
 
 
-class _FieldPathConst(object._SharedObject, collections.abc.Iterable):
-    _get_ref = staticmethod(native_bt.field_path_get_ref)
-    _put_ref = staticmethod(native_bt.field_path_put_ref)
+class _FieldPathConst(bt2_object._SharedObject, collections.abc.Iterable):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.field_path_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.field_path_put_ref(ptr)
 
     @property
     def root_scope(self):
index ed99ef079c924c1e046770ae33ee0bffd381159b..6bd4e1e7ee69ba8035344941b26420836ca153d8 100644 (file)
@@ -2,14 +2,19 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import interrupter as bt2_interrupter
-from bt2 import connection as bt2_connection
-from bt2 import component as bt2_component
 import functools
+
+from bt2 import mip as bt2_mip
 from bt2 import port as bt2_port
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
+from bt2 import value as bt2_value
+from bt2 import object as bt2_object
 from bt2 import logging as bt2_logging
-import bt2
+from bt2 import component as bt2_component
+from bt2 import native_bt
+from bt2 import connection as bt2_connection
+from bt2 import interrupter as bt2_interrupter
 
 
 def _graph_port_added_listener_from_native(
@@ -22,20 +27,25 @@ def _graph_port_added_listener_from_native(
     user_listener(component, port)
 
 
-class Graph(object._SharedObject):
-    _get_ref = staticmethod(native_bt.graph_get_ref)
-    _put_ref = staticmethod(native_bt.graph_put_ref)
+class Graph(bt2_object._SharedObject):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.graph_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.graph_put_ref(ptr)
 
     def __init__(self, mip_version=0):
-        utils._check_uint64(mip_version)
+        bt2_utils._check_uint64(mip_version)
 
-        if mip_version > bt2.get_maximal_mip_version():
+        if mip_version > bt2_mip.get_maximal_mip_version():
             raise ValueError("unknown MIP version {}".format(mip_version))
 
         ptr = native_bt.graph_create(mip_version)
 
         if ptr is None:
-            raise bt2._MemoryError("cannot create graph object")
+            raise bt2_error._MemoryError("cannot create graph object")
 
         super().__init__(ptr)
 
@@ -82,36 +92,38 @@ class Graph(object._SharedObject):
                 )
             )
 
-        utils._check_str(name)
-        utils._check_log_level(logging_level)
+        bt2_utils._check_str(name)
+        bt2_utils._check_log_level(logging_level)
         base_cc_ptr = component_class._bt_component_class_ptr()
 
         if obj is not None and not native_bt.bt2_is_python_component_class(base_cc_ptr):
             raise ValueError("cannot pass a Python object to a non-Python component")
 
-        if params is not None and not isinstance(params, (dict, bt2.MapValue)):
+        if params is not None and not isinstance(params, (dict, bt2_value.MapValue)):
             raise TypeError("'params' parameter is not a 'dict' or a 'bt2.MapValue'.")
 
-        params = bt2.create_value(params)
+        params = bt2_value.create_value(params)
 
         params_ptr = params._ptr if params is not None else None
 
         status, comp_ptr = add_fn(
             self._ptr, cc_ptr, name, params_ptr, obj, logging_level
         )
-        utils._handle_func_status(status, "cannot add component to graph")
+        bt2_utils._handle_func_status(status, "cannot add component to graph")
         assert comp_ptr
         return bt2_component._create_component_from_const_ptr_and_get_ref(
             comp_ptr, cc_type
         )
 
     def connect_ports(self, upstream_port, downstream_port):
-        utils._check_type(upstream_port, bt2_port._OutputPortConst)
-        utils._check_type(downstream_port, bt2_port._InputPortConst)
+        bt2_utils._check_type(upstream_port, bt2_port._OutputPortConst)
+        bt2_utils._check_type(downstream_port, bt2_port._InputPortConst)
         status, conn_ptr = native_bt.graph_connect_ports(
             self._ptr, upstream_port._ptr, downstream_port._ptr
         )
-        utils._handle_func_status(status, "cannot connect component ports within graph")
+        bt2_utils._handle_func_status(
+            status, "cannot connect component ports within graph"
+        )
         assert conn_ptr
         return bt2_connection._ConnectionConst._create_from_ptr_and_get_ref(conn_ptr)
 
@@ -126,21 +138,21 @@ class Graph(object._SharedObject):
 
         listener_ids = fn(self._ptr, listener_from_native)
         if listener_ids is None:
-            raise bt2._Error("cannot add listener to graph object")
+            raise bt2_error._Error("cannot add listener to graph object")
 
         # keep the partial's reference
         self._listener_partials.append(listener_from_native)
 
     def run_once(self):
         status = native_bt.graph_run_once(self._ptr)
-        utils._handle_func_status(status, "graph object could not run once")
+        bt2_utils._handle_func_status(status, "graph object could not run once")
 
     def run(self):
         status = native_bt.graph_run(self._ptr)
-        utils._handle_func_status(status, "graph object stopped running")
+        bt2_utils._handle_func_status(status, "graph object stopped running")
 
     def add_interrupter(self, interrupter):
-        utils._check_type(interrupter, bt2_interrupter.Interrupter)
+        bt2_utils._check_type(interrupter, bt2_interrupter.Interrupter)
         native_bt.graph_add_interrupter(self._ptr, interrupter._ptr)
 
     @property
index 79006f33c49cfe3edf0c05b2d056185c6c42453f..86bfd467163d0515a5521861843d0827542a2b19 100644 (file)
@@ -2,9 +2,12 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
 import collections.abc
-import bt2
+
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
+from bt2 import object as bt2_object
+from bt2 import native_bt
 
 
 class _IntegerRangeConst:
@@ -51,8 +54,8 @@ class _IntegerRange(_IntegerRangeConst):
 
 
 class _SignedIntegerRangeConst(_IntegerRangeConst):
-    _is_type = staticmethod(utils._is_int64)
-    _check_type = staticmethod(utils._check_int64)
+    _is_type = staticmethod(bt2_utils._is_int64)
+    _check_type = staticmethod(bt2_utils._check_int64)
 
 
 class SignedIntegerRange(_SignedIntegerRangeConst, _IntegerRange):
@@ -60,15 +63,15 @@ class SignedIntegerRange(_SignedIntegerRangeConst, _IntegerRange):
 
 
 class _UnsignedIntegerRangeConst(_IntegerRangeConst):
-    _is_type = staticmethod(utils._is_uint64)
-    _check_type = staticmethod(utils._check_uint64)
+    _is_type = staticmethod(bt2_utils._is_uint64)
+    _check_type = staticmethod(bt2_utils._check_uint64)
 
 
 class UnsignedIntegerRange(_UnsignedIntegerRangeConst, _IntegerRange):
     pass
 
 
-class _IntegerRangeSetConst(object._SharedObject, collections.abc.Set):
+class _IntegerRangeSetConst(bt2_object._SharedObject, collections.abc.Set):
     def __len__(self):
         range_set_ptr = self._as_range_set_ptr(self._ptr)
         count = native_bt.integer_range_set_get_range_count(range_set_ptr)
@@ -91,7 +94,6 @@ class _IntegerRangeSetConst(object._SharedObject, collections.abc.Set):
             yield self._range_pycls(lower, upper)
 
     def __eq__(self, other):
-
         if not isinstance(other, _IntegerRangeSetConst):
             return False
 
@@ -110,7 +112,7 @@ class _IntegerRangeSet(_IntegerRangeSetConst, collections.abc.MutableSet):
         ptr = self._create_range_set()
 
         if ptr is None:
-            raise bt2._MemoryError("cannot create range set object")
+            raise bt2_error._MemoryError("cannot create range set object")
 
         super().__init__(ptr)
 
@@ -128,15 +130,21 @@ class _IntegerRangeSet(_IntegerRangeSetConst, collections.abc.MutableSet):
                 rg = self._range_pycls(rg[0], rg[1])
 
         status = self._add_range(self._ptr, rg.lower, rg.upper)
-        utils._handle_func_status(status, "cannot add range to range set object")
+        bt2_utils._handle_func_status(status, "cannot add range to range set object")
 
     def discard(self, rg):
         raise NotImplementedError
 
 
 class _SignedIntegerRangeSetConst(_IntegerRangeSetConst):
-    _get_ref = staticmethod(native_bt.integer_range_set_signed_get_ref)
-    _put_ref = staticmethod(native_bt.integer_range_set_signed_put_ref)
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.integer_range_set_signed_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.integer_range_set_signed_put_ref(ptr)
+
     _as_range_set_ptr = staticmethod(
         native_bt.integer_range_set_signed_as_range_set_const
     )
@@ -156,8 +164,14 @@ class SignedIntegerRangeSet(_SignedIntegerRangeSetConst, _IntegerRangeSet):
 
 
 class _UnsignedIntegerRangeSetConst(_IntegerRangeSetConst):
-    _get_ref = staticmethod(native_bt.integer_range_set_unsigned_get_ref)
-    _put_ref = staticmethod(native_bt.integer_range_set_unsigned_put_ref)
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.integer_range_set_unsigned_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.integer_range_set_unsigned_put_ref(ptr)
+
     _as_range_set_ptr = staticmethod(
         native_bt.integer_range_set_unsigned_as_range_set_const
     )
index 39c22883d2d0304abfdc49b161e8af6efe2d142a..cd7ef806903f7a6658a441b76f8b23ece2ac421d 100644 (file)
@@ -2,19 +2,25 @@
 #
 # Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object
-import bt2
+from bt2 import error as bt2_error
+from bt2 import object as bt2_object
+from bt2 import native_bt
 
 
-class Interrupter(object._SharedObject):
-    _get_ref = staticmethod(native_bt.interrupter_get_ref)
-    _put_ref = staticmethod(native_bt.interrupter_put_ref)
+class Interrupter(bt2_object._SharedObject):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.interrupter_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.interrupter_put_ref(ptr)
 
     def __init__(self):
         ptr = native_bt.interrupter_create()
 
         if ptr is None:
-            raise bt2._MemoryError("cannot create interrupter object")
+            raise bt2_error._MemoryError("cannot create interrupter object")
 
         super().__init__(ptr)
 
index 5039d923b59208c1b057f78256ec85083f99e96e..c56f696190c3c7cb1ed2bf59ccb87b3fd9df9115 100644 (file)
@@ -2,11 +2,13 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import clock_snapshot as bt2_clock_snapshot
+from bt2 import event as bt2_event
+from bt2 import utils as bt2_utils
+from bt2 import object as bt2_object
 from bt2 import packet as bt2_packet
 from bt2 import stream as bt2_stream
-from bt2 import event as bt2_event
+from bt2 import native_bt
+from bt2 import clock_snapshot as bt2_clock_snapshot
 
 
 def _create_from_ptr(ptr):
@@ -14,9 +16,14 @@ def _create_from_ptr(ptr):
     return _MESSAGE_TYPE_TO_CLS[msg_type]._create_from_ptr(ptr)
 
 
-class _MessageConst(object._SharedObject):
-    _get_ref = staticmethod(native_bt.message_get_ref)
-    _put_ref = staticmethod(native_bt.message_put_ref)
+class _MessageConst(bt2_object._SharedObject):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.message_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.message_put_ref(ptr)
 
     @staticmethod
     def _check_has_default_clock_class(clock_class):
@@ -134,7 +141,7 @@ class _StreamMessageConst(_MessageConst, _MessageWithDefaultClockSnapshot):
 
 class _StreamMessage(_StreamMessageConst, _Message):
     def _default_clock_snapshot(self, raw_value):
-        utils._check_uint64(raw_value)
+        bt2_utils._check_uint64(raw_value)
         self._set_default_clock_snapshot(self._ptr, raw_value)
 
     _default_clock_snapshot = property(
@@ -232,7 +239,7 @@ class _DiscardedMessage(_DiscardedMessageConst, _Message):
     _stream_pycls = property(lambda _: bt2_stream._Stream)
 
     def _set_count(self, count):
-        utils._check_uint64(count)
+        bt2_utils._check_uint64(count)
 
         if count == 0:
             raise ValueError("discarded {} count is 0".format(self._item_name))
index e2efa732baba9729f2635456341d11121717b009..8703e90be10f095c8a5f0cb96d9deade24ebe488 100644 (file)
@@ -2,15 +2,18 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import message as bt2_message
 import collections.abc
-from bt2 import stream as bt2_stream
-from bt2 import event_class as bt2_event_class
-from bt2 import packet as bt2_packet
+
 from bt2 import port as bt2_port
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
+from bt2 import object as bt2_object
+from bt2 import packet as bt2_packet
+from bt2 import stream as bt2_stream
+from bt2 import message as bt2_message
+from bt2 import native_bt
 from bt2 import clock_class as bt2_clock_class
-import bt2
+from bt2 import event_class as bt2_event_class
 
 
 class _MessageIterator(collections.abc.Iterator):
@@ -18,9 +21,16 @@ class _MessageIterator(collections.abc.Iterator):
         raise NotImplementedError
 
 
-class _UserComponentInputPortMessageIterator(object._SharedObject, _MessageIterator):
-    _get_ref = staticmethod(native_bt.message_iterator_get_ref)
-    _put_ref = staticmethod(native_bt.message_iterator_put_ref)
+class _UserComponentInputPortMessageIterator(
+    bt2_object._SharedObject, _MessageIterator
+):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.message_iterator_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.message_iterator_put_ref(ptr)
 
     def __init__(self, ptr):
         self._current_msgs = []
@@ -32,7 +42,7 @@ class _UserComponentInputPortMessageIterator(object._SharedObject, _MessageItera
             status, msgs = native_bt.bt2_self_component_port_input_get_msg_range(
                 self._ptr
             )
-            utils._handle_func_status(
+            bt2_utils._handle_func_status(
                 status, "unexpected error: cannot advance the message iterator"
             )
             self._current_msgs = msgs
@@ -45,7 +55,7 @@ class _UserComponentInputPortMessageIterator(object._SharedObject, _MessageItera
 
     def can_seek_beginning(self):
         (status, res) = native_bt.message_iterator_can_seek_beginning(self._ptr)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status,
             "cannot check whether or not message iterator can seek its beginning",
         )
@@ -57,21 +67,21 @@ class _UserComponentInputPortMessageIterator(object._SharedObject, _MessageItera
         self._at = 0
 
         status = native_bt.message_iterator_seek_beginning(self._ptr)
-        utils._handle_func_status(status, "cannot seek message iterator beginning")
+        bt2_utils._handle_func_status(status, "cannot seek message iterator beginning")
 
     def can_seek_ns_from_origin(self, ns_from_origin):
-        utils._check_int64(ns_from_origin)
+        bt2_utils._check_int64(ns_from_origin)
         (status, res) = native_bt.message_iterator_can_seek_ns_from_origin(
             self._ptr, ns_from_origin
         )
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status,
             "cannot check whether or not message iterator can seek given ns from origin",
         )
         return res != 0
 
     def seek_ns_from_origin(self, ns_from_origin):
-        utils._check_int64(ns_from_origin)
+        bt2_utils._check_int64(ns_from_origin)
 
         # Forget about buffered messages, they won't be valid after seeking.
         self._current_msgs.clear()
@@ -80,7 +90,7 @@ class _UserComponentInputPortMessageIterator(object._SharedObject, _MessageItera
         status = native_bt.message_iterator_seek_ns_from_origin(
             self._ptr, ns_from_origin
         )
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "message iterator cannot seek given ns from origin"
         )
 
@@ -94,7 +104,7 @@ class _MessageIteratorConfiguration:
         self._ptr = ptr
 
     def can_seek_forward(self, value):
-        utils._check_bool(value)
+        bt2_utils._check_bool(value)
         native_bt.self_message_iterator_configuration_set_can_seek_forward(
             self._ptr, value
         )
@@ -158,18 +168,18 @@ class _UserMessageIterator(_MessageIterator):
         pass
 
     def __next__(self):
-        raise bt2.Stop
+        raise bt2_utils.Stop
 
     def _bt_next_from_native(self):
-        # this can raise anything: it's catched by the native part
+        # this can raise anything: it's caught by the native part
         try:
             msg = next(self)
         except StopIteration:
-            raise bt2.Stop
+            raise bt2_utils.Stop
         except Exception:
             raise
 
-        utils._check_type(msg, bt2_message._MessageConst)
+        bt2_utils._check_type(msg, bt2_message._MessageConst)
 
         # The reference we return will be given to the message array.
         # However, the `msg` Python object may stay alive, if the user has kept
@@ -186,7 +196,7 @@ class _UserMessageIterator(_MessageIterator):
         #   method indicates whether the iterator can seek beginning.
         if hasattr(self, "_user_can_seek_beginning"):
             can_seek_beginning = self._user_can_seek_beginning()
-            utils._check_bool(can_seek_beginning)
+            bt2_utils._check_bool(can_seek_beginning)
             return can_seek_beginning
         else:
             return hasattr(self, "_user_seek_beginning")
@@ -206,7 +216,7 @@ class _UserMessageIterator(_MessageIterator):
 
         if hasattr(self, "_user_can_seek_ns_from_origin"):
             can_seek_ns_from_origin = self._user_can_seek_ns_from_origin(ns_from_origin)
-            utils._check_bool(can_seek_ns_from_origin)
+            bt2_utils._check_bool(can_seek_ns_from_origin)
             return can_seek_ns_from_origin
         else:
             return hasattr(self, "_user_seek_ns_from_origin")
@@ -215,7 +225,7 @@ class _UserMessageIterator(_MessageIterator):
         self._user_seek_ns_from_origin(ns_from_origin)
 
     def _create_message_iterator(self, input_port):
-        utils._check_type(input_port, bt2_port._UserComponentInputPort)
+        bt2_utils._check_type(input_port, bt2_port._UserComponentInputPort)
 
         if not input_port.is_connected:
             raise ValueError("input port is not connected")
@@ -226,18 +236,18 @@ class _UserMessageIterator(_MessageIterator):
         ) = native_bt.bt2_message_iterator_create_from_message_iterator(
             self._bt_ptr, input_port._ptr
         )
-        utils._handle_func_status(status, "cannot create message iterator object")
+        bt2_utils._handle_func_status(status, "cannot create message iterator object")
         assert msg_iter_ptr is not None
 
         return _UserComponentInputPortMessageIterator(msg_iter_ptr)
 
     def _create_event_message(self, event_class, parent, default_clock_snapshot=None):
-        utils._check_type(event_class, bt2_event_class._EventClass)
+        bt2_utils._check_type(event_class, bt2_event_class._EventClass)
 
         if event_class.stream_class.supports_packets:
-            utils._check_type(parent, bt2_packet._Packet)
+            bt2_utils._check_type(parent, bt2_packet._Packet)
         else:
-            utils._check_type(parent, bt2_stream._Stream)
+            bt2_utils._check_type(parent, bt2_stream._Stream)
 
         if default_clock_snapshot is not None:
             if event_class.stream_class.default_clock_class is None:
@@ -245,7 +255,7 @@ class _UserMessageIterator(_MessageIterator):
                     "event messages in this stream must not have a default clock snapshot"
                 )
 
-            utils._check_uint64(default_clock_snapshot)
+            bt2_utils._check_uint64(default_clock_snapshot)
 
             if event_class.stream_class.supports_packets:
                 ptr = native_bt.message_event_create_with_packet_and_default_clock_snapshot(
@@ -271,27 +281,29 @@ class _UserMessageIterator(_MessageIterator):
                 )
 
         if ptr is None:
-            raise bt2._MemoryError("cannot create event message object")
+            raise bt2_error._MemoryError("cannot create event message object")
 
         return bt2_message._EventMessage(ptr)
 
     def _create_message_iterator_inactivity_message(self, clock_class, clock_snapshot):
-        utils._check_type(clock_class, bt2_clock_class._ClockClass)
+        bt2_utils._check_type(clock_class, bt2_clock_class._ClockClass)
         ptr = native_bt.message_message_iterator_inactivity_create(
             self._bt_ptr, clock_class._ptr, clock_snapshot
         )
 
         if ptr is None:
-            raise bt2._MemoryError("cannot create inactivity message object")
+            raise bt2_error._MemoryError("cannot create inactivity message object")
 
         return bt2_message._MessageIteratorInactivityMessage(ptr)
 
     def _create_stream_beginning_message(self, stream, default_clock_snapshot=None):
-        utils._check_type(stream, bt2_stream._Stream)
+        bt2_utils._check_type(stream, bt2_stream._Stream)
 
         ptr = native_bt.message_stream_beginning_create(self._bt_ptr, stream._ptr)
         if ptr is None:
-            raise bt2._MemoryError("cannot create stream beginning message object")
+            raise bt2_error._MemoryError(
+                "cannot create stream beginning message object"
+            )
 
         msg = bt2_message._StreamBeginningMessage(ptr)
 
@@ -301,11 +313,11 @@ class _UserMessageIterator(_MessageIterator):
         return msg
 
     def _create_stream_end_message(self, stream, default_clock_snapshot=None):
-        utils._check_type(stream, bt2_stream._Stream)
+        bt2_utils._check_type(stream, bt2_stream._Stream)
 
         ptr = native_bt.message_stream_end_create(self._bt_ptr, stream._ptr)
         if ptr is None:
-            raise bt2._MemoryError("cannot create stream end message object")
+            raise bt2_error._MemoryError("cannot create stream end message object")
 
         msg = bt2_message._StreamEndMessage(ptr)
 
@@ -315,7 +327,7 @@ class _UserMessageIterator(_MessageIterator):
         return msg
 
     def _create_packet_beginning_message(self, packet, default_clock_snapshot=None):
-        utils._check_type(packet, bt2_packet._Packet)
+        bt2_utils._check_type(packet, bt2_packet._Packet)
 
         if packet.stream.cls.packets_have_beginning_default_clock_snapshot:
             if default_clock_snapshot is None:
@@ -323,7 +335,7 @@ class _UserMessageIterator(_MessageIterator):
                     "packet beginning messages in this stream must have a default clock snapshot"
                 )
 
-            utils._check_uint64(default_clock_snapshot)
+            bt2_utils._check_uint64(default_clock_snapshot)
             ptr = native_bt.message_packet_beginning_create_with_default_clock_snapshot(
                 self._bt_ptr, packet._ptr, default_clock_snapshot
             )
@@ -336,12 +348,14 @@ class _UserMessageIterator(_MessageIterator):
             ptr = native_bt.message_packet_beginning_create(self._bt_ptr, packet._ptr)
 
         if ptr is None:
-            raise bt2._MemoryError("cannot create packet beginning message object")
+            raise bt2_error._MemoryError(
+                "cannot create packet beginning message object"
+            )
 
         return bt2_message._PacketBeginningMessage(ptr)
 
     def _create_packet_end_message(self, packet, default_clock_snapshot=None):
-        utils._check_type(packet, bt2_packet._Packet)
+        bt2_utils._check_type(packet, bt2_packet._Packet)
 
         if packet.stream.cls.packets_have_end_default_clock_snapshot:
             if default_clock_snapshot is None:
@@ -349,7 +363,7 @@ class _UserMessageIterator(_MessageIterator):
                     "packet end messages in this stream must have a default clock snapshot"
                 )
 
-            utils._check_uint64(default_clock_snapshot)
+            bt2_utils._check_uint64(default_clock_snapshot)
             ptr = native_bt.message_packet_end_create_with_default_clock_snapshot(
                 self._bt_ptr, packet._ptr, default_clock_snapshot
             )
@@ -362,14 +376,14 @@ class _UserMessageIterator(_MessageIterator):
             ptr = native_bt.message_packet_end_create(self._bt_ptr, packet._ptr)
 
         if ptr is None:
-            raise bt2._MemoryError("cannot create packet end message object")
+            raise bt2_error._MemoryError("cannot create packet end message object")
 
         return bt2_message._PacketEndMessage(ptr)
 
     def _create_discarded_events_message(
         self, stream, count=None, beg_clock_snapshot=None, end_clock_snapshot=None
     ):
-        utils._check_type(stream, bt2_stream._Stream)
+        bt2_utils._check_type(stream, bt2_stream._Stream)
 
         if not stream.cls.supports_discarded_events:
             raise ValueError("stream class does not support discarded events")
@@ -380,8 +394,8 @@ class _UserMessageIterator(_MessageIterator):
                     "discarded events have default clock snapshots for this stream class"
                 )
 
-            utils._check_uint64(beg_clock_snapshot)
-            utils._check_uint64(end_clock_snapshot)
+            bt2_utils._check_uint64(beg_clock_snapshot)
+            bt2_utils._check_uint64(end_clock_snapshot)
 
             if beg_clock_snapshot > end_clock_snapshot:
                 raise ValueError(
@@ -404,7 +418,7 @@ class _UserMessageIterator(_MessageIterator):
             ptr = native_bt.message_discarded_events_create(self._bt_ptr, stream._ptr)
 
         if ptr is None:
-            raise bt2._MemoryError("cannot discarded events message object")
+            raise bt2_error._MemoryError("cannot discarded events message object")
 
         msg = bt2_message._DiscardedEventsMessage(ptr)
 
@@ -416,7 +430,7 @@ class _UserMessageIterator(_MessageIterator):
     def _create_discarded_packets_message(
         self, stream, count=None, beg_clock_snapshot=None, end_clock_snapshot=None
     ):
-        utils._check_type(stream, bt2_stream._Stream)
+        bt2_utils._check_type(stream, bt2_stream._Stream)
 
         if not stream.cls.supports_discarded_packets:
             raise ValueError("stream class does not support discarded packets")
@@ -427,8 +441,8 @@ class _UserMessageIterator(_MessageIterator):
                     "discarded packets have default clock snapshots for this stream class"
                 )
 
-            utils._check_uint64(beg_clock_snapshot)
-            utils._check_uint64(end_clock_snapshot)
+            bt2_utils._check_uint64(beg_clock_snapshot)
+            bt2_utils._check_uint64(end_clock_snapshot)
 
             if beg_clock_snapshot > end_clock_snapshot:
                 raise ValueError(
@@ -451,7 +465,7 @@ class _UserMessageIterator(_MessageIterator):
             ptr = native_bt.message_discarded_packets_create(self._bt_ptr, stream._ptr)
 
         if ptr is None:
-            raise bt2._MemoryError("cannot discarded packets message object")
+            raise bt2_error._MemoryError("cannot discarded packets message object")
 
         msg = bt2_message._DiscardedPacketsMessage(ptr)
 
index 552c7b2aad5c0724e469fc4abbaab7429df9af12..21cb0c61b700719ad113e717c7b6c490ed0b36ea 100644 (file)
@@ -2,25 +2,28 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, utils
-import bt2
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
+from bt2 import logging as bt2_logging
+from bt2 import native_bt
+from bt2 import component_descriptor as bt2_component_descriptor
 
 
 def get_greatest_operative_mip_version(
-    component_descriptors, log_level=bt2.LoggingLevel.NONE
+    component_descriptors, log_level=bt2_logging.LoggingLevel.NONE
 ):
-    utils._check_log_level(log_level)
+    bt2_utils._check_log_level(log_level)
     comp_descr_set_ptr = native_bt.component_descriptor_set_create()
 
     if comp_descr_set_ptr is None:
-        raise bt2._MemoryError("cannot create component descriptor set object")
+        raise bt2_error._MemoryError("cannot create component descriptor set object")
 
     if len(component_descriptors) == 0:
         raise ValueError("no component descriptors")
 
     try:
         for descr in component_descriptors:
-            if type(descr) is not bt2.ComponentDescriptor:
+            if type(descr) is not bt2_component_descriptor.ComponentDescriptor:
                 raise TypeError("'{}' is not a component descriptor".format(descr))
 
             base_cc_ptr = descr.component_class._bt_component_class_ptr()
@@ -32,7 +35,7 @@ def get_greatest_operative_mip_version(
             status = native_bt.bt2_component_descriptor_set_add_descriptor_with_initialize_method_data(
                 comp_descr_set_ptr, base_cc_ptr, params_ptr, descr.obj
             )
-            utils._handle_func_status(
+            bt2_utils._handle_func_status(
                 status, "cannot add descriptor to component descriptor set"
             )
 
@@ -43,7 +46,9 @@ def get_greatest_operative_mip_version(
         if status == native_bt.__BT_FUNC_STATUS_NO_MATCH:
             return None
 
-        utils._handle_func_status(status, "cannot get greatest operative MIP version")
+        bt2_utils._handle_func_status(
+            status, "cannot get greatest operative MIP version"
+        )
         return version
     finally:
         native_bt.component_descriptor_set_put_ref(comp_descr_set_ptr)
index 21a869e606376d4bc9101f92a31d44aae8fc47de..b4b5e9048c14bfccce02deed98666c1f6084bb2d 100644 (file)
@@ -199,10 +199,11 @@ void bt_bt2_exit_handler(void);
 #define __BT_IN_BABELTRACE_H
 
 /*
- * Define `__BT_ATTR_FORMAT_PRINTF` to nothing, otherwise SWIG fails to parse
- * the included header files that use it.
+ * Define `__BT_ATTR_FORMAT_PRINTF` and `__BT_NOEXCEPT` to nothing,
+ * otherwise SWIG fails to parse the included header files that use it.
  */
 #define __BT_ATTR_FORMAT_PRINTF(_string_index, _first_to_check)
+#define __BT_NOEXCEPT
 
 /* Common types */
 %include <babeltrace2/types.h>
index d6a4d24bbe1ae0d3f089ee6639ac8ca081111d43..9b12e8c368a333199fcf7be6aff13694584b667e 100644 (file)
@@ -86,7 +86,7 @@ bt_value *bt_bt2_auto_discover_source_components(const bt_value *inputs,
                plugins,
                plugin_count,
                NULL,
-               bt_python_bindings_bt2_log_level,
+               (bt_logging_level) bt_python_bindings_bt2_log_level,
                &auto_disc,
                NULL);
        if (status != 0) {
index 8e284c0401f8281d66bfe94ecfb718cc76b3c059..628d58679b90f54349fa207e9ed25d2e641e3ec2 100644 (file)
@@ -298,7 +298,8 @@ component_class_get_supported_mip_versions(
 
        py_cls = lookup_cc_ptr_to_py_cls(component_class);
        if (!py_cls) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        "Cannot find Python class associated to native component class: "
                        "comp-cls-addr=%p", component_class);
                goto error;
@@ -307,7 +308,8 @@ component_class_get_supported_mip_versions(
        py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params),
                SWIGTYPE_p_bt_value, 0);
        if (!py_params_ptr) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        BT_FMT_SWIG_ALLOC_FAILED);
                goto error;
        }
@@ -324,7 +326,8 @@ component_class_get_supported_mip_versions(
                py_params_ptr, init_method_data ? init_method_data : Py_None,
                (int) log_level);
        if (!py_range_set_addr) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_WARNING, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_WARNING,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        "Failed to call Python class's _bt_get_supported_mip_versions_from_native() method: "
                        "py-cls-addr=%p", py_cls);
                status = py_exc_to_status_component_class_clear(self_component_class,
@@ -355,7 +358,8 @@ component_class_get_supported_mip_versions(
                        bt_integer_range_unsigned_get_lower(range),
                        bt_integer_range_unsigned_get_upper(range));
                if (add_range_status) {
-                       BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+                       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                               (enum bt_log_level) log_level, BT_LOG_TAG,
                                "Failed to add range to supported MIP versions range set.");
                        goto error;
                }
@@ -939,7 +943,8 @@ bt_component_class_query_method_status component_class_query(
 
        py_cls = lookup_cc_ptr_to_py_cls(component_class);
        if (!py_cls) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        "Cannot find Python class associated to native component class: "
                        "comp-cls-addr=%p", component_class);
                goto error;
@@ -948,7 +953,8 @@ bt_component_class_query_method_status component_class_query(
        py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params),
                SWIGTYPE_p_bt_value, 0);
        if (!py_params_ptr) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        BT_FMT_SWIG_ALLOC_FAILED);
                goto error;
        }
@@ -957,14 +963,16 @@ bt_component_class_query_method_status component_class_query(
                SWIG_as_voidptr(priv_query_executor),
                SWIGTYPE_p_bt_private_query_executor, 0);
        if (!py_priv_query_exec_ptr) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        BT_FMT_SWIG_ALLOC_FAILED);
                goto error;
        }
 
        py_object = SWIG_FromCharPtr(object);
        if (!py_object) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        "Failed to create a Python string.");
                goto error;
        }
@@ -985,8 +993,9 @@ bt_component_class_query_method_status component_class_query(
                        log_level);
                if (status < 0) {
 #define BT_FMT "Failed to call Python class's _bt_query_from_native() method: py-cls-addr=%p"
-                       BT_LOG_WRITE_CUR_LVL(BT_LOG_WARNING, log_level, BT_LOG_TAG,
-                               BT_FMT, py_cls);
+                       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_WARNING,
+                               (enum bt_log_level) log_level,
+                               BT_LOG_TAG, BT_FMT, py_cls);
                        BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT_CLASS(
                                self_component_class, BT_FMT, py_cls);
 #undef BT_FMT
index 053941517829a807e59f6a5a9260b5a49a85c101..78eeb28ae5e9d39a8c1aecb724e0544bf8e5e61e 100644 (file)
@@ -13,7 +13,8 @@ PyObject *bt_bt2_format_bt_error_cause(const bt_error_cause *error_cause)
        PyObject *py_error_cause_str = NULL;
 
        error_cause_str = format_bt_error_cause(error_cause, 80,
-               bt_python_bindings_bt2_log_level, BT_COMMON_COLOR_WHEN_NEVER);
+               (bt_logging_level) bt_python_bindings_bt2_log_level,
+               BT_COMMON_COLOR_WHEN_NEVER);
        BT_ASSERT(error_cause_str);
 
        py_error_cause_str = PyString_FromString(error_cause_str);
@@ -30,7 +31,8 @@ PyObject *bt_bt2_format_bt_error(const bt_error *error)
        PyObject *py_error_str = NULL;
 
        error_str = format_bt_error(error, 80,
-               bt_python_bindings_bt2_log_level, BT_COMMON_COLOR_WHEN_NEVER);
+               (bt_logging_level) bt_python_bindings_bt2_log_level,
+               BT_COMMON_COLOR_WHEN_NEVER);
        BT_ASSERT(error_str);
 
        py_error_str = PyString_FromString(error_str);
index fed138eced74e526699c074f53903df915d049a4..dcff667f49aaf52d1a889d29e06e8cfdcb187156 100644 (file)
@@ -3,6 +3,9 @@
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
 
+import abc
+
+
 class _BaseObject:
     # Ensure that the object always has _ptr set, even if it throws during
     # construction.
@@ -42,7 +45,6 @@ class _BaseObject:
 
 
 class _UniqueObject(_BaseObject):
-
     # Create a _UniqueObject.
     #
     #   - ptr: SWIG Object, pointer to the unique object.
@@ -69,14 +71,14 @@ class _UniqueObject(_BaseObject):
 
 
 # Python object that owns a reference to a Babeltrace object.
-class _SharedObject(_BaseObject):
-
+class _SharedObject(_BaseObject, abc.ABC):
     # Get a new reference on ptr.
     #
     # This must be implemented by subclasses to work correctly with a pointer
     # of the native type they wrap.
 
     @staticmethod
+    @abc.abstractmethod
     def _get_ref(ptr):
         raise NotImplementedError
 
@@ -86,6 +88,7 @@ class _SharedObject(_BaseObject):
     # of the native type they wrap.
 
     @staticmethod
+    @abc.abstractmethod
     def _put_ref(ptr):
         raise NotImplementedError
 
index 9325a7d6fc29cbb0168c9651761d72f1fa2d0642..0e7605f1660d253de5a4b5423c465154f3adfcbe 100644 (file)
@@ -2,8 +2,9 @@
 #
 # Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object
 from bt2 import field as bt2_field
+from bt2 import object as bt2_object
+from bt2 import native_bt
 
 
 def _bt2_stream():
@@ -12,9 +13,15 @@ def _bt2_stream():
     return bt2_stream
 
 
-class _PacketConst(object._SharedObject):
-    _get_ref = staticmethod(native_bt.packet_get_ref)
-    _put_ref = staticmethod(native_bt.packet_put_ref)
+class _PacketConst(bt2_object._SharedObject):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.packet_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.packet_put_ref(ptr)
+
     _borrow_stream_ptr = staticmethod(native_bt.packet_borrow_stream_const)
     _borrow_context_field_ptr = staticmethod(
         native_bt.packet_borrow_context_field_const
index 6888cb12f33b1352d56b567f941e9800b352c59f..0c9906679125773f3d449e9ff4396d60c041be84 100644 (file)
@@ -2,16 +2,19 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
+import os.path
 import collections.abc
+
+from bt2 import utils as bt2_utils
+from bt2 import object as bt2_object
 from bt2 import component as bt2_component
-import os.path
+from bt2 import native_bt
 
 
 def find_plugins_in_path(path, recurse=True, fail_on_load_error=False):
-    utils._check_str(path)
-    utils._check_bool(recurse)
-    utils._check_bool(fail_on_load_error)
+    bt2_utils._check_str(path)
+    bt2_utils._check_bool(recurse)
+    bt2_utils._check_bool(fail_on_load_error)
     plugin_set_ptr = None
 
     if os.path.isfile(path):
@@ -28,7 +31,7 @@ def find_plugins_in_path(path, recurse=True, fail_on_load_error=False):
     if status == native_bt.__BT_FUNC_STATUS_NOT_FOUND:
         return
 
-    utils._handle_func_status(status, "failed to find plugins")
+    bt2_utils._handle_func_status(status, "failed to find plugins")
     assert plugin_set_ptr is not None
     return _PluginSet._create_from_ptr(plugin_set_ptr)
 
@@ -40,11 +43,11 @@ def find_plugins(
     find_in_static=True,
     fail_on_load_error=False,
 ):
-    utils._check_bool(find_in_std_env_var)
-    utils._check_bool(find_in_user_dir)
-    utils._check_bool(find_in_sys_dir)
-    utils._check_bool(find_in_static)
-    utils._check_bool(fail_on_load_error)
+    bt2_utils._check_bool(find_in_std_env_var)
+    bt2_utils._check_bool(find_in_user_dir)
+    bt2_utils._check_bool(find_in_sys_dir)
+    bt2_utils._check_bool(find_in_static)
+    bt2_utils._check_bool(fail_on_load_error)
     plugin_set_ptr = None
 
     status, plugin_set_ptr = native_bt.bt2_plugin_find_all(
@@ -58,7 +61,7 @@ def find_plugins(
     if status == native_bt.__BT_FUNC_STATUS_NOT_FOUND:
         return
 
-    utils._handle_func_status(status, "failed to find plugins")
+    bt2_utils._handle_func_status(status, "failed to find plugins")
     assert plugin_set_ptr is not None
     return _PluginSet._create_from_ptr(plugin_set_ptr)
 
@@ -71,8 +74,8 @@ def find_plugin(
     find_in_static=True,
     fail_on_load_error=False,
 ):
-    utils._check_str(name)
-    utils._check_bool(fail_on_load_error)
+    bt2_utils._check_str(name)
+    bt2_utils._check_bool(fail_on_load_error)
     status, ptr = native_bt.bt2_plugin_find(
         name,
         int(find_in_std_env_var),
@@ -85,14 +88,19 @@ def find_plugin(
     if status == native_bt.__BT_FUNC_STATUS_NOT_FOUND:
         return
 
-    utils._handle_func_status(status, "failed to find plugin")
+    bt2_utils._handle_func_status(status, "failed to find plugin")
     assert ptr is not None
     return _Plugin._create_from_ptr(ptr)
 
 
-class _PluginSet(object._SharedObject, collections.abc.Sequence):
-    _put_ref = staticmethod(native_bt.plugin_set_put_ref)
-    _get_ref = staticmethod(native_bt.plugin_set_get_ref)
+class _PluginSet(bt2_object._SharedObject, collections.abc.Sequence):
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.plugin_set_put_ref(ptr)
+
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.plugin_set_get_ref(ptr)
 
     def __len__(self):
         count = native_bt.plugin_set_get_plugin_count(self._ptr)
@@ -100,7 +108,7 @@ class _PluginSet(object._SharedObject, collections.abc.Sequence):
         return count
 
     def __getitem__(self, index):
-        utils._check_uint64(index)
+        bt2_utils._check_uint64(index)
 
         if index >= len(self):
             raise IndexError
@@ -175,7 +183,7 @@ class _PluginComponentClasses(collections.abc.Mapping):
         self._plugin = plugin
 
     def __getitem__(self, key):
-        utils._check_str(key)
+        bt2_utils._check_str(key)
         cc_ptr = self._borrow_component_class_by_name(self._plugin._ptr, key)
 
         if cc_ptr is None:
@@ -231,9 +239,14 @@ class _PluginSinkComponentClasses(_PluginComponentClasses):
     _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_SINK
 
 
-class _Plugin(object._SharedObject):
-    _put_ref = staticmethod(native_bt.plugin_put_ref)
-    _get_ref = staticmethod(native_bt.plugin_get_ref)
+class _Plugin(bt2_object._SharedObject):
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.plugin_put_ref(ptr)
+
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.plugin_get_ref(ptr)
 
     @property
     def name(self):
index 0d6484782b7f710698189c4d1d23e5083b1e0650..b9a5529af81abc110bd62f186dd1e9fa726c1bef 100644 (file)
@@ -2,7 +2,8 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object
+from bt2 import object as bt2_object
+from bt2 import native_bt
 
 
 def _bt2_connection():
@@ -29,7 +30,7 @@ def _create_self_from_ptr_and_get_ref(ptr, port_type):
     return cls._create_from_ptr_and_get_ref(ptr)
 
 
-class _PortConst(object._SharedObject):
+class _PortConst(bt2_object._SharedObject):
     @classmethod
     def _get_ref(cls, ptr):
         ptr = cls._as_port_ptr(ptr)
index 6f067e8b4d59c4e794362372a05ccf8f008393ec..2a7414bb5c415f6212a851263f08a2c2de2637f7 100644 (file)
@@ -2,10 +2,10 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import utils
-from bt2 import component as bt2_component
 import sys
 
+from bt2 import utils as bt2_utils
+from bt2 import component as bt2_component
 
 # Python plugin path to `_PluginInfo` (cache)
 _plugin_infos = {}
@@ -27,16 +27,16 @@ def register_plugin(
             "cannot find module '{}' in loaded modules".format(module_name)
         )
 
-    utils._check_str(name)
+    bt2_utils._check_str(name)
 
     if description is not None:
-        utils._check_str(description)
+        bt2_utils._check_str(description)
 
     if author is not None:
-        utils._check_str(author)
+        bt2_utils._check_str(author)
 
     if license is not None:
-        utils._check_str(license)
+        bt2_utils._check_str(license)
 
     if version is not None:
         if not _validate_version(version):
@@ -91,9 +91,9 @@ def _try_load_plugin_module(path):
         # do not load module and create plugin info twice for this path
         return _plugin_infos[path]
 
-    import importlib.machinery
-    import inspect
     import hashlib
+    import inspect
+    import importlib.machinery
 
     if path is None:
         raise TypeError("missing path")
@@ -105,8 +105,18 @@ def _try_load_plugin_module(path):
     h.update(path.encode())
     module_name = "bt_plugin_{}".format(h.hexdigest())
     assert module_name not in sys.modules
-    # try loading the module: any raised exception is catched by the caller
-    mod = importlib.machinery.SourceFileLoader(module_name, path).load_module()
+
+    # try loading the module: any raised exception is caught by the caller
+    if sys.version_info < (3, 5):
+        mod = importlib.machinery.SourceFileLoader(module_name, path).load_module()
+    else:
+        import importlib.util
+
+        loader = importlib.machinery.SourceFileLoader(module_name, path)
+        spec = importlib.util.spec_from_file_location(module_name, path, loader=loader)
+        mod = importlib.util.module_from_spec(spec)
+        sys.modules[mod.__name__] = mod
+        loader.exec_module(mod)
 
     # we have the module: look for its plugin info first
     if not hasattr(mod, "_bt_plugin_info"):
index 354404a6e3ee199d7fe7aff4c52eff621d476b76..de6f4441c9089372e73af63a34e0dbd03b1551c2 100644 (file)
@@ -2,10 +2,12 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import interrupter as bt2_interrupter
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
 from bt2 import value as bt2_value
-import bt2
+from bt2 import object as bt2_object
+from bt2 import native_bt
+from bt2 import interrupter as bt2_interrupter
 
 
 def _bt2_component():
@@ -29,9 +31,14 @@ class _QueryExecutorCommon:
         return native_bt.query_executor_get_logging_level(self._common_ptr)
 
 
-class QueryExecutor(object._SharedObject, _QueryExecutorCommon):
-    _get_ref = staticmethod(native_bt.query_executor_get_ref)
-    _put_ref = staticmethod(native_bt.query_executor_put_ref)
+class QueryExecutor(bt2_object._SharedObject, _QueryExecutorCommon):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.query_executor_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.query_executor_put_ref(ptr)
 
     def _as_query_executor_ptr(self):
         return self._ptr
@@ -50,12 +57,12 @@ class QueryExecutor(object._SharedObject, _QueryExecutorCommon):
                 o = component_class
                 raise TypeError("'{}' is not a component class object".format(o))
 
-        utils._check_str(object_name)
+        bt2_utils._check_str(object_name)
 
         if params is None:
             params_ptr = native_bt.value_null
         else:
-            params = bt2.create_value(params)
+            params = bt2_value.create_value(params)
             params_ptr = params._ptr
 
         cc_ptr = component_class._bt_component_class_ptr()
@@ -73,7 +80,7 @@ class QueryExecutor(object._SharedObject, _QueryExecutorCommon):
         )
 
         if ptr is None:
-            raise bt2._MemoryError("cannot create query executor object")
+            raise bt2_error._MemoryError("cannot create query executor object")
 
         super().__init__(ptr)
 
@@ -83,7 +90,7 @@ class QueryExecutor(object._SharedObject, _QueryExecutorCommon):
         self._method_obj = method_obj
 
     def add_interrupter(self, interrupter):
-        utils._check_type(interrupter, bt2_interrupter.Interrupter)
+        bt2_utils._check_type(interrupter, bt2_interrupter.Interrupter)
         native_bt.query_executor_add_interrupter(self._ptr, interrupter._ptr)
 
     @property
@@ -92,9 +99,11 @@ class QueryExecutor(object._SharedObject, _QueryExecutorCommon):
         return bt2_interrupter.Interrupter._create_from_ptr_and_get_ref(ptr)
 
     def _set_logging_level(self, log_level):
-        utils._check_log_level(log_level)
+        bt2_utils._check_log_level(log_level)
         status = native_bt.query_executor_set_logging_level(self._ptr, log_level)
-        utils._handle_func_status(status, "cannot set query executor's logging level")
+        bt2_utils._handle_func_status(
+            status, "cannot set query executor's logging level"
+        )
 
     logging_level = property(
         fget=_QueryExecutorCommon.logging_level, fset=_set_logging_level
@@ -107,7 +116,7 @@ class QueryExecutor(object._SharedObject, _QueryExecutorCommon):
 
     def query(self):
         status, result_ptr = native_bt.query_executor_query(self._ptr)
-        utils._handle_func_status(status, "cannot query component class")
+        bt2_utils._handle_func_status(status, "cannot query component class")
         assert result_ptr is not None
         return bt2_value._create_from_const_ptr(result_ptr)
 
index f1329623c2633c780c5bc06f2035c70ef6f6c0cf..9d8d1ae9a816138a974c84c5c85a48f4219b743b 100644 (file)
@@ -2,12 +2,13 @@
 #
 # Copyright (c) 2016-2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, utils
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
+from bt2 import value as bt2_value
 from bt2 import object as bt2_object
 from bt2 import packet as bt2_packet
+from bt2 import native_bt
 from bt2 import stream_class as bt2_stream_class
-from bt2 import value as bt2_value
-import bt2
 
 
 def _bt2_trace():
@@ -17,8 +18,14 @@ def _bt2_trace():
 
 
 class _StreamConst(bt2_object._SharedObject):
-    _get_ref = staticmethod(native_bt.stream_get_ref)
-    _put_ref = staticmethod(native_bt.stream_put_ref)
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.stream_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.stream_put_ref(ptr)
+
     _borrow_class_ptr = staticmethod(native_bt.stream_borrow_class_const)
     _borrow_user_attributes_ptr = staticmethod(
         native_bt.stream_borrow_user_attributes_const
@@ -77,13 +84,13 @@ class _Stream(_StreamConst):
         packet_ptr = native_bt.packet_create(self._ptr)
 
         if packet_ptr is None:
-            raise bt2._MemoryError("cannot create packet object")
+            raise bt2_error._MemoryError("cannot create packet object")
 
         return bt2_packet._Packet._create_from_ptr(packet_ptr)
 
     def _user_attributes(self, user_attributes):
         value = bt2_value.create_value(user_attributes)
-        utils._check_type(value, bt2_value.MapValue)
+        bt2_utils._check_type(value, bt2_value.MapValue)
         native_bt.stream_set_user_attributes(self._ptr, value._ptr)
 
     _user_attributes = property(
@@ -91,7 +98,7 @@ class _Stream(_StreamConst):
     )
 
     def _name(self, name):
-        utils._check_str(name)
+        bt2_utils._check_str(name)
         native_bt.stream_set_name(self._ptr, name)
 
     _name = property(fget=_StreamConst.name.fget, fset=_name)
index 885ba746dae01ae37b9db9ae5a0873453fe58804..5c6faa07fd6c25c3bd1352ffcb94e9534646200f 100644 (file)
@@ -2,13 +2,16 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-from bt2 import field_class as bt2_field_class
-from bt2 import event_class as bt2_event_class
-from bt2 import clock_class as bt2_clock_class
-from bt2 import value as bt2_value
 import collections.abc
 
+from bt2 import utils as bt2_utils
+from bt2 import value as bt2_value
+from bt2 import object as bt2_object
+from bt2 import native_bt
+from bt2 import clock_class as bt2_clock_class
+from bt2 import event_class as bt2_event_class
+from bt2 import field_class as bt2_field_class
+
 
 def _bt2_trace_class():
     from bt2 import trace_class as bt2_trace_class
@@ -16,9 +19,15 @@ def _bt2_trace_class():
     return bt2_trace_class
 
 
-class _StreamClassConst(object._SharedObject, collections.abc.Mapping):
-    _get_ref = staticmethod(native_bt.stream_class_get_ref)
-    _put_ref = staticmethod(native_bt.stream_class_put_ref)
+class _StreamClassConst(bt2_object._SharedObject, collections.abc.Mapping):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.stream_class_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.stream_class_put_ref(ptr)
+
     _borrow_event_class_ptr_by_id = staticmethod(
         native_bt.stream_class_borrow_event_class_by_id_const
     )
@@ -46,7 +55,7 @@ class _StreamClassConst(object._SharedObject, collections.abc.Mapping):
     _clock_class_cls = property(lambda _: bt2_clock_class._ClockClassConst)
 
     def __getitem__(self, key):
-        utils._check_int64(key)
+        bt2_utils._check_int64(key)
         ec_ptr = self._borrow_event_class_ptr_by_id(self._ptr, key)
 
         if ec_ptr is None:
@@ -165,8 +174,14 @@ class _StreamClassConst(object._SharedObject, collections.abc.Mapping):
 
 
 class _StreamClass(_StreamClassConst):
-    _get_ref = staticmethod(native_bt.stream_class_get_ref)
-    _put_ref = staticmethod(native_bt.stream_class_put_ref)
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.stream_class_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.stream_class_put_ref(ptr)
+
     _borrow_event_class_ptr_by_id = staticmethod(
         native_bt.stream_class_borrow_event_class_by_id
     )
@@ -224,7 +239,7 @@ class _StreamClass(_StreamClassConst):
                     "id not provided, but stream class does not assign automatic event class ids"
                 )
 
-            utils._check_uint64(id)
+            bt2_utils._check_uint64(id)
             ec_ptr = native_bt.event_class_create_with_id(self._ptr, id)
 
         event_class = bt2_event_class._EventClass._create_from_ptr(ec_ptr)
@@ -257,7 +272,7 @@ class _StreamClass(_StreamClassConst):
 
     def _name(self, name):
         status = native_bt.stream_class_set_name(self._ptr, name)
-        utils._handle_func_status(status, "cannot set stream class object's name")
+        bt2_utils._handle_func_status(status, "cannot set stream class object's name")
 
     _name = property(fset=_name)
 
@@ -294,7 +309,7 @@ class _StreamClass(_StreamClassConst):
         status = native_bt.stream_class_set_packet_context_field_class(
             self._ptr, packet_context_field_class._ptr
         )
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot set stream class' packet context field class"
         )
 
@@ -303,7 +318,7 @@ class _StreamClass(_StreamClassConst):
     def _event_common_context_field_class(self, event_common_context_field_class):
         set_context_fn = native_bt.stream_class_set_event_common_context_field_class
         status = set_context_fn(self._ptr, event_common_context_field_class._ptr)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot set stream class' event context field type"
         )
 
@@ -334,12 +349,12 @@ class _StreamClass(_StreamClassConst):
     ):
         # Name
         if name is not None:
-            utils._check_str(name)
+            bt2_utils._check_str(name)
 
         # User attributes
         if user_attributes is not None:
             value = bt2_value.create_value(user_attributes)
-            utils._check_type(value, bt2_value.MapValue)
+            bt2_utils._check_type(value, bt2_value.MapValue)
 
         # Packet context field class
         if packet_context_field_class is not None:
@@ -348,30 +363,30 @@ class _StreamClass(_StreamClassConst):
                     "cannot have a packet context field class without supporting packets"
                 )
 
-            utils._check_type(
+            bt2_utils._check_type(
                 packet_context_field_class, bt2_field_class._StructureFieldClass
             )
 
         # Event common context field class
         if event_common_context_field_class is not None:
-            utils._check_type(
+            bt2_utils._check_type(
                 event_common_context_field_class, bt2_field_class._StructureFieldClass
             )
 
         # Default clock class
         if default_clock_class is not None:
-            utils._check_type(default_clock_class, bt2_clock_class._ClockClass)
+            bt2_utils._check_type(default_clock_class, bt2_clock_class._ClockClass)
 
         # Assigns automatic event class id
-        utils._check_bool(assigns_automatic_event_class_id)
+        bt2_utils._check_bool(assigns_automatic_event_class_id)
 
         # Assigns automatic stream id
-        utils._check_bool(assigns_automatic_stream_id)
+        bt2_utils._check_bool(assigns_automatic_stream_id)
 
         # Packets
-        utils._check_bool(supports_packets)
-        utils._check_bool(packets_have_beginning_default_clock_snapshot)
-        utils._check_bool(packets_have_end_default_clock_snapshot)
+        bt2_utils._check_bool(supports_packets)
+        bt2_utils._check_bool(packets_have_beginning_default_clock_snapshot)
+        bt2_utils._check_bool(packets_have_end_default_clock_snapshot)
 
         if not supports_packets:
             if packets_have_beginning_default_clock_snapshot:
@@ -384,8 +399,8 @@ class _StreamClass(_StreamClassConst):
                 )
 
         # Discarded events
-        utils._check_bool(supports_discarded_events)
-        utils._check_bool(discarded_events_have_default_clock_snapshots)
+        bt2_utils._check_bool(supports_discarded_events)
+        bt2_utils._check_bool(discarded_events_have_default_clock_snapshots)
 
         if discarded_events_have_default_clock_snapshots:
             if not supports_discarded_events:
@@ -399,8 +414,8 @@ class _StreamClass(_StreamClassConst):
                 )
 
         # Discarded packets
-        utils._check_bool(supports_discarded_packets)
-        utils._check_bool(discarded_packets_have_default_clock_snapshots)
+        bt2_utils._check_bool(supports_discarded_packets)
+        bt2_utils._check_bool(discarded_packets_have_default_clock_snapshots)
 
         if supports_discarded_packets and not supports_packets:
             raise ValueError(
index 1c56b034c400789beade4d3aff1f1fa55bcb2678..a7038b2880757ec0f713f36eac8196be853a14bf 100644 (file)
@@ -2,14 +2,17 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
+import uuid as uuidp
+import functools
 import collections.abc
+
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
 from bt2 import value as bt2_value
+from bt2 import object as bt2_object
 from bt2 import stream as bt2_stream
+from bt2 import native_bt
 from bt2 import stream_class as bt2_stream_class
-import bt2
-import functools
-import uuid as uuidp
 
 
 def _bt2_trace_class():
@@ -27,7 +30,7 @@ class _TraceEnvironmentConst(collections.abc.Mapping):
         self._trace = trace
 
     def __getitem__(self, key):
-        utils._check_str(key)
+        bt2_utils._check_str(key)
 
         borrow_entry_fn = native_bt.trace_borrow_environment_entry_value_by_name_const
         value_ptr = borrow_entry_fn(self._trace._ptr, key)
@@ -66,15 +69,23 @@ class _TraceEnvironment(_TraceEnvironmentConst, collections.abc.MutableMapping):
             raise TypeError("expected str or int, got {}".format(type(value)))
 
         status = set_env_entry_fn(self._trace._ptr, key, value)
-        utils._handle_func_status(status, "cannot set trace object's environment entry")
+        bt2_utils._handle_func_status(
+            status, "cannot set trace object's environment entry"
+        )
 
     def __delitem__(self, key):
         raise NotImplementedError
 
 
-class _TraceConst(object._SharedObject, collections.abc.Mapping):
-    _get_ref = staticmethod(native_bt.trace_get_ref)
-    _put_ref = staticmethod(native_bt.trace_put_ref)
+class _TraceConst(bt2_object._SharedObject, collections.abc.Mapping):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.trace_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.trace_put_ref(ptr)
+
     _borrow_stream_ptr_by_id = staticmethod(native_bt.trace_borrow_stream_by_id_const)
     _borrow_stream_ptr_by_index = staticmethod(
         native_bt.trace_borrow_stream_by_index_const
@@ -96,7 +107,7 @@ class _TraceConst(object._SharedObject, collections.abc.Mapping):
         return count
 
     def __getitem__(self, id):
-        utils._check_uint64(id)
+        bt2_utils._check_uint64(id)
 
         stream_ptr = self._borrow_stream_ptr_by_id(self._ptr, id)
 
@@ -148,7 +159,7 @@ class _TraceConst(object._SharedObject, collections.abc.Mapping):
         if not callable(listener):
             raise TypeError("'listener' parameter is not callable")
 
-        handle = utils._ListenerHandle(self.addr)
+        handle = bt2_utils._ListenerHandle(self.addr)
 
         fn = native_bt.bt2_trace_add_destruction_listener
         listener_from_native = functools.partial(
@@ -156,7 +167,7 @@ class _TraceConst(object._SharedObject, collections.abc.Mapping):
         )
 
         status, listener_id = fn(self._ptr, listener_from_native)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot add destruction listener to trace object"
         )
 
@@ -165,7 +176,7 @@ class _TraceConst(object._SharedObject, collections.abc.Mapping):
         return handle
 
     def remove_destruction_listener(self, listener_handle):
-        utils._check_type(listener_handle, utils._ListenerHandle)
+        bt2_utils._check_type(listener_handle, bt2_utils._ListenerHandle)
 
         if listener_handle._addr != self.addr:
             raise ValueError(
@@ -178,7 +189,7 @@ class _TraceConst(object._SharedObject, collections.abc.Mapping):
         status = native_bt.trace_remove_destruction_listener(
             self._ptr, listener_handle._listener_id
         )
-        utils._handle_func_status(status)
+        bt2_utils._handle_func_status(status)
         listener_handle._invalidate()
 
 
@@ -195,27 +206,27 @@ class _Trace(_TraceConst):
     _trace_env_pycls = property(lambda _: _TraceEnvironment)
 
     def _name(self, name):
-        utils._check_str(name)
+        bt2_utils._check_str(name)
         status = native_bt.trace_set_name(self._ptr, name)
-        utils._handle_func_status(status, "cannot set trace class object's name")
+        bt2_utils._handle_func_status(status, "cannot set trace class object's name")
 
     _name = property(fset=_name)
 
     def _user_attributes(self, user_attributes):
         value = bt2_value.create_value(user_attributes)
-        utils._check_type(value, bt2_value.MapValue)
+        bt2_utils._check_type(value, bt2_value.MapValue)
         native_bt.trace_set_user_attributes(self._ptr, value._ptr)
 
     _user_attributes = property(fset=_user_attributes)
 
     def _uuid(self, uuid):
-        utils._check_type(uuid, uuidp.UUID)
+        bt2_utils._check_type(uuid, uuidp.UUID)
         native_bt.trace_set_uuid(self._ptr, uuid.bytes)
 
     _uuid = property(fset=_uuid)
 
     def create_stream(self, stream_class, id=None, name=None, user_attributes=None):
-        utils._check_type(stream_class, bt2_stream_class._StreamClass)
+        bt2_utils._check_type(stream_class, bt2_stream_class._StreamClass)
 
         if stream_class.assigns_automatic_stream_id:
             if id is not None:
@@ -230,13 +241,13 @@ class _Trace(_TraceConst):
                     "id not provided, but stream class does not assign automatic stream ids"
                 )
 
-            utils._check_uint64(id)
+            bt2_utils._check_uint64(id)
             stream_ptr = native_bt.stream_create_with_id(
                 stream_class._ptr, self._ptr, id
             )
 
         if stream_ptr is None:
-            raise bt2._MemoryError("cannot create stream object")
+            raise bt2_error._MemoryError("cannot create stream object")
 
         stream = bt2_stream._Stream._create_from_ptr(stream_ptr)
 
index f66f385527934d5348e8e83ed9aa3730396db293..a691da30d1492748a950d12cff8ea0a06fb8614d 100644 (file)
@@ -4,28 +4,37 @@
 # Copyright (c) 2018 Francis Deslauriers <francis.deslauriers@efficios.com>
 # Copyright (c) 2019 Simon Marchi <simon.marchi@efficios.com>
 
-from bt2 import native_bt, utils, object
-from bt2 import stream_class as bt2_stream_class
-from bt2 import field_class as bt2_field_class
-from bt2 import integer_range_set as bt2_integer_range_set
+import functools
+import collections.abc
+
+from bt2 import error as bt2_error
 from bt2 import trace as bt2_trace
+from bt2 import utils as bt2_utils
 from bt2 import value as bt2_value
-import collections.abc
-import functools
-import bt2
+from bt2 import object as bt2_object
+from bt2 import native_bt
+from bt2 import field_class as bt2_field_class
+from bt2 import stream_class as bt2_stream_class
+from bt2 import integer_range_set as bt2_integer_range_set
 
 
 def _trace_class_destruction_listener_from_native(
     user_listener, handle, trace_class_ptr
 ):
-    trace_class = _TraceClass._create_from_ptr_and_get_ref(trace_class_ptr)
+    trace_class = _TraceClassConst._create_from_ptr_and_get_ref(trace_class_ptr)
     user_listener(trace_class)
     handle._invalidate()
 
 
-class _TraceClassConst(object._SharedObject, collections.abc.Mapping):
-    _get_ref = staticmethod(native_bt.trace_class_get_ref)
-    _put_ref = staticmethod(native_bt.trace_class_put_ref)
+class _TraceClassConst(bt2_object._SharedObject, collections.abc.Mapping):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.trace_class_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.trace_class_put_ref(ptr)
+
     _borrow_stream_class_ptr_by_index = staticmethod(
         native_bt.trace_class_borrow_stream_class_by_index_const
     )
@@ -56,7 +65,7 @@ class _TraceClassConst(object._SharedObject, collections.abc.Mapping):
     # Get a stream class by stream id.
 
     def __getitem__(self, key):
-        utils._check_uint64(key)
+        bt2_utils._check_uint64(key)
 
         sc_ptr = self._borrow_stream_class_ptr_by_id(self._ptr, key)
         if sc_ptr is None:
@@ -81,11 +90,10 @@ class _TraceClassConst(object._SharedObject, collections.abc.Mapping):
     # Add a listener to be called when the trace class is destroyed.
 
     def add_destruction_listener(self, listener):
-
         if not callable(listener):
             raise TypeError("'listener' parameter is not callable")
 
-        handle = utils._ListenerHandle(self.addr)
+        handle = bt2_utils._ListenerHandle(self.addr)
 
         listener_from_native = functools.partial(
             _trace_class_destruction_listener_from_native, listener, handle
@@ -93,7 +101,7 @@ class _TraceClassConst(object._SharedObject, collections.abc.Mapping):
 
         fn = native_bt.bt2_trace_class_add_destruction_listener
         status, listener_id = fn(self._ptr, listener_from_native)
-        utils._handle_func_status(
+        bt2_utils._handle_func_status(
             status, "cannot add destruction listener to trace class object"
         )
 
@@ -102,7 +110,7 @@ class _TraceClassConst(object._SharedObject, collections.abc.Mapping):
         return handle
 
     def remove_destruction_listener(self, listener_handle):
-        utils._check_type(listener_handle, utils._ListenerHandle)
+        bt2_utils._check_type(listener_handle, bt2_utils._ListenerHandle)
 
         if listener_handle._addr != self.addr:
             raise ValueError(
@@ -117,7 +125,7 @@ class _TraceClassConst(object._SharedObject, collections.abc.Mapping):
         status = native_bt.trace_class_remove_destruction_listener(
             self._ptr, listener_handle._listener_id
         )
-        utils._handle_func_status(status)
+        bt2_utils._handle_func_status(status)
         listener_handle._invalidate()
 
 
@@ -142,7 +150,7 @@ class _TraceClass(_TraceClassConst):
         trace_ptr = native_bt.trace_create(self._ptr)
 
         if trace_ptr is None:
-            raise bt2._MemoryError("cannot create trace class object")
+            raise bt2_error._MemoryError("cannot create trace class object")
 
         trace = bt2_trace._Trace._create_from_ptr(trace_ptr)
 
@@ -210,7 +218,7 @@ class _TraceClass(_TraceClassConst):
                     "id not provided, but trace class does not assign automatic stream class ids"
                 )
 
-            utils._check_uint64(id)
+            bt2_utils._check_uint64(id)
             sc_ptr = native_bt.stream_class_create_with_id(self._ptr, id)
 
         sc = bt2_stream_class._StreamClass._create_from_ptr(sc_ptr)
@@ -255,13 +263,13 @@ class _TraceClass(_TraceClassConst):
 
     def _user_attributes(self, user_attributes):
         value = bt2_value.create_value(user_attributes)
-        utils._check_type(value, bt2_value.MapValue)
+        bt2_utils._check_type(value, bt2_value.MapValue)
         native_bt.trace_class_set_user_attributes(self._ptr, value._ptr)
 
     _user_attributes = property(fset=_user_attributes)
 
     def _assigns_automatic_stream_class_id(self, auto_id):
-        utils._check_bool(auto_id)
+        bt2_utils._check_bool(auto_id)
         return native_bt.trace_class_set_assigns_automatic_stream_class_id(
             self._ptr, auto_id
         )
@@ -274,7 +282,9 @@ class _TraceClass(_TraceClassConst):
 
     def _check_field_class_create_status(self, ptr, type_name):
         if ptr is None:
-            raise bt2._MemoryError("cannot create {} field class".format(type_name))
+            raise bt2_error._MemoryError(
+                "cannot create {} field class".format(type_name)
+            )
 
     @staticmethod
     def _set_field_class_user_attrs(fc, user_attributes):
@@ -289,7 +299,7 @@ class _TraceClass(_TraceClassConst):
         return fc
 
     def create_bit_array_field_class(self, length, user_attributes=None):
-        utils._check_uint64(length)
+        bt2_utils._check_uint64(length)
 
         if length < 1 or length > 64:
             raise ValueError(
@@ -414,8 +424,8 @@ class _TraceClass(_TraceClassConst):
         return fc
 
     def create_static_array_field_class(self, elem_fc, length, user_attributes=None):
-        utils._check_type(elem_fc, bt2_field_class._FieldClass)
-        utils._check_uint64(length)
+        bt2_utils._check_type(elem_fc, bt2_field_class._FieldClass)
+        bt2_utils._check_uint64(length)
         ptr = native_bt.field_class_array_static_create(self._ptr, elem_fc._ptr, length)
         self._check_field_class_create_status(ptr, "static array")
         fc = bt2_field_class._StaticArrayFieldClass._create_from_ptr(ptr)
@@ -425,11 +435,11 @@ class _TraceClass(_TraceClassConst):
     def create_dynamic_array_field_class(
         self, elem_fc, length_fc=None, user_attributes=None
     ):
-        utils._check_type(elem_fc, bt2_field_class._FieldClass)
+        bt2_utils._check_type(elem_fc, bt2_field_class._FieldClass)
         length_fc_ptr = None
 
         if length_fc is not None:
-            utils._check_type(length_fc, bt2_field_class._UnsignedIntegerFieldClass)
+            bt2_utils._check_type(length_fc, bt2_field_class._UnsignedIntegerFieldClass)
             length_fc_ptr = length_fc._ptr
 
         ptr = native_bt.field_class_array_dynamic_create(
@@ -443,7 +453,7 @@ class _TraceClass(_TraceClassConst):
     def create_option_without_selector_field_class(
         self, content_fc, user_attributes=None
     ):
-        utils._check_type(content_fc, bt2_field_class._FieldClass)
+        bt2_utils._check_type(content_fc, bt2_field_class._FieldClass)
         ptr = native_bt.field_class_option_without_selector_create(
             self._ptr, content_fc._ptr
         )
@@ -455,9 +465,9 @@ class _TraceClass(_TraceClassConst):
     def create_option_with_bool_selector_field_class(
         self, content_fc, selector_fc, selector_is_reversed=False, user_attributes=None
     ):
-        utils._check_type(content_fc, bt2_field_class._FieldClass)
-        utils._check_bool(selector_is_reversed)
-        utils._check_type(selector_fc, bt2_field_class._BoolFieldClass)
+        bt2_utils._check_type(content_fc, bt2_field_class._FieldClass)
+        bt2_utils._check_bool(selector_is_reversed)
+        bt2_utils._check_type(selector_fc, bt2_field_class._BoolFieldClass)
         ptr = native_bt.field_class_option_with_selector_field_bool_create(
             self._ptr, content_fc._ptr, selector_fc._ptr
         )
@@ -470,19 +480,19 @@ class _TraceClass(_TraceClassConst):
     def create_option_with_integer_selector_field_class(
         self, content_fc, selector_fc, ranges, user_attributes=None
     ):
-        utils._check_type(content_fc, bt2_field_class._FieldClass)
-        utils._check_type(selector_fc, bt2_field_class._IntegerFieldClass)
+        bt2_utils._check_type(content_fc, bt2_field_class._FieldClass)
+        bt2_utils._check_type(selector_fc, bt2_field_class._IntegerFieldClass)
 
         if len(ranges) == 0:
             raise ValueError("integer range set is empty")
 
         if isinstance(selector_fc, bt2_field_class._UnsignedIntegerFieldClass):
-            utils._check_type(ranges, bt2_integer_range_set.UnsignedIntegerRangeSet)
+            bt2_utils._check_type(ranges, bt2_integer_range_set.UnsignedIntegerRangeSet)
             ptr = native_bt.field_class_option_with_selector_field_integer_unsigned_create(
                 self._ptr, content_fc._ptr, selector_fc._ptr, ranges._ptr
             )
         else:
-            utils._check_type(ranges, bt2_integer_range_set.SignedIntegerRangeSet)
+            bt2_utils._check_type(ranges, bt2_integer_range_set.SignedIntegerRangeSet)
             ptr = (
                 native_bt.field_class_option_with_selector_field_integer_signed_create(
                     self._ptr, content_fc._ptr, selector_fc._ptr, ranges._ptr
@@ -498,7 +508,7 @@ class _TraceClass(_TraceClassConst):
         selector_fc_ptr = None
 
         if selector_fc is not None:
-            utils._check_type(selector_fc, bt2_field_class._IntegerFieldClass)
+            bt2_utils._check_type(selector_fc, bt2_field_class._IntegerFieldClass)
             selector_fc_ptr = selector_fc._ptr
 
         ptr = native_bt.field_class_variant_create(self._ptr, selector_fc_ptr)
index ed474cd09c92a04475e1b348f681ef719605badd..3a6b95212137980f6a98b1f0d3cf336a7f87f872 100644 (file)
@@ -2,18 +2,24 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import utils, native_bt
-import bt2
+import numbers
+import datetime
 import itertools
-from bt2 import message_iterator as bt2_message_iterator
+from collections import namedtuple
+
+from bt2 import mip as bt2_mip
 from bt2 import port as bt2_port
-from bt2 import component as bt2_component
+from bt2 import error as bt2_error
+from bt2 import graph as bt2_graph
+from bt2 import utils as bt2_utils
 from bt2 import value as bt2_value
 from bt2 import plugin as bt2_plugin
-import datetime
-from collections import namedtuple
-import numbers
-
+from bt2 import logging as bt2_logging
+from bt2 import component as bt2_component
+from bt2 import native_bt
+from bt2 import query_executor as bt2_query_executor
+from bt2 import message_iterator as bt2_message_iterator
+from bt2 import component_descriptor as bt2_component_descriptor
 
 # a pair of component and ComponentSpec
 _ComponentAndSpec = namedtuple("_ComponentAndSpec", ["comp", "spec"])
@@ -24,9 +30,9 @@ class _BaseComponentSpec:
     # TraceCollectionMessageIterator.
     def __init__(self, params, obj, logging_level):
         if logging_level is not None:
-            utils._check_log_level(logging_level)
+            bt2_utils._check_log_level(logging_level)
 
-        self._params = bt2.create_value(params)
+        self._params = bt2_value.create_value(params)
         self._obj = obj
         self._logging_level = logging_level
 
@@ -50,7 +56,7 @@ class ComponentSpec(_BaseComponentSpec):
         component_class,
         params=None,
         obj=None,
-        logging_level=bt2.LoggingLevel.NONE,
+        logging_level=bt2_logging.LoggingLevel.NONE,
     ):
         if type(params) is str:
             params = {"inputs": [params]}
@@ -59,12 +65,16 @@ class ComponentSpec(_BaseComponentSpec):
 
         is_cc_object = isinstance(
             component_class,
-            (bt2._SourceComponentClassConst, bt2._FilterComponentClassConst),
+            (
+                bt2_component._SourceComponentClassConst,
+                bt2_component._FilterComponentClassConst,
+            ),
         )
         is_user_cc_type = isinstance(
             component_class, bt2_component._UserComponentType
         ) and issubclass(
-            component_class, (bt2._UserSourceComponent, bt2._UserFilterComponent)
+            component_class,
+            (bt2_component._UserSourceComponent, bt2_component._UserFilterComponent),
         )
 
         if not is_cc_object and not is_user_cc_type:
@@ -87,9 +97,9 @@ class ComponentSpec(_BaseComponentSpec):
         component_class_name,
         params=None,
         obj=None,
-        logging_level=bt2.LoggingLevel.NONE,
+        logging_level=bt2_logging.LoggingLevel.NONE,
     ):
-        plugin = bt2.find_plugin(plugin_name)
+        plugin = bt2_plugin.find_plugin(plugin_name)
 
         if plugin is None:
             raise ValueError("no such plugin: {}".format(plugin_name))
@@ -124,54 +134,54 @@ class AutoSourceComponentSpec(_BaseComponentSpec):
 def _auto_discover_source_component_specs(auto_source_comp_specs, plugin_set):
     # Transform a list of `AutoSourceComponentSpec` in a list of `ComponentSpec`
     # using the automatic source discovery mechanism.
-    inputs = bt2.ArrayValue([spec.input for spec in auto_source_comp_specs])
+    inputs = bt2_value.ArrayValue([spec.input for spec in auto_source_comp_specs])
 
     if plugin_set is None:
-        plugin_set = bt2.find_plugins()
+        plugin_set = bt2_plugin.find_plugins()
     else:
-        utils._check_type(plugin_set, bt2_plugin._PluginSet)
+        bt2_utils._check_type(plugin_set, bt2_plugin._PluginSet)
 
     res_ptr = native_bt.bt2_auto_discover_source_components(
         inputs._ptr, plugin_set._ptr
     )
 
     if res_ptr is None:
-        raise bt2._MemoryError("cannot auto discover source components")
+        raise bt2_error._MemoryError("cannot auto discover source components")
 
     res = bt2_value._create_from_ptr(res_ptr)
 
-    assert type(res) == bt2.MapValue
+    assert type(res) is bt2_value.MapValue
     assert "status" in res
 
     status = res["status"]
-    utils._handle_func_status(status, "cannot auto-discover source components")
+    bt2_utils._handle_func_status(status, "cannot auto-discover source components")
 
     comp_specs = []
     comp_specs_raw = res["results"]
-    assert type(comp_specs_raw) == bt2.ArrayValue
+    assert type(comp_specs_raw) is bt2_value.ArrayValue
 
     used_input_indices = set()
 
     for comp_spec_raw in comp_specs_raw:
-        assert type(comp_spec_raw) == bt2.ArrayValue
+        assert type(comp_spec_raw) is bt2_value.ArrayValue
         assert len(comp_spec_raw) == 4
 
         plugin_name = comp_spec_raw[0]
-        assert type(plugin_name) == bt2.StringValue
+        assert type(plugin_name) is bt2_value.StringValue
         plugin_name = str(plugin_name)
 
         class_name = comp_spec_raw[1]
-        assert type(class_name) == bt2.StringValue
+        assert type(class_name) is bt2_value.StringValue
         class_name = str(class_name)
 
         comp_inputs = comp_spec_raw[2]
-        assert type(comp_inputs) == bt2.ArrayValue
+        assert type(comp_inputs) is bt2_value.ArrayValue
 
         comp_orig_indices = comp_spec_raw[3]
         assert type(comp_orig_indices)
 
-        params = bt2.MapValue()
-        logging_level = bt2.LoggingLevel.NONE
+        params = bt2_value.MapValue()
+        logging_level = bt2_logging.LoggingLevel.NONE
         obj = None
 
         # Compute `params` for this component by piling up params given to all
@@ -264,7 +274,7 @@ class TraceCollectionMessageIterator(bt2_message_iterator._MessageIterator):
         end=None,
         plugin_set=None,
     ):
-        utils._check_bool(stream_intersection_mode)
+        bt2_utils._check_bool(stream_intersection_mode)
         self._stream_intersection_mode = stream_intersection_mode
         self._begin_ns = _get_ns(begin)
         self._end_ns = _get_ns(end)
@@ -329,7 +339,7 @@ class TraceCollectionMessageIterator(bt2_message_iterator._MessageIterator):
             # Query the port's component for the `babeltrace.trace-infos`
             # object which contains the range for each stream, from which we can
             # compute the intersection of the streams in each trace.
-            query_exec = bt2.QueryExecutor(
+            query_exec = bt2_query_executor.QueryExecutor(
                 src_comp_and_spec.spec.component_class,
                 "babeltrace.trace-infos",
                 src_comp_and_spec.spec.params,
@@ -391,7 +401,7 @@ class TraceCollectionMessageIterator(bt2_message_iterator._MessageIterator):
         return self._create_trimmer(begin, end, name)
 
     def _create_muxer(self):
-        plugin = bt2.find_plugin("utils")
+        plugin = bt2_plugin.find_plugin("utils")
 
         if plugin is None:
             raise RuntimeError('cannot find "utils" plugin (needed for the muxer)')
@@ -405,7 +415,7 @@ class TraceCollectionMessageIterator(bt2_message_iterator._MessageIterator):
         return self._graph.add_component(comp_cls, "muxer")
 
     def _create_trimmer(self, begin_ns, end_ns, name):
-        plugin = bt2.find_plugin("utils")
+        plugin = bt2_plugin.find_plugin("utils")
 
         if plugin is None:
             raise RuntimeError('cannot find "utils" plugin (needed for the trimmer)')
@@ -491,7 +501,7 @@ class TraceCollectionMessageIterator(bt2_message_iterator._MessageIterator):
         def append_comp_specs_descriptors(descriptors, comp_specs):
             for comp_spec in comp_specs:
                 descriptors.append(
-                    bt2.ComponentDescriptor(
+                    bt2_component_descriptor.ComponentDescriptor(
                         comp_spec.component_class, comp_spec.params, comp_spec.obj
                     )
                 )
@@ -507,7 +517,7 @@ class TraceCollectionMessageIterator(bt2_message_iterator._MessageIterator):
             )
             append_comp_specs_descriptors(descriptors, [comp_spec])
 
-        mip_version = bt2.get_greatest_operative_mip_version(descriptors)
+        mip_version = bt2_mip.get_greatest_operative_mip_version(descriptors)
 
         if mip_version is None:
             msg = "failed to find an operative message interchange protocol version (components are not interoperable)"
@@ -516,7 +526,7 @@ class TraceCollectionMessageIterator(bt2_message_iterator._MessageIterator):
         return mip_version
 
     def _build_graph(self):
-        self._graph = bt2.Graph(self._get_greatest_operative_mip_version())
+        self._graph = bt2_graph.Graph(self._get_greatest_operative_mip_version())
         self._graph.add_port_added_listener(self._graph_port_added)
         self._muxer_comp = self._create_muxer()
 
index 393f7ed883ec94f651355b7250d02601b190ff1f..fc6d24ce79d8b9c4915238b87ed3f7205c97b63f 100644 (file)
@@ -2,11 +2,33 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-import bt2
+
+from bt2 import error as bt2_error
 from bt2 import logging as bt2_logging
 from bt2 import native_bt
 
 
+class UnknownObject(Exception):
+    """
+    Raised when a component class handles a query for an object it doesn't
+    know about.
+    """
+
+    pass
+
+
+class _OverflowError(bt2_error._Error, OverflowError):
+    pass
+
+
+class TryAgain(Exception):
+    pass
+
+
+class Stop(StopIteration):
+    pass
+
+
 def _check_bool(o):
     if not isinstance(o, bool):
         raise TypeError("'{}' is not a 'bool' object".format(o.__class__.__name__))
@@ -119,30 +141,27 @@ def _handle_func_status(status, msg=None):
 
     if status == native_bt.__BT_FUNC_STATUS_ERROR:
         assert msg is not None
-        raise bt2._Error(msg)
+        raise bt2_error._Error(msg)
     elif status == native_bt.__BT_FUNC_STATUS_MEMORY_ERROR:
         assert msg is not None
-        raise bt2._MemoryError(msg)
+        raise bt2_error._MemoryError(msg)
     elif status == native_bt.__BT_FUNC_STATUS_END:
         if msg is None:
-            raise bt2.Stop
+            raise Stop
         else:
-            raise bt2.Stop(msg)
+            raise Stop(msg)
     elif status == native_bt.__BT_FUNC_STATUS_AGAIN:
         if msg is None:
-            raise bt2.TryAgain
+            raise TryAgain
         else:
-            raise bt2.TryAgain(msg)
+            raise TryAgain(msg)
     elif status == native_bt.__BT_FUNC_STATUS_OVERFLOW_ERROR:
-        if msg is None:
-            raise bt2._OverflowError
-        else:
-            raise bt2._OverflowError(msg)
+        raise _OverflowError(msg)
     elif status == native_bt.__BT_FUNC_STATUS_UNKNOWN_OBJECT:
         if msg is None:
-            raise bt2.UnknownObject
+            raise UnknownObject
         else:
-            raise bt2.UnknownObject(msg)
+            raise UnknownObject(msg)
     else:
         assert False
 
index 04016dec1a404d69003558861cc4a5d47a982717..c938ff9046bba925db8b2d09b9f73f572c3aa4d4 100644 (file)
@@ -2,13 +2,16 @@
 #
 # Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
 
-from bt2 import native_bt, object, utils
-import collections.abc
-import functools
-import numbers
-import math
 import abc
-import bt2
+import math
+import numbers
+import functools
+import collections.abc
+
+from bt2 import error as bt2_error
+from bt2 import utils as bt2_utils
+from bt2 import object as bt2_object
+from bt2 import native_bt
 
 
 def _create_from_ptr_template(ptr, object_map):
@@ -81,9 +84,15 @@ def create_value(value):
     )
 
 
-class _ValueConst(object._SharedObject, metaclass=abc.ABCMeta):
-    _get_ref = staticmethod(native_bt.value_get_ref)
-    _put_ref = staticmethod(native_bt.value_put_ref)
+class _ValueConst(bt2_object._SharedObject, metaclass=abc.ABCMeta):
+    @staticmethod
+    def _get_ref(ptr):
+        native_bt.value_get_ref(ptr)
+
+    @staticmethod
+    def _put_ref(ptr):
+        native_bt.value_put_ref(ptr)
+
     _create_value_from_ptr = staticmethod(_create_from_const_ptr)
     _create_value_from_ptr_and_get_ref = staticmethod(
         _create_from_const_ptr_and_get_ref
@@ -94,7 +103,7 @@ class _ValueConst(object._SharedObject, metaclass=abc.ABCMeta):
 
     def _check_create_status(self, ptr):
         if ptr is None:
-            raise bt2._MemoryError(
+            raise bt2_error._MemoryError(
                 "cannot create {} value object".format(self._NAME.lower())
             )
 
@@ -331,7 +340,7 @@ class _UnsignedIntegerValueConst(_IntegerValueConst):
 
 class UnsignedIntegerValue(_UnsignedIntegerValueConst, _IntegerValue):
     _NAME = "Unsigned integer"
-    _check_int_range = staticmethod(utils._check_uint64)
+    _check_int_range = staticmethod(bt2_utils._check_uint64)
     _create_default_value = staticmethod(native_bt.value_integer_unsigned_create)
     _create_value = staticmethod(native_bt.value_integer_unsigned_create_init)
     _set_value = staticmethod(native_bt.value_integer_unsigned_set)
@@ -344,7 +353,7 @@ class _SignedIntegerValueConst(_IntegerValueConst):
 
 class SignedIntegerValue(_SignedIntegerValueConst, _IntegerValue):
     _NAME = "Signed integer"
-    _check_int_range = staticmethod(utils._check_int64)
+    _check_int_range = staticmethod(bt2_utils._check_int64)
     _create_default_value = staticmethod(native_bt.value_integer_signed_create)
     _create_value = staticmethod(native_bt.value_integer_signed_create_init)
     _set_value = staticmethod(native_bt.value_integer_signed_set)
@@ -393,7 +402,7 @@ class _StringValueConst(collections.abc.Sequence, _Value):
         if isinstance(value, _StringValueConst):
             value = value._value
 
-        utils._check_str(value)
+        bt2_utils._check_str(value)
         return value
 
     @property
@@ -442,7 +451,7 @@ class StringValue(_StringValueConst, _Value):
 
     def _set_value(self, value):
         status = native_bt.value_string_set(self._ptr, self._value_to_str(value))
-        utils._handle_func_status(status)
+        bt2_utils._handle_func_status(status)
 
     value = property(fset=_set_value)
 
@@ -540,7 +549,7 @@ class ArrayValue(_ArrayValueConst, _Container, collections.abc.MutableSequence,
             ptr = value._ptr
 
         status = native_bt.value_array_set_element_by_index(self._ptr, index, ptr)
-        utils._handle_func_status(status)
+        bt2_utils._handle_func_status(status)
 
     def append(self, value):
         value = create_value(value)
@@ -551,7 +560,7 @@ class ArrayValue(_ArrayValueConst, _Container, collections.abc.MutableSequence,
             ptr = value._ptr
 
         status = native_bt.value_array_append_element(self._ptr, ptr)
-        utils._handle_func_status(status)
+        bt2_utils._handle_func_status(status)
 
     def __iadd__(self, iterable):
         # Python will raise a TypeError if there's anything wrong with
@@ -619,7 +628,7 @@ class _MapValueConst(_ContainerConst, collections.abc.Mapping, _ValueConst):
         return native_bt.value_map_has_entry(self._ptr, key)
 
     def _check_key_type(self, key):
-        utils._check_str(key)
+        bt2_utils._check_str(key)
 
     def _check_key(self, key):
         if key not in self:
@@ -664,7 +673,7 @@ class MapValue(_MapValueConst, _Container, collections.abc.MutableMapping, _Valu
             ptr = value._ptr
 
         status = native_bt.value_map_insert_entry(self._ptr, key, ptr)
-        utils._handle_func_status(status)
+        bt2_utils._handle_func_status(status)
 
 
 _TYPE_TO_OBJ = {
index 44b1dc6733985c6198b86784cab4c98df9255100..3c81447ea80a2d8dd928f43aba69feea2bf7f980 100644 (file)
@@ -5,9 +5,39 @@
 
 import sys
 import os
-import distutils.sysconfig
+import shutil
+import subprocess
+
+# Distutils was removed in Python 3.12, use setuptools as an alternative.
+if sys.version_info >= (3, 12):
+    from setuptools import setup, Extension
+else:
+    from distutils.core import setup, Extension
+
+# Starting with Debian's Python 3.10, the default install scheme is
+# 'posix_local' which is a Debian specific scheme based on 'posix_prefix' but
+# with an added 'local' prefix. This is the default so users doing system wide
+# manual installations of python modules end up in '/usr/local'. This
+# interferes with our autotools based install which already defaults to
+# '/usr/local' and expect a provided prefix to be used verbatim.
+#
+# Monkeypatch sysconfig to override this scheme and use 'posix_prefix' instead.
+if sys.version_info >= (3, 10):
+    import sysconfig
+
+    original_get_preferred_scheme = sysconfig.get_preferred_scheme
+
+    def our_get_preferred_scheme(key):
+        scheme = original_get_preferred_scheme(key)
+        if scheme == "posix_local":
+            return "posix_prefix"
+        else:
+            return scheme
+
+    sysconfig.get_preferred_scheme = our_get_preferred_scheme
 
-from distutils.core import setup, Extension
+else:
+    import distutils.sysconfig as sysconfig
 
 PY_PATH_WARN_MSG = """
 -------------------------------------WARNING------------------------------------
@@ -19,7 +49,7 @@ following command to your .bashrc/.zshrc:
 --------------------------------------------------------------------------------
 """
 
-original_get_config_vars = distutils.sysconfig.get_config_vars
+original_get_config_vars = sysconfig.get_config_vars
 
 
 def get_cflags():
@@ -32,12 +62,12 @@ def get_cflags():
 
 
 # distutils performs a similar transformation step on LDSHARED on
-# darwin to use the overriden CC as the default command for LDSHARED
+# darwin to use the overridden CC as the default command for LDSHARED
 # (see distutils' customize_compiler() step in the sysconfig module).
 #
 # This takes it a step further by using our own LDFLAGS (when available)
-# along with the overriden compiler and ensure that flags that are unsupported
-# by either the Python interprter's CC or the overriden CC don't cause a
+# along with the overridden compiler and ensure that flags that are unsupported
+# by either the Python interprter's CC or the overridden CC don't cause a
 # build failure.
 def get_ldshared():
     cc = os.environ.get("CC")
@@ -59,43 +89,78 @@ def get_ldshared():
 
 
 def our_get_config_vars(*args):
-    overridden_config_vars = {
-        "CFLAGS": get_cflags(),
-        "LDSHARED": get_ldshared(),
+    overridden_config_vars_funcs = {
+        "CFLAGS": get_cflags,
+        "LDSHARED": get_ldshared,
     }
 
     if len(args) == 0:
+        # Return a dict with all config vars.
         all_config_vars = original_get_config_vars()
-        for name in overridden_config_vars:
-            all_config_vars[name] = overridden_config_vars[name]
+        for name in overridden_config_vars_funcs:
+            all_config_vars[name] = overridden_config_vars_funcs[name]()
+
         return all_config_vars
+    else:
+        # Return a list with the requested config vars.
+        subset_config_vars = []
+        for name in args:
+            if name in overridden_config_vars_funcs:
+                subset_config_vars.append(overridden_config_vars_funcs[name]())
+            else:
+                subset_config_vars.append(original_get_config_vars(name)[0])
+
+        return subset_config_vars
 
-    subset_config_vars = []
-    for name in args:
-        if name not in overridden_config_vars:
-            [var] = original_get_config_vars(name)
-            subset_config_vars.append(var)
-            continue
 
-        subset_config_vars.append(overridden_config_vars[name])
+sysconfig.get_config_vars = our_get_config_vars
 
-    return subset_config_vars
 
+# Returns 'True' when running on a MinGW system.
+def is_mingw():
+    return sys.platform == "win32" and shutil.which("cygpath") != None
 
-distutils.sysconfig.get_config_vars = our_get_config_vars
+
+# On MinGW systems run 'cygpath -m' on 'path', on other systems return 'path' as-is.
+def cygpath_m(path: str):
+    if is_mingw():
+        return subprocess.check_output(
+            'cygpath -m "{}"'.format(path), shell=True, encoding="utf-8"
+        ).strip("\n")
+
+    return path
+
+
+# On MinGW systems, check CFLAGS and CPPFLAGS for absolute include paths
+# (starts with '-I/') and convert them to valid Windows paths using cygpath.
+if is_mingw():
+    for flagvar in ["CFLAGS", "CPPFLAGS"]:
+        cur_flags = os.getenv(flagvar)
+        if cur_flags != None:
+            new_flags = ""
+            for flag in cur_flags.split():
+                if flag.startswith("-I/"):
+                    flag = "-I{}".format(cygpath_m(flag[2:]))
+
+                new_flags += " {}".format(flag)
+
+            os.environ[flagvar] = new_flags
 
 
 def main():
     babeltrace_ext = Extension(
         "bt2._native_bt",
-        sources=["bt2/native_bt.c", "@srcdir@/bt2/logging.c"],
+        sources=[
+            "bt2/native_bt.c",
+            cygpath_m("@srcdir@/bt2/logging.c"),
+        ],
         libraries=["babeltrace2", "glib-2.0"],
         extra_objects=[
-            "@top_builddir@/src/autodisc/.libs/libbabeltrace2-autodisc.a",
-            "@top_builddir@/src/logging/.libs/libbabeltrace2-logging.a",
-            "@top_builddir@/src/common/.libs/libbabeltrace2-common.a",
-            "@top_builddir@/src/py-common/.libs/libbabeltrace2-py-common.a",
-            "@top_builddir@/src/string-format/.libs/libbabeltrace2-string-format.a",
+            "@top_builddir@/src/autodisc/.libs/libautodisc.a",
+            "@top_builddir@/src/logging/.libs/liblogging.a",
+            "@top_builddir@/src/common/.libs/libcommon.a",
+            "@top_builddir@/src/py-common/.libs/libpy-common.a",
+            "@top_builddir@/src/string-format/.libs/libstring-format.a",
         ],
     )
 
index adbf2bb06b271911cf25e392a7e4650f1b57e6af..4525487809ef4f7fc8643708d8c52a5a1aef6c3e 100644 (file)
@@ -1,6 +1,8 @@
+# SPDX-FileCopyrightText: 2019-2023 EfficiOS, Inc.
 # SPDX-License-Identifier: MIT
 
-PLUGINS_PATH = $(abs_top_builddir)/src/plugins
+include $(top_srcdir)/src/Makefile.common.inc
+
 LTTNG_UTILS_PLUGIN_PATH =
 
 if ENABLE_DEBUG_INFO
@@ -38,7 +40,7 @@ babeltrace2_bin_SOURCES = \
        babeltrace2-plugins.h \
        babeltrace2-query.c \
        babeltrace2-query.h \
-       logging.c \
+       logging.cpp \
        logging.h
 
 # -Wl,--no-as-needed is needed for recent gold linker who seems to think
@@ -57,31 +59,17 @@ EXTRA_babeltrace2_bin_DEPENDENCIES =
 # directly).
 babeltrace2_bin_LDADD = \
        $(top_builddir)/src/argpar/libargpar.la \
-       $(top_builddir)/src/autodisc/libbabeltrace2-autodisc.la \
-       $(top_builddir)/src/param-parse/libbabeltrace2-param-parse.la \
-       $(top_builddir)/src/string-format/libbabeltrace2-string-format.la \
+       $(top_builddir)/src/autodisc/libautodisc.la \
+       $(top_builddir)/src/param-parse/libparam-parse.la \
+       $(top_builddir)/src/string-format/libstring-format.la \
        $(top_builddir)/src/lib/libbabeltrace2.la \
        $(top_builddir)/src/compat/libcompat.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/ctfser/libbabeltrace2-ctfser.la
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/cpp-common/vendor/fmt/libfmt.la \
+       $(top_builddir)/src/logging/liblogging.la \
+       $(top_builddir)/src/ctfser/libctfser.la
 
 if ENABLE_BUILT_IN_PLUGINS
-# Takes a plugin name and outputs the needed LDFLAGS to embed it.
-#
-# The --whole-archive option is important here. From the GNU linker's
-# documentation:
-#
-#     For each archive mentioned on the command line after the
-#     --whole-archive option, include every object file in the archive in
-#     the link, rather than searching the archive for the required object
-#     files.
-#
-# In our case, we find the plugins thanks to special sections in the
-# binary that are filled by plugin objects. If the linker discards those
-# symbols because the CLI does not use them directly, the CLI reports
-# no plugins found (plugins are effectively not embedded).
-pluginarchive = $(LD_WHOLE_ARCHIVE)$(PLUGINS_PATH)/$(1)/.libs/babeltrace-plugin-$(1).a$(LD_NO_WHOLE_ARCHIVE)
 
 # Built-in plugins
 babeltrace2_bin_LDFLAGS += $(call pluginarchive,ctf)
index 580a50cc0f6d2af1fc62a6914ce99a54fa411843..48a442e7ea26f5d531617dbe9dd990c6e5c182b1 100644 (file)
@@ -9,7 +9,6 @@
 
 #include <stdlib.h>
 #include <stdint.h>
-#include <babeltrace2/value.h>
 #include <glib.h>
 #include "babeltrace2-cfg.h"
 
index 0e657720bc5788a489a047d4bc0f8daddb344ca2..e8cb2e1dd541f0328b75d660d19e165f71883ad0 100644 (file)
@@ -42,7 +42,7 @@ enum bt_config_cli_args_status bt_config_cli_args_create_with_default(int argc,
        g_setenv("LIBBABELTRACE2_PLUGIN_PROVIDER_DIR", CONFIG_IN_TREE_PROVIDER_DIR, 0);
 #else
        /*
-        * If the Pyhton plugin provider is disabled, use a non-exitent path to avoid
+        * If the Python plugin provider is disabled, use a non-exitent path to avoid
         * loading the system installed provider if it exit, if the env variable is
         * already set, do not overwrite it.
         */
index 6c187b4f12df7a7b0c2a7b2b88d231138d4c4f2b..2fdadf219958088e13c5122a9fc445945df65464 100644 (file)
@@ -132,17 +132,14 @@ void plugin_comp_cls_names(const char *arg, char **name, char **plugin,
                        *name = NULL;
                        g_string_free(gs_name, TRUE);
                } else {
-                       *name = gs_name->str;
-                       g_string_free(gs_name, FALSE);
+                       *name = g_string_free(gs_name, FALSE);
                }
        } else {
                g_string_free(gs_name, TRUE);
        }
 
-       *plugin = gs_plugin->str;
-       *comp_cls = gs_comp_cls->str;
-       g_string_free(gs_plugin, FALSE);
-       g_string_free(gs_comp_cls, FALSE);
+       *plugin = g_string_free(gs_plugin, FALSE);
+       *comp_cls = g_string_free(gs_comp_cls, FALSE);
        gs_name = NULL;
        gs_plugin = NULL;
        gs_comp_cls = NULL;
@@ -2425,7 +2422,6 @@ const struct argpar_opt_descr convert_options[] = {
        { OPT_OUTPUT, 'w', "output", true },
        { OPT_OUTPUT_FORMAT, 'o', "output-format", true },
        { OPT_PARAMS, 'p', "params", true },
-       { OPT_PLUGIN_PATH, '\0', "plugin-path", true },
        { OPT_RETRY_DURATION, '\0', "retry-duration", true },
        { OPT_RUN_ARGS, '\0', "run-args", false },
        { OPT_RUN_ARGS_0, '\0', "run-args-0", false },
@@ -3088,10 +3084,8 @@ int split_timerange(const char *arg, char **begin, char **end)
 
        BT_ASSERT(begin);
        BT_ASSERT(end);
-       *begin = g_begin->str;
-       *end = g_end->str;
-       g_string_free(g_begin, FALSE);
-       g_string_free(g_end, FALSE);
+       *begin = g_string_free(g_begin, FALSE);
+       *end = g_string_free(g_end, FALSE);
        g_begin = NULL;
        g_end = NULL;
        goto end;
index 92f499950a7b72d4919275b6cd92bab2af4ad340..1ceded7e56254f1cb76225ccbd904408eb74e206 100644 (file)
 #include <stdbool.h>
 #include <stdlib.h>
 #include <stdint.h>
-#include <babeltrace2/value.h>
 #include "lib/object.h"
 #include "compat/compiler.h"
-#include <babeltrace2/graph/component.h>
 #include <glib.h>
 
 #include "babeltrace2-cfg.h"
index f9e0ce937ed5928f7968408787d29be6f8ee9113..79e1161064c44bc86ddc7b5a7323a2a3b2fe9a5f 100644 (file)
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdbool.h>
-#include <babeltrace2/value.h>
 #include "lib/object.h"
 #include "compat/compiler.h"
-#include <babeltrace2/graph/component-class.h>
 #include <glib.h>
 
 enum bt_config_command {
index c3f59b6c2d2e97f8b537c7760dd4839b705ba30a..c95eda90bbbcf45b59c36b13cbebf5e3e0b8b6a8 100644 (file)
 #include <babeltrace2/babeltrace.h>
 #include "common/macros.h"
 
-BT_HIDDEN void init_loaded_plugins(void);
-BT_HIDDEN void fini_loaded_plugins(void);
+void init_loaded_plugins(void);
+void fini_loaded_plugins(void);
 
-BT_HIDDEN int require_loaded_plugins(const bt_value *plugin_paths);
+int require_loaded_plugins(const bt_value *plugin_paths);
 
-BT_HIDDEN size_t get_loaded_plugins_count(void);
-BT_HIDDEN const bt_plugin **borrow_loaded_plugins(void);
-BT_HIDDEN const bt_plugin *borrow_loaded_plugin_by_index(size_t index);
-BT_HIDDEN const bt_plugin *borrow_loaded_plugin_by_name(const char *name);
+size_t get_loaded_plugins_count(void);
+const bt_plugin **borrow_loaded_plugins(void);
+const bt_plugin *borrow_loaded_plugin_by_index(size_t index);
+const bt_plugin *borrow_loaded_plugin_by_name(const char *name);
 
 
 #endif /* CLI_BABELTRACE_PLUGINS_H */
index 38f42b5849585e3f9b2372d9641850fac9929743..4493b21a680d89fbc4b416dabc528584134675b2 100644 (file)
@@ -21,7 +21,6 @@ void set_fail_reason(const char **fail_reason, const char *reason)
        }
 }
 
-BT_HIDDEN
 bt_query_executor_query_status cli_query(const bt_component_class *comp_cls,
                const char *obj, const bt_value *params,
                bt_logging_level log_level, const bt_interrupter *interrupter,
index 2b4f55f4c746989b4a9ed5ff81a74caa14f021a5..c128bb6cac287b78c66fa486335fd973c03a9822 100644 (file)
@@ -10,7 +10,6 @@
 #include <babeltrace2/babeltrace.h>
 #include "common/macros.h"
 
-BT_HIDDEN
 bt_query_executor_query_status cli_query(const bt_component_class *comp_cls,
                const char *obj, const bt_value *params,
                bt_logging_level log_level, const bt_interrupter *interrupter,
index bf6c49d53ede25b6e81ef4a7d877b9dedc5aebe4..fbe38a87d93c2d82a5ab63163372b3f1de9e9a75 100644 (file)
@@ -59,7 +59,7 @@ static bt_interrupter *the_interrupter;
 #include <windows.h>
 
 static
-BOOL WINAPI signal_handler(DWORD signal) {
+BOOL WINAPI signal_handler(DWORD signal __attribute__((unused))) {
        if (the_interrupter) {
                bt_interrupter_set(the_interrupter);
        }
@@ -253,7 +253,9 @@ end:
 
 static
 bt_value_map_foreach_entry_const_func_status collect_map_keys(
-               const char *key, const bt_value *object, void *data)
+               const char *key,
+               const bt_value *object __attribute__((unused)),
+               void *data)
 {
        GPtrArray *map_keys = data;
 
@@ -1647,7 +1649,7 @@ end:
 
 static
 bt_graph_listener_func_status graph_source_output_port_added_listener(
-               const bt_component_source *component,
+               const bt_component_source *component __attribute__((unused)),
                const bt_port_output *port, void *data)
 {
        return graph_output_port_added_listener(data, port);
@@ -1655,7 +1657,7 @@ bt_graph_listener_func_status graph_source_output_port_added_listener(
 
 static
 bt_graph_listener_func_status graph_filter_output_port_added_listener(
-               const bt_component_filter *component,
+               const bt_component_filter *component __attribute__((unused)),
                const bt_port_output *port, void *data)
 {
        return graph_output_port_added_listener(data, port);
@@ -1811,7 +1813,7 @@ bt_get_greatest_operative_mip_version_status get_greatest_operative_mip_version(
        }
 
        status = bt_get_greatest_operative_mip_version(comp_descr_set,
-               bt_cli_log_level, mip_version);
+               (bt_logging_level) bt_cli_log_level, mip_version);
 
 end:
        bt_component_descriptor_set_put_ref(comp_descr_set);
@@ -2563,7 +2565,7 @@ void warn_command_name_and_directory_clash(struct bt_config *cfg)
 
        if (g_file_test(cfg->command_name,
                        G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
-               _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__,
+               bt_log_write_printf(__FILE__, __func__, __LINE__,
                                BT_LOG_WARNING, BT_LOG_TAG,
                                "The `%s` command was executed. "
                                "If you meant to convert a trace located in "
@@ -2606,8 +2608,8 @@ void print_error_causes(void)
         */
        fputc('\n',  stderr);
 
-       error_str = format_bt_error(error, columns, bt_cli_log_level,
-               BT_COMMON_COLOR_WHEN_AUTO);
+       error_str = format_bt_error(error, columns,
+               (bt_logging_level) bt_cli_log_level, BT_COMMON_COLOR_WHEN_AUTO);
        BT_ASSERT(error_str);
 
        fprintf(stderr, "%s\n", error_str);
diff --git a/src/cli/logging.c b/src/cli/logging.c
deleted file mode 100644 (file)
index 329cb70..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_LOG_OUTPUT_LEVEL bt_cli_log_level
-#include "logging/log.h"
-
-BT_LOG_INIT_LOG_LEVEL(bt_cli_log_level, "BABELTRACE_CLI_LOG_LEVEL");
diff --git a/src/cli/logging.cpp b/src/cli/logging.cpp
new file mode 100644 (file)
index 0000000..36d59bf
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_LOG_OUTPUT_LEVEL bt_cli_log_level
+#include "logging/log-api.h"
+
+BT_LOG_INIT_LOG_LEVEL(bt_cli_log_level, "BABELTRACE_CLI_LOG_LEVEL");
index 59c936190001ca9f2b571aac3f37b4f603ca91f1..967b2382824167b931235efc0cc9631495c0576c 100644 (file)
@@ -14,7 +14,7 @@ BT_LOG_LEVEL_EXTERN_SYMBOL(bt_cli_log_level);
 
 #define BT_CLI_LOG_AND_APPEND(_lvl, _fmt, ...)                         \
        do {                                                            \
-               BT_LOG_WRITE(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__);    \
+               BT_LOG_WRITE_PRINTF(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__); \
                (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( \
                        "Babeltrace CLI", _fmt, ##__VA_ARGS__);         \
        } while (0)
diff --git a/src/clock-correlation-validator/clock-correlation-validator.cpp b/src/clock-correlation-validator/clock-correlation-validator.cpp
new file mode 100644 (file)
index 0000000..4bb1a72
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2024 EfficiOS, Inc.
+ */
+
+#include "cpp-common/bt2/clock-class.hpp"
+#include "cpp-common/bt2/message.hpp"
+#include "cpp-common/bt2/wrap.hpp"
+
+#include "clock-correlation-validator.h"
+#include "clock-correlation-validator.hpp"
+
+namespace bt2ccv {
+
+void ClockCorrelationValidator::_validate(const bt2::ConstMessage msg)
+{
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> clockCls;
+    bt2::OptionalBorrowedObject<bt2::ConstStreamClass> streamCls;
+
+    switch (msg.type()) {
+    case bt2::MessageType::StreamBeginning:
+        streamCls = msg.asStreamBeginning().stream().cls();
+        clockCls = streamCls->defaultClockClass();
+        break;
+
+    case bt2::MessageType::MessageIteratorInactivity:
+        clockCls = msg.asMessageIteratorInactivity().clockSnapshot().clockClass();
+        break;
+
+    default:
+        bt_common_abort();
+    }
+
+    switch (_mExpectation) {
+    case PropsExpectation::Unset:
+        /*
+         * This is the first analysis of 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 (!clockCls) {
+            _mExpectation = PropsExpectation::None;
+        } else if (clockCls->originIsUnixEpoch()) {
+            _mExpectation = PropsExpectation::OriginUnix;
+        } else if (const auto uuid = clockCls->uuid()) {
+            _mExpectation = PropsExpectation::OriginOtherUuid;
+            _mUuid = *uuid;
+        } else {
+            _mExpectation = PropsExpectation::OriginOtherNoUuid;
+            _mClockClass = clockCls->shared();
+        }
+
+        break;
+
+    case PropsExpectation::None:
+        if (clockCls) {
+            throw ClockCorrelationError {ClockCorrelationError::Type::ExpectingNoClockClassGotOne,
+                                         bt2s::nullopt,
+                                         *clockCls,
+                                         {},
+                                         streamCls};
+        }
+
+        break;
+
+    case PropsExpectation::OriginUnix:
+        if (!clockCls) {
+            throw ClockCorrelationError {ClockCorrelationError::Type::ExpectingOriginUnixGotNone,
+                                         bt2s::nullopt,
+                                         {},
+                                         {},
+                                         streamCls};
+        }
+
+        if (!clockCls->originIsUnixEpoch()) {
+            throw ClockCorrelationError {ClockCorrelationError::Type::ExpectingOriginUnixGotOther,
+                                         bt2s::nullopt,
+                                         *clockCls,
+                                         {},
+                                         streamCls};
+        }
+
+        break;
+
+    case PropsExpectation::OriginOtherUuid:
+    {
+        if (!clockCls) {
+            throw ClockCorrelationError {ClockCorrelationError::Type::ExpectingOriginUuidGotNone,
+                                         bt2s::nullopt,
+                                         {},
+                                         {},
+                                         streamCls};
+        }
+
+        if (clockCls->originIsUnixEpoch()) {
+            throw ClockCorrelationError {ClockCorrelationError::Type::ExpectingOriginUuidGotUnix,
+                                         bt2s::nullopt,
+                                         *clockCls,
+                                         {},
+                                         streamCls};
+        }
+
+        const auto uuid = clockCls->uuid();
+
+        if (!uuid) {
+            throw ClockCorrelationError {ClockCorrelationError::Type::ExpectingOriginUuidGotNoUuid,
+                                         bt2s::nullopt,
+                                         *clockCls,
+                                         {},
+                                         streamCls};
+        }
+
+        if (*uuid != _mUuid) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::ExpectingOriginUuidGotOtherUuid,
+                _mUuid,
+                *clockCls,
+                {},
+                streamCls};
+        }
+
+        break;
+    }
+
+    case PropsExpectation::OriginOtherNoUuid:
+        if (!clockCls) {
+            throw ClockCorrelationError {ClockCorrelationError::Type::ExpectingOriginNoUuidGotNone,
+                                         bt2s::nullopt,
+                                         {},
+                                         {},
+                                         streamCls};
+        }
+
+        if (clockCls->libObjPtr() != _mClockClass->libObjPtr()) {
+            throw ClockCorrelationError {ClockCorrelationError::Type::ExpectingOriginNoUuidGotOther,
+                                         bt2s::nullopt, *clockCls, *_mClockClass, streamCls};
+        }
+
+        break;
+
+    default:
+        bt_common_abort();
+    }
+}
+
+} /* namespace bt2ccv */
+
+bt_clock_correlation_validator *bt_clock_correlation_validator_create() noexcept
+{
+    try {
+        return reinterpret_cast<bt_clock_correlation_validator *>(
+            new bt2ccv::ClockCorrelationValidator);
+    } catch (const std::bad_alloc&) {
+        return nullptr;
+    }
+}
+
+bool bt_clock_correlation_validator_validate_message(
+    bt_clock_correlation_validator * const validator, const bt_message * const msg,
+    bt_clock_correlation_validator_error_type * const type, bt_uuid * const expectedUuidOut,
+    const bt_clock_class ** const actualClockClsOut,
+    const bt_clock_class ** const expectedClockClsOut) noexcept
+{
+    try {
+        reinterpret_cast<bt2ccv::ClockCorrelationValidator *>(validator)->validate(bt2::wrap(msg));
+        return true;
+    } catch (const bt2ccv::ClockCorrelationError& error) {
+        *type = static_cast<bt_clock_correlation_validator_error_type>(error.type());
+
+        if (error.expectedUuid()) {
+            *expectedUuidOut = error.expectedUuid()->data();
+        } else {
+            *expectedUuidOut = nullptr;
+        }
+
+        if (error.actualClockCls()) {
+            *actualClockClsOut = error.actualClockCls()->libObjPtr();
+        } else {
+            *actualClockClsOut = nullptr;
+        }
+
+        if (error.expectedClockCls()) {
+            *expectedClockClsOut = error.expectedClockCls()->libObjPtr();
+        } else {
+            *expectedClockClsOut = nullptr;
+        }
+
+        return false;
+    }
+}
+
+void bt_clock_correlation_validator_destroy(
+    bt_clock_correlation_validator * const validator) noexcept
+{
+    delete reinterpret_cast<bt2ccv::ClockCorrelationValidator *>(validator);
+}
diff --git a/src/clock-correlation-validator/clock-correlation-validator.h b/src/clock-correlation-validator/clock-correlation-validator.h
new file mode 100644 (file)
index 0000000..4ccb759
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2024 EfficiOS, Inc.
+ */
+
+#ifndef CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_H
+#define CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_H
+
+#include <glib.h>
+#include <stdbool.h>
+#include <babeltrace2/babeltrace.h>
+
+#include "common/macros.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bt_clock_class;
+struct bt_message;
+
+enum bt_clock_correlation_validator_error_type
+{
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_NO_CLOCK_CLASS_GOT_ONE,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_NONE,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_OTHER,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NONE,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_UNIX,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NO_UUID,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_OTHER_UUID,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_NONE,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_OTHER,
+};
+
+struct bt_clock_correlation_validator *bt_clock_correlation_validator_create(
+       void) BT_NOEXCEPT;
+
+bool bt_clock_correlation_validator_validate_message(
+       struct bt_clock_correlation_validator *validator,
+       const struct bt_message *msg,
+       enum bt_clock_correlation_validator_error_type *type,
+       bt_uuid *expected_uuid,
+       const struct bt_clock_class ** const actual_clock_cls,
+       const struct bt_clock_class ** const expected_clock_cls) BT_NOEXCEPT;
+
+void bt_clock_correlation_validator_destroy(
+       struct bt_clock_correlation_validator *validator) BT_NOEXCEPT;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_H */
diff --git a/src/clock-correlation-validator/clock-correlation-validator.hpp b/src/clock-correlation-validator/clock-correlation-validator.hpp
new file mode 100644 (file)
index 0000000..314551c
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2024 EfficiOS, Inc.
+ */
+
+#ifndef CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_HPP
+#define CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_HPP
+
+#include "cpp-common/bt2/message.hpp"
+
+#include "clock-correlation-validator/clock-correlation-validator.h"
+
+namespace bt2ccv {
+
+class ClockCorrelationError final : public std::runtime_error
+{
+public:
+    enum class Type
+    {
+        ExpectingNoClockClassGotOne =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_NO_CLOCK_CLASS_GOT_ONE,
+        ExpectingOriginUnixGotNone =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_NONE,
+        ExpectingOriginUnixGotOther =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_OTHER,
+        ExpectingOriginUuidGotNone =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NONE,
+        ExpectingOriginUuidGotUnix =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_UNIX,
+        ExpectingOriginUuidGotNoUuid =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NO_UUID,
+        ExpectingOriginUuidGotOtherUuid =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_OTHER_UUID,
+        ExpectingOriginNoUuidGotNone =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_NONE,
+        ExpectingOriginNoUuidGotOther =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_OTHER,
+    };
+
+    explicit ClockCorrelationError(
+        Type type, const bt2s::optional<bt2c::UuidView> expectedUuid,
+        const bt2::OptionalBorrowedObject<bt2::ConstClockClass> actualClockCls,
+        const bt2::OptionalBorrowedObject<bt2::ConstClockClass> expectedClockCls,
+        const bt2::OptionalBorrowedObject<bt2::ConstStreamClass> streamCls) noexcept :
+        std::runtime_error {"Clock classes are not correlatable"},
+        _mType {type}, _mExpectedUuid {expectedUuid}, _mActualClockCls {actualClockCls},
+        _mExpectedClockCls {expectedClockCls}, _mStreamCls {streamCls}
+
+    {
+    }
+
+    Type type() const noexcept
+    {
+        return _mType;
+    }
+
+    bt2s::optional<bt2c::UuidView> expectedUuid() const noexcept
+    {
+        return _mExpectedUuid;
+    }
+
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> actualClockCls() const noexcept
+    {
+        return _mActualClockCls;
+    }
+
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> expectedClockCls() const noexcept
+    {
+        return _mExpectedClockCls;
+    }
+
+    bt2::OptionalBorrowedObject<bt2::ConstStreamClass> streamCls() const noexcept
+    {
+        return _mStreamCls;
+    }
+
+private:
+    Type _mType;
+    bt2s::optional<bt2c::UuidView> _mExpectedUuid;
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> _mActualClockCls;
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> _mExpectedClockCls;
+    bt2::OptionalBorrowedObject<bt2::ConstStreamClass> _mStreamCls;
+};
+
+class ClockCorrelationValidator final
+{
+private:
+    enum class PropsExpectation
+    {
+        /* We haven't recorded clock properties yet. */
+        Unset,
+
+        /* Expect to have no clock. */
+        None,
+
+        /* Expect a clock with a Unix epoch origin. */
+        OriginUnix,
+
+        /* Expect a clock without a Unix epoch origin, but with a UUID. */
+        OriginOtherUuid,
+
+        /* Expect a clock without a Unix epoch origin and without a UUID. */
+        OriginOtherNoUuid,
+    };
+
+public:
+    void validate(const bt2::ConstMessage msg)
+    {
+        if (!msg.isStreamBeginning() && !msg.isMessageIteratorInactivity()) {
+            return;
+        }
+
+        this->_validate(msg);
+    }
+
+private:
+    void _validate(const bt2::ConstMessage msg);
+
+    PropsExpectation _mExpectation = PropsExpectation::Unset;
+
+    /*
+     * Expected UUID of the clock, if `_mExpectation` is
+     * `PropsExpectation::ORIGIN_OTHER_UUID`.
+     *
+     * If the origin of the clock is the Unix epoch, then the UUID is
+     * irrelevant because the clock will have a correlation with other
+     * clocks having the same origin.
+     */
+    bt2c::Uuid _mUuid;
+
+    /*
+     * Expected clock class, if `_mExpectation` is
+     * `ClockExpectation::ORIGIN_OTHER_NO_UUID`.
+     *
+     * If the first analyzed clock class has an unknown origin and no
+     * UUID, then all subsequent analyzed clock classes must be the same
+     * instance.
+     *
+     * To make sure that the clock class pointed to by this member
+     * doesn't get freed and another one reallocated at the same
+     * address, which could potentially bypass the clock expectation
+     * check, we keep a strong reference, ensuring that the clock class
+     * lives at least as long as the owner of this validator.
+     */
+    bt2::ConstClockClass::Shared _mClockClass;
+};
+
+} /* namespace bt2ccv */
+
+#endif /* CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_HPP */
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
deleted file mode 100644 (file)
index cc17ad7..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-AM_CPPFLAGS += -DBABELTRACE_PLUGINS_DIR=\"$(BABELTRACE_PLUGINS_DIR)\"
-
-noinst_LTLIBRARIES = libbabeltrace2-common.la
-
-libbabeltrace2_common_la_SOURCES = \
-       assert.c \
-       assert.h \
-       common.c \
-       common.h \
-       uuid.c \
-       uuid.h
-
-noinst_HEADERS = \
-       align.h \
-       list.h \
-       macros.h \
-       mmap-align.h \
-       safe.h
-
-# The following section is based on a similar feature in LTTng-tools.
-
-##
-## This target generates an include file that contains the git version
-## string of the current branch, it must be continuously updated when
-## we build in the git repo and shipped in dist tarballs to reflect the
-## status of the tree when it was generated. If the tree is clean and
-## the current commit is tag a starting with "v", consider this a
-## release version and set an empty git version.
-##
-## Here is what the inline script does:
-##
-## First, delete any stale "version.i.tmp" file.
-##
-## If "bootstrap" and ".git" exists in the top source directory and the git
-## executable is available, get the current git version string in the form:
-##
-##  "latest_tag"(-"number_of_commits_on_top")(-g"latest_commit_hash")(-dirty)
-##
-## And store it in "version.i.tmp", if the current commit is tagged, the tag
-## starts with "v" and the tree is clean, consider this a release version and
-## overwrite the git version with an empty string in "version.i.tmp".
-##
-## If we don't have a "version.i.tmp" nor a "version.i", generate an empty
-## string as a failover. If a "version.i" is present, for example when building
-## from a distribution tarball, get the git_version using grep.
-##
-## Fetch the BT_VERSION_EXTRA_NAME define from "version/extra_version_name" and output it
-## to "version.i.tmp".
-##
-## Fetch the BT_VERSION_EXTRA_DESCRIPTION define from "version/extra_version_description",
-## sanitize and format it with a sed script to replace all non-alpha-numeric values
-## with "-" and join all lines by replacing "\n" with litteral string c-style "\n" and
-## output it to "version.i.tmp".
-##
-## Repeat the same logic for the "version/extra_patches" directory.
-## Data fetched from "version/extra_patches" must be sanitized and
-## formatted.
-## The data is fetched using "ls" with an ignore pattern for the README.adoc file.
-## The sanitize step uses sed with a script to replace all
-## non-alpha-numeric values, except " " (space), to "-".
-## The formatting step uses sed with a script to join all lines
-## by replacing "\n" with litteral string c-style "\n".
-##
-## If we don't have a "version.i" or we have both files (version.i, version.i.tmp)
-## and they are different, copy "version.i.tmp" over "version.i".
-## This way the dependent targets are only rebuilt when the git version
-## string or either one of extra version string change.
-##
-version_verbose = $(version_verbose_@AM_V@)
-version_verbose_ = $(version_verbose_@AM_DEFAULT_V@)
-version_verbose_0 = @echo "  GEN       " $@;
-
-version.i:
-       $(version_verbose)rm -f version.i.tmp; \
-       if (test ! -f version.i && test -f "$(top_srcdir)/include/version.i"); then \
-               cp "$(top_srcdir)/include/version.i" version.i; \
-       fi; \
-       if (test -r "$(top_srcdir)/bootstrap" && test -r "$(top_srcdir)/.git") && \
-                       test -x "`which git 2>&1;true`"; then \
-               GIT_VERSION_STR="`cd "$(top_srcdir)" && git describe --tags --dirty`"; \
-               GIT_CURRENT_TAG="`cd "$(top_srcdir)" && git describe --tags --exact-match --match="v[0-9]*" HEAD 2> /dev/null`"; \
-               echo "#define BT_VERSION_GIT \"$$GIT_VERSION_STR\"" > version.i.tmp; \
-               if ! $(GREP) -- "-dirty" version.i.tmp > /dev/null && \
-                               test "x$$GIT_CURRENT_TAG" != "x"; then \
-                       echo "#define BT_VERSION_GIT \"\"" > version.i.tmp; \
-               fi; \
-       fi; \
-       if test ! -f version.i.tmp; then \
-               if test -f version.i; then \
-                       $(GREP) "^#define \bBT_VERSION_GIT\b.*" version.i > version.i.tmp; \
-               else \
-                       echo '#define BT_VERSION_GIT ""' > version.i.tmp; \
-               fi; \
-       fi; \
-       echo "#define BT_VERSION_EXTRA_NAME \"`$(SED) -n '1p' "$(top_srcdir)/version/extra_version_name" 2> /dev/null`\"" >> version.i.tmp; \
-       echo "#define BT_VERSION_EXTRA_DESCRIPTION \"`$(SED) -E ':a ; N ; $$!ba ; s/[^a-zA-Z0-9 \n\t\.,]/-/g ; s/\r{0,1}\n/\\\n/g' "$(top_srcdir)/version/extra_version_description" 2> /dev/null`\"" >> version.i.tmp; \
-       echo "#define BT_VERSION_EXTRA_PATCHES \"`ls -1 "$(top_srcdir)/version/extra_patches" | $(GREP) -v '^README.adoc' | $(SED) -E ':a ; N ; $$!ba ; s/[^a-zA-Z0-9 \n\t\.]/-/g ; s/\r{0,1}\n/\\\n/g' 2> /dev/null`\"" >> version.i.tmp; \
-       if test ! -f version.i || \
-                       test x"`cat version.i.tmp`" != x"`cat version.i`"; then \
-               mv version.i.tmp version.i; \
-       fi; \
-       rm -f version.i.tmp; \
-       true
-
-##
-## version.i is defined as a .PHONY target even if it's a real file,
-## we want the target to be re-run on every make.
-##
-.PHONY: version.i
-
-CLEANFILES = version.i.tmp
-
-##
-## Only clean "version.i" on dist-clean, we need to keep it on regular
-## clean when it's part of a dist tarball.
-##
-DISTCLEANFILES = version.i
-
-noinst_HEADERS += version.h version.i
index dd9b91276730c342c1ee174bc912c52240f4893f..52349e6404cd469865a3da837866110b75997305 100644 (file)
@@ -17,7 +17,6 @@
 extern "C" {
 #endif
 
-BT_HIDDEN
 extern void bt_common_assert_failed(const char *file, int line,
                const char *func, const char *assertion)
                __attribute__((noreturn));
index c82bee330c2e2ff963c10a044e5bcdcef63fd813..e28836ddbec8d59b9fd4371c4223953ec6f1c3dc 100644 (file)
@@ -221,20 +221,17 @@ void __attribute__((constructor)) bt_common_color_ctor(void)
        color_codes.bg_light_gray = BT_COMMON_COLOR_BG_LIGHT_GRAY;
 }
 
-BT_HIDDEN
 const char *bt_common_get_system_plugin_path(void)
 {
        return SYSTEM_PLUGIN_PATH;
 }
 
 #ifdef __MINGW32__
-BT_HIDDEN
 bool bt_common_is_setuid_setgid(void)
 {
        return false;
 }
 #else /* __MINGW32__ */
-BT_HIDDEN
 bool bt_common_is_setuid_setgid(void)
 {
        return (geteuid() != getuid() || getegid() != getgid());
@@ -243,13 +240,13 @@ bool bt_common_is_setuid_setgid(void)
 
 #ifdef __MINGW32__
 static
-const char *bt_get_home_dir(int log_level)
+const char *bt_get_home_dir(int log_level __attribute__((unused)))
 {
        return g_get_home_dir();
 }
 #else /* __MINGW32__ */
 static
-char *bt_secure_getenv(const char *name, int log_level)
+char *bt_secure_getenv(const char *name, int log_level __attribute__((unused)))
 {
        if (bt_common_is_setuid_setgid()) {
                BT_LOGD("Disregarding environment variable for setuid/setgid binary: "
@@ -280,7 +277,6 @@ end:
 }
 #endif /* __MINGW32__ */
 
-BT_HIDDEN
 char *bt_common_get_home_plugin_path(int log_level)
 {
        char *path = NULL;
@@ -312,7 +308,6 @@ end:
        return path;
 }
 
-BT_HIDDEN
 int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs)
 {
        int ret = 0;
@@ -396,7 +391,6 @@ end:
        return istty;
 }
 
-BT_HIDDEN
 bool bt_common_colors_supported(void)
 {
        static bool supports_colors = false;
@@ -452,157 +446,131 @@ end:
        return supports_colors;
 }
 
-BT_HIDDEN
 const char *bt_common_color_reset(void)
 {
        return bt_common_color_code_reset;
 }
 
-BT_HIDDEN
 const char *bt_common_color_bold(void)
 {
        return bt_common_color_code_bold;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_default(void)
 {
        return bt_common_color_code_fg_default;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_red(void)
 {
        return bt_common_color_code_fg_red;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_green(void)
 {
        return bt_common_color_code_fg_green;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_yellow(void)
 {
        return bt_common_color_code_fg_yellow;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_blue(void)
 {
        return bt_common_color_code_fg_blue;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_magenta(void)
 {
        return bt_common_color_code_fg_magenta;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_cyan(void)
 {
        return bt_common_color_code_fg_cyan;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_light_gray(void)
 {
        return bt_common_color_code_fg_light_gray;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_red(void)
 {
        return bt_common_color_code_fg_bright_red;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_green(void)
 {
        return bt_common_color_code_fg_bright_green;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_yellow(void)
 {
        return bt_common_color_code_fg_bright_yellow;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_blue(void)
 {
        return bt_common_color_code_fg_bright_blue;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_magenta(void)
 {
        return bt_common_color_code_fg_bright_magenta;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_cyan(void)
 {
        return bt_common_color_code_fg_bright_cyan;
 }
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_light_gray(void)
 {
        return bt_common_color_code_fg_bright_light_gray;
 }
 
-BT_HIDDEN
 const char *bt_common_color_bg_default(void)
 {
        return bt_common_color_code_bg_default;
 }
 
-BT_HIDDEN
 const char *bt_common_color_bg_red(void)
 {
        return bt_common_color_code_bg_red;
 }
 
-BT_HIDDEN
 const char *bt_common_color_bg_green(void)
 {
        return bt_common_color_code_bg_green;
 }
 
-BT_HIDDEN
 const char *bt_common_color_bg_yellow(void)
 {
        return bt_common_color_code_bg_yellow;
 }
 
-BT_HIDDEN
 const char *bt_common_color_bg_blue(void)
 {
        return bt_common_color_code_bg_blue;
 }
 
-BT_HIDDEN
 const char *bt_common_color_bg_magenta(void)
 {
        return bt_common_color_code_bg_magenta;
 }
 
-BT_HIDDEN
 const char *bt_common_color_bg_cyan(void)
 {
        return bt_common_color_code_bg_cyan;
 }
 
-BT_HIDDEN
 const char *bt_common_color_bg_light_gray(void)
 {
        return bt_common_color_code_bg_light_gray;
 }
 
-BT_HIDDEN
 void bt_common_color_get_codes(struct bt_common_color_codes *codes,
                enum bt_common_color_when use_colors)
 {
@@ -621,7 +589,6 @@ void bt_common_color_get_codes(struct bt_common_color_codes *codes,
        }
 }
 
-BT_HIDDEN
 GString *bt_common_string_until(const char *input, const char *escapable_chars,
                const char *end_chars, size_t *end_pos)
 {
@@ -704,7 +671,6 @@ end:
        return output;
 }
 
-BT_HIDDEN
 GString *bt_common_shell_quote(const char *input, bool with_single_quotes)
 {
        GString *output = g_string_new(NULL);
@@ -760,7 +726,6 @@ end:
        return output;
 }
 
-BT_HIDDEN
 bool bt_common_string_is_printable(const char *input)
 {
        const char *ch;
@@ -779,7 +744,6 @@ end:
        return printable;
 }
 
-BT_HIDDEN
 void bt_common_destroy_lttng_live_url_parts(
                struct bt_common_lttng_live_url_parts *parts)
 {
@@ -811,7 +775,6 @@ end:
        return;
 }
 
-BT_HIDDEN
 struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url(
                const char *url, char *error_buf, size_t error_buf_size)
 {
@@ -990,7 +953,6 @@ end:
        return parts;
 }
 
-BT_HIDDEN
 void bt_common_normalize_star_glob_pattern(char *pattern)
 {
        const char *p;
@@ -1050,7 +1012,6 @@ bool at_end_of_pattern(const char *p, const char *pattern, size_t pattern_len)
  * string length of `pattern` or `candidate` if the string is
  * null-terminated.
  */
-BT_HIDDEN
 bool bt_common_star_glob_match(const char *pattern, size_t pattern_len,
                const char *candidate, size_t candidate_len) {
        const char *retry_c = candidate, *retry_p = pattern, *c, *p;
@@ -1285,8 +1246,8 @@ end_of_pattern:
 }
 
 #ifdef __MINGW32__
-BT_HIDDEN
-GString *bt_common_normalize_path(const char *path, const char *wd)
+GString *bt_common_normalize_path(const char *path,
+               const char *wd __attribute__((unused)))
 {
        char *tmp;
        GString *norm_path = NULL;
@@ -1347,7 +1308,6 @@ void destroy_gstring(void *gstring)
        (void) g_string_free(gstring, TRUE);
 }
 
-BT_HIDDEN
 GString *bt_common_normalize_path(const char *path, const char *wd)
 {
        size_t i;
@@ -1432,7 +1392,6 @@ end:
 }
 #endif
 
-BT_HIDDEN
 size_t bt_common_get_page_size(int log_level)
 {
        int page_size;
@@ -1707,7 +1666,6 @@ update_rw_fmt:
        *out_fmt_ch = fmt_ch;
 }
 
-BT_HIDDEN
 void bt_common_custom_vsnprintf(char *buf, size_t buf_size,
                char intro,
                bt_common_handle_custom_specifier_func handle_specifier,
@@ -1751,20 +1709,6 @@ void bt_common_custom_vsnprintf(char *buf, size_t buf_size,
        *buf_ch = '\0';
 }
 
-BT_HIDDEN
-void bt_common_custom_snprintf(char *buf, size_t buf_size,
-               char intro,
-               bt_common_handle_custom_specifier_func handle_specifier,
-               void *priv_data, const char *fmt, ...)
-{
-       va_list args;
-       va_start(args, fmt);
-       bt_common_custom_vsnprintf(buf, buf_size, intro, handle_specifier,
-               priv_data, fmt, &args);
-       va_end(args);
-}
-
-BT_HIDDEN
 void bt_common_sep_digits(char *str, unsigned int digits_per_group, char sep)
 {
        const char *rd;
@@ -1867,7 +1811,6 @@ void bt_common_sep_digits(char *str, unsigned int digits_per_group, char sep)
        }
 }
 
-BT_HIDDEN
 GString *bt_common_fold(const char *str, unsigned int total_length,
                unsigned int indent)
 {
@@ -1986,14 +1929,13 @@ end:
 }
 
 #ifdef __MINGW32__
-BT_HIDDEN
-int bt_common_get_term_size(unsigned int *width, unsigned int *height)
+int bt_common_get_term_size(unsigned int *width __attribute__((unused)),
+               unsigned int *height __attribute__((unused)))
 {
        /* Not supported on Windows yet */
        return -1;
 }
 #else /* __MINGW32__ */
-BT_HIDDEN
 int bt_common_get_term_size(unsigned int *width, unsigned int *height)
 {
        int ret = 0;
@@ -2017,7 +1959,6 @@ end:
 }
 #endif /* __MINGW32__ */
 
-BT_HIDDEN
 int bt_common_g_string_append_printf(GString *str, const char *fmt, ...)
 {
        va_list ap;
@@ -2041,7 +1982,7 @@ int bt_common_g_string_append_printf(GString *str, const char *fmt, ...)
                /* Resize. */
                g_string_set_size(str, len + print_len);
                va_start(ap, fmt);
-               print_len = vsprintf(str->str + len, fmt, ap);
+               print_len = vsnprintf(str->str + len, print_len + 1, fmt, ap);
                va_end(ap);
        } else {
                str->len = len + print_len;
@@ -2049,7 +1990,6 @@ int bt_common_g_string_append_printf(GString *str, const char *fmt, ...)
        return print_len;
 }
 
-BT_HIDDEN
 int bt_common_append_file_content_to_g_string(GString *str, FILE *fp)
 {
        const size_t chunk_size = 4096;
@@ -2090,7 +2030,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void bt_common_abort(void)
 {
        static const char * const exec_on_abort_env_name =
index 2c9ef3825c6a648addc5eca1ae5d669df845d31d..fd9da4e70f98a3430f6f9a44cfb751f31df28e1c 100644 (file)
@@ -113,14 +113,12 @@ struct bt_common_lttng_live_url_parts {
  * Checks if the current process has setuid or setgid access rights.
  * Returns `true` if so.
  */
-BT_HIDDEN
 bool bt_common_is_setuid_setgid(void);
 
 /*
  * Returns the system-wide plugin path, e.g.
  * `/usr/lib/babeltrace2/plugins`. Do not free the return value.
  */
-BT_HIDDEN
 const char *bt_common_get_system_plugin_path(void);
 
 /*
@@ -128,7 +126,6 @@ const char *bt_common_get_system_plugin_path(void);
  * `/home/user/.local/lib/babeltrace2/plugins`. You need to free the
  * return value.
  */
-BT_HIDDEN
 char *bt_common_get_home_plugin_path(int log_level);
 
 /*
@@ -136,92 +133,64 @@ char *bt_common_get_home_plugin_path(int log_level);
  * `paths` is a list of directories separated by `:`. Returns 0 on
  * success.
  */
-BT_HIDDEN
 int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs);
 
 /*
  * Returns `true` if terminal color codes are supported for this
  * process.
  */
-BT_HIDDEN
 bool bt_common_colors_supported(void);
 
-BT_HIDDEN
 const char *bt_common_color_reset(void);
 
-BT_HIDDEN
 const char *bt_common_color_bold(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_default(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_red(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_green(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_yellow(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_blue(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_magenta(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_cyan(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_light_gray(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_red(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_green(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_yellow(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_blue(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_magenta(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_cyan(void);
 
-BT_HIDDEN
 const char *bt_common_color_fg_bright_light_gray(void);
 
-BT_HIDDEN
 const char *bt_common_color_bg_default(void);
 
-BT_HIDDEN
 const char *bt_common_color_bg_red(void);
 
-BT_HIDDEN
 const char *bt_common_color_bg_green(void);
 
-BT_HIDDEN
 const char *bt_common_color_bg_yellow(void);
 
-BT_HIDDEN
 const char *bt_common_color_bg_blue(void);
 
-BT_HIDDEN
 const char *bt_common_color_bg_magenta(void);
 
-BT_HIDDEN
 const char *bt_common_color_bg_cyan(void);
 
-BT_HIDDEN
 const char *bt_common_color_bg_light_gray(void);
 
-BT_HIDDEN
 void bt_common_color_get_codes(struct bt_common_color_codes *codes,
                enum bt_common_color_when use_colors);
 
@@ -231,7 +200,6 @@ void bt_common_color_get_codes(struct bt_common_color_codes *codes,
  * found in `escapable_chars`, and sets `*end_pos` to the position of
  * the end (from `input`). The caller owns the returned GString.
  */
-BT_HIDDEN
 GString *bt_common_string_until(const char *input, const char *escapable_chars,
                     const char *end_chars, size_t *end_pos);
 
@@ -242,21 +210,18 @@ GString *bt_common_string_until(const char *input, const char *escapable_chars,
  * prepend and append them manually, although they are not always
  * required. The caller owns the returned GString.
  */
-BT_HIDDEN
 GString *bt_common_shell_quote(const char *input, bool with_single_quotes);
 
 /*
  * Returns `true` if `input` is a string made only of printable
  * characters.
  */
-BT_HIDDEN
 bool bt_common_string_is_printable(const char *input);
 
 /*
  * Destroys the parts of an LTTng live URL as returned by
  * bt_common_parse_lttng_live_url().
  */
-BT_HIDDEN
 void bt_common_destroy_lttng_live_url_parts(
                struct bt_common_lttng_live_url_parts *parts);
 
@@ -266,7 +231,6 @@ void bt_common_destroy_lttng_live_url_parts(
  * up to `error_buf_size` bytes. You must destroy the returned value
  * with bt_common_destroy_lttng_live_url_parts().
  */
-BT_HIDDEN
 struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url(
                const char *url, char *error_buf, size_t error_buf_size);
 
@@ -274,14 +238,12 @@ struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url(
  * Normalizes (in place) a star globbing pattern to be used with
  * bt_common_star_glob_match(). This function always succeeds.
  */
-BT_HIDDEN
 void bt_common_normalize_star_glob_pattern(char *pattern);
 
 /*
  * Returns `true` if `candidate` (of size `candidate_len`) matches
  * the star globbing pattern `pattern` (of size `pattern_len`).
  */
-BT_HIDDEN
 bool bt_common_star_glob_match(const char *pattern, size_t pattern_len,
                 const char *candidate, size_t candidate_len);
 
@@ -297,7 +259,6 @@ bool bt_common_star_glob_match(const char *pattern, size_t pattern_len,
  *
  * The caller owns the returned GString.
  */
-BT_HIDDEN
 GString *bt_common_normalize_path(const char *path, const char *wd);
 
 typedef void (* bt_common_handle_custom_specifier_func)(void *priv_data,
@@ -352,31 +313,20 @@ typedef void (* bt_common_handle_custom_specifier_func)(void *priv_data,
  * do not return error codes: they abort when there's any error (bad
  * format string, for example).
  */
-BT_HIDDEN
 void bt_common_custom_vsnprintf(char *buf, size_t buf_size,
                char intro,
                bt_common_handle_custom_specifier_func handle_specifier,
                void *priv_data, const char *fmt, va_list *args);
 
-/*
- * Variadic form of bt_common_custom_vsnprintf().
- */
-BT_HIDDEN
-void bt_common_custom_snprintf(char *buf, size_t buf_size,
-               char intro,
-               bt_common_handle_custom_specifier_func handle_specifier,
-               void *priv_data, const char *fmt, ...);
-
 /*
  * Returns the system page size.
  */
-BT_HIDDEN
 size_t bt_common_get_page_size(int log_level);
 
 /*
  * Adds the digit separator `sep` as many times as needed to form groups
  * of `digits_per_group` digits within `str`. `str` must have enough
- * room to accomodate the new separators, that is:
+ * room to accommodate the new separators, that is:
  *
  *     strlen(str) + (strlen(str) / digits_per_group) + 1
  *
@@ -386,7 +336,6 @@ size_t bt_common_get_page_size(int log_level);
  * `strlen(str)` must not be 0. `digits_per_group` must not be 0. `sep`
  * must not be `\0`.
  */
-BT_HIDDEN
 void bt_common_sep_digits(char *str, unsigned int digits_per_group, char sep);
 
 /*
@@ -402,7 +351,6 @@ void bt_common_sep_digits(char *str, unsigned int digits_per_group, char sep);
  *
  * The returned string, on success, is owned by the caller.
  */
-BT_HIDDEN
 GString *bt_common_fold(const char *str, unsigned int total_length,
                unsigned int indent);
 
@@ -410,7 +358,6 @@ GString *bt_common_fold(const char *str, unsigned int total_length,
  * Writes the terminal's width to `*width`, its height to `*height`,
  * and returns 0 on success, or returns -1 on error.
  */
-BT_HIDDEN
 int bt_common_get_term_size(unsigned int *width, unsigned int *height);
 
 /*
@@ -419,64 +366,10 @@ int bt_common_get_term_size(unsigned int *width, unsigned int *height);
  *
  * This function does NOT rewind `fp` once it's done or on error.
  */
-BT_HIDDEN
 int bt_common_append_file_content_to_g_string(GString *str, FILE *fp);
 
-BT_HIDDEN
 void bt_common_abort(void) __attribute__((noreturn));
 
-/*
- * Wraps read() function to handle EINTR and partial reads.
- * On success, it returns `count` received as parameter. On error, it returns a
- * value smaller than the requested `count`.
- */
-static inline
-ssize_t bt_common_read(int fd, void *buf, size_t count, int log_level)
-{
-       size_t i = 0;
-       ssize_t ret;
-
-       BT_ASSERT_DBG(buf);
-
-       /* Never return an overflow value. */
-       BT_ASSERT_DBG(count <= SSIZE_MAX);
-
-       do {
-               ret = read(fd, ((char *) buf) + i, count - i);
-               if (ret < 0) {
-                       if (errno == EINTR) {
-#ifdef BT_LOG_WRITE_CUR_LVL
-                               BT_LOG_WRITE_CUR_LVL(BT_LOG_DEBUG, log_level,
-                                       BT_LOG_TAG,
-                                       "read() call interrupted; retrying...");
-#endif
-                               /* retry operation */
-                               continue;
-                       } else {
-#ifdef BT_LOG_WRITE_ERRNO_CUR_LVL
-                               BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_ERROR,
-                                       log_level, BT_LOG_TAG,
-                                       "Error while reading", ": fd=%d", fd);
-#endif
-                               goto end;
-                       }
-               }
-               i += ret;
-               BT_ASSERT_DBG(i <= count);
-       } while (count - i > 0 && ret > 0);
-
-end:
-       if (ret >= 0) {
-               if (i == 0) {
-                       ret = -1;
-               } else {
-                       ret = i;
-               }
-       }
-
-       return ret;
-}
-
 static inline
 const char *bt_common_field_class_type_string(enum bt_field_class_type class_type)
 {
@@ -651,44 +544,6 @@ const char *bt_common_value_type_string(enum bt_value_type type)
        bt_common_abort();
 };
 
-static inline
-GString *bt_common_field_path_string(struct bt_field_path *path)
-{
-       GString *str = g_string_new(NULL);
-       uint64_t i;
-
-       BT_ASSERT_DBG(path);
-
-       if (!str) {
-               goto end;
-       }
-
-       g_string_append_printf(str, "[%s", bt_common_scope_string(
-               bt_field_path_get_root_scope(path)));
-
-       for (i = 0; i < bt_field_path_get_item_count(path); i++) {
-               const struct bt_field_path_item *fp_item =
-                       bt_field_path_borrow_item_by_index_const(path, i);
-
-               switch (bt_field_path_item_get_type(fp_item)) {
-               case BT_FIELD_PATH_ITEM_TYPE_INDEX:
-                       g_string_append_printf(str, ", %" PRIu64,
-                               bt_field_path_item_index_get_index(fp_item));
-                       break;
-               case BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT:
-                       g_string_append(str, ", <CUR>");
-                       break;
-               default:
-                       bt_common_abort();
-               }
-       }
-
-       g_string_append(str, "]");
-
-end:
-       return str;
-}
-
 static inline
 const char *bt_common_logging_level_string(
                enum bt_logging_level level)
@@ -839,7 +694,7 @@ end:
  * bt_g_string_append_printf cannot be inlined because it expects a
  * variadic argument list.
  */
-BT_HIDDEN __BT_ATTR_FORMAT_PRINTF(2, 3)
+__BT_ATTR_FORMAT_PRINTF(2, 3)
 int bt_common_g_string_append_printf(GString *str, const char *fmt, ...);
 
 static inline
index 193bf2d7063c3955daed255a8c61abc80465d316..fd1f471cbb210bd1870c59a0a09c26f60e4a86e3 100644 (file)
@@ -1,7 +1,7 @@
 /*
+ * SPDX-FileCopyrightText: 2002 Free Software Foundation, Inc.
  * SPDX-License-Identifier: LGPL-2.1-only
  *
- * Copyright (C) 2002 Free Software Foundation, Inc.
  * This file is part of the GNU C Library.
  * Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
  */
index 6a3e061d5f17464222d85701e96a0cbf6e871121..8120515c63fe5bbcd81672f8c7b4cf1df89d9ce8 100644 (file)
@@ -21,14 +21,28 @@ extern "C" {
        ((type) (a) > (type) (b) ? (type) (a) : (type) (b))
 
 /*
- * BT_HIDDEN: set the hidden attribute for internal functions
- * On Windows, symbols are local unless explicitly exported,
- * see https://gcc.gnu.org/wiki/Visibility
+ * BT_EXPORT: set the visibility for exported functions.
  */
 #if defined(_WIN32) || defined(__CYGWIN__)
-#define BT_HIDDEN
+#define BT_EXPORT
 #else
-#define BT_HIDDEN __attribute__((visibility("hidden")))
+#define BT_EXPORT __attribute__((visibility("default")))
+#endif
+
+/*
+ * BT_NOEXCEPT: defined to `noexcept` if compiling as C++, else empty.
+ */
+#if defined(__cplusplus)
+#define BT_NOEXCEPT noexcept
+#else
+#define BT_NOEXCEPT
+#endif
+
+/* Enable `txt` if developer mode is enabled. */
+#ifdef BT_DEV_MODE
+#define BT_IF_DEV_MODE(txt) txt
+#else
+#define BT_IF_DEV_MODE(txt)
 #endif
 
 /*
@@ -54,6 +68,10 @@ extern "C" {
                _ref;                   \
        })
 
+/* Wrapper for g_array_index that adds bound checking.  */
+#define bt_g_array_index(a, t, i)              \
+       g_array_index((a), t, ({ BT_ASSERT_DBG((i) < (a)->len); (i); }))
+
 /*
  * Copied from:
  * <https://stackoverflow.com/questions/37411809/how-to-elegantly-fix-this-unused-variable-warning/37412551#37412551>:
@@ -81,6 +99,11 @@ extern "C" {
        ((void) sizeof((void) (_expr1), (void) (_expr2),                \
                (void) (_expr3), (void) (_expr4), (void) (_expr5), 0))
 
+#define BT_DIAG_PUSH _Pragma ("GCC diagnostic push")
+#define BT_DIAG_POP _Pragma ("GCC diagnostic push")
+
+#define BT_DIAG_IGNORE_SHADOW _Pragma("GCC diagnostic ignored \"-Wshadow\"")
+
 #if defined __clang__
 #  if __has_warning("-Wunused-but-set-variable")
 #    define BT_DIAG_IGNORE_UNUSED_BUT_SET_VARIABLE \
index 7c16493530eef9043d82ac1744141b7eba0ad085..4d81b25714283a1959fecd41bb3524f2043c14ef 100644 (file)
@@ -58,7 +58,7 @@ struct mmap_align_data *mmap_align(size_t length, int prot,
         * boundary.
         */
        mma->page_aligned_length = BT_ALIGN(length + offset - page_aligned_offset, page_size);
-       mma->page_aligned_addr = bt_mmap(NULL, mma->page_aligned_length,
+       mma->page_aligned_addr = bt_mmap(mma->page_aligned_length,
                prot, flags, fd, page_aligned_offset, log_level);
        if (mma->page_aligned_addr == MAP_FAILED) {
                free(mma);
diff --git a/src/common/prio-heap.c b/src/common/prio-heap.c
new file mode 100644 (file)
index 0000000..55ed5e8
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Static-sized priority heap containing pointers. Based on CLRS,
+ * chapter 6.
+ */
+
+#include "common/macros.h"
+#include "common/assert.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/prio-heap.h"
+
+#ifdef DEBUG_HEAP
+void check_heap(const struct ptr_heap *heap)
+{
+       size_t i;
+
+       if (!heap->len)
+               return;
+
+       for (i = 1; i < heap->len; i++)
+               BT_ASSERT_DBG(!heap->gt(heap->ptrs[i], heap->ptrs[0]));
+}
+#endif
+
+static
+size_t parent(size_t i)
+{
+       return (i - 1) >> 1;
+}
+
+static
+size_t left(size_t i)
+{
+       return (i << 1) + 1;
+}
+
+static
+size_t right(size_t i)
+{
+       return (i << 1) + 2;
+}
+
+/*
+ * Copy of heap->ptrs pointer is invalid after heap_grow.
+ */
+static
+int heap_grow(struct ptr_heap *heap, size_t new_len)
+{
+       void **new_ptrs;
+
+       if (G_LIKELY(heap->alloc_len >= new_len))
+               return 0;
+
+       heap->alloc_len = bt_max_t(size_t, new_len, heap->alloc_len << 1);
+       new_ptrs = calloc(heap->alloc_len, sizeof(void *));
+       if (G_UNLIKELY(!new_ptrs))
+               return -ENOMEM;
+       if (G_LIKELY(heap->ptrs))
+               memcpy(new_ptrs, heap->ptrs, heap->len * sizeof(void *));
+       free(heap->ptrs);
+       heap->ptrs = new_ptrs;
+       return 0;
+}
+
+static
+int heap_set_len(struct ptr_heap *heap, size_t new_len)
+{
+       int ret;
+
+       ret = heap_grow(heap, new_len);
+       if (G_UNLIKELY(ret))
+               return ret;
+       heap->len = new_len;
+       return 0;
+}
+
+int bt_heap_init(struct ptr_heap *heap, size_t alloc_len,
+             int gt(void *a, void *b))
+{
+       heap->ptrs = NULL;
+       heap->len = 0;
+       heap->alloc_len = 0;
+       heap->gt = gt;
+       /*
+        * Minimum size allocated is 1 entry to ensure memory allocation
+        * never fails within bt_heap_replace_max.
+        */
+       return heap_grow(heap, bt_max_t(size_t, 1, alloc_len));
+}
+
+void bt_heap_free(struct ptr_heap *heap)
+{
+       free(heap->ptrs);
+}
+
+static void heapify(struct ptr_heap *heap, size_t i)
+{
+       void **ptrs = heap->ptrs;
+       size_t l, r, largest;
+
+       for (;;) {
+               void *tmp;
+
+               l = left(i);
+               r = right(i);
+               if (l < heap->len && heap->gt(ptrs[l], ptrs[i]))
+                       largest = l;
+               else
+                       largest = i;
+               if (r < heap->len && heap->gt(ptrs[r], ptrs[largest]))
+                       largest = r;
+               if (G_UNLIKELY(largest == i))
+                       break;
+               tmp = ptrs[i];
+               ptrs[i] = ptrs[largest];
+               ptrs[largest] = tmp;
+               i = largest;
+       }
+       check_heap(heap);
+}
+
+void *bt_heap_replace_max(struct ptr_heap *heap, void *p)
+{
+       void *res;
+
+       if (G_UNLIKELY(!heap->len)) {
+               (void) heap_set_len(heap, 1);
+               heap->ptrs[0] = p;
+               check_heap(heap);
+               return NULL;
+       }
+
+       /* Replace the current max and heapify */
+       res = heap->ptrs[0];
+       heap->ptrs[0] = p;
+       heapify(heap, 0);
+       return res;
+}
+
+int bt_heap_insert(struct ptr_heap *heap, void *p)
+{
+       void **ptrs;
+       size_t pos;
+       int ret;
+
+       ret = heap_set_len(heap, heap->len + 1);
+       if (G_UNLIKELY(ret))
+               return ret;
+       ptrs = heap->ptrs;
+       pos = heap->len - 1;
+       while (pos > 0 && heap->gt(p, ptrs[parent(pos)])) {
+               /* Move parent down until we find the right spot */
+               ptrs[pos] = ptrs[parent(pos)];
+               pos = parent(pos);
+       }
+       ptrs[pos] = p;
+       check_heap(heap);
+       return 0;
+}
+
+void *bt_heap_remove(struct ptr_heap *heap)
+{
+       switch (heap->len) {
+       case 0:
+               return NULL;
+       case 1:
+               (void) heap_set_len(heap, 0);
+               return heap->ptrs[0];
+       }
+       /* Shrink, replace the current max by previous last entry and heapify */
+       heap_set_len(heap, heap->len - 1);
+       /* len changed. previous last entry is at heap->len */
+       return bt_heap_replace_max(heap, heap->ptrs[heap->len]);
+}
+
+void *bt_heap_cherrypick(struct ptr_heap *heap, void *p)
+{
+       size_t pos, len = heap->len;
+
+       for (pos = 0; pos < len; pos++)
+               if (G_UNLIKELY(heap->ptrs[pos] == p))
+                       goto found;
+       return NULL;
+found:
+       if (G_UNLIKELY(heap->len == 1)) {
+               (void) heap_set_len(heap, 0);
+               check_heap(heap);
+               return heap->ptrs[0];
+       }
+       /* Replace p with previous last entry and heapify. */
+       heap_set_len(heap, heap->len - 1);
+       /* len changed. previous last entry is at heap->len */
+       heap->ptrs[pos] = heap->ptrs[heap->len];
+       heapify(heap, pos);
+       return p;
+}
+
+int bt_heap_copy(struct ptr_heap *dst, struct ptr_heap *src)
+{
+       int ret;
+
+       ret = bt_heap_init(dst, src->alloc_len, src->gt);
+       if (ret < 0)
+               goto end;
+
+       ret = heap_set_len(dst, src->len);
+       if (ret < 0)
+               goto end;
+
+       memcpy(dst->ptrs, src->ptrs, src->len * sizeof(void *));
+
+end:
+       return ret;
+}
diff --git a/src/common/prio-heap.h b/src/common/prio-heap.h
new file mode 100644 (file)
index 0000000..7e56521
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Static-sized priority heap containing pointers. Based on CLRS,
+ * chapter 6.
+ */
+
+#ifndef BABELTRACE_COMMON_PRIO_HEAP_H
+#define BABELTRACE_COMMON_PRIO_HEAP_H
+
+#include <unistd.h>
+#include "common/macros.h"
+
+struct ptr_heap {
+       size_t len, alloc_len;
+       void **ptrs;
+       int (*gt)(void *a, void *b);
+};
+
+#ifdef DEBUG_HEAP
+void check_heap(const struct ptr_heap *heap);
+#else
+static inline
+void check_heap(const struct ptr_heap *heap __attribute__((unused)))
+{
+}
+#endif
+
+/**
+ * bt_heap_maximum - return the largest element in the heap
+ * @heap: the heap to be operated on
+ *
+ * Returns the largest element in the heap, without performing any modification
+ * to the heap structure. Returns NULL if the heap is empty.
+ */
+static inline void *bt_heap_maximum(const struct ptr_heap *heap)
+{
+       check_heap(heap);
+       return G_LIKELY(heap->len) ? heap->ptrs[0] : NULL;
+}
+
+/**
+ * bt_heap_init - initialize the heap
+ * @heap: the heap to initialize
+ * @alloc_len: number of elements initially allocated
+ * @gt: function to compare the elements
+ *
+ * Returns -ENOMEM if out of memory.
+ */
+extern int bt_heap_init(struct ptr_heap *heap,
+                    size_t alloc_len,
+                    int gt(void *a, void *b));
+
+/**
+ * bt_heap_free - free the heap
+ * @heap: the heap to free
+ */
+extern void bt_heap_free(struct ptr_heap *heap);
+
+/**
+ * bt_heap_insert - insert an element into the heap
+ * @heap: the heap to be operated on
+ * @p: the element to add
+ *
+ * Insert an element into the heap.
+ *
+ * Returns -ENOMEM if out of memory.
+ */
+extern int bt_heap_insert(struct ptr_heap *heap, void *p);
+
+/**
+ * bt_heap_remove - remove the largest element from the heap
+ * @heap: the heap to be operated on
+ *
+ * Returns the largest element in the heap. It removes this element from the
+ * heap. Returns NULL if the heap is empty.
+ */
+extern void *bt_heap_remove(struct ptr_heap *heap);
+
+/**
+ * bt_heap_cherrypick - remove a given element from the heap
+ * @heap: the heap to be operated on
+ * @p: the element
+ *
+ * Remove the given element from the heap. Return the element if present, else
+ * return NULL. This algorithm has a complexity of O(n), which is higher than
+ * O(log(n)) provided by the rest of this API.
+ */
+extern void *bt_heap_cherrypick(struct ptr_heap *heap, void *p);
+
+/**
+ * bt_heap_replace_max - replace the the largest element from the heap
+ * @heap: the heap to be operated on
+ * @p: the pointer to be inserted as topmost element replacement
+ *
+ * Returns the largest element in the heap. It removes this element from the
+ * heap. The heap is rebalanced only once after the insertion. Returns NULL if
+ * the heap is empty.
+ *
+ * This is the equivalent of calling bt_heap_remove() and then bt_heap_insert(), but
+ * it only rebalances the heap once. It never allocates memory.
+ */
+extern void *bt_heap_replace_max(struct ptr_heap *heap, void *p);
+
+/**
+ * bt_heap_copy - copy a heap
+ * @dst: the destination heap (must be allocated)
+ * @src: the source heap
+ *
+ * Returns -ENOMEM if out of memory.
+ */
+extern int bt_heap_copy(struct ptr_heap *dst, struct ptr_heap *src);
+
+#endif /* BABELTRACE_COMMON_PRIO_HEAP_H */
index a0af2bc04dc75b57238cfda7d88ed000f92bbe32..5c709f73e2eca890550b5f8666327323fbdad3bf 100644 (file)
@@ -16,7 +16,6 @@
 /*
  * Generate a random UUID according to RFC4122, section 4.4.
  */
-BT_HIDDEN
 void bt_uuid_generate(bt_uuid_t uuid_out)
 {
        int i;
@@ -51,16 +50,14 @@ void bt_uuid_generate(bt_uuid_t uuid_out)
        g_rand_free(rand);
 }
 
-BT_HIDDEN
 void bt_uuid_to_str(const bt_uuid_t uuid_in, char *str_out)
 {
        BT_ASSERT_DBG(uuid_in);
        BT_ASSERT_DBG(str_out);
 
-       sprintf(str_out, BT_UUID_FMT, BT_UUID_FMT_VALUES(uuid_in));
+       snprintf(str_out, BT_UUID_STR_LEN + 1, BT_UUID_FMT, BT_UUID_FMT_VALUES(uuid_in));
 }
 
-BT_HIDDEN
 int bt_uuid_from_str(const char *str_in, bt_uuid_t uuid_out)
 {
        int ret = 0;
@@ -84,13 +81,11 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_uuid_compare(const bt_uuid_t uuid_a, const bt_uuid_t uuid_b)
 {
        return memcmp(uuid_a, uuid_b, BT_UUID_LEN);
 }
 
-BT_HIDDEN
 void bt_uuid_copy(bt_uuid_t uuid_dest, const bt_uuid_t uuid_src)
 {
        BT_ASSERT(uuid_dest);
index 73ae1d8af339aea7779cb78127aee04de7b4af13..c8418a67e6273037f4ac13aa8aa048a922531e7e 100644 (file)
@@ -7,6 +7,7 @@
 #ifndef _BABELTRACE_COMMON_UUID_H
 #define _BABELTRACE_COMMON_UUID_H
 
+#include <inttypes.h>
 #include <stdint.h>
 #include "common/macros.h"
 
@@ -36,11 +37,11 @@ extern "C" {
 
 typedef uint8_t bt_uuid_t[BT_UUID_LEN];
 
-BT_HIDDEN void bt_uuid_generate(bt_uuid_t uuid_out);
-BT_HIDDEN void bt_uuid_to_str(const bt_uuid_t uuid_in, char *str_out);
-BT_HIDDEN int bt_uuid_from_str(const char *str_in, bt_uuid_t uuid_out);
-BT_HIDDEN int bt_uuid_compare(const bt_uuid_t uuid_a, const bt_uuid_t uuid_b);
-BT_HIDDEN void bt_uuid_copy(bt_uuid_t uuid_dest, const bt_uuid_t uuid_src);
+void bt_uuid_generate(bt_uuid_t uuid_out);
+void bt_uuid_to_str(const bt_uuid_t uuid_in, char *str_out);
+int bt_uuid_from_str(const char *str_in, bt_uuid_t uuid_out);
+int bt_uuid_compare(const bt_uuid_t uuid_a, const bt_uuid_t uuid_b);
+void bt_uuid_copy(bt_uuid_t uuid_dest, const bt_uuid_t uuid_src);
 
 #ifdef __cplusplus
 }
diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am
deleted file mode 100644 (file)
index c857372..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libcompat.la
-
-libcompat_la_SOURCES = \
-       mman.c \
-       mman.h
-
-libcompat_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LD_NO_AS_NEEDED)
-
-noinst_HEADERS = \
-       bitfield.h \
-       compiler.h \
-       endian.h \
-       fcntl.h \
-       glib.h \
-       limits.h \
-       memstream.h \
-       socket.h \
-       stdio.h \
-       stdlib.h \
-       string.h \
-       time.h \
-       unistd.h \
-       utc.h
index dcadf048828daefc57ec61ee02ba442d71f456c0..374a5e729a2a644058b6f8d6292ad534084023cb 100644 (file)
@@ -174,7 +174,10 @@ FILE *bt_open_memstream(char **ptr, size_t *sizeloc)
 }
 
 static inline
-int bt_close_memstream(char **buf, size_t *size, FILE *fp)
+int bt_close_memstream(
+               char **buf __attribute__((unused)),
+               size_t *size __attribute__((unused)),
+               FILE *fp)
 {
        return fclose(fp);
 }
@@ -194,7 +197,8 @@ int bt_close_memstream(char **buf, size_t *size, FILE *fp)
  * into the buffer (which we allocate).
  */
 static inline
-FILE *bt_open_memstream(char **ptr, size_t *sizeloc)
+FILE *bt_open_memstream(char **ptr __attribute__((unused)),
+               size_t *sizeloc __attribute__((unused)))
 {
        char *tmpname;
        FILE *fp;
@@ -233,7 +237,8 @@ error_free:
  * into the buffer (which we allocate).
  */
 static inline
-FILE *bt_open_memstream(char **ptr, size_t *sizeloc)
+FILE *bt_open_memstream(char **ptr __attribute__((unused)),
+               size_t *sizeloc __attribute__((unused)))
 {
        char *tmpname;
        int ret;
index 7cd7a6fd8105dff245209cf3311473c0e8e84957..251e87354d1a40e1908c522d4031d7095b1eed47 100644 (file)
@@ -20,7 +20,6 @@
  * On macOS, we need a dummy symbol so that the linker won't
  * complain of an empty table of contents.
  */
-BT_HIDDEN
 int bt_mman_dummy_symbol;
 #endif /* __APPLE__ */
 
@@ -153,9 +152,8 @@ DWORD map_prot_flags(int prot, DWORD *dwDesiredAccess)
        return 0;
 }
 
-BT_HIDDEN
-void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd,
-               off_t offset, int log_level)
+void *bt_mmap(size_t length, int prot, int flags, int fd, off_t offset,
+               int log_level)
 {
        struct mmap_mapping *mapping = NULL;
        void *mapping_addr;
@@ -254,8 +252,7 @@ error:
        return MAP_FAILED;
 }
 
-BT_HIDDEN
-int bt_munmap(void *addr, size_t length)
+int bt_munmap(void *addr, size_t length __attribute__((unused)))
 {
        int ret = 0;
        struct mmap_mapping *mapping = addr;
@@ -283,13 +280,12 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 size_t bt_mmap_get_offset_align_size(int log_level)
 {
        SYSTEM_INFO sysinfo;
 
        GetNativeSystemInfo(&sysinfo);
-       BT_LOG_WRITE_CUR_LVL(BT_LOG_DEBUG, log_level, BT_LOG_TAG,
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_DEBUG, log_level, BT_LOG_TAG,
                "Allocator granularity is %lu.",
                sysinfo.dwAllocationGranularity);
 
index 0053e3071cf7dbcdeb8512eddd821a26827b042d..33febccbb1c1e760c601a821ed6d3522e9e2bebe 100644 (file)
@@ -30,7 +30,7 @@
  * Note that some platforms (e.g. Windows) do not allow read-only
  * mappings to exceed the file's size (even within a page).
  */
-BT_EXTERN_C void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd,
+BT_EXTERN_C void *bt_mmap(size_t length, int prot, int flags, int fd,
        off_t offset, int log_level);
 
 BT_EXTERN_C int bt_munmap(void *addr, size_t length);
@@ -47,10 +47,10 @@ BT_EXTERN_C size_t bt_mmap_get_offset_align_size(int log_level);
 #include "common/common.h"
 
 static inline
-void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd,
-       off_t offset, int log_level)
+void *bt_mmap(size_t length, int prot, int flags, int fd,
+       off_t offset, int log_level __attribute__((unused)))
 {
-       return (void *) mmap(addr, length, prot, flags, fd, offset);
+       return (void *) mmap(NULL, length, prot, flags, fd, offset);
 }
 
 static inline
diff --git a/src/compat/socket.h b/src/compat/socket.h
deleted file mode 100644 (file)
index 7a57678..0000000
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2015-2017 Michael Jeanson <mjeanson@efficios.com>
- * Copyright (C) 2015 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#ifndef _BABELTRACE_COMPAT_SOCKET_H
-#define _BABELTRACE_COMPAT_SOCKET_H
-
-#include <stdbool.h>
-
-#ifdef __MINGW32__
-
-#include <winsock2.h>
-
-#define BT_INVALID_SOCKET INVALID_SOCKET
-#define BT_SOCKET_ERROR SOCKET_ERROR
-#define BT_SOCKET SOCKET
-
-static inline
-int bt_socket_init(int log_level)
-{
-       WORD verreq;
-       WSADATA wsa;
-       int ret;
-
-       /* Request winsock 2.2 support */
-       verreq = MAKEWORD(2, 2);
-
-       ret = WSAStartup(verreq, &wsa);
-       if (ret != 0) {
-#ifdef BT_LOG_WRITE_CUR_LVL
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
-                               "Winsock init failed with error: %d", ret);
-#endif
-               goto end;
-       }
-
-       if (LOBYTE(wsa.wVersion) != 2 || HIBYTE(wsa.wVersion) != 2) {
-#ifdef BT_LOG_WRITE_CUR_LVL
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
-                               "Could not init winsock 2.2 support");
-#endif
-               WSACleanup();
-               ret = -1;
-       }
-
-end:
-       return ret;
-}
-
-static inline
-int bt_socket_fini(void)
-{
-       return WSACleanup();
-}
-
-static inline
-int bt_socket_send(int sockfd, const void *buf, size_t len, int flags)
-{
-       return send(sockfd, (const char *) buf, len, flags);
-}
-
-static inline
-int bt_socket_recv(int sockfd, void *buf, size_t len, int flags)
-{
-       return recv(sockfd, (char *) buf, len, flags);
-}
-
-static inline
-int bt_socket_close(int fd)
-{
-       return closesocket(fd);
-}
-
-static inline
-bool bt_socket_interrupted(void)
-{
-       /* There is no equivalent to EINTR in winsock 2.2 */
-       return false;
-}
-
-static inline
-const char *bt_socket_errormsg(void)
-{
-       const char *errstr;
-       int error = WSAGetLastError();
-
-       switch (error) {
-       case WSAEINTR:
-               errstr = "Call interrupted";
-               break;
-       case WSAEBADF:
-               errstr = "Bad file";
-               break;
-       case WSAEACCES:
-               errstr = "Bad access";
-               break;
-       case WSAEFAULT:
-               errstr = "Bad argument";
-               break;
-       case WSAEINVAL:
-               errstr = "Invalid arguments";
-               break;
-       case WSAEMFILE:
-               errstr = "Out of file descriptors";
-               break;
-       case WSAEWOULDBLOCK:
-               errstr = "Call would block";
-               break;
-       case WSAEINPROGRESS:
-       case WSAEALREADY:
-               errstr = "Blocking call in progress";
-               break;
-       case WSAENOTSOCK:
-               errstr = "Descriptor is not a socket";
-               break;
-       case WSAEDESTADDRREQ:
-               errstr = "Need destination address";
-               break;
-       case WSAEMSGSIZE:
-               errstr = "Bad message size";
-               break;
-       case WSAEPROTOTYPE:
-               errstr = "Bad protocol";
-               break;
-       case WSAENOPROTOOPT:
-               errstr = "Protocol option is unsupported";
-               break;
-       case WSAEPROTONOSUPPORT:
-               errstr = "Protocol is unsupported";
-               break;
-       case WSAESOCKTNOSUPPORT:
-               errstr = "Socket is unsupported";
-               break;
-       case WSAEOPNOTSUPP:
-               errstr = "Operation not supported";
-               break;
-       case WSAEAFNOSUPPORT:
-               errstr = "Address family not supported";
-               break;
-       case WSAEPFNOSUPPORT:
-               errstr = "Protocol family not supported";
-               break;
-       case WSAEADDRINUSE:
-               errstr = "Address already in use";
-               break;
-       case WSAEADDRNOTAVAIL:
-               errstr = "Address not available";
-               break;
-       case WSAENETDOWN:
-               errstr = "Network down";
-               break;
-       case WSAENETUNREACH:
-               errstr = "Network unreachable";
-               break;
-       case WSAENETRESET:
-               errstr = "Network has been reset";
-               break;
-       case WSAECONNABORTED:
-               errstr = "Connection was aborted";
-               break;
-       case WSAECONNRESET:
-               errstr = "Connection was reset";
-               break;
-       case WSAENOBUFS:
-               errstr = "No buffer space";
-               break;
-       case WSAEISCONN:
-               errstr = "Socket is already connected";
-               break;
-       case WSAENOTCONN:
-               errstr = "Socket is not connected";
-               break;
-       case WSAESHUTDOWN:
-               errstr = "Socket has been shut down";
-               break;
-       case WSAETOOMANYREFS:
-               errstr = "Too many references";
-               break;
-       case WSAETIMEDOUT:
-               errstr = "Timed out";
-               break;
-       case WSAECONNREFUSED:
-               errstr = "Connection refused";
-               break;
-       case WSAELOOP:
-               errstr = "Loop??";
-               break;
-       case WSAENAMETOOLONG:
-               errstr = "Name too long";
-               break;
-       case WSAEHOSTDOWN:
-               errstr = "Host down";
-               break;
-       case WSAEHOSTUNREACH:
-               errstr = "Host unreachable";
-               break;
-       case WSAENOTEMPTY:
-               errstr = "Not empty";
-               break;
-       case WSAEPROCLIM:
-               errstr = "Process limit reached";
-               break;
-       case WSAEUSERS:
-               errstr = "Too many users";
-               break;
-       case WSAEDQUOT:
-               errstr = "Bad quota";
-               break;
-       case WSAESTALE:
-               errstr = "Something is stale";
-               break;
-       case WSAEREMOTE:
-               errstr = "Remote error";
-               break;
-       case WSAEDISCON:
-               errstr = "Disconnected";
-               break;
-
-       /* Extended Winsock errors */
-       case WSASYSNOTREADY:
-               errstr = "Winsock library is not ready";
-               break;
-       case WSANOTINITIALISED:
-               errstr = "Winsock library not initialised";
-               break;
-       case WSAVERNOTSUPPORTED:
-               errstr = "Winsock version not supported";
-               break;
-
-       /* getXbyY() errors (already handled in herrmsg):
-        * Authoritative Answer: Host not found */
-       case WSAHOST_NOT_FOUND:
-               errstr = "Host not found";
-               break;
-
-       /* Non-Authoritative: Host not found, or SERVERFAIL */
-       case WSATRY_AGAIN:
-               errstr = "Host not found, try again";
-               break;
-
-       /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
-       case WSANO_RECOVERY:
-               errstr = "Unrecoverable error in call to nameserver";
-               break;
-
-       /* Valid name, no data record of requested type */
-       case WSANO_DATA:
-               errstr = "No data record of requested type";
-               break;
-
-       default:
-               errstr = "Unknown error";
-       }
-
-       return errstr;
-}
-
-#else /* __MINGW32__ */
-
-#include <errno.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include <glib.h>
-
-#define BT_INVALID_SOCKET -1
-#define BT_SOCKET_ERROR -1
-#define BT_SOCKET int
-
-static inline
-int bt_socket_init(int log_level)
-{
-       return 0;
-}
-
-static inline
-int bt_socket_fini(void)
-{
-       return 0;
-}
-
-static inline
-int bt_socket_send(int sockfd, const void *buf, size_t len, int flags)
-{
-       return send(sockfd, buf, len, flags);
-}
-
-static inline
-int bt_socket_recv(int sockfd, void *buf, size_t len, int flags)
-{
-       return recv(sockfd, buf, len, flags);
-}
-
-static inline
-int bt_socket_close(int fd)
-{
-       return close(fd);
-}
-
-static inline
-bool bt_socket_interrupted(void)
-{
-       return (errno == EINTR);
-}
-
-static inline
-const char *bt_socket_errormsg(void)
-{
-       return g_strerror(errno);
-}
-#endif
-
-
-/*
- * This wrapper is used on platforms that have no way of ignoring SIGPIPE
- * during a send().
- */
-
-#ifndef MSG_NOSIGNAL
-# ifdef SO_NOSIGPIPE
-#   define MSG_NOSIGNAL SO_NOSIGPIPE
-# elif defined(__MINGW32__)
-#   define MSG_NOSIGNAL 0
-# endif
-#endif
-
-#if defined(MSG_NOSIGNAL)
-static inline
-ssize_t bt_socket_send_nosigpipe(int fd, const void *buffer, size_t size)
-{
-       return bt_socket_send(fd, buffer, size, MSG_NOSIGNAL);
-}
-#else
-
-#include <signal.h>
-
-static inline
-ssize_t bt_socket_send_nosigpipe(int fd, const void *buffer, size_t size)
-{
-       ssize_t sent;
-       int saved_err;
-       sigset_t sigpipe_set, pending_set, old_set;
-       int sigpipe_was_pending;
-
-       /*
-        * Discard the SIGPIPE from send(), not disturbing any SIGPIPE
-        * that might be already pending. If a bogus SIGPIPE is sent to
-        * the entire process concurrently by a malicious user, it may
-        * be simply discarded.
-        */
-       if (sigemptyset(&pending_set)) {
-               return -1;
-       }
-       /*
-        * sigpending returns the mask of signals that are _both_
-        * blocked for the thread _and_ pending for either the thread or
-        * the entire process.
-        */
-       if (sigpending(&pending_set)) {
-               return -1;
-       }
-       sigpipe_was_pending = sigismember(&pending_set, SIGPIPE);
-       /*
-        * If sigpipe was pending, it means it was already blocked, so
-        * no need to block it.
-        */
-       if (!sigpipe_was_pending) {
-               if (sigemptyset(&sigpipe_set)) {
-                       return -1;
-               }
-               if (sigaddset(&sigpipe_set, SIGPIPE)) {
-                       return -1;
-               }
-               if (pthread_sigmask(SIG_BLOCK, &sigpipe_set, &old_set)) {
-                       return -1;
-               }
-       }
-
-       /* Send and save errno. */
-       sent = bt_socket_send(fd, buffer, size, 0);
-       saved_err = errno;
-
-       if (sent == -1 && errno == EPIPE && !sigpipe_was_pending) {
-               struct timespec timeout = { 0, 0 };
-               int ret;
-
-               do {
-                       ret = sigtimedwait(&sigpipe_set, NULL,
-                               &timeout);
-               } while (ret == -1 && errno == EINTR);
-       }
-       if (!sigpipe_was_pending) {
-               if (pthread_sigmask(SIG_SETMASK, &old_set, NULL)) {
-                       return -1;
-               }
-       }
-       /* Restore send() errno */
-       errno = saved_err;
-
-       return sent;
-}
-#endif
-
-
-#endif /* _BABELTRACE_COMPAT_SOCKET_H */
diff --git a/src/compat/socket.hpp b/src/compat/socket.hpp
new file mode 100644 (file)
index 0000000..972f692
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2015-2017 Michael Jeanson <mjeanson@efficios.com>
+ * Copyright (C) 2015 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef BABELTRACE_COMPAT_SOCKET_HPP
+#define BABELTRACE_COMPAT_SOCKET_HPP
+
+#include <stdbool.h>
+
+#include "cpp-common/bt2c/logging.hpp"
+
+#ifdef __MINGW32__
+
+#    include <winsock2.h>
+
+#    define BT_INVALID_SOCKET INVALID_SOCKET
+#    define BT_SOCKET_ERROR   SOCKET_ERROR
+#    define BT_SOCKET         SOCKET
+
+static inline int bt_socket_init(const bt2c::Logger& logger)
+{
+    WORD verreq;
+    WSADATA wsa;
+    int ret;
+
+    /* Request winsock 2.2 support */
+    verreq = MAKEWORD(2, 2);
+
+    ret = WSAStartup(verreq, &wsa);
+    if (ret != 0) {
+        BT_CPPLOGE_SPEC(logger, "Winsock init failed with error: {}", ret);
+        goto end;
+    }
+
+    if (LOBYTE(wsa.wVersion) != 2 || HIBYTE(wsa.wVersion) != 2) {
+        BT_CPPLOGE_SPEC(logger, "Could not init winsock 2.2 support");
+        WSACleanup();
+        ret = -1;
+    }
+
+end:
+    return ret;
+}
+
+static inline int bt_socket_fini(void)
+{
+    return WSACleanup();
+}
+
+static inline int bt_socket_send(int sockfd, const void *buf, size_t len, int flags)
+{
+    return send(sockfd, (const char *) buf, len, flags);
+}
+
+static inline int bt_socket_recv(int sockfd, void *buf, size_t len, int flags)
+{
+    return recv(sockfd, (char *) buf, len, flags);
+}
+
+static inline int bt_socket_close(int fd)
+{
+    return closesocket(fd);
+}
+
+static inline bool bt_socket_interrupted(void)
+{
+    /* There is no equivalent to EINTR in winsock 2.2 */
+    return false;
+}
+
+static inline const char *bt_socket_errormsg(void)
+{
+    const char *errstr;
+    int error = WSAGetLastError();
+
+    switch (error) {
+    case WSAEINTR:
+        errstr = "Call interrupted";
+        break;
+    case WSAEBADF:
+        errstr = "Bad file";
+        break;
+    case WSAEACCES:
+        errstr = "Bad access";
+        break;
+    case WSAEFAULT:
+        errstr = "Bad argument";
+        break;
+    case WSAEINVAL:
+        errstr = "Invalid arguments";
+        break;
+    case WSAEMFILE:
+        errstr = "Out of file descriptors";
+        break;
+    case WSAEWOULDBLOCK:
+        errstr = "Call would block";
+        break;
+    case WSAEINPROGRESS:
+    case WSAEALREADY:
+        errstr = "Blocking call in progress";
+        break;
+    case WSAENOTSOCK:
+        errstr = "Descriptor is not a socket";
+        break;
+    case WSAEDESTADDRREQ:
+        errstr = "Need destination address";
+        break;
+    case WSAEMSGSIZE:
+        errstr = "Bad message size";
+        break;
+    case WSAEPROTOTYPE:
+        errstr = "Bad protocol";
+        break;
+    case WSAENOPROTOOPT:
+        errstr = "Protocol option is unsupported";
+        break;
+    case WSAEPROTONOSUPPORT:
+        errstr = "Protocol is unsupported";
+        break;
+    case WSAESOCKTNOSUPPORT:
+        errstr = "Socket is unsupported";
+        break;
+    case WSAEOPNOTSUPP:
+        errstr = "Operation not supported";
+        break;
+    case WSAEAFNOSUPPORT:
+        errstr = "Address family not supported";
+        break;
+    case WSAEPFNOSUPPORT:
+        errstr = "Protocol family not supported";
+        break;
+    case WSAEADDRINUSE:
+        errstr = "Address already in use";
+        break;
+    case WSAEADDRNOTAVAIL:
+        errstr = "Address not available";
+        break;
+    case WSAENETDOWN:
+        errstr = "Network down";
+        break;
+    case WSAENETUNREACH:
+        errstr = "Network unreachable";
+        break;
+    case WSAENETRESET:
+        errstr = "Network has been reset";
+        break;
+    case WSAECONNABORTED:
+        errstr = "Connection was aborted";
+        break;
+    case WSAECONNRESET:
+        errstr = "Connection was reset";
+        break;
+    case WSAENOBUFS:
+        errstr = "No buffer space";
+        break;
+    case WSAEISCONN:
+        errstr = "Socket is already connected";
+        break;
+    case WSAENOTCONN:
+        errstr = "Socket is not connected";
+        break;
+    case WSAESHUTDOWN:
+        errstr = "Socket has been shut down";
+        break;
+    case WSAETOOMANYREFS:
+        errstr = "Too many references";
+        break;
+    case WSAETIMEDOUT:
+        errstr = "Timed out";
+        break;
+    case WSAECONNREFUSED:
+        errstr = "Connection refused";
+        break;
+    case WSAELOOP:
+        errstr = "Loop??";
+        break;
+    case WSAENAMETOOLONG:
+        errstr = "Name too long";
+        break;
+    case WSAEHOSTDOWN:
+        errstr = "Host down";
+        break;
+    case WSAEHOSTUNREACH:
+        errstr = "Host unreachable";
+        break;
+    case WSAENOTEMPTY:
+        errstr = "Not empty";
+        break;
+    case WSAEPROCLIM:
+        errstr = "Process limit reached";
+        break;
+    case WSAEUSERS:
+        errstr = "Too many users";
+        break;
+    case WSAEDQUOT:
+        errstr = "Bad quota";
+        break;
+    case WSAESTALE:
+        errstr = "Something is stale";
+        break;
+    case WSAEREMOTE:
+        errstr = "Remote error";
+        break;
+    case WSAEDISCON:
+        errstr = "Disconnected";
+        break;
+
+    /* Extended Winsock errors */
+    case WSASYSNOTREADY:
+        errstr = "Winsock library is not ready";
+        break;
+    case WSANOTINITIALISED:
+        errstr = "Winsock library not initialised";
+        break;
+    case WSAVERNOTSUPPORTED:
+        errstr = "Winsock version not supported";
+        break;
+
+    /* getXbyY() errors (already handled in herrmsg):
+        * Authoritative Answer: Host not found */
+    case WSAHOST_NOT_FOUND:
+        errstr = "Host not found";
+        break;
+
+    /* Non-Authoritative: Host not found, or SERVERFAIL */
+    case WSATRY_AGAIN:
+        errstr = "Host not found, try again";
+        break;
+
+    /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
+    case WSANO_RECOVERY:
+        errstr = "Unrecoverable error in call to nameserver";
+        break;
+
+    /* Valid name, no data record of requested type */
+    case WSANO_DATA:
+        errstr = "No data record of requested type";
+        break;
+
+    default:
+        errstr = "Unknown error";
+    }
+
+    return errstr;
+}
+
+#else /* __MINGW32__ */
+
+#    include <errno.h>
+#    include <glib.h>
+#    include <netdb.h>
+#    include <netinet/in.h>
+#    include <sys/socket.h>
+#    include <unistd.h>
+
+#    define BT_INVALID_SOCKET -1
+#    define BT_SOCKET_ERROR   -1
+#    define BT_SOCKET         int
+
+static inline int bt_socket_init(const bt2c::Logger&)
+{
+    return 0;
+}
+
+static inline int bt_socket_fini(void)
+{
+    return 0;
+}
+
+static inline int bt_socket_send(int sockfd, const void *buf, size_t len, int flags)
+{
+    return send(sockfd, buf, len, flags);
+}
+
+static inline int bt_socket_recv(int sockfd, void *buf, size_t len, int flags)
+{
+    return recv(sockfd, buf, len, flags);
+}
+
+static inline int bt_socket_close(int fd)
+{
+    return close(fd);
+}
+
+static inline bool bt_socket_interrupted(void)
+{
+    return (errno == EINTR);
+}
+
+static inline const char *bt_socket_errormsg(void)
+{
+    return g_strerror(errno);
+}
+#endif
+
+/*
+ * This wrapper is used on platforms that have no way of ignoring SIGPIPE
+ * during a send().
+ */
+
+#ifndef MSG_NOSIGNAL
+#    ifdef SO_NOSIGPIPE
+#        define MSG_NOSIGNAL SO_NOSIGPIPE
+#    elif defined(__MINGW32__)
+#        define MSG_NOSIGNAL 0
+#    endif
+#endif
+
+#if defined(MSG_NOSIGNAL)
+static inline ssize_t bt_socket_send_nosigpipe(int fd, const void *buffer, size_t size)
+{
+    return bt_socket_send(fd, buffer, size, MSG_NOSIGNAL);
+}
+#else
+
+#    include <signal.h>
+
+static inline ssize_t bt_socket_send_nosigpipe(int fd, const void *buffer, size_t size)
+{
+    ssize_t sent;
+    int saved_err;
+    sigset_t sigpipe_set, pending_set, old_set;
+    int sigpipe_was_pending;
+
+    /*
+        * Discard the SIGPIPE from send(), not disturbing any SIGPIPE
+        * that might be already pending. If a bogus SIGPIPE is sent to
+        * the entire process concurrently by a malicious user, it may
+        * be simply discarded.
+        */
+    if (sigemptyset(&pending_set)) {
+        return -1;
+    }
+    /*
+        * sigpending returns the mask of signals that are _both_
+        * blocked for the thread _and_ pending for either the thread or
+        * the entire process.
+        */
+    if (sigpending(&pending_set)) {
+        return -1;
+    }
+    sigpipe_was_pending = sigismember(&pending_set, SIGPIPE);
+    /*
+        * If sigpipe was pending, it means it was already blocked, so
+        * no need to block it.
+        */
+    if (!sigpipe_was_pending) {
+        if (sigemptyset(&sigpipe_set)) {
+            return -1;
+        }
+        if (sigaddset(&sigpipe_set, SIGPIPE)) {
+            return -1;
+        }
+        if (pthread_sigmask(SIG_BLOCK, &sigpipe_set, &old_set)) {
+            return -1;
+        }
+    }
+
+    /* Send and save errno. */
+    sent = bt_socket_send(fd, buffer, size, 0);
+    saved_err = errno;
+
+    if (sent == -1 && errno == EPIPE && !sigpipe_was_pending) {
+        struct timespec timeout = {0, 0};
+        int ret;
+
+        do {
+            ret = sigtimedwait(&sigpipe_set, NULL, &timeout);
+        } while (ret == -1 && errno == EINTR);
+    }
+    if (!sigpipe_was_pending) {
+        if (pthread_sigmask(SIG_SETMASK, &old_set, NULL)) {
+            return -1;
+        }
+    }
+    /* Restore send() errno */
+    errno = saved_err;
+
+    return sent;
+}
+#endif
+
+#endif /* BABELTRACE_COMPAT_SOCKET_HPP */
diff --git a/src/cpp-common/Makefile.am b/src/cpp-common/Makefile.am
deleted file mode 100644 (file)
index a61c1d1..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-EXTRA_DIST = bt2 optional.hpp string_view.hpp
diff --git a/src/cpp-common/bt2/borrowed-object-iterator.hpp b/src/cpp-common/bt2/borrowed-object-iterator.hpp
new file mode 100644 (file)
index 0000000..3602a4e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_BORROWED_OBJECT_ITERATOR_HPP
+#define BABELTRACE_CPP_COMMON_BT2_BORROWED_OBJECT_ITERATOR_HPP
+
+#include <cstdint>
+#include <type_traits>
+#include <utility>
+
+#include "common/assert.h"
+
+#include "borrowed-object-proxy.hpp"
+
+namespace bt2 {
+
+/*
+ * An iterator class to iterate an instance of a borrowed object
+ * container of type `ContainerT`.
+ *
+ * `ContainerT` must implement:
+ *
+ *     // Returns the number of contained borrowed objects.
+ *     std::uint64_t length() const noexcept;
+ *
+ *     // Returns the borrowed object at index `i`.
+ *     SomeObject operator[](std::uint64_t i) const noexcept;
+ */
+template <typename ContainerT>
+class BorrowedObjectIterator final
+{
+    friend ContainerT;
+
+public:
+    using Object = typename std::remove_reference<
+        decltype(std::declval<ContainerT>()[std::declval<std::uint64_t>()])>::type;
+
+private:
+    explicit BorrowedObjectIterator(const ContainerT container, const uint64_t idx) :
+        _mContainer {container}, _mIdx {idx}
+    {
+    }
+
+public:
+    BorrowedObjectIterator& operator++() noexcept
+    {
+        ++_mIdx;
+        return *this;
+    }
+
+    BorrowedObjectIterator operator++(int) noexcept
+    {
+        const auto tmp = *this;
+
+        ++(*this);
+        return tmp;
+    }
+
+    bool operator==(const BorrowedObjectIterator& other) const noexcept
+    {
+        BT_ASSERT_DBG(_mContainer.isSame(other._mContainer));
+        return _mIdx == other._mIdx;
+    }
+
+    bool operator!=(const BorrowedObjectIterator& other) const noexcept
+    {
+        return !(*this == other);
+    }
+
+    Object operator*() const noexcept
+    {
+        return _mContainer[_mIdx];
+    }
+
+    BorrowedObjectProxy<Object> operator->() const noexcept
+    {
+        return BorrowedObjectProxy<Object> {(**this).libObjPtr()};
+    }
+
+private:
+    ContainerT _mContainer;
+    std::uint64_t _mIdx;
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_BORROWED_OBJECT_ITERATOR_HPP */
diff --git a/src/cpp-common/bt2/borrowed-object-proxy.hpp b/src/cpp-common/bt2/borrowed-object-proxy.hpp
new file mode 100644 (file)
index 0000000..1b20cc8
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_BORROWED_OBJECT_PROXY_HPP
+#define BABELTRACE_CPP_COMMON_BT2_BORROWED_OBJECT_PROXY_HPP
+
+namespace bt2 {
+
+/*
+ * A proxy containing a valid borrowed object instance of `ObjT` to make
+ * Something::operator->() work when only a libbabeltrace2 object
+ * pointer is available.
+ */
+template <typename ObjT>
+class BorrowedObjectProxy final
+{
+public:
+    explicit BorrowedObjectProxy(typename ObjT::LibObjPtr libObjPtr) noexcept : _mObj {libObjPtr}
+    {
+    }
+
+    const ObjT *operator->() const noexcept
+    {
+        return &_mObj;
+    }
+
+private:
+    ObjT _mObj;
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_BORROWED_OBJECT_PROXY_HPP */
diff --git a/src/cpp-common/bt2/borrowed-object.hpp b/src/cpp-common/bt2/borrowed-object.hpp
new file mode 100644 (file)
index 0000000..fbfd5b1
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2019-2020 (c) Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_BORROWED_OBJECT_HPP
+#define BABELTRACE_CPP_COMMON_BT2_BORROWED_OBJECT_HPP
+
+#include <functional>
+#include <type_traits>
+
+#include "common/assert.h"
+
+namespace bt2 {
+
+/*
+ * An instance of this class wraps a pointer to a libbabeltrace2 object
+ * of type `LibObjT` without managing any reference counting.
+ *
+ * This is an abstract base class for any libbabeltrace2 object wrapper.
+ *
+ * `LibObjT` is the direct libbabeltrace2 object type, for example
+ * `bt_stream_class` or `const bt_value`.
+ *
+ * The user of a borrowed object, including methods of a derived class,
+ * can call libObjPtr() to access the libbabeltrace2 object pointer.
+ *
+ * You may only build a borrowed object with a pointer which isn't
+ * `nullptr`. See `bt2::OptionalBorrowedObject` for an optional version.
+ */
+template <typename LibObjT>
+class BorrowedObject
+{
+    static_assert(!std::is_pointer<LibObjT>::value, "`LibObjT` must not be a pointer");
+
+    /*
+     * This makes it possible for a `BorrowedObject<const bt_something>`
+     * instance to get assigned an instance of
+     * `BorrowedObject<bt_something>` ("copy" constructor and
+     * "assignment" operator).
+     *
+     * C++ forbids the other way around.
+     */
+    template <typename>
+    friend class BorrowedObject;
+
+private:
+    /*
+     * Provides `val` which indicates whether or not you can assign this
+     * object from a borrowed object of type `OtherLibObjT`.
+     */
+    template <typename OtherLibObjT>
+    struct _AssignableFromConst final
+    {
+        /*
+         * If `LibObjT` is const (for example, `const bt_value`), then
+         * you may always assign from its non-const equivalent (for
+         * example, `bt_value`). In C (correct):
+         *
+         *     bt_value * const meow = bt_value_bool_create_init(BT_TRUE);
+         *     const bt_value * const mix = meow;
+         *
+         * If `LibObjT` is non-const, then you may not assign from its
+         * const equivalent. In C (not correct):
+         *
+         *     const bt_value * const meow =
+         *         bt_value_array_borrow_element_by_index_const(some_val, 17);
+         *     bt_value * const mix = meow;
+         */
+        static constexpr bool val =
+            std::is_const<LibObjT>::value || !std::is_const<OtherLibObjT>::value;
+    };
+
+protected:
+    /* This complete borrowed object */
+    using _ThisBorrowedObject = BorrowedObject<LibObjT>;
+
+public:
+    /* libbabeltrace2 object */
+    using LibObj = LibObjT;
+
+    /* libbabeltrace2 object pointer */
+    using LibObjPtr = LibObjT *;
+
+protected:
+    /*
+     * Builds a borrowed object to wrap the libbabeltrace2 object
+     * pointer `libObjPtr`.
+     *
+     * `libObjPtr` must not be `nullptr`.
+     */
+    explicit BorrowedObject(const LibObjPtr libObjPtr) noexcept : _mLibObjPtr {libObjPtr}
+    {
+        BT_ASSERT_DBG(libObjPtr);
+    }
+
+    /*
+     * Generic "copy" constructor.
+     *
+     * This converting constructor accepts both an instance of
+     * `_ThisBorrowedObject` and an instance (`other`) of
+     * `BorrowedObject<ConstLibObjT>`, where `ConstLibObjT` is the
+     * `const` version of `LibObjT`, if applicable.
+     *
+     * This makes it possible for a `BorrowedObject<const bt_something>`
+     * instance to be built from an instance of
+     * `BorrowedObject<bt_something>`. C++ forbids the other way around.
+     */
+    template <typename OtherLibObjT>
+    BorrowedObject(const BorrowedObject<OtherLibObjT>& other) noexcept :
+        BorrowedObject {other._mLibObjPtr}
+    {
+        static_assert(_AssignableFromConst<OtherLibObjT>::val,
+                      "Don't assign a non-const wrapper from a const wrapper.");
+    }
+
+    /*
+     * Generic "assignment" operator.
+     *
+     * This operator accepts both an instance of
+     * `_ThisBorrowedObject` and an instance (`other`) of
+     * `BorrowedObject<ConstLibObjT>`, where `ConstLibObjT` is the
+     * `const` version of `LibObjT`, if applicable.
+     *
+     * This makes it possible for a `BorrowedObject<const bt_something>`
+     * instance to get assigned an instance of
+     * `BorrowedObject<bt_something>`. C++ forbids the other way around,
+     * therefore we use `_EnableIfAssignableT` to show a more relevant
+     * context in the compiler error message.
+     */
+    template <typename OtherLibObjT>
+    _ThisBorrowedObject operator=(const BorrowedObject<OtherLibObjT>& other) noexcept
+    {
+        static_assert(_AssignableFromConst<OtherLibObjT>::val,
+                      "Don't assign a non-const wrapper from a const wrapper.");
+
+        _mLibObjPtr = other._mLibObjPtr;
+        return *this;
+    }
+
+public:
+    /*
+     * Returns a hash of this object, solely based on its raw
+     * libbabeltrace2 pointer.
+     */
+    std::size_t hash() const noexcept
+    {
+        return std::hash<LibObjPtr> {}(_mLibObjPtr);
+    }
+
+    /*
+     * Returns whether or not this object is the exact same as `other`,
+     * solely based on the raw libbabeltrace2 pointers.
+     */
+    bool isSame(const _ThisBorrowedObject& other) const noexcept
+    {
+        return _mLibObjPtr == other._mLibObjPtr;
+    }
+
+    /* Wrapped libbabeltrace2 object pointer */
+    LibObjPtr libObjPtr() const noexcept
+    {
+        return _mLibObjPtr;
+    }
+
+private:
+    LibObjPtr _mLibObjPtr;
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_BORROWED_OBJECT_HPP */
index e55220960889f1ca1ecca616249f558f1172c6a3..5b80890d2e7deaf936bf64872afa35f54e04cbf8 100644 (file)
@@ -7,31 +7,32 @@
 #ifndef BABELTRACE_CPP_COMMON_BT2_CLOCK_CLASS_HPP
 #define BABELTRACE_CPP_COMMON_BT2_CLOCK_CLASS_HPP
 
-#include <type_traits>
 #include <cstdint>
-#include <string>
+#include <type_traits>
+
 #include <babeltrace2/babeltrace.h>
 
-#include "internal/borrowed-obj.hpp"
-#include "internal/shared-obj.hpp"
-#include "cpp-common/optional.hpp"
-#include "cpp-common/string_view.hpp"
-#include "cpp-common/uuid-view.hpp"
-#include "lib-error.hpp"
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/bt2c/uuid.hpp"
+#include "cpp-common/bt2s/optional.hpp"
+
+#include "borrowed-object.hpp"
+#include "exc.hpp"
+#include "internal/utils.hpp"
+#include "shared-object.hpp"
 #include "value.hpp"
 
 namespace bt2 {
-
 namespace internal {
 
 struct ClockClassRefFuncs final
 {
-    static void get(const bt_clock_class * const libObjPtr)
+    static void get(const bt_clock_class * const libObjPtr) noexcept
     {
         bt_clock_class_get_ref(libObjPtr);
     }
 
-    static void put(const bt_clock_class * const libObjPtr)
+    static void put(const bt_clock_class * const libObjPtr) noexcept
     {
         bt_clock_class_put_ref(libObjPtr);
     }
@@ -40,7 +41,7 @@ struct ClockClassRefFuncs final
 template <typename LibObjT>
 struct CommonClockClassSpec;
 
-// Functions specific to mutable clock classes
+/* Functions specific to mutable clock classes */
 template <>
 struct CommonClockClassSpec<bt_clock_class> final
 {
@@ -50,7 +51,7 @@ struct CommonClockClassSpec<bt_clock_class> final
     }
 };
 
-// Functions specific to constant clock classes
+/* Functions specific to constant clock classes */
 template <>
 struct CommonClockClassSpec<const bt_clock_class> final
 {
@@ -60,19 +61,16 @@ struct CommonClockClassSpec<const bt_clock_class> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
-class ClockClassOffset final
+class ClockOffset final
 {
 public:
-    explicit ClockClassOffset(const std::int64_t seconds, const std::uint64_t cycles) :
+    explicit ClockOffset(const std::int64_t seconds, const std::uint64_t cycles) :
         _mSeconds {seconds}, _mCycles {cycles}
     {
     }
 
-    ClockClassOffset(const ClockClassOffset&) noexcept = default;
-    ClockClassOffset& operator=(const ClockClassOffset&) noexcept = default;
-
     std::int64_t seconds() const noexcept
     {
         return _mSeconds;
@@ -89,188 +87,174 @@ private:
 };
 
 template <typename LibObjT>
-class CommonClockClass final : public internal::BorrowedObj<LibObjT>
+class CommonClockClass final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ThisCommonClockClass = CommonClockClass<LibObjT>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
 
 public:
-    using Shared =
-        internal::SharedObj<_ThisCommonClockClass, LibObjT, internal::ClockClassRefFuncs>;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedObject<CommonClockClass, LibObjT, internal::ClockClassRefFuncs>;
+    using UserAttributes = internal::DepUserAttrs<LibObjT>;
 
-    using UserAttributes =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstMapValue, MapValue>::type;
-
-    explicit CommonClockClass(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonClockClass(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonClockClass(const CommonClockClass<OtherLibObjT>& clkClass) noexcept :
-        _ThisBorrowedObj {clkClass}
+    CommonClockClass(const CommonClockClass<OtherLibObjT> clkClass) noexcept :
+        _ThisBorrowedObject {clkClass}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonClockClass& operator=(const CommonClockClass<OtherLibObjT>& clkClass) noexcept
+    CommonClockClass& operator=(const CommonClockClass<OtherLibObjT> clkClass) noexcept
     {
-        _ThisBorrowedObj::operator=(clkClass);
+        _ThisBorrowedObject::operator=(clkClass);
         return *this;
     }
 
-    void frequency(const std::uint64_t frequency) noexcept
+    CommonClockClass<const bt_clock_class> asConst() const noexcept
+    {
+        return CommonClockClass<const bt_clock_class> {*this};
+    }
+
+    CommonClockClass frequency(const std::uint64_t frequency) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstClockClass`.");
 
-        bt_clock_class_set_frequency(this->_libObjPtr(), frequency);
+        bt_clock_class_set_frequency(this->libObjPtr(), frequency);
+        return *this;
     }
 
     std::uint64_t frequency() const noexcept
     {
-        return bt_clock_class_get_frequency(this->_libObjPtr());
+        return bt_clock_class_get_frequency(this->libObjPtr());
     }
 
-    void offset(const ClockClassOffset& offset) noexcept
+    CommonClockClass offsetFromOrigin(const ClockOffset& offsetFromOrigin) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstClockClass`.");
 
-        bt_clock_class_set_offset(this->_libObjPtr(), offset.seconds(), offset.cycles());
+        bt_clock_class_set_offset(this->libObjPtr(), offsetFromOrigin.seconds(),
+                                  offsetFromOrigin.cycles());
+        return *this;
     }
 
-    ClockClassOffset offset() const noexcept
+    ClockOffset offsetFromOrigin() const noexcept
     {
         std::int64_t seconds;
         std::uint64_t cycles;
 
-        bt_clock_class_get_offset(this->_libObjPtr(), &seconds, &cycles);
-        return ClockClassOffset {seconds, cycles};
+        bt_clock_class_get_offset(this->libObjPtr(), &seconds, &cycles);
+        return ClockOffset {seconds, cycles};
     }
 
-    void precision(const std::uint64_t precision) noexcept
+    CommonClockClass precision(const std::uint64_t precision) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstClockClass`.");
 
-        bt_clock_class_set_precision(this->_libObjPtr(), precision);
+        bt_clock_class_set_precision(this->libObjPtr(), precision);
+        return *this;
     }
 
     std::uint64_t precision() const noexcept
     {
-        return bt_clock_class_get_precision(this->_libObjPtr());
+        return bt_clock_class_get_precision(this->libObjPtr());
     }
 
-    void originIsUnixEpoch(const bool originIsUnixEpoch) noexcept
+    CommonClockClass originIsUnixEpoch(const bool originIsUnixEpoch) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstClockClass`.");
 
-        bt_clock_class_set_origin_is_unix_epoch(this->_libObjPtr(),
+        bt_clock_class_set_origin_is_unix_epoch(this->libObjPtr(),
                                                 static_cast<bt_bool>(originIsUnixEpoch));
+        return *this;
     }
 
     bool originIsUnixEpoch() const noexcept
     {
-        return static_cast<bool>(bt_clock_class_origin_is_unix_epoch(this->_libObjPtr()));
+        return static_cast<bool>(bt_clock_class_origin_is_unix_epoch(this->libObjPtr()));
     }
 
-    void name(const char * const name)
+    CommonClockClass name(const bt2c::CStringView name) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstClockClass`.");
 
-        const auto status = bt_clock_class_set_name(this->_libObjPtr(), name);
+        const auto status = bt_clock_class_set_name(this->libObjPtr(), name);
 
         if (status == BT_CLOCK_CLASS_SET_NAME_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    void name(const std::string& name)
-    {
-        this->name(name.data());
+        return *this;
     }
 
-    nonstd::optional<bpstd::string_view> name() const noexcept
+    bt2c::CStringView name() const noexcept
     {
-        const auto name = bt_clock_class_get_name(this->_libObjPtr());
-
-        if (name) {
-            return name;
-        }
-
-        return nonstd::nullopt;
+        return bt_clock_class_get_name(this->libObjPtr());
     }
 
-    void description(const char * const description)
+    CommonClockClass description(const bt2c::CStringView description) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstClockClass`.");
 
-        const auto status = bt_clock_class_set_description(this->_libObjPtr(), description);
+        const auto status = bt_clock_class_set_description(this->libObjPtr(), description);
 
         if (status == BT_CLOCK_CLASS_SET_DESCRIPTION_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    void description(const std::string& description)
-    {
-        this->description(description.data());
+        return *this;
     }
 
-    nonstd::optional<bpstd::string_view> description() const noexcept
+    bt2c::CStringView description() const noexcept
     {
-        const auto description = bt_clock_class_get_description(this->_libObjPtr());
-
-        if (description) {
-            return description;
-        }
-
-        return nonstd::nullopt;
+        return bt_clock_class_get_description(this->libObjPtr());
     }
 
-    void uuid(const std::uint8_t * const uuid) noexcept
+    CommonClockClass uuid(const bt2c::UuidView uuid) const noexcept
     {
-        bt_clock_class_set_uuid(this->_libObjPtr(), uuid);
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstClockClass`.");
+
+        bt_clock_class_set_uuid(this->libObjPtr(), uuid.data());
+        return *this;
     }
 
-    nonstd::optional<bt2_common::UuidView> uuid() const noexcept
+    bt2s::optional<bt2c::UuidView> uuid() const noexcept
     {
-        const auto uuid = bt_clock_class_get_uuid(this->_libObjPtr());
+        const auto uuid = bt_clock_class_get_uuid(this->libObjPtr());
 
         if (uuid) {
-            return bt2_common::UuidView {uuid};
+            return bt2c::UuidView {uuid};
         }
 
-        return nonstd::nullopt;
+        return bt2s::nullopt;
     }
 
     template <typename LibValT>
-    void userAttributes(const CommonMapValue<LibValT>& userAttrs)
+    CommonClockClass userAttributes(const CommonMapValue<LibValT> userAttrs) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstClockClass`.");
 
-        bt_clock_class_set_user_attributes(this->_libObjPtr(), userAttrs._libObjPtr());
-    }
-
-    ConstMapValue userAttributes() const noexcept
-    {
-        return ConstMapValue {internal::CommonClockClassSpec<const bt_clock_class>::userAttributes(
-            this->_libObjPtr())};
+        bt_clock_class_set_user_attributes(this->libObjPtr(), userAttrs.libObjPtr());
+        return *this;
     }
 
-    UserAttributes userAttributes() noexcept
+    UserAttributes userAttributes() const noexcept
     {
         return UserAttributes {
-            internal::CommonClockClassSpec<LibObjT>::userAttributes(this->_libObjPtr())};
+            internal::CommonClockClassSpec<LibObjT>::userAttributes(this->libObjPtr())};
     }
 
     std::int64_t cyclesToNsFromOrigin(const std::uint64_t value) const
     {
         std::int64_t nsFromOrigin;
         const auto status =
-            bt_clock_class_cycles_to_ns_from_origin(this->_libObjPtr(), value, &nsFromOrigin);
+            bt_clock_class_cycles_to_ns_from_origin(this->libObjPtr(), value, &nsFromOrigin);
 
         if (status == BT_CLOCK_CLASS_CYCLES_TO_NS_FROM_ORIGIN_STATUS_OVERFLOW_ERROR) {
-            throw LibOverflowError {};
+            throw OverflowError {};
         }
 
         return nsFromOrigin;
@@ -278,13 +262,32 @@ public:
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using ClockClass = CommonClockClass<bt_clock_class>;
 using ConstClockClass = CommonClockClass<const bt_clock_class>;
 
-} // namespace bt2
+namespace internal {
+
+struct ClockClassTypeDescr
+{
+    using Const = ConstClockClass;
+    using NonConst = ClockClass;
+};
+
+template <>
+struct TypeDescr<ClockClass> : public ClockClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstClockClass> : public ClockClassTypeDescr
+{
+};
+
+} /* namespace internal */
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_CLOCK_CLASS_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_CLOCK_CLASS_HPP */
index 26ec9e8368746aa822938e4f7d92303e820cdaff..38ea940b56adcfa4652d0bbfa1b632ac9d099b7c 100644 (file)
@@ -8,34 +8,31 @@
 #define BABELTRACE_CPP_COMMON_BT2_CLOCK_SNAPSHOT_HPP
 
 #include <cstdint>
+
 #include <babeltrace2/babeltrace.h>
 
-#include "internal/borrowed-obj.hpp"
-#include "lib-error.hpp"
+#include "borrowed-object.hpp"
+#include "clock-class.hpp"
+#include "exc.hpp"
 
 namespace bt2 {
 
-class ConstClockSnapshot final : public internal::BorrowedObj<const bt_clock_snapshot>
+class ConstClockSnapshot final : public BorrowedObject<const bt_clock_snapshot>
 {
 public:
-    explicit ConstClockSnapshot(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
-    {
-    }
-
-    ConstClockSnapshot(const ConstClockSnapshot& clkSnapshot) noexcept :
-        _ThisBorrowedObj {clkSnapshot}
+    explicit ConstClockSnapshot(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
     {
     }
 
-    ConstClockSnapshot& operator=(const ConstClockSnapshot& clkSnapshot) noexcept
+    ConstClockClass clockClass() const noexcept
     {
-        _ThisBorrowedObj::operator=(clkSnapshot);
-        return *this;
+        return ConstClockClass {bt_clock_snapshot_borrow_clock_class_const(this->libObjPtr())};
     }
 
     std::uint64_t value() const noexcept
     {
-        return bt_clock_snapshot_get_value(this->_libObjPtr());
+        return bt_clock_snapshot_get_value(this->libObjPtr());
     }
 
     operator std::uint64_t() const noexcept
@@ -46,16 +43,16 @@ public:
     std::int64_t nsFromOrigin() const
     {
         std::int64_t nsFromOrigin;
-        const auto status = bt_clock_snapshot_get_ns_from_origin(this->_libObjPtr(), &nsFromOrigin);
+        const auto status = bt_clock_snapshot_get_ns_from_origin(this->libObjPtr(), &nsFromOrigin);
 
         if (status == BT_CLOCK_SNAPSHOT_GET_NS_FROM_ORIGIN_STATUS_OVERFLOW_ERROR) {
-            throw LibOverflowError {};
+            throw OverflowError {};
         }
 
         return nsFromOrigin;
     }
 };
 
-} // namespace bt2
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_CLOCK_SNAPSHOT_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_CLOCK_SNAPSHOT_HPP */
diff --git a/src/cpp-common/bt2/component-class-dev.hpp b/src/cpp-common/bt2/component-class-dev.hpp
new file mode 100644 (file)
index 0000000..e0d45d6
--- /dev/null
@@ -0,0 +1,962 @@
+/*
+ * Copyright (c) 2024 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_DEV_HPP
+#define BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_DEV_HPP
+
+#include <cstdint>
+
+#include <glib.h>
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/bt2c/logging.hpp"
+#include "cpp-common/vendor/fmt/core.h"
+
+#include "exc.hpp"
+#include "internal/comp-cls-bridge.hpp"
+#include "private-query-executor.hpp"
+#include "self-component-port.hpp"
+
+namespace bt2 {
+
+template <typename UserMessageIteratorT, typename UserComponentT>
+class UserMessageIterator;
+
+/*
+ * Base class of any user component.
+ *
+ * See the specific `bt2::UserSourceComponent`,
+ * `bt2::UserFilterComponent`, and `bt2::UserSinkComponent`.
+ */
+template <typename SelfCompT, typename InitDataT, typename QueryDataT>
+class UserComponent
+{
+    /* Give a related message iterator access to this logger */
+    template <typename, typename>
+    friend class UserMessageIterator;
+
+public:
+    using InitData = InitDataT;
+    using QueryData = QueryDataT;
+
+    static constexpr auto description = nullptr;
+    static constexpr auto help = nullptr;
+
+protected:
+    explicit UserComponent(const SelfCompT selfComp, const std::string& logTag) :
+        _mLogger {selfComp, fmt::format("{}/[{}]", logTag, selfComp.name())}, _mSelfComp {selfComp}
+    {
+    }
+
+    bt2c::CStringView _name() const noexcept
+    {
+        return _mSelfComp.name();
+    }
+
+    LoggingLevel _loggingLevel() const noexcept
+    {
+        return _mSelfComp.loggingLevel();
+    }
+
+    std::uint64_t _graphMipVersion() const noexcept
+    {
+        return _mSelfComp.graphMipVersion();
+    }
+
+    SelfCompT _selfComp() noexcept
+    {
+        return _mSelfComp;
+    }
+
+    bt2c::Logger _mLogger;
+
+private:
+    SelfCompT _mSelfComp;
+};
+
+/*
+ * Base class of a user source component `UserComponentT` (CRTP).
+ *
+ * `UserComponentT` must define a static member `name` of type
+ * `const char *` to provide the name of the component class.
+ *
+ * `UserComponentT` may define the static members `description` and/or
+ * `help` of type `const char *` to provide the description and/or help
+ * of the component class.
+ *
+ * UserComponentT::UserComponentT() must accept, in this order:
+ *
+ *  1. A `bt2::SelfSourceComponent` parameter, which it needs to forward
+ *     to bt2::UserSourceComponent::UserSourceComponent().
+ *
+ *  2. A `bt2::ConstValue` parameter (the initialization parameters).
+ *
+ *  3. An `InitDataT *` parameter (the initialization method data).
+ *
+ * `UserMessageIteratorT`, the message iterator class to use, must inherit
+ * `UserMessageIterator`.
+ *
+ * UserComponentT::_query() receives a query method data pointer of type
+ * `QueryDataT *` as its last parameter.
+ */
+template <typename UserComponentT, typename UserMessageIteratorT, typename InitDataT = void,
+          typename QueryDataT = void>
+class UserSourceComponent : public UserComponent<SelfSourceComponent, InitDataT, QueryDataT>
+{
+    static_assert(std::is_base_of<UserMessageIterator<UserMessageIteratorT, UserComponentT>,
+                                  UserMessageIteratorT>::value,
+                  "`UserMessageIteratorT` inherits `UserMessageIterator`");
+
+public:
+    using MessageIterator = UserMessageIteratorT;
+
+protected:
+    using _OutputPorts = SelfSourceComponent::OutputPorts;
+
+    explicit UserSourceComponent(const SelfSourceComponent selfComp, const std::string& logTag) :
+        UserComponent<SelfSourceComponent, InitDataT, QueryDataT> {selfComp, logTag}
+    {
+    }
+
+public:
+    static Value::Shared query(const SelfComponentClass selfCompCls,
+                               const PrivateQueryExecutor privQueryExec,
+                               const bt2c::CStringView obj, const ConstValue params,
+                               QueryDataT * const data)
+    {
+        return UserComponentT::_query(selfCompCls, privQueryExec, obj, params, data);
+    }
+
+    static void getSupportedMipVersions(const SelfComponentClass selfCompCls,
+                                        const ConstMapValue params, const LoggingLevel loggingLevel,
+                                        const UnsignedIntegerRangeSet ranges)
+    {
+        UserComponentT::_getSupportedMipVersions(selfCompCls, params, loggingLevel, ranges);
+    }
+
+    void outputPortConnected(const SelfComponentOutputPort outputPort,
+                             const ConstInputPort inputPort)
+    {
+        static_cast<UserComponentT&>(*this)._outputPortConnected(outputPort, inputPort);
+    }
+
+protected:
+    /* Overloadable */
+    static Value::Shared _query(SelfComponentClass, PrivateQueryExecutor, bt2c::CStringView,
+                                ConstValue, QueryDataT *)
+    {
+        throw UnknownObject {};
+    }
+
+    /* Overloadable */
+    static void _getSupportedMipVersions(SelfComponentClass, ConstMapValue, LoggingLevel,
+                                         const UnsignedIntegerRangeSet ranges)
+    {
+        ranges.addRange(0, 0);
+    }
+
+    /* Overloadable */
+    void _outputPortConnected(SelfComponentOutputPort, ConstInputPort)
+    {
+    }
+
+    template <typename DataT>
+    _OutputPorts::Port _addOutputPort(const bt2c::CStringView name, DataT& data)
+    {
+        return this->_selfComp().addOutputPort(name, data);
+    }
+
+    _OutputPorts::Port _addOutputPort(const bt2c::CStringView name)
+    {
+        return this->_selfComp().addOutputPort(name);
+    }
+
+    _OutputPorts _outputPorts() noexcept
+    {
+        return this->_selfComp().outputPorts();
+    }
+};
+
+/*
+ * Base class of a user filter component `UserComponentT` (CRTP).
+ *
+ * `UserComponentT` must define a static member `name` of type
+ * `const char *` to provide the name of the component class.
+ *
+ * `UserComponentT` may define the static members `description` and/or
+ * `help` of type `const char *` to provide the description and/or help
+ * of the component class.
+ *
+ * UserComponentT::UserComponentT() must accept, in this order:
+ *
+ *  1. A `bt2::SelfFilterComponent` parameter, which it needs to forward
+ *     to bt2::UserFilterComponent::UserFilterComponent().
+ *
+ *  2. A `bt2::ConstValue` parameter (the initialization parameters).
+ *
+ *  3. An `InitDataT *` parameter (the initialization method data).
+ *
+ * `UserMessageIteratorT`, the message iterator class to use, must inherit
+ * `UserMessageIterator`.
+ *
+ * UserComponentT::_query() receives a query method data pointer of type
+ * `QueryDataT *` as its last parameter.
+ */
+template <typename UserComponentT, typename UserMessageIteratorT, typename InitDataT = void,
+          typename QueryDataT = void>
+class UserFilterComponent : public UserComponent<SelfFilterComponent, InitDataT, QueryDataT>
+{
+    static_assert(std::is_base_of<UserMessageIterator<UserMessageIteratorT, UserComponentT>,
+                                  UserMessageIteratorT>::value,
+                  "`UserMessageIteratorT` inherits `UserMessageIterator`");
+
+public:
+    using MessageIterator = UserMessageIteratorT;
+
+protected:
+    using _InputPorts = SelfFilterComponent::InputPorts;
+    using _OutputPorts = SelfFilterComponent::OutputPorts;
+
+    explicit UserFilterComponent(const SelfFilterComponent selfComp, const std::string& logTag) :
+        UserComponent<SelfFilterComponent, InitDataT, QueryDataT> {selfComp, logTag}
+    {
+    }
+
+public:
+    static Value::Shared query(const SelfComponentClass selfCompCls,
+                               const PrivateQueryExecutor privQueryExec,
+                               const bt2c::CStringView obj, const ConstValue params,
+                               QueryDataT * const data)
+    {
+        return UserComponentT::_query(selfCompCls, privQueryExec, obj, params, data);
+    }
+
+    static void getSupportedMipVersions(const SelfComponentClass selfCompCls,
+                                        const ConstMapValue params, const LoggingLevel loggingLevel,
+                                        const UnsignedIntegerRangeSet ranges)
+    {
+        UserComponentT::_getSupportedMipVersions(selfCompCls, params, loggingLevel, ranges);
+    }
+
+    void inputPortConnected(const SelfComponentInputPort inputPort,
+                            const ConstOutputPort outputPort)
+    {
+        static_cast<UserComponentT&>(*this)._inputPortConnected(inputPort, outputPort);
+    }
+
+    void outputPortConnected(const SelfComponentOutputPort outputPort,
+                             const ConstInputPort inputPort)
+    {
+        static_cast<UserComponentT&>(*this)._outputPortConnected(outputPort, inputPort);
+    }
+
+protected:
+    /* Overloadable */
+    static Value::Shared _query(SelfComponentClass, PrivateQueryExecutor, bt2c::CStringView,
+                                ConstValue, QueryDataT *)
+    {
+        throw UnknownObject {};
+    }
+
+    /* Overloadable */
+    static void _getSupportedMipVersions(SelfComponentClass, ConstMapValue, LoggingLevel,
+                                         const UnsignedIntegerRangeSet ranges)
+    {
+        ranges.addRange(0, 0);
+    }
+
+    /* Overloadable */
+    void _inputPortConnected(SelfComponentInputPort, ConstOutputPort)
+    {
+    }
+
+    /* Overloadable */
+    void _outputPortConnected(SelfComponentOutputPort, ConstInputPort)
+    {
+    }
+
+    template <typename DataT>
+    _OutputPorts::Port _addInputPort(const bt2c::CStringView name, DataT& data)
+    {
+        return this->_selfComp().addInputPort(name, data);
+    }
+
+    _InputPorts::Port _addInputPort(const bt2c::CStringView name)
+    {
+        return this->_selfComp().addInputPort(name);
+    }
+
+    _InputPorts _inputPorts() noexcept
+    {
+        return this->_selfComp().inputPorts();
+    }
+
+    template <typename DataT>
+    _OutputPorts::Port _addOutputPort(const bt2c::CStringView name, DataT& data)
+    {
+        return this->_selfComp().addOutputPort(name, data);
+    }
+
+    _OutputPorts::Port _addOutputPort(const bt2c::CStringView name)
+    {
+        return this->_selfComp().addOutputPort(name);
+    }
+
+    _OutputPorts _outputPorts() noexcept
+    {
+        return this->_selfComp().outputPorts();
+    }
+};
+
+/*
+ * Base class of a user sink component `UserComponentT` (CRTP).
+ *
+ * `UserComponentT` must define a static member `name` of type
+ * `const char *` to provide the name of the component class.
+ *
+ * `UserComponentT` may define the static members `description` and/or
+ * `help` of type `const char *` to provide the description and/or help
+ * of the component class.
+ *
+ * UserComponentT::UserComponentT() must accept, in this order:
+ *
+ *  1. A `bt2::SelfSinkComponent` parameter, which it needs to forward
+ *     to bt2::UserSinkComponent::UserSinkComponent().
+ *
+ *  2. A `bt2::ConstValue` parameter (the initialization parameters).
+ *
+ *  3. An `InitDataT *` parameter (the initialization method data).
+ *
+ * `UserComponentT` must implement:
+ *
+ *     bool _consume();
+ *
+ * This method returns `true` if the sink component still needs to
+ * consume, or `false` if it's finished.
+ *
+ * UserComponentT::_query() receives a query method data pointer of type
+ * `QueryDataT *` as its last parameter.
+
+ */
+template <typename UserComponentT, typename InitDataT = void, typename QueryDataT = void>
+class UserSinkComponent : public UserComponent<SelfSinkComponent, InitDataT, QueryDataT>
+{
+protected:
+    using _InputPorts = SelfSinkComponent::InputPorts;
+
+    explicit UserSinkComponent(const SelfSinkComponent selfComp, const std::string& logTag) :
+        UserComponent<SelfSinkComponent, InitDataT, QueryDataT> {selfComp, logTag}
+    {
+    }
+
+public:
+    static Value::Shared query(const SelfComponentClass selfCompCls,
+                               const PrivateQueryExecutor privQueryExec,
+                               const bt2c::CStringView obj, const ConstValue params,
+                               QueryDataT * const data)
+    {
+        return UserComponentT::_query(selfCompCls, privQueryExec, obj, params, data);
+    }
+
+    static void getSupportedMipVersions(const SelfComponentClass selfCompCls,
+                                        const ConstMapValue params, const LoggingLevel loggingLevel,
+                                        const UnsignedIntegerRangeSet ranges)
+    {
+        UserComponentT::_getSupportedMipVersions(selfCompCls, params, loggingLevel, ranges);
+    }
+
+    void graphIsConfigured()
+    {
+        static_cast<UserComponentT&>(*this)._graphIsConfigured();
+    }
+
+    void inputPortConnected(const SelfComponentInputPort inputPort,
+                            const ConstOutputPort outputPort)
+    {
+        static_cast<UserComponentT&>(*this)._inputPortConnected(inputPort, outputPort);
+    }
+
+    bool consume()
+    {
+        return static_cast<UserComponentT&>(*this)._consume();
+    }
+
+protected:
+    /* Overloadable */
+    static Value::Shared _query(SelfComponentClass, PrivateQueryExecutor, bt2c::CStringView,
+                                ConstValue, QueryDataT *)
+    {
+        throw UnknownObject {};
+    }
+
+    /* Overloadable */
+    static void _getSupportedMipVersions(SelfComponentClass, ConstMapValue, LoggingLevel,
+                                         const UnsignedIntegerRangeSet ranges)
+    {
+        ranges.addRange(0, 0);
+    }
+
+    /* Overloadable */
+    void _graphIsConfigured()
+    {
+    }
+
+    /* Overloadable */
+    void _inputPortConnected(SelfComponentInputPort, ConstOutputPort)
+    {
+    }
+
+    MessageIterator::Shared _createMessageIterator(const _InputPorts::Port port)
+    {
+        return this->_selfComp().createMessageIterator(port);
+    }
+
+    template <typename DataT>
+    _InputPorts::Port _addInputPort(const bt2c::CStringView name, DataT& data)
+    {
+        return this->_selfComp().addInputPort(name, data);
+    }
+
+    _InputPorts::Port _addInputPort(const bt2c::CStringView name)
+    {
+        return this->_selfComp().addInputPort(name);
+    }
+
+    _InputPorts _inputPorts() noexcept
+    {
+        return this->_selfComp().inputPorts();
+    }
+};
+
+/*
+ * Base class of a user message iterator `UserMessageIteratorT` (CRTP)
+ * of which the parent user component class is `UserComponentT`.
+ *
+ * `UserMessageIteratorT::UserMessageIteratorT()` must accept a
+ * `bt2::SelfMessageIterator` parameter, which it needs to forward to
+ * bt2::UserMessageIterator::UserMessageIterator().
+ *
+ * The public next() method below (called by the bridge) implements the
+ * very common pattern of appending messages into the output array, and,
+ * meanwhile:
+ *
+ * If it catches a `bt2::TryAgain` exception:
+ *     If the message array isn't empty, transform this into a success
+ *     (don't throw).
+ *
+ *     Otherwise rethrow.
+ *
+ * If it catches an error:
+ *     If the message array isn't empty, transform this into a success
+ *     (don't throw), but save the error of the current thread and the
+ *     type of error to throw the next time the user calls next().
+ *
+ *     Otherwise rethrow.
+ *
+ * `UserMessageIteratorT` must implement:
+ *
+ *     void _next(bt2::ConstMessageArray& messages);
+ *
+ * This method fills `messages` with at most `messages.capacity()`
+ * messages and may throw `bt2::TryAgain` or a valid error whenever.
+ * Leaving an empty `messages` means the end of iteration.
+ */
+template <typename UserMessageIteratorT, typename UserComponentT>
+class UserMessageIterator
+{
+private:
+    /* Type of `_mExcToThrowType` */
+    enum class _ExcToThrowType
+    {
+        None,
+        Error,
+        MemError,
+    };
+
+protected:
+    explicit UserMessageIterator(const SelfMessageIterator selfMsgIter,
+                                 const std::string& logTagSuffix) :
+        _mSelfMsgIter {selfMsgIter},
+        _mLogger {selfMsgIter,
+                  fmt::format("{}/{}", this->_component()._mLogger.tag(), logTagSuffix)}
+    {
+    }
+
+public:
+    void next(ConstMessageArray& messages)
+    {
+        /* Any saved error? Now is the time to throw */
+        if (G_UNLIKELY(_mExcToThrowType != _ExcToThrowType::None)) {
+            /* Move `_mSavedLibError`, if any, as current thread error */
+            if (_mSavedLibError) {
+                bt_current_thread_move_error(_mSavedLibError.release());
+            }
+
+            /* Throw the corresponding exception */
+            if (_mExcToThrowType == _ExcToThrowType::Error) {
+                throw Error {};
+            } else {
+                BT_ASSERT(_mExcToThrowType == _ExcToThrowType::MemError);
+                throw MemoryError {};
+            }
+        }
+
+        /*
+         * When catching some exception below, if our message array
+         * isn't empty, then return immediately before throwing to
+         * provide those messages to downstream.
+         *
+         * When catching an error, also save the current thread error,
+         * if any, so that we can restore it later (see the beginning of
+         * this method).
+         */
+        BT_ASSERT_DBG(_mExcToThrowType == _ExcToThrowType::None);
+
+        try {
+            this->_userObj()._next(messages);
+
+            /* We're done: everything below is exception handling */
+            return;
+        } catch (const TryAgain&) {
+            if (messages.isEmpty()) {
+                throw;
+            }
+        } catch (const std::bad_alloc&) {
+            if (messages.isEmpty()) {
+                throw;
+            }
+
+            _mExcToThrowType = _ExcToThrowType::MemError;
+        } catch (const Error&) {
+            if (messages.isEmpty()) {
+                throw;
+            }
+
+            _mExcToThrowType = _ExcToThrowType::Error;
+        }
+
+        if (_mExcToThrowType != _ExcToThrowType::None) {
+            BT_CPPLOGE(
+                "An error occurred, but there are {} messages to return: delaying the error reporting.",
+                messages.length());
+            BT_ASSERT(!_mSavedLibError);
+            _mSavedLibError.reset(bt_current_thread_take_error());
+        }
+    }
+
+    bool canSeekBeginning()
+    {
+        this->_resetError();
+        return this->_userObj()._canSeekBeginning();
+    }
+
+    void seekBeginning()
+    {
+        this->_resetError();
+        return this->_userObj()._seekBeginning();
+    }
+
+    bool canSeekNsFromOrigin(const std::int64_t nsFromOrigin)
+    {
+        this->_resetError();
+        return this->_userObj()._canSeekNsFromOrigin(nsFromOrigin);
+    }
+
+    void seekNsFromOrigin(const std::int64_t nsFromOrigin)
+    {
+        this->_resetError();
+        this->_userObj()._seekNsFromOrigin(nsFromOrigin);
+    }
+
+protected:
+    /* Overloadable */
+    bool _canSeekBeginning() noexcept
+    {
+        return false;
+    }
+
+    /* Overloadable */
+    void _seekBeginning() noexcept
+    {
+    }
+
+    /* Overloadable */
+    bool _canSeekNsFromOrigin(std::int64_t) noexcept
+    {
+        return false;
+    }
+
+    /* Overloadable */
+    void _seekNsFromOrigin(std::int64_t) noexcept
+    {
+    }
+
+    MessageIterator::Shared _createMessageIterator(const SelfComponentInputPort port)
+    {
+        return _mSelfMsgIter.createMessageIterator(port);
+    }
+
+    StreamBeginningMessage::Shared _createStreamBeginningMessage(const ConstStream stream) const
+    {
+        return _mSelfMsgIter.createStreamBeginningMessage(stream);
+    }
+
+    StreamEndMessage::Shared _createStreamEndMessage(const ConstStream stream) const
+    {
+        return _mSelfMsgIter.createStreamEndMessage(stream);
+    }
+
+    EventMessage::Shared _createEventMessage(const ConstEventClass eventCls,
+                                             const ConstStream stream) const
+    {
+        return _mSelfMsgIter.createEventMessage(eventCls, stream);
+    }
+
+    EventMessage::Shared _createEventMessage(const ConstEventClass eventCls,
+                                             const ConstStream stream,
+                                             const std::uint64_t clockSnapshotValue) const
+    {
+        return _mSelfMsgIter.createEventMessage(eventCls, stream, clockSnapshotValue);
+    }
+
+    EventMessage::Shared _createEventMessage(const ConstEventClass eventCls,
+                                             const ConstPacket packet) const
+    {
+        return _mSelfMsgIter.createEventMessage(eventCls, packet);
+    }
+
+    EventMessage::Shared _createEventMessage(const ConstEventClass eventCls,
+                                             const ConstPacket packet,
+                                             const std::uint64_t clockSnapshotValue) const
+    {
+        return _mSelfMsgIter.createEventMessage(eventCls, packet, clockSnapshotValue);
+    }
+
+    PacketBeginningMessage::Shared _createPacketBeginningMessage(const ConstPacket packet) const
+    {
+        return _mSelfMsgIter.createPacketBeginningMessage(packet);
+    }
+
+    PacketBeginningMessage::Shared
+    _createPacketBeginningMessage(const ConstPacket packet,
+                                  const std::uint64_t clockSnapshotValue) const
+    {
+        return _mSelfMsgIter.createPacketBeginningMessage(packet, clockSnapshotValue);
+    }
+
+    PacketEndMessage::Shared _createPacketEndMessage(const ConstPacket packet) const
+    {
+        return _mSelfMsgIter.createPacketEndMessage(packet);
+    }
+
+    PacketEndMessage::Shared _createPacketEndMessage(const ConstPacket packet,
+                                                     const std::uint64_t clockSnapshotValue) const
+    {
+        return _mSelfMsgIter.createPacketEndMessage(packet, clockSnapshotValue);
+    }
+
+    DiscardedEventsMessage::Shared _createDiscardedEventsMessage(const ConstStream stream)
+    {
+        return _mSelfMsgIter.createDiscardedEventsMessage(stream);
+    }
+
+    DiscardedEventsMessage::Shared
+    _createDiscardedEventsMessage(const ConstStream stream,
+                                  const std::uint64_t beginningClockSnapshotValue,
+                                  const std::uint64_t endClockSnapshotValue)
+    {
+        return _mSelfMsgIter.createDiscardedEventsMessage(stream, beginningClockSnapshotValue,
+                                                          endClockSnapshotValue);
+    }
+
+    DiscardedPacketsMessage::Shared _createDiscardedPacketsMessage(const ConstStream stream)
+    {
+        return _mSelfMsgIter.createDiscardedPacketsMessage(stream);
+    }
+
+    DiscardedPacketsMessage::Shared
+    _createDiscardedPacketsMessage(const ConstStream stream,
+                                   const std::uint64_t beginningClockSnapshotValue,
+                                   const std::uint64_t endClockSnapshotValue)
+    {
+        return _mSelfMsgIter.createDiscardedPacketsMessage(stream, beginningClockSnapshotValue,
+                                                           endClockSnapshotValue);
+    }
+
+    MessageIteratorInactivityMessage::Shared
+    _createMessageIteratorInactivityMessage(const ConstClockClass clockClass,
+                                            const std::uint64_t clockSnapshotValue)
+    {
+        return _mSelfMsgIter.createMessageIteratorInactivityMessage(clockClass, clockSnapshotValue);
+    }
+
+    UserComponentT& _component() noexcept
+    {
+        return _mSelfMsgIter.component().template data<UserComponentT>();
+    }
+
+    SelfComponentOutputPort _port() noexcept
+    {
+        return _mSelfMsgIter.port();
+    }
+
+    bool _isInterrupted() const noexcept
+    {
+        return _mSelfMsgIter.isInterrupted();
+    }
+
+private:
+    UserMessageIteratorT& _userObj() noexcept
+    {
+        return static_cast<UserMessageIteratorT&>(*this);
+    }
+
+    void _resetError() noexcept
+    {
+        _mExcToThrowType = _ExcToThrowType::None;
+        _mSavedLibError.reset();
+    }
+
+    SelfMessageIterator _mSelfMsgIter;
+
+    /*
+     * next() may accumulate messages, and then catch an error before
+     * returning. In that case, it saves the error of the current thread
+     * here so that it can return its accumulated messages and throw the
+     * next time.
+     *
+     * It also saves the type of the exception to throw the next time.
+     */
+    _ExcToThrowType _mExcToThrowType = _ExcToThrowType::None;
+
+    struct LibErrorDeleter final
+    {
+        void operator()(const bt_error * const error) const noexcept
+        {
+            bt_error_release(error);
+        }
+    };
+
+    std::unique_ptr<const bt_error, LibErrorDeleter> _mSavedLibError;
+
+protected:
+    bt2c::Logger _mLogger;
+};
+
+namespace internal {
+
+template <typename UserComponentT, typename CompClsBridgeT, typename LibSpecCompClsPtrT,
+          typename AsCompClsFuncT, typename SetInitMethodFuncT, typename SetFinalizeMethodFuncT,
+          typename SetGetSupportedMipVersionsMethodFuncT, typename SetQueryMethodFuncT>
+void setCompClsCommonProps(
+    LibSpecCompClsPtrT * const libSpecCompClsPtr, AsCompClsFuncT&& asCompClsFunc,
+    SetInitMethodFuncT&& setInitMethodFunc, SetFinalizeMethodFuncT&& setFinalizeMethodFunc,
+    SetGetSupportedMipVersionsMethodFuncT&& setGetSupportedMipVersionsMethodFunc,
+    SetQueryMethodFuncT&& setQueryMethodFunc)
+{
+    const auto libCompClsPtr = asCompClsFunc(libSpecCompClsPtr);
+
+    if (UserComponentT::description != nullptr) {
+        const auto status =
+            bt_component_class_set_description(libCompClsPtr, UserComponentT::description);
+
+        if (status == BT_COMPONENT_CLASS_SET_DESCRIPTION_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
+        }
+    }
+
+    if (UserComponentT::help != nullptr) {
+        const auto status = bt_component_class_set_help(libCompClsPtr, UserComponentT::help);
+
+        if (status == BT_COMPONENT_CLASS_SET_HELP_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
+        }
+    }
+
+    {
+        const auto status = setInitMethodFunc(libSpecCompClsPtr, CompClsBridgeT::init);
+
+        BT_ASSERT(status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    {
+        const auto status = setFinalizeMethodFunc(libSpecCompClsPtr, CompClsBridgeT::finalize);
+
+        BT_ASSERT(status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    {
+        const auto status = setGetSupportedMipVersionsMethodFunc(
+            libSpecCompClsPtr, CompClsBridgeT::getSupportedMipVersions);
+
+        BT_ASSERT(status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    {
+        const auto status = setQueryMethodFunc(libSpecCompClsPtr, CompClsBridgeT::query);
+
+        BT_ASSERT(status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+    }
+}
+
+template <typename MsgIterClsBridgeT>
+bt_message_iterator_class *createLibMsgIterCls()
+{
+    const auto libMsgIterClsPtr = bt_message_iterator_class_create(MsgIterClsBridgeT::next);
+
+    if (!libMsgIterClsPtr) {
+        throw MemoryError {};
+    }
+
+    {
+        const auto status = bt_message_iterator_class_set_initialize_method(
+            libMsgIterClsPtr, MsgIterClsBridgeT::init);
+
+        BT_ASSERT(status == BT_MESSAGE_ITERATOR_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    {
+        const auto status = bt_message_iterator_class_set_finalize_method(
+            libMsgIterClsPtr, MsgIterClsBridgeT::finalize);
+
+        BT_ASSERT(status == BT_MESSAGE_ITERATOR_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    return libMsgIterClsPtr;
+}
+
+template <typename UserComponentT>
+bt_component_class_source *createSourceCompCls()
+{
+    static_assert(
+        std::is_base_of<UserSourceComponent<
+                            UserComponentT, typename UserComponentT::MessageIterator,
+                            typename UserComponentT::InitData, typename UserComponentT::QueryData>,
+                        UserComponentT>::value,
+        "`UserComponentT` inherits `UserSourceComponent`");
+
+    using CompClsBridge = internal::SrcCompClsBridge<UserComponentT>;
+    using MsgIterClsBridge = internal::MsgIterClsBridge<typename UserComponentT::MessageIterator>;
+
+    const auto libMsgIterClsPtr = createLibMsgIterCls<MsgIterClsBridge>();
+    const auto libCompClsPtr =
+        bt_component_class_source_create(UserComponentT::name, libMsgIterClsPtr);
+
+    bt_message_iterator_class_put_ref(libMsgIterClsPtr);
+
+    if (!libCompClsPtr) {
+        throw MemoryError {};
+    }
+
+    setCompClsCommonProps<UserComponentT, CompClsBridge>(
+        libCompClsPtr, bt_component_class_source_as_component_class,
+        bt_component_class_source_set_initialize_method,
+        bt_component_class_source_set_finalize_method,
+        bt_component_class_source_set_get_supported_mip_versions_method,
+        bt_component_class_source_set_query_method);
+
+    {
+        const auto status = bt_component_class_source_set_output_port_connected_method(
+            libCompClsPtr, CompClsBridge::outputPortConnected);
+
+        BT_ASSERT(status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    return libCompClsPtr;
+}
+
+template <typename UserComponentT>
+bt_component_class_filter *createFilterCompCls()
+{
+    static_assert(
+        std::is_base_of<UserFilterComponent<
+                            UserComponentT, typename UserComponentT::MessageIterator,
+                            typename UserComponentT::InitData, typename UserComponentT::QueryData>,
+                        UserComponentT>::value,
+        "`UserComponentT` inherits `UserFilterComponent`");
+
+    using CompClsBridge = internal::FltCompClsBridge<UserComponentT>;
+    using MsgIterClsBridge = internal::MsgIterClsBridge<typename UserComponentT::MessageIterator>;
+
+    const auto libMsgIterClsPtr = createLibMsgIterCls<MsgIterClsBridge>();
+    const auto libCompClsPtr =
+        bt_component_class_filter_create(UserComponentT::name, libMsgIterClsPtr);
+
+    bt_message_iterator_class_put_ref(libMsgIterClsPtr);
+
+    if (!libCompClsPtr) {
+        throw MemoryError {};
+    }
+
+    setCompClsCommonProps<UserComponentT, CompClsBridge>(
+        libCompClsPtr, bt_component_class_filter_as_component_class,
+        bt_component_class_filter_set_initialize_method,
+        bt_component_class_filter_set_finalize_method,
+        bt_component_class_filter_set_get_supported_mip_versions_method,
+        bt_component_class_filter_set_query_method);
+
+    {
+        const auto status = bt_component_class_filter_set_input_port_connected_method(
+            libCompClsPtr, CompClsBridge::inputPortConnected);
+
+        BT_ASSERT(status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    {
+        const auto status = bt_component_class_filter_set_output_port_connected_method(
+            libCompClsPtr, CompClsBridge::outputPortConnected);
+
+        BT_ASSERT(status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    return libCompClsPtr;
+}
+
+template <typename UserComponentT>
+bt_component_class_sink *createSinkCompCls()
+{
+    static_assert(
+        std::is_base_of<UserSinkComponent<UserComponentT, typename UserComponentT::InitData,
+                                          typename UserComponentT::QueryData>,
+                        UserComponentT>::value,
+        "`UserComponentT` inherits `UserSinkComponent`");
+
+    using CompClsBridge = internal::SinkCompClsBridge<UserComponentT>;
+
+    const auto libCompClsPtr =
+        bt_component_class_sink_create(UserComponentT::name, CompClsBridge::consume);
+
+    if (!libCompClsPtr) {
+        throw MemoryError {};
+    }
+
+    setCompClsCommonProps<UserComponentT, CompClsBridge>(
+        libCompClsPtr, bt_component_class_sink_as_component_class,
+        bt_component_class_sink_set_initialize_method, bt_component_class_sink_set_finalize_method,
+        bt_component_class_sink_set_get_supported_mip_versions_method,
+        bt_component_class_sink_set_query_method);
+
+    {
+        const auto status = bt_component_class_sink_set_graph_is_configured_method(
+            libCompClsPtr, CompClsBridge::graphIsConfigured);
+
+        BT_ASSERT(status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    {
+        const auto status = bt_component_class_sink_set_input_port_connected_method(
+            libCompClsPtr, CompClsBridge::inputPortConnected);
+
+        BT_ASSERT(status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+    }
+
+    return libCompClsPtr;
+}
+
+} /* namespace internal */
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_DEV_HPP */
diff --git a/src/cpp-common/bt2/component-class.hpp b/src/cpp-common/bt2/component-class.hpp
new file mode 100644 (file)
index 0000000..2933d74
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2024 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_HPP
+#define BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+
+#include "borrowed-object.hpp"
+#include "component-class-dev.hpp"
+#include "shared-object.hpp"
+
+namespace bt2 {
+namespace internal {
+
+struct ComponentClassRefFuncs final
+{
+    static void get(const bt_component_class * const libObjPtr) noexcept
+    {
+        bt_component_class_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_component_class * const libObjPtr) noexcept
+    {
+        bt_component_class_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+enum class ComponentClassType
+{
+    Source = BT_COMPONENT_CLASS_TYPE_SOURCE,
+    Filter = BT_COMPONENT_CLASS_TYPE_FILTER,
+    Sink = BT_COMPONENT_CLASS_TYPE_SINK,
+};
+
+template <typename LibObjT>
+class CommonSourceComponentClass;
+
+template <typename LibObjT>
+class CommonFilterComponentClass;
+
+template <typename LibObjT>
+class CommonSinkComponentClass;
+
+template <typename LibObjT>
+class CommonComponentClass : public BorrowedObject<LibObjT>
+{
+private:
+    using _ThisBorrowedObject = BorrowedObject<LibObjT>;
+
+public:
+    using typename _ThisBorrowedObject::LibObjPtr;
+    using Shared = SharedObject<CommonComponentClass, LibObjT, internal::ComponentClassRefFuncs>;
+
+    explicit CommonComponentClass(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonComponentClass(const CommonComponentClass<OtherLibObjT> compCls) noexcept :
+        _ThisBorrowedObject {compCls}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonComponentClass operator=(const CommonComponentClass<OtherLibObjT> compCls) noexcept
+    {
+        _ThisBorrowedObject::operator=(compCls);
+        return *this;
+    }
+
+    /* Not `explicit` to make them behave like copy constructors */
+    CommonComponentClass(
+        const CommonSourceComponentClass<const bt_component_class_source> other) noexcept;
+    CommonComponentClass(
+        const CommonSourceComponentClass<bt_component_class_source> other) noexcept;
+    CommonComponentClass(
+        const CommonFilterComponentClass<const bt_component_class_filter> other) noexcept;
+    CommonComponentClass(
+        const CommonFilterComponentClass<bt_component_class_filter> other) noexcept;
+    CommonComponentClass(
+        const CommonSinkComponentClass<const bt_component_class_sink> other) noexcept;
+    CommonComponentClass(const CommonSinkComponentClass<bt_component_class_sink> other) noexcept;
+
+    CommonComponentClass
+    operator=(CommonSourceComponentClass<const bt_component_class_source> other) noexcept;
+    CommonComponentClass
+    operator=(CommonSourceComponentClass<bt_component_class_source> other) noexcept;
+    CommonComponentClass
+    operator=(CommonFilterComponentClass<const bt_component_class_filter> other) noexcept;
+    CommonComponentClass
+    operator=(CommonFilterComponentClass<bt_component_class_filter> other) noexcept;
+    CommonComponentClass
+    operator=(CommonSinkComponentClass<const bt_component_class_sink> other) noexcept;
+    CommonComponentClass
+    operator=(CommonSinkComponentClass<bt_component_class_sink> other) noexcept;
+
+    bool isSource() const noexcept
+    {
+        return static_cast<bool>(bt_component_class_is_source(this->libObjPtr()));
+    }
+
+    bool isFilter() const noexcept
+    {
+        return static_cast<bool>(bt_component_class_is_filter(this->libObjPtr()));
+    }
+
+    bool isSink() const noexcept
+    {
+        return static_cast<bool>(bt_component_class_is_sink(this->libObjPtr()));
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return bt_component_class_get_name(this->libObjPtr());
+    }
+
+    bt2c::CStringView description() const noexcept
+    {
+        return bt_component_class_get_description(this->libObjPtr());
+    }
+
+    bt2c::CStringView help() const noexcept
+    {
+        return bt_component_class_get_help(this->libObjPtr());
+    }
+};
+
+using ComponentClass = CommonComponentClass<bt_component_class>;
+using ConstComponentClass = CommonComponentClass<const bt_component_class>;
+
+namespace internal {
+
+struct SourceComponentClassRefFuncs final
+{
+    static void get(const bt_component_class_source * const libObjPtr) noexcept
+    {
+        bt_component_class_source_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_component_class_source * const libObjPtr) noexcept
+    {
+        bt_component_class_source_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+template <typename LibObjT>
+class CommonSourceComponentClass final : public BorrowedObject<LibObjT>
+{
+private:
+    using _ThisBorrowedObject = BorrowedObject<LibObjT>;
+
+public:
+    using typename _ThisBorrowedObject::LibObjPtr;
+    using Shared =
+        SharedObject<CommonSourceComponentClass, LibObjT, internal::SourceComponentClassRefFuncs>;
+
+    CommonSourceComponentClass(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonSourceComponentClass(const CommonSourceComponentClass<OtherLibObjT> compCls) noexcept :
+        _ThisBorrowedObject {compCls}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonSourceComponentClass
+    operator=(const CommonSourceComponentClass<OtherLibObjT> compCls) noexcept
+    {
+        _ThisBorrowedObject::operator=(compCls);
+        return *this;
+    }
+
+    template <typename UserComponentT>
+    static CommonSourceComponentClass<LibObjT>::Shared create()
+    {
+        return CommonSourceComponentClass::Shared::createWithoutRef(
+            internal::createSourceCompCls<UserComponentT>());
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return this->_constComponentClass().name();
+    }
+
+    bt2c::CStringView description() const noexcept
+    {
+        return this->_constComponentClass().description();
+    }
+
+    bt2c::CStringView help() const noexcept
+    {
+        return this->_constComponentClass().help();
+    }
+
+private:
+    ConstComponentClass _constComponentClass() const noexcept
+    {
+        return ConstComponentClass {
+            bt_component_class_source_as_component_class_const(this->libObjPtr())};
+    }
+};
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT>::CommonComponentClass(
+    const CommonSourceComponentClass<const bt_component_class_source> other) noexcept :
+    _ThisBorrowedObject {bt_component_class_source_as_component_class_const(other.libObjPtr())}
+{
+}
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT>::CommonComponentClass(
+    const CommonSourceComponentClass<bt_component_class_source> other) noexcept :
+    _ThisBorrowedObject {bt_component_class_source_as_component_class(other.libObjPtr())}
+{
+}
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT> CommonComponentClass<LibObjT>::operator=(
+    const CommonSourceComponentClass<const bt_component_class_source> other) noexcept
+{
+    BorrowedObject<LibObjT>::operator=(ConstComponentClass {
+        bt_component_class_source_as_component_class_const(other.libObjPtr())});
+    return *this;
+}
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT> CommonComponentClass<LibObjT>::operator=(
+    const CommonSourceComponentClass<bt_component_class_source> other) noexcept
+{
+    BorrowedObject<LibObjT>::operator=(
+        ComponentClass {bt_component_class_source_as_component_class(other.libObjPtr())});
+    return *this;
+}
+
+using SourceComponentClass = CommonSourceComponentClass<bt_component_class_source>;
+using ConstSourceComponentClass = CommonSourceComponentClass<const bt_component_class_source>;
+
+namespace internal {
+
+struct FilterComponentClassRefFuncs final
+{
+    static void get(const bt_component_class_filter * const libObjPtr) noexcept
+    {
+        bt_component_class_filter_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_component_class_filter * const libObjPtr) noexcept
+    {
+        bt_component_class_filter_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+template <typename LibObjT>
+class CommonFilterComponentClass final : public BorrowedObject<LibObjT>
+{
+private:
+    using _ThisBorrowedObject = BorrowedObject<LibObjT>;
+
+public:
+    using typename _ThisBorrowedObject::LibObjPtr;
+    using Shared =
+        SharedObject<CommonFilterComponentClass, LibObjT, internal::FilterComponentClassRefFuncs>;
+
+    CommonFilterComponentClass(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonFilterComponentClass(const CommonFilterComponentClass<OtherLibObjT> compCls) noexcept :
+        _ThisBorrowedObject {compCls}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonFilterComponentClass
+    operator=(const CommonFilterComponentClass<OtherLibObjT> compCls) noexcept
+    {
+        _ThisBorrowedObject::operator=(compCls);
+        return *this;
+    }
+
+    template <typename UserComponentT>
+    static CommonFilterComponentClass<LibObjT>::Shared create()
+    {
+        return CommonFilterComponentClass::Shared::createWithoutRef(
+            internal::createFilterCompCls<UserComponentT>());
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return this->_constComponentClass().name();
+    }
+
+    bt2c::CStringView description() const noexcept
+    {
+        return this->_constComponentClass().description();
+    }
+
+    bt2c::CStringView help() const noexcept
+    {
+        return this->_constComponentClass().help();
+    }
+
+private:
+    ConstComponentClass _constComponentClass() const noexcept
+    {
+        return ConstComponentClass {
+            bt_component_class_filter_as_component_class_const(this->libObjPtr())};
+    }
+};
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT>::CommonComponentClass(
+    const CommonFilterComponentClass<const bt_component_class_filter> other) noexcept :
+    _ThisBorrowedObject {bt_component_class_filter_as_component_class_const(other.libObjPtr())}
+{
+}
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT>::CommonComponentClass(
+    const CommonFilterComponentClass<bt_component_class_filter> other) noexcept :
+    _ThisBorrowedObject {bt_component_class_filter_as_component_class(other.libObjPtr())}
+{
+}
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT> CommonComponentClass<LibObjT>::operator=(
+    const CommonFilterComponentClass<const bt_component_class_filter> other) noexcept
+{
+    BorrowedObject<LibObjT>::operator=(ConstComponentClass {
+        bt_component_class_filter_as_component_class_const(other.libObjPtr())});
+    return *this;
+}
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT> CommonComponentClass<LibObjT>::operator=(
+    const CommonFilterComponentClass<bt_component_class_filter> other) noexcept
+{
+    BorrowedObject<LibObjT>::operator=(
+        ComponentClass {bt_component_class_filter_as_component_class(other.libObjPtr())});
+    return *this;
+}
+
+using FilterComponentClass = CommonFilterComponentClass<bt_component_class_filter>;
+using ConstFilterComponentClass = CommonFilterComponentClass<const bt_component_class_filter>;
+
+namespace internal {
+
+struct SinkComponentClassRefFuncs final
+{
+    static void get(const bt_component_class_sink * const libObjPtr) noexcept
+    {
+        bt_component_class_sink_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_component_class_sink * const libObjPtr) noexcept
+    {
+        bt_component_class_sink_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+template <typename LibObjT>
+class CommonSinkComponentClass final : public BorrowedObject<LibObjT>
+{
+private:
+    using _ThisBorrowedObject = BorrowedObject<LibObjT>;
+
+public:
+    using typename _ThisBorrowedObject::LibObjPtr;
+    using Shared =
+        SharedObject<CommonSinkComponentClass, LibObjT, internal::SinkComponentClassRefFuncs>;
+
+    CommonSinkComponentClass(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonSinkComponentClass(const CommonSinkComponentClass<OtherLibObjT> compCls) noexcept :
+        _ThisBorrowedObject {compCls}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonSinkComponentClass
+    operator=(const CommonSinkComponentClass<OtherLibObjT> compCls) noexcept
+    {
+        _ThisBorrowedObject::operator=(compCls);
+        return *this;
+    }
+
+    template <typename UserComponentT>
+    static CommonSinkComponentClass<LibObjT>::Shared create()
+    {
+        return CommonSinkComponentClass::Shared::createWithoutRef(
+            internal::createSinkCompCls<UserComponentT>());
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return this->_constComponentClass().name();
+    }
+
+    bt2c::CStringView description() const noexcept
+    {
+        return this->_constComponentClass().description();
+    }
+
+    bt2c::CStringView help() const noexcept
+    {
+        return this->_constComponentClass().help();
+    }
+
+private:
+    ConstComponentClass _constComponentClass() const noexcept
+    {
+        return ConstComponentClass {
+            bt_component_class_sink_as_component_class_const(this->libObjPtr())};
+    }
+};
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT>::CommonComponentClass(
+    const CommonSinkComponentClass<const bt_component_class_sink> other) noexcept :
+    _ThisBorrowedObject {bt_component_class_sink_as_component_class_const(other.libObjPtr())}
+{
+}
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT>::CommonComponentClass(
+    const CommonSinkComponentClass<bt_component_class_sink> other) noexcept :
+    _ThisBorrowedObject {bt_component_class_sink_as_component_class(other.libObjPtr())}
+{
+}
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT> CommonComponentClass<LibObjT>::operator=(
+    const CommonSinkComponentClass<const bt_component_class_sink> other) noexcept
+{
+    BorrowedObject<LibObjT>::operator=(
+        ConstComponentClass {bt_component_class_sink_as_component_class_const(other.libObjPtr())});
+    return *this;
+}
+
+template <typename LibObjT>
+CommonComponentClass<LibObjT> CommonComponentClass<LibObjT>::operator=(
+    const CommonSinkComponentClass<bt_component_class_sink> other) noexcept
+{
+    BorrowedObject<LibObjT>::operator=(
+        ComponentClass {bt_component_class_sink_as_component_class(other.libObjPtr())});
+    return *this;
+}
+
+using SinkComponentClass = CommonSinkComponentClass<bt_component_class_sink>;
+using ConstSinkComponentClass = CommonSinkComponentClass<const bt_component_class_sink>;
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_COMPONENT_CLASS_HPP */
diff --git a/src/cpp-common/bt2/component-port.hpp b/src/cpp-common/bt2/component-port.hpp
new file mode 100644 (file)
index 0000000..930b2e6
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_COMPONENT_PORT_HPP
+#define BABELTRACE_CPP_COMMON_BT2_COMPONENT_PORT_HPP
+
+#include <cstdint>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "logging.hpp"
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+
+#include "borrowed-object-iterator.hpp"
+#include "borrowed-object.hpp"
+#include "shared-object.hpp"
+
+namespace bt2 {
+namespace internal {
+
+struct ConstComponentRefFuncs final
+{
+    static void get(const bt_component * const libObjPtr) noexcept
+    {
+        bt_component_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_component * const libObjPtr) noexcept
+    {
+        bt_component_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+class ConstSourceComponent;
+class ConstFilterComponent;
+class ConstSinkComponent;
+
+class ConstComponent final : public BorrowedObject<const bt_component>
+{
+private:
+    using typename BorrowedObject<const bt_component>::_ThisBorrowedObject;
+
+public:
+    using Shared =
+        SharedObject<ConstComponent, const bt_component, internal::ConstComponentRefFuncs>;
+
+    explicit ConstComponent(const bt_component * const libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    explicit ConstComponent(const bt_component_source * const libObjPtr) noexcept :
+        _ThisBorrowedObject {bt_component_source_as_component_const(libObjPtr)}
+    {
+    }
+
+    explicit ConstComponent(const bt_component_filter * const libObjPtr) noexcept :
+        _ThisBorrowedObject {bt_component_filter_as_component_const(libObjPtr)}
+    {
+    }
+
+    explicit ConstComponent(const bt_component_sink * const libObjPtr) noexcept :
+        _ThisBorrowedObject {bt_component_sink_as_component_const(libObjPtr)}
+    {
+    }
+
+    /* Not `explicit` to make them behave like copy constructors */
+    ConstComponent(ConstSourceComponent other) noexcept;
+    ConstComponent(ConstFilterComponent other) noexcept;
+    ConstComponent(ConstSinkComponent other) noexcept;
+
+    ConstComponent operator=(ConstSourceComponent other) noexcept;
+    ConstComponent operator=(ConstFilterComponent other) noexcept;
+    ConstComponent operator=(ConstSinkComponent other) noexcept;
+
+    bool isSource() const noexcept
+    {
+        return static_cast<bool>(bt_component_is_source(this->libObjPtr()));
+    }
+
+    bool isFilter() const noexcept
+    {
+        return static_cast<bool>(bt_component_is_filter(this->libObjPtr()));
+    }
+
+    bool isSink() const noexcept
+    {
+        return static_cast<bool>(bt_component_is_sink(this->libObjPtr()));
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return bt_component_get_name(this->libObjPtr());
+    }
+
+    LoggingLevel loggingLevel() const noexcept
+    {
+        return static_cast<LoggingLevel>(bt_component_get_logging_level(this->libObjPtr()));
+    }
+
+    Shared shared() const noexcept
+    {
+        return Shared::createWithRef(*this);
+    }
+};
+
+template <typename LibObjT>
+class ConstSpecificComponent : public BorrowedObject<LibObjT>
+{
+public:
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+
+protected:
+    explicit ConstSpecificComponent(const LibObjPtr libObjPtr) noexcept :
+        BorrowedObject<LibObjT> {libObjPtr}
+    {
+    }
+
+public:
+    bt2c::CStringView name() const noexcept
+    {
+        return this->_constComponent().name();
+    }
+
+    LoggingLevel loggingLevel() const noexcept
+    {
+        return this->_constComponent().loggingLevel();
+    }
+
+    ConstComponent::Shared sharedComponent() const noexcept
+    {
+        return this->_constComponent().shared();
+    }
+
+private:
+    ConstComponent _constComponent() const noexcept
+    {
+        return ConstComponent {this->libObjPtr()};
+    }
+};
+
+namespace internal {
+
+template <typename LibCompT, typename LibPortPtrT>
+struct ConstComponentPortsSpec;
+
+template <>
+struct ConstComponentPortsSpec<const bt_component_source, const bt_port_output> final
+{
+    static std::uint64_t portCount(const bt_component_source * const libCompPtr) noexcept
+    {
+        return bt_component_source_get_output_port_count(libCompPtr);
+    }
+
+    static const bt_port_output *portByIndex(const bt_component_source * const libCompPtr,
+                                             const std::uint64_t index) noexcept
+    {
+        return bt_component_source_borrow_output_port_by_index_const(libCompPtr, index);
+    }
+
+    static const bt_port_output *portByName(const bt_component_source * const libCompPtr,
+                                            const char * const name) noexcept
+    {
+        return bt_component_source_borrow_output_port_by_name_const(libCompPtr, name);
+    }
+};
+
+template <>
+struct ConstComponentPortsSpec<const bt_component_filter, const bt_port_output> final
+{
+    static std::uint64_t portCount(const bt_component_filter * const libCompPtr) noexcept
+    {
+        return bt_component_filter_get_output_port_count(libCompPtr);
+    }
+
+    static const bt_port_output *portByIndex(const bt_component_filter * const libCompPtr,
+                                             const std::uint64_t index) noexcept
+    {
+        return bt_component_filter_borrow_output_port_by_index_const(libCompPtr, index);
+    }
+
+    static const bt_port_output *portByName(const bt_component_filter * const libCompPtr,
+                                            const char * const name) noexcept
+    {
+        return bt_component_filter_borrow_output_port_by_name_const(libCompPtr, name);
+    }
+};
+
+template <>
+struct ConstComponentPortsSpec<const bt_component_filter, const bt_port_input> final
+{
+    static std::uint64_t portCount(const bt_component_filter * const libCompPtr) noexcept
+    {
+        return bt_component_filter_get_input_port_count(libCompPtr);
+    }
+
+    static const bt_port_input *portByIndex(const bt_component_filter * const libCompPtr,
+                                            const std::uint64_t index) noexcept
+    {
+        return bt_component_filter_borrow_input_port_by_index_const(libCompPtr, index);
+    }
+
+    static const bt_port_input *portByName(const bt_component_filter * const libCompPtr,
+                                           const char * const name) noexcept
+    {
+        return bt_component_filter_borrow_input_port_by_name_const(libCompPtr, name);
+    }
+};
+
+template <>
+struct ConstComponentPortsSpec<const bt_component_sink, const bt_port_input> final
+{
+    static std::uint64_t portCount(const bt_component_sink * const libCompPtr) noexcept
+    {
+        return bt_component_sink_get_input_port_count(libCompPtr);
+    }
+
+    static const bt_port_input *portByIndex(const bt_component_sink * const libCompPtr,
+                                            const std::uint64_t index) noexcept
+    {
+        return bt_component_sink_borrow_input_port_by_index_const(libCompPtr, index);
+    }
+
+    static const bt_port_input *portByName(const bt_component_sink * const libCompPtr,
+                                           const char * const name) noexcept
+    {
+        return bt_component_sink_borrow_input_port_by_name_const(libCompPtr, name);
+    }
+};
+
+} /* namespace internal */
+
+template <typename>
+class ConstPort;
+
+template <typename LibCompT, typename LibPortT>
+class ConstComponentPorts final : public BorrowedObject<LibCompT>
+{
+private:
+    using _Spec = internal::ConstComponentPortsSpec<LibCompT, LibPortT>;
+
+public:
+    using typename BorrowedObject<LibCompT>::LibObjPtr;
+    using Port = ConstPort<LibPortT>;
+    using Iterator = BorrowedObjectIterator<ConstComponentPorts>;
+
+    explicit ConstComponentPorts(const LibObjPtr libObjPtr) noexcept :
+        BorrowedObject<LibCompT> {libObjPtr}
+    {
+    }
+
+    std::uint64_t length() const noexcept
+    {
+        return _Spec::portCount(this->libObjPtr());
+    }
+
+    Port operator[](std::uint64_t index) const noexcept;
+    OptionalBorrowedObject<Port> operator[](bt2c::CStringView name) const noexcept;
+    Iterator begin() const noexcept;
+    Iterator end() const noexcept;
+};
+
+namespace internal {
+
+struct ConstSourceComponentRefFuncs final
+{
+    static void get(const bt_component_source * const libObjPtr) noexcept
+    {
+        bt_component_source_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_component_source * const libObjPtr) noexcept
+    {
+        bt_component_source_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+class ConstSourceComponent final : public ConstSpecificComponent<const bt_component_source>
+{
+public:
+    using Shared = SharedObject<ConstSourceComponent, const bt_component_source,
+                                internal::ConstSourceComponentRefFuncs>;
+
+    using OutputPorts = ConstComponentPorts<const bt_component_source, const bt_port_output>;
+
+    explicit ConstSourceComponent(const bt_component_source * const libObjPtr) noexcept :
+        ConstSpecificComponent {libObjPtr}
+    {
+    }
+
+    OutputPorts outputPorts() const noexcept;
+
+    Shared shared() const noexcept
+    {
+        return Shared::createWithRef(*this);
+    }
+};
+
+namespace internal {
+
+struct ConstFilterComponentRefFuncs final
+{
+    static void get(const bt_component_filter * const libObjPtr) noexcept
+    {
+        bt_component_filter_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_component_filter * const libObjPtr) noexcept
+    {
+        bt_component_filter_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+class ConstFilterComponent final : public ConstSpecificComponent<const bt_component_filter>
+{
+public:
+    using Shared = SharedObject<ConstFilterComponent, const bt_component_filter,
+                                internal::ConstFilterComponentRefFuncs>;
+
+    using InputPorts = ConstComponentPorts<const bt_component_filter, const bt_port_input>;
+    using OutputPorts = ConstComponentPorts<const bt_component_filter, const bt_port_output>;
+
+    explicit ConstFilterComponent(const bt_component_filter * const libObjPtr) noexcept :
+        ConstSpecificComponent {libObjPtr}
+    {
+    }
+
+    InputPorts inputPorts() const noexcept;
+    OutputPorts outputPorts() const noexcept;
+
+    Shared shared() const noexcept
+    {
+        return Shared::createWithRef(*this);
+    }
+};
+
+namespace internal {
+
+struct ConstSinkComponentRefFuncs final
+{
+    static void get(const bt_component_sink * const libObjPtr) noexcept
+    {
+        bt_component_sink_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_component_sink * const libObjPtr) noexcept
+    {
+        bt_component_sink_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+class ConstSinkComponent final : public ConstSpecificComponent<const bt_component_sink>
+{
+public:
+    using Shared = SharedObject<ConstSinkComponent, const bt_component_sink,
+                                internal::ConstSinkComponentRefFuncs>;
+
+    using InputPorts = ConstComponentPorts<const bt_component_sink, const bt_port_input>;
+
+    explicit ConstSinkComponent(const bt_component_sink * const libObjPtr) noexcept :
+        ConstSpecificComponent {libObjPtr}
+    {
+    }
+
+    InputPorts inputPorts() const noexcept;
+
+    Shared shared() const noexcept
+    {
+        return Shared::createWithRef(*this);
+    }
+};
+
+inline ConstComponent::ConstComponent(const ConstSourceComponent other) noexcept :
+    ConstComponent {other.libObjPtr()}
+{
+}
+
+inline ConstComponent::ConstComponent(const ConstFilterComponent other) noexcept :
+    ConstComponent {other.libObjPtr()}
+{
+}
+
+inline ConstComponent::ConstComponent(const ConstSinkComponent other) noexcept :
+    ConstComponent {other.libObjPtr()}
+{
+}
+
+inline ConstComponent ConstComponent::operator=(const ConstSourceComponent other) noexcept
+{
+    *this = ConstComponent {other.libObjPtr()};
+    return *this;
+}
+
+inline ConstComponent ConstComponent::operator=(const ConstFilterComponent other) noexcept
+{
+    *this = ConstComponent {other.libObjPtr()};
+    return *this;
+}
+
+inline ConstComponent ConstComponent::operator=(const ConstSinkComponent other) noexcept
+{
+    *this = ConstComponent {other.libObjPtr()};
+    return *this;
+}
+
+namespace internal {
+
+template <typename LibObjT>
+struct ConstPortSpec;
+
+/* Functions specific to constant input ports */
+template <>
+struct ConstPortSpec<const bt_port_input> final
+{
+    static const bt_port *asPort(const bt_port_input * const libObjPtr) noexcept
+    {
+        return bt_port_input_as_port_const(libObjPtr);
+    }
+};
+
+/* Functions specific to constant output ports */
+template <>
+struct ConstPortSpec<const bt_port_output> final
+{
+    static const bt_port *asPort(const bt_port_output * const libObjPtr) noexcept
+    {
+        return bt_port_output_as_port_const(libObjPtr);
+    }
+};
+
+template <typename LibObjT>
+struct ConstPortRefFuncs final
+{
+    static void get(LibObjT * const libObjPtr) noexcept
+    {
+        bt_port_get_ref(ConstPortSpec<LibObjT>::port(libObjPtr));
+    }
+
+    static void put(LibObjT * const libObjPtr) noexcept
+    {
+        bt_port_put_ref(ConstPortSpec<LibObjT>::port(libObjPtr));
+    }
+};
+
+} /* namespace internal */
+
+template <typename LibObjT>
+class ConstPort final : public BorrowedObject<LibObjT>
+{
+public:
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedObject<ConstPort, LibObjT, internal::ConstPortRefFuncs<LibObjT>>;
+
+    explicit ConstPort(const LibObjPtr libObjPtr) noexcept : BorrowedObject<LibObjT> {libObjPtr}
+    {
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return bt_port_get_name(this->_libConstPortPtr());
+    }
+
+    bool isConnected() const noexcept
+    {
+        return static_cast<bool>(bt_port_is_connected(this->_libConstPortPtr()));
+    }
+
+    ConstComponent component() const noexcept
+    {
+        return ConstComponent {bt_port_borrow_component_const(this->_libConstPortPtr())};
+    }
+
+    Shared shared() const noexcept
+    {
+        return Shared::createWithRef(*this);
+    }
+
+private:
+    const bt_port *_libConstPortPtr() const noexcept
+    {
+        return internal::ConstPortSpec<LibObjT>::asPort(this->libObjPtr());
+    }
+};
+
+template <typename LibCompT, typename LibPortT>
+typename ConstComponentPorts<LibCompT, LibPortT>::Port
+ConstComponentPorts<LibCompT, LibPortT>::operator[](const std::uint64_t index) const noexcept
+{
+    return Port {_Spec::portByIndex(this->libObjPtr(), index)};
+}
+
+template <typename LibCompT, typename LibPortT>
+OptionalBorrowedObject<typename ConstComponentPorts<LibCompT, LibPortT>::Port>
+ConstComponentPorts<LibCompT, LibPortT>::operator[](const bt2c::CStringView name) const noexcept
+{
+    return _Spec::portByName(this->libObjPtr(), name);
+}
+
+template <typename LibCompT, typename LibPortT>
+typename ConstComponentPorts<LibCompT, LibPortT>::Iterator
+ConstComponentPorts<LibCompT, LibPortT>::begin() const noexcept
+{
+    return Iterator {*this, 0};
+}
+
+template <typename LibCompT, typename LibPortT>
+typename ConstComponentPorts<LibCompT, LibPortT>::Iterator
+ConstComponentPorts<LibCompT, LibPortT>::end() const noexcept
+{
+    return Iterator {*this, this->length()};
+}
+
+using ConstInputPort = ConstPort<const bt_port_input>;
+using ConstOutputPort = ConstPort<const bt_port_output>;
+
+inline ConstSourceComponent::OutputPorts ConstSourceComponent::outputPorts() const noexcept
+{
+    return OutputPorts {this->libObjPtr()};
+}
+
+inline ConstFilterComponent::OutputPorts ConstFilterComponent::outputPorts() const noexcept
+{
+    return OutputPorts {this->libObjPtr()};
+}
+
+inline ConstFilterComponent::InputPorts ConstFilterComponent::inputPorts() const noexcept
+{
+    return InputPorts {this->libObjPtr()};
+}
+
+inline ConstSinkComponent::InputPorts ConstSinkComponent::inputPorts() const noexcept
+{
+    return InputPorts {this->libObjPtr()};
+}
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_COMPONENT_PORT_HPP */
diff --git a/src/cpp-common/bt2/error.hpp b/src/cpp-common/bt2/error.hpp
new file mode 100644 (file)
index 0000000..d4dfeaa
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2024 EfficiOS Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_ERROR_HPP
+#define BABELTRACE_CPP_COMMON_BT2_ERROR_HPP
+
+#include <cstdint>
+#include <memory>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+
+#include "borrowed-object.hpp"
+#include "component-class.hpp"
+
+namespace bt2 {
+
+class ConstComponentClassErrorCause;
+class ConstComponentErrorCause;
+class ConstMessageIteratorErrorCause;
+
+enum class ErrorCauseActorType
+{
+    Unknown = BT_ERROR_CAUSE_ACTOR_TYPE_UNKNOWN,
+    Component = BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT,
+    ComponentClass = BT_ERROR_CAUSE_ACTOR_TYPE_COMPONENT_CLASS,
+    MessageIterator = BT_ERROR_CAUSE_ACTOR_TYPE_MESSAGE_ITERATOR,
+};
+
+class ConstErrorCause : public BorrowedObject<const bt_error_cause>
+{
+public:
+    explicit ConstErrorCause(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    ErrorCauseActorType actorType() const noexcept
+    {
+        return static_cast<ErrorCauseActorType>(bt_error_cause_get_actor_type(this->libObjPtr()));
+    }
+
+    bool actorTypeIsComponentClass() const noexcept
+    {
+        return this->actorType() == ErrorCauseActorType::ComponentClass;
+    }
+
+    bool actorTypeIsComponent() const noexcept
+    {
+        return this->actorType() == ErrorCauseActorType::Component;
+    }
+
+    bool actorTypeIsMessageIterator() const noexcept
+    {
+        return this->actorType() == ErrorCauseActorType::MessageIterator;
+    }
+
+    ConstComponentClassErrorCause asComponentClass() const noexcept;
+    ConstComponentErrorCause asComponent() const noexcept;
+    ConstMessageIteratorErrorCause asMessageIterator() const noexcept;
+
+    bt2c::CStringView message() const noexcept
+    {
+        return bt_error_cause_get_message(this->libObjPtr());
+    }
+
+    bt2c::CStringView moduleName() const noexcept
+    {
+        return bt_error_cause_get_module_name(this->libObjPtr());
+    }
+
+    bt2c::CStringView fileName() const noexcept
+    {
+        return bt_error_cause_get_file_name(this->libObjPtr());
+    }
+
+    std::uint64_t lineNumber() const noexcept
+    {
+        return bt_error_cause_get_line_number(this->libObjPtr());
+    }
+};
+
+class ConstComponentClassErrorCause final : public ConstErrorCause
+{
+public:
+    explicit ConstComponentClassErrorCause(const LibObjPtr libObjPtr) : ConstErrorCause {libObjPtr}
+    {
+        BT_ASSERT(this->actorTypeIsComponentClass());
+    }
+
+    bt2::ComponentClassType componentClassType() const noexcept
+    {
+        return static_cast<bt2::ComponentClassType>(
+            bt_error_cause_component_class_actor_get_component_class_type(this->libObjPtr()));
+    }
+
+    bt2c::CStringView componentClassName() const noexcept
+    {
+        return bt_error_cause_component_class_actor_get_component_class_name(this->libObjPtr());
+    }
+
+    bt2c::CStringView pluginName() const noexcept
+    {
+        return bt_error_cause_component_class_actor_get_plugin_name(this->libObjPtr());
+    }
+};
+
+inline ConstComponentClassErrorCause ConstErrorCause::asComponentClass() const noexcept
+{
+    return ConstComponentClassErrorCause {this->libObjPtr()};
+}
+
+class ConstComponentErrorCause final : public ConstErrorCause
+{
+public:
+    explicit ConstComponentErrorCause(const LibObjPtr libObjPtr) : ConstErrorCause {libObjPtr}
+    {
+        BT_ASSERT(this->actorTypeIsComponent());
+    }
+
+    bt2c::CStringView componentName() const noexcept
+    {
+        return bt_error_cause_component_actor_get_component_name(this->libObjPtr());
+    }
+
+    bt2::ComponentClassType componentClassType() const noexcept
+    {
+        return static_cast<bt2::ComponentClassType>(
+            bt_error_cause_component_actor_get_component_class_type(this->libObjPtr()));
+    }
+
+    bt2c::CStringView componentClassName() const noexcept
+    {
+        return bt_error_cause_component_actor_get_component_class_name(this->libObjPtr());
+    }
+
+    bt2c::CStringView pluginName() const noexcept
+    {
+        return bt_error_cause_component_actor_get_plugin_name(this->libObjPtr());
+    }
+};
+
+inline ConstComponentErrorCause ConstErrorCause::asComponent() const noexcept
+{
+    return ConstComponentErrorCause {this->libObjPtr()};
+}
+
+class ConstMessageIteratorErrorCause final : public ConstErrorCause
+{
+public:
+    explicit ConstMessageIteratorErrorCause(const LibObjPtr libObjPtr) : ConstErrorCause {libObjPtr}
+    {
+        BT_ASSERT(this->actorTypeIsMessageIterator());
+    }
+
+    bt2c::CStringView componentOutputPortName() const noexcept
+    {
+        return bt_error_cause_message_iterator_actor_get_component_name(this->libObjPtr());
+    }
+
+    bt2c::CStringView componentName() const noexcept
+    {
+        return bt_error_cause_message_iterator_actor_get_component_name(this->libObjPtr());
+    }
+
+    bt2::ComponentClassType componentClassType() const noexcept
+    {
+        return static_cast<bt2::ComponentClassType>(
+            bt_error_cause_message_iterator_actor_get_component_class_type(this->libObjPtr()));
+    }
+
+    bt2c::CStringView componentClassName() const noexcept
+    {
+        return bt_error_cause_message_iterator_actor_get_component_class_name(this->libObjPtr());
+    }
+
+    bt2c::CStringView pluginName() const noexcept
+    {
+        return bt_error_cause_message_iterator_actor_get_plugin_name(this->libObjPtr());
+    }
+};
+
+inline ConstMessageIteratorErrorCause ConstErrorCause::asMessageIterator() const noexcept
+{
+    return ConstMessageIteratorErrorCause {this->libObjPtr()};
+}
+
+class ConstErrorIterator;
+
+class ConstErrorCauseProxy final
+{
+    friend ConstErrorIterator;
+
+private:
+    explicit ConstErrorCauseProxy(const ConstErrorCause cause) noexcept : _mCause {cause}
+    {
+    }
+
+public:
+    const ConstErrorCause *operator->() const noexcept
+    {
+        return &_mCause;
+    }
+
+private:
+    ConstErrorCause _mCause;
+};
+
+class UniqueConstError;
+
+class ConstErrorIterator final
+{
+    friend UniqueConstError;
+
+private:
+    explicit ConstErrorIterator(const UniqueConstError& error, const std::uint64_t index) noexcept :
+        _mError {&error}, _mIndex {index}
+    {
+    }
+
+public:
+    bool operator==(const ConstErrorIterator& other) const noexcept
+    {
+        BT_ASSERT(&other._mError == &_mError);
+        return other._mIndex == _mIndex;
+    }
+
+    bool operator!=(const ConstErrorIterator& other) const noexcept
+    {
+        return !(*this == other);
+    }
+
+    ConstErrorIterator& operator++() noexcept
+    {
+        ++_mIndex;
+        return *this;
+    }
+
+    ConstErrorIterator operator++(int) noexcept
+    {
+        const auto ret = *this;
+
+        ++_mIndex;
+        return ret;
+    }
+
+    ConstErrorCause operator*() const noexcept;
+
+    ConstErrorCauseProxy operator->() const noexcept
+    {
+        return ConstErrorCauseProxy {**this};
+    }
+
+private:
+    const UniqueConstError *_mError;
+    std::uint64_t _mIndex;
+};
+
+class UniqueConstError final
+{
+public:
+    using LibObjPtr = const bt_error *;
+
+    explicit UniqueConstError(const LibObjPtr libError) noexcept : _mLibError {libError}
+    {
+    }
+
+    explicit operator bool() const noexcept
+    {
+        return this->libObjPtr();
+    }
+
+    LibObjPtr libObjPtr() const noexcept
+    {
+        return _mLibError.get();
+    }
+
+    LibObjPtr release() noexcept
+    {
+        return _mLibError.release();
+    }
+
+    std::uint64_t length() const noexcept
+    {
+        return bt_error_get_cause_count(this->libObjPtr());
+    }
+
+    ConstErrorCause operator[](const std::uint64_t index) const noexcept
+    {
+        return ConstErrorCause {bt_error_borrow_cause_by_index(this->libObjPtr(), index)};
+    }
+
+    ConstErrorIterator begin() const noexcept
+    {
+        BT_ASSERT(_mLibError);
+        return ConstErrorIterator {*this, 0};
+    }
+
+    ConstErrorIterator end() const noexcept
+    {
+        BT_ASSERT(_mLibError);
+        return ConstErrorIterator {*this, this->length()};
+    }
+
+private:
+    struct _LibErrorDeleter final
+    {
+        void operator()(const LibObjPtr libError) const noexcept
+        {
+            bt_error_release(libError);
+        }
+    };
+
+    std::unique_ptr<std::remove_pointer<LibObjPtr>::type, _LibErrorDeleter> _mLibError;
+};
+
+inline ConstErrorCause ConstErrorIterator::operator*() const noexcept
+{
+    return (*_mError)[_mIndex];
+}
+
+inline UniqueConstError takeCurrentThreadError() noexcept
+{
+    return UniqueConstError {bt_current_thread_take_error()};
+}
+
+inline void moveErrorToCurrentThread(UniqueConstError error) noexcept
+{
+    bt_current_thread_move_error(error.release());
+}
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_ERROR_HPP */
diff --git a/src/cpp-common/bt2/exc.hpp b/src/cpp-common/bt2/exc.hpp
new file mode 100644 (file)
index 0000000..126d35a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020-2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_EXC_HPP
+#define BABELTRACE_CPP_COMMON_BT2_EXC_HPP
+
+#include "cpp-common/bt2c/exc.hpp"
+
+namespace bt2 {
+
+using Error = bt2c::Error;
+using OverflowError = bt2c::OverflowError;
+using MemoryError = bt2c::MemoryError;
+using TryAgain = bt2c::TryAgain;
+
+/*
+ * Unknown query object.
+ */
+class UnknownObject : public std::exception
+{
+public:
+    explicit UnknownObject() noexcept = default;
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_EXC_HPP */
index 787a8cb3bdd5b5eff4de06c967ec29c1c2e0e49e..83cd52157cc4af723e6bc895817a52204e193cee 100644 (file)
@@ -7,43 +7,45 @@
 #ifndef BABELTRACE_CPP_COMMON_BT2_FIELD_CLASS_HPP
 #define BABELTRACE_CPP_COMMON_BT2_FIELD_CLASS_HPP
 
-#include <type_traits>
 #include <cstdint>
+#include <type_traits>
+
 #include <babeltrace2/babeltrace.h>
 
 #include "common/assert.h"
-#include "internal/borrowed-obj.hpp"
-#include "internal/shared-obj.hpp"
-#include "cpp-common/optional.hpp"
-#include "cpp-common/string_view.hpp"
-#include "lib-error.hpp"
-#include "integer-range-set.hpp"
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/bt2s/optional.hpp"
+
+#include "borrowed-object-iterator.hpp"
+#include "borrowed-object.hpp"
+#include "exc.hpp"
 #include "field-path.hpp"
+#include "integer-range-set.hpp"
+#include "internal/utils.hpp"
+#include "optional-borrowed-object.hpp"
+#include "shared-object.hpp"
+#include "value.hpp"
 
 namespace bt2 {
-
 namespace internal {
 
 struct FieldClassRefFuncs final
 {
-    static void get(const bt_field_class * const libObjPtr)
+    static void get(const bt_field_class * const libObjPtr) noexcept
     {
         bt_field_class_get_ref(libObjPtr);
     }
 
-    static void put(const bt_field_class * const libObjPtr)
+    static void put(const bt_field_class * const libObjPtr) noexcept
     {
         bt_field_class_put_ref(libObjPtr);
     }
 };
 
-template <typename ObjT, typename LibObjT>
-using SharedFieldClass = internal::SharedObj<ObjT, LibObjT, internal::FieldClassRefFuncs>;
-
 template <typename LibObjT>
 struct CommonFieldClassSpec;
 
-// Functions specific to mutable field classes
+/* Functions specific to mutable field classes */
 template <>
 struct CommonFieldClassSpec<bt_field_class> final
 {
@@ -53,7 +55,7 @@ struct CommonFieldClassSpec<bt_field_class> final
     }
 };
 
-// Functions specific to constant field classes
+/* Functions specific to constant field classes */
 template <>
 struct CommonFieldClassSpec<const bt_field_class> final
 {
@@ -63,7 +65,10 @@ struct CommonFieldClassSpec<const bt_field_class> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
+
+template <typename ObjT, typename LibObjT>
+using SharedFieldClass = SharedObject<ObjT, LibObjT, internal::FieldClassRefFuncs>;
 
 template <typename LibObjT>
 class CommonBitArrayFieldClass;
@@ -74,6 +79,9 @@ class CommonIntegerFieldClass;
 template <typename LibObjT>
 class ConstEnumerationFieldClassMapping;
 
+template <typename LibObjT>
+class CommonBaseEnumerationFieldClass;
+
 template <typename LibObjT, typename MappingT>
 class CommonEnumerationFieldClass;
 
@@ -107,6 +115,9 @@ class CommonOptionWithIntegerSelectorFieldClass;
 template <typename LibObjT>
 class CommonVariantWithoutSelectorFieldClass;
 
+template <typename LibObjT>
+class CommonVariantWithSelectorFieldClass;
+
 template <typename LibObjT>
 class ConstVariantWithIntegerSelectorFieldClassOption;
 
@@ -124,90 +135,69 @@ class CommonTraceClass;
 
 enum class FieldClassType
 {
-    BOOL = BT_FIELD_CLASS_TYPE_BOOL,
-    BIT_ARRAY = BT_FIELD_CLASS_TYPE_BIT_ARRAY,
-    UNSIGNED_INTEGER = BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER,
-    SIGNED_INTEGER = BT_FIELD_CLASS_TYPE_SIGNED_INTEGER,
-    UNSIGNED_ENUMERATION = BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION,
-    SIGNED_ENUMERATION = BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION,
-    SINGLE_PRECISION_REAL = BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL,
-    DOUBLE_PRECISION_REAL = BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL,
-    STRING = BT_FIELD_CLASS_TYPE_STRING,
-    STRUCTURE = BT_FIELD_CLASS_TYPE_STRUCTURE,
-    STATIC_ARRAY = BT_FIELD_CLASS_TYPE_STATIC_ARRAY,
-    DYNAMIC_ARRAY_WITHOUT_LENGTH = BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITHOUT_LENGTH_FIELD,
-    DYNAMIC_ARRAY_WITH_LENGTH = BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD,
-    OPTION_WITHOUT_SELECTOR = BT_FIELD_CLASS_TYPE_OPTION_WITHOUT_SELECTOR_FIELD,
-    OPTION_WITH_BOOL_SELECTOR = BT_FIELD_CLASS_TYPE_OPTION_WITH_BOOL_SELECTOR_FIELD,
-    OPTION_WITH_UNSIGNED_INTEGER_SELECTOR =
+    Bool = BT_FIELD_CLASS_TYPE_BOOL,
+    BitArray = BT_FIELD_CLASS_TYPE_BIT_ARRAY,
+    UnsignedInteger = BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER,
+    SignedInteger = BT_FIELD_CLASS_TYPE_SIGNED_INTEGER,
+    UnsignedEnumeration = BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION,
+    SignedEnumeration = BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION,
+    SinglePrecisionReal = BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL,
+    DoublePrecisionReal = BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL,
+    String = BT_FIELD_CLASS_TYPE_STRING,
+    Structure = BT_FIELD_CLASS_TYPE_STRUCTURE,
+    StaticArray = BT_FIELD_CLASS_TYPE_STATIC_ARRAY,
+    DynamicArrayWithoutLength = BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITHOUT_LENGTH_FIELD,
+    DynamicArrayWithLength = BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD,
+    OptionWithoutSelector = BT_FIELD_CLASS_TYPE_OPTION_WITHOUT_SELECTOR_FIELD,
+    OptionWithBoolSelector = BT_FIELD_CLASS_TYPE_OPTION_WITH_BOOL_SELECTOR_FIELD,
+    OptionWithUnsignedIntegerSelector =
         BT_FIELD_CLASS_TYPE_OPTION_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD,
-    OPTION_WITH_SIGNED_INTEGER_SELECTOR =
-        BT_FIELD_CLASS_TYPE_OPTION_WITH_SIGNED_INTEGER_SELECTOR_FIELD,
-    VARIANT_WITHOUT_SELECTOR = BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD,
-    VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR =
+    OptionWithSignedIntegerSelector = BT_FIELD_CLASS_TYPE_OPTION_WITH_SIGNED_INTEGER_SELECTOR_FIELD,
+    VariantWithoutSelector = BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD,
+    VariantWithUnsignedIntegerSelector =
         BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD,
-    VARIANT_WITH_SIGNED_INTEGER_SELECTOR =
+    VariantWithSignedIntegerSelector =
         BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD,
 };
 
 template <typename LibObjT>
-class CommonFieldClass : public internal::BorrowedObj<LibObjT>
+class CommonFieldClass : public BorrowedObject<LibObjT>
 {
-    // Allow appendMember() to call `fc._libObjPtr()`
-    friend class CommonStructureFieldClass<bt_field_class>;
-
-    // Allow appendOption() to call `fc._libObjPtr()`
-    friend class CommonVariantWithoutSelectorFieldClass<bt_field_class>;
-
-    friend class CommonVariantWithIntegerSelectorFieldClass<
-        bt_field_class,
-        ConstVariantWithIntegerSelectorFieldClassOption<
-            const bt_field_class_variant_with_selector_field_integer_unsigned_option>>;
-
-    friend class CommonVariantWithIntegerSelectorFieldClass<
-        bt_field_class,
-        ConstVariantWithIntegerSelectorFieldClassOption<
-            const bt_field_class_variant_with_selector_field_integer_signed_option>>;
-
-    // Allow *FieldClass() to call `fc._libObjPtr()`
-    friend class CommonEventClass<bt_event_class>;
-    friend class CommonStreamClass<bt_stream_class>;
-
-    // Allow create*FieldClass() to call `fc._libObjPtr()`
-    friend class CommonTraceClass<bt_trace_class>;
-
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
 
 protected:
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
     using _ThisCommonFieldClass = CommonFieldClass<LibObjT>;
 
 public:
-    using Shared = internal::SharedFieldClass<CommonFieldClass<LibObjT>, LibObjT>;
-
-    using UserAttributes =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstMapValue, MapValue>::type;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonFieldClass<LibObjT>, LibObjT>;
+    using UserAttributes = internal::DepUserAttrs<LibObjT>;
 
-    explicit CommonFieldClass(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonFieldClass(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonFieldClass(const CommonFieldClass<OtherLibObjT>& fc) noexcept : _ThisBorrowedObj {fc}
+    CommonFieldClass(const CommonFieldClass<OtherLibObjT> fc) noexcept : _ThisBorrowedObject {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonFieldClass& operator=(const CommonFieldClass<OtherLibObjT>& fc) noexcept
+    CommonFieldClass operator=(const CommonFieldClass<OtherLibObjT> fc) noexcept
     {
-        _ThisBorrowedObj::operator=(fc);
+        _ThisBorrowedObject::operator=(fc);
         return *this;
     }
 
+    CommonFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonFieldClass<const bt_field_class> {*this};
+    }
+
     FieldClassType type() const noexcept
     {
-        return static_cast<FieldClassType>(bt_field_class_get_type(this->_libObjPtr()));
+        return static_cast<FieldClassType>(bt_field_class_get_type(this->libObjPtr()));
     }
 
     bool isBool() const noexcept
@@ -250,6 +240,11 @@ public:
         return this->_libTypeIs(BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION);
     }
 
+    bool isReal() const noexcept
+    {
+        return this->_libTypeIs(BT_FIELD_CLASS_TYPE_REAL);
+    }
+
     bool isSinglePrecisionReal() const noexcept
     {
         return this->_libTypeIs(BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL);
@@ -360,9 +355,15 @@ public:
         return this->_libTypeIs(BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD);
     }
 
+    template <typename FieldClassT>
+    FieldClassT as() const noexcept
+    {
+        return FieldClassT {this->libObjPtr()};
+    }
+
     CommonBitArrayFieldClass<LibObjT> asBitArray() const noexcept;
     CommonIntegerFieldClass<LibObjT> asInteger() const noexcept;
-
+    CommonBaseEnumerationFieldClass<LibObjT> asEnumeration() const noexcept;
     CommonEnumerationFieldClass<LibObjT, ConstEnumerationFieldClassMapping<
                                              const bt_field_class_enumeration_unsigned_mapping>>
     asUnsignedEnumeration() const noexcept;
@@ -387,6 +388,7 @@ public:
 
     CommonVariantFieldClass<LibObjT> asVariant() const noexcept;
     CommonVariantWithoutSelectorFieldClass<LibObjT> asVariantWithoutSelector() const noexcept;
+    CommonVariantWithSelectorFieldClass<LibObjT> asVariantWithSelector() const noexcept;
 
     CommonVariantWithIntegerSelectorFieldClass<
         LibObjT, ConstVariantWithIntegerSelectorFieldClassOption<
@@ -399,90 +401,130 @@ public:
     asVariantWithSignedIntegerSelector() const noexcept;
 
     template <typename LibValT>
-    void userAttributes(const CommonMapValue<LibValT>& userAttrs)
+    CommonFieldClass userAttributes(const CommonMapValue<LibValT> userAttrs) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstFieldClass`.");
 
-        bt_field_class_set_user_attributes(this->_libObjPtr(), userAttrs._libObjPtr());
-    }
-
-    ConstMapValue userAttributes() const noexcept
-    {
-        return ConstMapValue {internal::CommonFieldClassSpec<const bt_field_class>::userAttributes(
-            this->_libObjPtr())};
+        bt_field_class_set_user_attributes(this->libObjPtr(), userAttrs.libObjPtr());
+        return *this;
     }
 
-    UserAttributes userAttributes() noexcept
+    UserAttributes userAttributes() const noexcept
     {
         return UserAttributes {
-            internal::CommonFieldClassSpec<LibObjT>::userAttributes(this->_libObjPtr())};
+            internal::CommonFieldClassSpec<LibObjT>::userAttributes(this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 
 protected:
     bool _libTypeIs(const bt_field_class_type type) const noexcept
     {
-        return bt_field_class_type_is(bt_field_class_get_type(this->_libObjPtr()), type);
+        return bt_field_class_type_is(bt_field_class_get_type(this->libObjPtr()), type);
     }
 };
 
 using FieldClass = CommonFieldClass<bt_field_class>;
 using ConstFieldClass = CommonFieldClass<const bt_field_class>;
 
+namespace internal {
+
+struct FieldClassTypeDescr
+{
+    using Const = ConstFieldClass;
+    using NonConst = FieldClass;
+};
+
+template <>
+struct TypeDescr<FieldClass> : public FieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstFieldClass> : public FieldClassTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonBitArrayFieldClass final : public CommonFieldClass<LibObjT>
 {
 private:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
     using typename CommonFieldClass<LibObjT>::_ThisCommonFieldClass;
 
 public:
-    using Shared = internal::SharedFieldClass<CommonBitArrayFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonBitArrayFieldClass<LibObjT>, LibObjT>;
 
-    explicit CommonBitArrayFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonBitArrayFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isBitArray());
     }
 
     template <typename OtherLibObjT>
-    CommonBitArrayFieldClass(const CommonBitArrayFieldClass<OtherLibObjT>& fc) noexcept :
+    CommonBitArrayFieldClass(const CommonBitArrayFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonBitArrayFieldClass<LibObjT>&
-    operator=(const CommonBitArrayFieldClass<OtherLibObjT>& fc) noexcept
+    CommonBitArrayFieldClass<LibObjT>
+    operator=(const CommonBitArrayFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonFieldClass::operator=(fc);
         return *this;
     }
 
+    CommonBitArrayFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonBitArrayFieldClass<const bt_field_class> {*this};
+    }
+
     std::uint64_t length() const noexcept
     {
-        return bt_field_class_bit_array_get_length(this->_libObjPtr());
+        return bt_field_class_bit_array_get_length(this->libObjPtr());
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using BitArrayFieldClass = CommonBitArrayFieldClass<bt_field_class>;
 using ConstBitArrayFieldClass = CommonBitArrayFieldClass<const bt_field_class>;
 
+namespace internal {
+
+struct BitArrayFieldClassTypeDescr
+{
+    using Const = ConstBitArrayFieldClass;
+    using NonConst = BitArrayFieldClass;
+};
+
+template <>
+struct TypeDescr<BitArrayFieldClass> : public BitArrayFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstBitArrayFieldClass> : public BitArrayFieldClassTypeDescr
+{
+};
+
+} /* namespace internal */
+
 enum class DisplayBase
 {
-    BINARY = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY,
-    OCTAL = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL,
-    DECIMAL = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
-    HEXADECIMAL = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL,
+    Binary = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY,
+    Octal = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL,
+    Decimal = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+    Hexadecimal = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL,
 };
 
 template <typename LibObjT>
@@ -492,61 +534,69 @@ private:
     using typename CommonFieldClass<LibObjT>::_ThisCommonFieldClass;
 
 protected:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
     using _ThisCommonIntegerFieldClass = CommonIntegerFieldClass<LibObjT>;
 
 public:
-    using Shared = internal::SharedFieldClass<CommonIntegerFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonIntegerFieldClass<LibObjT>, LibObjT>;
 
-    explicit CommonIntegerFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonIntegerFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isInteger());
     }
 
     template <typename OtherLibObjT>
-    CommonIntegerFieldClass(const CommonIntegerFieldClass<OtherLibObjT>& fc) noexcept :
+    CommonIntegerFieldClass(const CommonIntegerFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonIntegerFieldClass&
-    operator=(const CommonIntegerFieldClass<OtherLibObjT>& fc) noexcept
+    CommonIntegerFieldClass operator=(const CommonIntegerFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonFieldClass::operator=(fc);
         return *this;
     }
 
-    void fieldValueRange(const std::uint64_t n) noexcept
+    CommonIntegerFieldClass<const bt_field_class> asConst() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return CommonIntegerFieldClass<const bt_field_class> {*this};
+    }
+
+    CommonIntegerFieldClass fieldValueRange(const std::uint64_t n) const noexcept
+    {
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstIntegerFieldClass`.");
 
-        bt_field_class_integer_get_field_value_range(this->_libObjPtr(), n);
+        bt_field_class_integer_set_field_value_range(this->libObjPtr(), n);
+        return *this;
     }
 
     std::uint64_t fieldValueRange() const noexcept
     {
-        return bt_field_class_integer_get_field_value_range(this->_libObjPtr());
+        return bt_field_class_integer_get_field_value_range(this->libObjPtr());
     }
 
-    void preferredDisplayBase(const DisplayBase base) noexcept
+    CommonIntegerFieldClass preferredDisplayBase(const DisplayBase base) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstIntegerFieldClass`.");
 
         bt_field_class_integer_set_preferred_display_base(
-            this->_libObjPtr(), static_cast<bt_field_class_integer_preferred_display_base>(base));
+            this->libObjPtr(), static_cast<bt_field_class_integer_preferred_display_base>(base));
+        return *this;
     }
 
     DisplayBase preferredDisplayBase() const noexcept
     {
         return static_cast<DisplayBase>(
-            bt_field_class_integer_get_preferred_display_base(this->_libObjPtr()));
+            bt_field_class_integer_get_preferred_display_base(this->libObjPtr()));
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -555,10 +605,26 @@ using ConstIntegerFieldClass = CommonIntegerFieldClass<const bt_field_class>;
 
 namespace internal {
 
+struct IntegerFieldClassTypeDescr
+{
+    using Const = ConstIntegerFieldClass;
+    using NonConst = IntegerFieldClass;
+};
+
+template <>
+struct TypeDescr<IntegerFieldClass> : public IntegerFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstIntegerFieldClass> : public IntegerFieldClassTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct ConstEnumerationFieldClassMappingSpec;
 
-// Functions specific to unsigned enumeration field class mappings
+/* Functions specific to unsigned enumeration field class mappings */
 template <>
 struct ConstEnumerationFieldClassMappingSpec<const bt_field_class_enumeration_unsigned_mapping>
     final
@@ -577,7 +643,7 @@ struct ConstEnumerationFieldClassMappingSpec<const bt_field_class_enumeration_un
     }
 };
 
-// Functions specific to signed enumeration field class mappings
+/* Functions specific to signed enumeration field class mappings */
 template <>
 struct ConstEnumerationFieldClassMappingSpec<const bt_field_class_enumeration_signed_mapping> final
 {
@@ -595,48 +661,47 @@ struct ConstEnumerationFieldClassMappingSpec<const bt_field_class_enumeration_si
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class ConstEnumerationFieldClassMapping final : public internal::BorrowedObj<LibObjT>
+class ConstEnumerationFieldClassMapping final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ThisConstEnumerationFieldClassMapping = ConstEnumerationFieldClassMapping<LibObjT>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
 
 public:
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+
     using RangeSet = typename std::conditional<
         std::is_same<LibObjT, const bt_field_class_enumeration_unsigned_mapping>::value,
         ConstUnsignedIntegerRangeSet, ConstSignedIntegerRangeSet>::type;
 
-    explicit ConstEnumerationFieldClassMapping(const _LibObjPtr libObjPtr) noexcept :
-        _ThisBorrowedObj {libObjPtr}
+    explicit ConstEnumerationFieldClassMapping(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
     {
     }
 
-    ConstEnumerationFieldClassMapping(
-        const _ThisConstEnumerationFieldClassMapping& mapping) noexcept :
-        _ThisBorrowedObj {mapping}
+    ConstEnumerationFieldClassMapping(const ConstEnumerationFieldClassMapping& mapping) noexcept :
+        _ThisBorrowedObject {mapping}
     {
     }
 
-    _ThisConstEnumerationFieldClassMapping&
-    operator=(const _ThisConstEnumerationFieldClassMapping& mapping) noexcept
+    ConstEnumerationFieldClassMapping
+    operator=(const ConstEnumerationFieldClassMapping& mapping) noexcept
     {
-        _ThisBorrowedObj::operator=(mapping);
+        _ThisBorrowedObject::operator=(mapping);
         return *this;
     }
 
     RangeSet ranges() const noexcept
     {
         return RangeSet {
-            internal::ConstEnumerationFieldClassMappingSpec<LibObjT>::ranges(this->_libObjPtr())};
+            internal::ConstEnumerationFieldClassMappingSpec<LibObjT>::ranges(this->libObjPtr())};
     }
 
-    bpstd::string_view label() const noexcept
+    bt2c::CStringView label() const noexcept
     {
-        return internal::ConstEnumerationFieldClassMappingSpec<LibObjT>::label(this->_libObjPtr());
+        return internal::ConstEnumerationFieldClassMappingSpec<LibObjT>::label(this->libObjPtr());
     }
 };
 
@@ -651,7 +716,7 @@ namespace internal {
 template <typename MappingT>
 struct CommonEnumerationFieldClassSpec;
 
-// Functions specific to unsigned enumeration field classes
+/* Functions specific to unsigned enumeration field classes */
 template <>
 struct CommonEnumerationFieldClassSpec<ConstUnsignedEnumerationFieldClassMapping> final
 {
@@ -666,9 +731,16 @@ struct CommonEnumerationFieldClassSpec<ConstUnsignedEnumerationFieldClassMapping
     {
         return bt_field_class_enumeration_unsigned_borrow_mapping_by_label_const(libObjPtr, label);
     }
+
+    static bt_field_class_enumeration_add_mapping_status
+    addMapping(bt_field_class * const libObjPtr, const char * const label,
+               const bt_integer_range_set_unsigned * const libRanges) noexcept
+    {
+        return bt_field_class_enumeration_unsigned_add_mapping(libObjPtr, label, libRanges);
+    }
 };
 
-// Functions specific to signed enumeration field classes
+/* Functions specific to signed enumeration field classes */
 template <>
 struct CommonEnumerationFieldClassSpec<ConstSignedEnumerationFieldClassMapping> final
 {
@@ -683,77 +755,144 @@ struct CommonEnumerationFieldClassSpec<ConstSignedEnumerationFieldClassMapping>
     {
         return bt_field_class_enumeration_signed_borrow_mapping_by_label_const(libObjPtr, label);
     }
+
+    static bt_field_class_enumeration_add_mapping_status
+    addMapping(bt_field_class * const libObjPtr, const char * const label,
+               const bt_integer_range_set_signed * const libRanges) noexcept
+    {
+        return bt_field_class_enumeration_signed_add_mapping(libObjPtr, label, libRanges);
+    }
 };
 
-} // namespace internal
+} /* namespace internal */
 
-template <typename LibObjT, typename MappingT>
-class CommonEnumerationFieldClass final : public CommonIntegerFieldClass<LibObjT>
+template <typename LibObjT>
+class CommonBaseEnumerationFieldClass : public CommonIntegerFieldClass<LibObjT>
 {
 private:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
     using typename CommonIntegerFieldClass<LibObjT>::_ThisCommonIntegerFieldClass;
-    using _ThisCommonEnumerationFieldClass = CommonEnumerationFieldClass<LibObjT, MappingT>;
+
+protected:
+    using _ThisCommonBaseEnumerationFieldClass = CommonBaseEnumerationFieldClass<LibObjT>;
 
 public:
-    using Shared = internal::SharedFieldClass<_ThisCommonEnumerationFieldClass, LibObjT>;
-    using Mapping = MappingT;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<_ThisCommonBaseEnumerationFieldClass, LibObjT>;
 
-    explicit CommonEnumerationFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonBaseEnumerationFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonIntegerFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isEnumeration());
     }
 
     template <typename OtherLibObjT>
-    CommonEnumerationFieldClass(
-        const CommonEnumerationFieldClass<OtherLibObjT, MappingT>& fc) noexcept :
+    CommonBaseEnumerationFieldClass(const CommonBaseEnumerationFieldClass<OtherLibObjT> fc) noexcept
+        :
         _ThisCommonIntegerFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonEnumerationFieldClass&
-    operator=(const CommonEnumerationFieldClass<OtherLibObjT, MappingT>& fc) noexcept
+    CommonBaseEnumerationFieldClass
+    operator=(const CommonBaseEnumerationFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonIntegerFieldClass::operator=(fc);
         return *this;
     }
 
-    std::uint64_t size() const noexcept
+    CommonBaseEnumerationFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonBaseEnumerationFieldClass<const bt_field_class> {*this};
+    }
+
+    std::uint64_t length() const noexcept
+    {
+        return bt_field_class_enumeration_get_mapping_count(this->libObjPtr());
+    }
+
+    Shared shared() const noexcept
     {
-        return bt_field_class_enumeration_get_mapping_count(this->_libObjPtr());
+        return Shared::createWithRef(*this);
+    }
+};
+
+template <typename LibObjT, typename MappingT>
+class CommonEnumerationFieldClass final : public CommonBaseEnumerationFieldClass<LibObjT>
+{
+private:
+    using typename CommonBaseEnumerationFieldClass<LibObjT>::_ThisCommonBaseEnumerationFieldClass;
+
+public:
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonEnumerationFieldClass, LibObjT>;
+    using Iterator = BorrowedObjectIterator<CommonEnumerationFieldClass>;
+    using Mapping = MappingT;
+
+    explicit CommonEnumerationFieldClass(const LibObjPtr libObjPtr) noexcept :
+        _ThisCommonBaseEnumerationFieldClass {libObjPtr}
+    {
+        BT_ASSERT_DBG(this->isEnumeration());
+    }
+
+    template <typename OtherLibObjT>
+    CommonEnumerationFieldClass(
+        const CommonEnumerationFieldClass<OtherLibObjT, MappingT> fc) noexcept :
+        CommonEnumerationFieldClass {fc}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonEnumerationFieldClass
+    operator=(const CommonEnumerationFieldClass<OtherLibObjT, MappingT> fc) noexcept
+    {
+        CommonEnumerationFieldClass::operator=(fc);
+        return *this;
     }
 
     Mapping operator[](const std::uint64_t index) const noexcept
     {
         return Mapping {internal::CommonEnumerationFieldClassSpec<MappingT>::mappingByIndex(
-            this->_libObjPtr(), index)};
+            this->libObjPtr(), index)};
+    }
+
+    OptionalBorrowedObject<Mapping> operator[](const bt2c::CStringView label) const noexcept
+    {
+        return internal::CommonEnumerationFieldClassSpec<MappingT>::mappingByLabel(
+            this->libObjPtr(), label);
     }
 
-    nonstd::optional<Mapping> operator[](const char * const label) const noexcept
+    CommonEnumerationFieldClass addMapping(const bt2c::CStringView label,
+                                           const typename Mapping::RangeSet ranges) const
     {
-        const auto libObjPtr = internal::CommonEnumerationFieldClassSpec<MappingT>::mappingByLabel(
-            this->_libObjPtr(), label);
+        const auto status = internal::CommonEnumerationFieldClassSpec<MappingT>::addMapping(
+            this->libObjPtr(), label, ranges.libObjPtr());
 
-        if (libObjPtr) {
-            return Mapping {libObjPtr};
+        if (status == BT_FIELD_CLASS_ENUMERATION_ADD_MAPPING_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
         }
 
-        return nonstd::nullopt;
+        return *this;
+    }
+
+    Iterator begin() const noexcept
+    {
+        return Iterator {*this, 0};
     }
 
-    nonstd::optional<Mapping> operator[](const std::string& label) const noexcept
+    Iterator end() const noexcept
     {
-        return (*this)[label.data()];
+        return Iterator {*this, this->length()};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
+using EnumerationFieldClass = CommonBaseEnumerationFieldClass<bt_field_class>;
+using ConstEnumerationFieldClass = CommonBaseEnumerationFieldClass<const bt_field_class>;
+
 using UnsignedEnumerationFieldClass =
     CommonEnumerationFieldClass<bt_field_class, ConstUnsignedEnumerationFieldClassMapping>;
 
@@ -768,10 +907,42 @@ using ConstSignedEnumerationFieldClass =
 
 namespace internal {
 
+struct UnsignedEnumerationFieldClassTypeDescr
+{
+    using Const = ConstUnsignedEnumerationFieldClass;
+    using NonConst = UnsignedEnumerationFieldClass;
+};
+
+template <>
+struct TypeDescr<UnsignedEnumerationFieldClass> : public UnsignedEnumerationFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstUnsignedEnumerationFieldClass> : public UnsignedEnumerationFieldClassTypeDescr
+{
+};
+
+struct SignedEnumerationFieldClassTypeDescr
+{
+    using Const = ConstSignedEnumerationFieldClass;
+    using NonConst = SignedEnumerationFieldClass;
+};
+
+template <>
+struct TypeDescr<SignedEnumerationFieldClass> : public SignedEnumerationFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstSignedEnumerationFieldClass> : public SignedEnumerationFieldClassTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonStructureFieldClassMemberSpec;
 
-// Functions specific to mutable structure field class members
+/* Functions specific to mutable structure field class members */
 template <>
 struct CommonStructureFieldClassMemberSpec<bt_field_class_structure_member> final
 {
@@ -779,9 +950,14 @@ struct CommonStructureFieldClassMemberSpec<bt_field_class_structure_member> fina
     {
         return bt_field_class_structure_member_borrow_field_class(libObjPtr);
     }
+
+    static bt_value *userAttributes(bt_field_class_structure_member * const libObjPtr) noexcept
+    {
+        return bt_field_class_structure_member_borrow_user_attributes(libObjPtr);
+    }
 };
 
-// Functions specific to constant structure field class members
+/* Functions specific to constant structure field class members */
 template <>
 struct CommonStructureFieldClassMemberSpec<const bt_field_class_structure_member> final
 {
@@ -790,56 +966,80 @@ struct CommonStructureFieldClassMemberSpec<const bt_field_class_structure_member
     {
         return bt_field_class_structure_member_borrow_field_class_const(libObjPtr);
     }
+
+    static const bt_value *
+    userAttributes(const bt_field_class_structure_member * const libObjPtr) noexcept
+    {
+        return bt_field_class_structure_member_borrow_user_attributes_const(libObjPtr);
+    }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class CommonStructureFieldClassMember final : public internal::BorrowedObj<LibObjT>
+class CommonStructureFieldClassMember final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-
-    using _FieldClass =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstFieldClass, FieldClass>::type;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
+    using _FieldClass = internal::DepFc<LibObjT>;
 
 public:
-    explicit CommonStructureFieldClassMember(const _LibObjPtr libObjPtr) noexcept :
-        _ThisBorrowedObj {libObjPtr}
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using UserAttributes = internal::DepUserAttrs<LibObjT>;
+
+    explicit CommonStructureFieldClassMember(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStructureFieldClassMember(
-        const CommonStructureFieldClassMember<OtherLibObjT>& fc) noexcept :
-        _ThisBorrowedObj {fc}
+    CommonStructureFieldClassMember(const CommonStructureFieldClassMember<OtherLibObjT> fc) noexcept
+        :
+        _ThisBorrowedObject {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStructureFieldClassMember<LibObjT>&
-    operator=(const CommonStructureFieldClassMember<OtherLibObjT>& fc) noexcept
+    CommonStructureFieldClassMember<LibObjT>
+    operator=(const CommonStructureFieldClassMember<OtherLibObjT> fc) noexcept
     {
-        _ThisBorrowedObj::operator=(fc);
+        _ThisBorrowedObject::operator=(fc);
         return *this;
     }
 
-    bpstd::string_view name() const noexcept
+    CommonStructureFieldClassMember<const bt_field_class_structure_member> asConst() const noexcept
     {
-        return bt_field_class_structure_member_get_name(this->_libObjPtr());
+        return CommonStructureFieldClassMember<const bt_field_class_structure_member> {*this};
     }
 
-    ConstFieldClass fieldClass() const noexcept
+    bt2c::CStringView name() const noexcept
     {
-        return ConstFieldClass {internal::CommonStructureFieldClassMemberSpec<
-            const bt_field_class_structure_member>::fieldClass(this->_libObjPtr())};
+        return bt_field_class_structure_member_get_name(this->libObjPtr());
     }
 
-    _FieldClass fieldClass() noexcept
+    _FieldClass fieldClass() const noexcept
     {
         return _FieldClass {
-            internal::CommonStructureFieldClassMemberSpec<LibObjT>::fieldClass(this->_libObjPtr())};
+            internal::CommonStructureFieldClassMemberSpec<LibObjT>::fieldClass(this->libObjPtr())};
+    }
+
+    template <typename LibValT>
+    CommonStructureFieldClassMember
+    userAttributes(const CommonMapValue<LibValT> userAttrs) const noexcept
+    {
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStructureFieldClassMember`.");
+
+        bt_field_class_structure_member_set_user_attributes(this->libObjPtr(),
+                                                            userAttrs.libObjPtr());
+        return *this;
+    }
+
+    UserAttributes userAttributes() const noexcept
+    {
+        return UserAttributes {
+            internal::CommonStructureFieldClassMemberSpec<LibObjT>::userAttributes(
+                this->libObjPtr())};
     }
 };
 
@@ -850,10 +1050,26 @@ using ConstStructureFieldClassMember =
 
 namespace internal {
 
+struct StructureFieldClassMemberTypeDescr
+{
+    using Const = ConstStructureFieldClassMember;
+    using NonConst = StructureFieldClassMember;
+};
+
+template <>
+struct TypeDescr<StructureFieldClassMember> : public StructureFieldClassMemberTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstStructureFieldClassMember> : public StructureFieldClassMemberTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonStructureFieldClassSpec;
 
-// Functions specific to mutable structure field classes
+/* Functions specific to mutable structure field classes */
 template <>
 struct CommonStructureFieldClassSpec<bt_field_class> final
 {
@@ -870,7 +1086,7 @@ struct CommonStructureFieldClassSpec<bt_field_class> final
     }
 };
 
-// Functions specific to constant structure field classes
+/* Functions specific to constant structure field classes */
 template <>
 struct CommonStructureFieldClassSpec<const bt_field_class> final
 {
@@ -887,117 +1103,91 @@ struct CommonStructureFieldClassSpec<const bt_field_class> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonStructureFieldClass final : public CommonFieldClass<LibObjT>
 {
 private:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
     using typename CommonFieldClass<LibObjT>::_ThisCommonFieldClass;
 
 public:
-    using Shared = internal::SharedFieldClass<CommonStructureFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonStructureFieldClass<LibObjT>, LibObjT>;
+    using Iterator = BorrowedObjectIterator<CommonStructureFieldClass<LibObjT>>;
 
     using Member =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstStructureFieldClassMember,
-                                  StructureFieldClassMember>::type;
+        internal::DepType<LibObjT, StructureFieldClassMember, ConstStructureFieldClassMember>;
 
-    explicit CommonStructureFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonStructureFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isStructure());
     }
 
     template <typename OtherLibObjT>
-    CommonStructureFieldClass(const CommonStructureFieldClass<OtherLibObjT>& fc) noexcept :
+    CommonStructureFieldClass(const CommonStructureFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStructureFieldClass<LibObjT>&
-    operator=(const CommonStructureFieldClass<OtherLibObjT>& fc) noexcept
+    CommonStructureFieldClass operator=(const CommonStructureFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonFieldClass::operator=(fc);
         return *this;
     }
 
-    void appendMember(const char * const name, const FieldClass& fc)
+    CommonStructureFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonStructureFieldClass<const bt_field_class> {*this};
+    }
+
+    CommonStructureFieldClass appendMember(const bt2c::CStringView name, const FieldClass fc) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStructureFieldClass`.");
 
         const auto status =
-            bt_field_class_structure_append_member(this->_libObjPtr(), name, fc._libObjPtr());
+            bt_field_class_structure_append_member(this->libObjPtr(), name, fc.libObjPtr());
 
         if (status == BT_FIELD_CLASS_STRUCTURE_APPEND_MEMBER_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
-
-    void appendMember(const std::string& name, const FieldClass& fc)
-    {
-        this->appendMember(name.data(), fc);
-    }
-
-    std::uint64_t size() const noexcept
-    {
-        return bt_field_class_structure_get_member_count(this->_libObjPtr());
-    }
 
-    ConstStructureFieldClassMember operator[](const std::uint64_t index) const noexcept
-    {
-        return ConstStructureFieldClassMember {
-            internal::CommonStructureFieldClassSpec<const bt_field_class>::memberByIndex(
-                this->_libObjPtr(), index)};
+        return *this;
     }
 
-    Member operator[](const std::uint64_t index) noexcept
+    std::uint64_t length() const noexcept
     {
-        return Member {internal::CommonStructureFieldClassSpec<LibObjT>::memberByIndex(
-            this->_libObjPtr(), index)};
+        return bt_field_class_structure_get_member_count(this->libObjPtr());
     }
 
-    nonstd::optional<ConstStructureFieldClassMember>
-    operator[](const char * const name) const noexcept
+    Iterator begin() const noexcept
     {
-        const auto libObjPtr =
-            internal::CommonStructureFieldClassSpec<const bt_field_class>::memberByName(
-                this->_libObjPtr(), name);
-
-        if (libObjPtr) {
-            return ConstStructureFieldClassMember {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return Iterator {*this, 0};
     }
 
-    nonstd::optional<ConstStructureFieldClassMember>
-    operator[](const std::string& name) const noexcept
+    Iterator end() const noexcept
     {
-        return (*this)[name.data()];
+        return Iterator {*this, this->length()};
     }
 
-    nonstd::optional<Member> operator[](const char * const name) noexcept
+    Member operator[](const std::uint64_t index) const noexcept
     {
-        const auto libObjPtr = internal::CommonStructureFieldClassSpec<LibObjT>::memberByName(
-            this->_libObjPtr(), name);
-
-        if (libObjPtr) {
-            return Member {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return Member {internal::CommonStructureFieldClassSpec<LibObjT>::memberByIndex(
+            this->libObjPtr(), index)};
     }
 
-    nonstd::optional<Member> operator[](const std::string& name) noexcept
+    OptionalBorrowedObject<Member> operator[](const bt2c::CStringView name) const noexcept
     {
-        return (*this)[name.data()];
+        return internal::CommonStructureFieldClassSpec<LibObjT>::memberByName(this->libObjPtr(),
+                                                                              name);
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -1006,10 +1196,26 @@ using ConstStructureFieldClass = CommonStructureFieldClass<const bt_field_class>
 
 namespace internal {
 
+struct StructureFieldClassTypeDescr
+{
+    using Const = ConstStructureFieldClass;
+    using NonConst = StructureFieldClass;
+};
+
+template <>
+struct TypeDescr<StructureFieldClass> : public StructureFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstStructureFieldClass> : public StructureFieldClassTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonArrayFieldClassSpec;
 
-// Functions specific to mutable array field classes
+/* Functions specific to mutable array field classes */
 template <>
 struct CommonArrayFieldClassSpec<bt_field_class> final
 {
@@ -1019,7 +1225,7 @@ struct CommonArrayFieldClassSpec<bt_field_class> final
     }
 };
 
-// Functions specific to constant array field classes
+/* Functions specific to constant array field classes */
 template <>
 struct CommonArrayFieldClassSpec<const bt_field_class> final
 {
@@ -1029,121 +1235,161 @@ struct CommonArrayFieldClassSpec<const bt_field_class> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonArrayFieldClass : public CommonFieldClass<LibObjT>
 {
 private:
     using typename CommonFieldClass<LibObjT>::_ThisCommonFieldClass;
-
-    using _FieldClass =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstFieldClass, FieldClass>::type;
+    using _FieldClass = internal::DepFc<LibObjT>;
 
 protected:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
     using _ThisCommonArrayFieldClass = CommonArrayFieldClass<LibObjT>;
 
 public:
-    using Shared = internal::SharedFieldClass<CommonArrayFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonArrayFieldClass<LibObjT>, LibObjT>;
 
-    explicit CommonArrayFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonArrayFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isArray());
     }
 
     template <typename OtherLibObjT>
-    CommonArrayFieldClass(const CommonArrayFieldClass<OtherLibObjT>& fc) noexcept :
+    CommonArrayFieldClass(const CommonArrayFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonArrayFieldClass& operator=(const CommonArrayFieldClass<OtherLibObjT>& fc) noexcept
+    CommonArrayFieldClass operator=(const CommonArrayFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonFieldClass::operator=(fc);
         return *this;
     }
 
-    ConstFieldClass elementFieldClass() const noexcept
+    CommonArrayFieldClass<const bt_field_class> asConst() const noexcept
     {
-        return ConstFieldClass {
-            internal::CommonArrayFieldClassSpec<const bt_field_class>::elementFieldClass(
-                this->_libObjPtr())};
+        return CommonArrayFieldClass<const bt_field_class> {*this};
     }
 
-    _FieldClass elementFieldClass() noexcept
+    _FieldClass elementFieldClass() const noexcept
     {
         return _FieldClass {
-            internal::CommonArrayFieldClassSpec<LibObjT>::elementFieldClass(this->_libObjPtr())};
+            internal::CommonArrayFieldClassSpec<LibObjT>::elementFieldClass(this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using ArrayFieldClass = CommonArrayFieldClass<bt_field_class>;
 using ConstArrayFieldClass = CommonArrayFieldClass<const bt_field_class>;
 
+namespace internal {
+
+struct ArrayFieldClassTypeDescr
+{
+    using Const = ConstArrayFieldClass;
+    using NonConst = ArrayFieldClass;
+};
+
+template <>
+struct TypeDescr<ArrayFieldClass> : public ArrayFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstArrayFieldClass> : public ArrayFieldClassTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonStaticArrayFieldClass final : public CommonArrayFieldClass<LibObjT>
 {
 private:
     using typename CommonArrayFieldClass<LibObjT>::_ThisCommonArrayFieldClass;
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
 
 public:
-    using Shared = internal::SharedFieldClass<CommonStaticArrayFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonStaticArrayFieldClass<LibObjT>, LibObjT>;
 
-    explicit CommonStaticArrayFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonStaticArrayFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonArrayFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isStaticArray());
     }
 
     template <typename OtherLibObjT>
-    CommonStaticArrayFieldClass(const CommonStaticArrayFieldClass<OtherLibObjT>& fc) noexcept :
+    CommonStaticArrayFieldClass(const CommonStaticArrayFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonArrayFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStaticArrayFieldClass<LibObjT>&
-    operator=(const CommonStaticArrayFieldClass<OtherLibObjT>& fc) noexcept
+    CommonStaticArrayFieldClass
+    operator=(const CommonStaticArrayFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonArrayFieldClass::operator=(fc);
         return *this;
     }
 
+    CommonStaticArrayFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonStaticArrayFieldClass<const bt_field_class> {*this};
+    }
+
     std::uint64_t length() const noexcept
     {
-        return bt_field_class_array_static_get_length(this->_libObjPtr());
+        return bt_field_class_array_static_get_length(this->libObjPtr());
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using StaticArrayFieldClass = CommonStaticArrayFieldClass<bt_field_class>;
 using ConstStaticArrayFieldClass = CommonStaticArrayFieldClass<const bt_field_class>;
 
+namespace internal {
+
+struct StaticArrayFieldClassTypeDescr
+{
+    using Const = ConstStaticArrayFieldClass;
+    using NonConst = StaticArrayFieldClass;
+};
+
+template <>
+struct TypeDescr<StaticArrayFieldClass> : public StaticArrayFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstStaticArrayFieldClass> : public StaticArrayFieldClassTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonDynamicArrayWithLengthFieldClass final : public CommonArrayFieldClass<LibObjT>
 {
 private:
     using typename CommonArrayFieldClass<LibObjT>::_ThisCommonArrayFieldClass;
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
 
 public:
-    using Shared =
-        internal::SharedFieldClass<CommonDynamicArrayWithLengthFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonDynamicArrayWithLengthFieldClass<LibObjT>, LibObjT>;
 
-    explicit CommonDynamicArrayWithLengthFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonDynamicArrayWithLengthFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonArrayFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isDynamicArrayWithLength());
@@ -1151,29 +1397,34 @@ public:
 
     template <typename OtherLibObjT>
     CommonDynamicArrayWithLengthFieldClass(
-        const CommonDynamicArrayWithLengthFieldClass<OtherLibObjT>& fc) noexcept :
+        const CommonDynamicArrayWithLengthFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonArrayFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonDynamicArrayWithLengthFieldClass<LibObjT>&
-    operator=(const CommonDynamicArrayWithLengthFieldClass<OtherLibObjT>& fc) noexcept
+    CommonDynamicArrayWithLengthFieldClass
+    operator=(const CommonDynamicArrayWithLengthFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonArrayFieldClass::operator=(fc);
         return *this;
     }
 
+    CommonDynamicArrayWithLengthFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonDynamicArrayWithLengthFieldClass<const bt_field_class> {*this};
+    }
+
     ConstFieldPath lengthFieldPath() const noexcept
     {
         return ConstFieldPath {
             bt_field_class_array_dynamic_with_length_field_borrow_length_field_path_const(
-                this->_libObjPtr())};
+                this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -1184,10 +1435,28 @@ using ConstDynamicArrayWithLengthFieldClass =
 
 namespace internal {
 
+struct DynamicArrayWithLengthFieldClassTypeDescr
+{
+    using Const = ConstDynamicArrayWithLengthFieldClass;
+    using NonConst = DynamicArrayWithLengthFieldClass;
+};
+
+template <>
+struct TypeDescr<DynamicArrayWithLengthFieldClass> :
+    public DynamicArrayWithLengthFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstDynamicArrayWithLengthFieldClass> :
+    public DynamicArrayWithLengthFieldClassTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonOptionFieldClassSpec;
 
-// Functions specific to mutable option field classes
+/* Functions specific to mutable option field classes */
 template <>
 struct CommonOptionFieldClassSpec<bt_field_class> final
 {
@@ -1197,7 +1466,7 @@ struct CommonOptionFieldClassSpec<bt_field_class> final
     }
 };
 
-// Functions specific to constant option field classes
+/* Functions specific to constant option field classes */
 template <>
 struct CommonOptionFieldClassSpec<const bt_field_class> final
 {
@@ -1207,65 +1476,81 @@ struct CommonOptionFieldClassSpec<const bt_field_class> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonOptionFieldClass : public CommonFieldClass<LibObjT>
 {
 private:
     using typename CommonFieldClass<LibObjT>::_ThisCommonFieldClass;
-
-    using _FieldClass =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstFieldClass, FieldClass>::type;
+    using _FieldClass = internal::DepFc<LibObjT>;
 
 protected:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
     using _ThisCommonOptionFieldClass = CommonOptionFieldClass<LibObjT>;
 
 public:
-    using Shared = internal::SharedFieldClass<CommonOptionFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonOptionFieldClass<LibObjT>, LibObjT>;
 
-    explicit CommonOptionFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonOptionFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isOption());
     }
 
     template <typename OtherLibObjT>
-    CommonOptionFieldClass(const CommonOptionFieldClass<OtherLibObjT>& fc) noexcept :
+    CommonOptionFieldClass(const CommonOptionFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonOptionFieldClass& operator=(const CommonOptionFieldClass<OtherLibObjT>& fc) noexcept
+    CommonOptionFieldClass operator=(const CommonOptionFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonFieldClass::operator=(fc);
         return *this;
     }
 
-    ConstFieldClass fieldClass() const noexcept
+    CommonOptionFieldClass<const bt_field_class> asConst() const noexcept
     {
-        return ConstFieldClass {
-            internal::CommonOptionFieldClassSpec<const bt_field_class>::fieldClass(
-                this->_libObjPtr())};
+        return CommonOptionFieldClass<const bt_field_class> {*this};
     }
 
-    _FieldClass fieldClass() noexcept
+    _FieldClass fieldClass() const noexcept
     {
         return _FieldClass {
-            internal::CommonOptionFieldClassSpec<LibObjT>::fieldClass(this->_libObjPtr())};
+            internal::CommonOptionFieldClassSpec<LibObjT>::fieldClass(this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using OptionFieldClass = CommonOptionFieldClass<bt_field_class>;
 using ConstOptionFieldClass = CommonOptionFieldClass<const bt_field_class>;
 
+namespace internal {
+
+struct OptionFieldClassTypeDescr
+{
+    using Const = ConstOptionFieldClass;
+    using NonConst = OptionFieldClass;
+};
+
+template <>
+struct TypeDescr<OptionFieldClass> : public OptionFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstOptionFieldClass> : public OptionFieldClassTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonOptionWithSelectorFieldClass : public CommonOptionFieldClass<LibObjT>
 {
@@ -1273,13 +1558,13 @@ private:
     using typename CommonOptionFieldClass<LibObjT>::_ThisCommonOptionFieldClass;
 
 protected:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
     using _ThisCommonOptionWithSelectorFieldClass = CommonOptionWithSelectorFieldClass<LibObjT>;
 
 public:
-    using Shared = internal::SharedFieldClass<CommonOptionWithSelectorFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonOptionWithSelectorFieldClass<LibObjT>, LibObjT>;
 
-    explicit CommonOptionWithSelectorFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonOptionWithSelectorFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonOptionFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isOptionWithSelector());
@@ -1287,49 +1572,72 @@ public:
 
     template <typename OtherLibObjT>
     CommonOptionWithSelectorFieldClass(
-        const CommonOptionWithSelectorFieldClass<OtherLibObjT>& fc) noexcept :
+        const CommonOptionWithSelectorFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonOptionFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonOptionWithSelectorFieldClass&
-    operator=(const CommonOptionWithSelectorFieldClass<OtherLibObjT>& fc) noexcept
+    CommonOptionWithSelectorFieldClass
+    operator=(const CommonOptionWithSelectorFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonOptionFieldClass::operator=(fc);
         return *this;
     }
 
+    CommonOptionWithSelectorFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonOptionWithSelectorFieldClass<const bt_field_class> {*this};
+    }
+
     ConstFieldPath selectorFieldPath() const noexcept
     {
         return ConstFieldPath {
             bt_field_class_option_with_selector_field_borrow_selector_field_path_const(
-                this->_libObjPtr())};
+                this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using OptionWithSelectorFieldClass = CommonOptionWithSelectorFieldClass<bt_field_class>;
 using ConstOptionWithSelectorFieldClass = CommonOptionWithSelectorFieldClass<const bt_field_class>;
 
+namespace internal {
+
+struct OptionWithSelectorFieldClassTypeDescr
+{
+    using Const = ConstOptionWithSelectorFieldClass;
+    using NonConst = OptionWithSelectorFieldClass;
+};
+
+template <>
+struct TypeDescr<OptionWithSelectorFieldClass> : public OptionWithSelectorFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstOptionWithSelectorFieldClass> : public OptionWithSelectorFieldClassTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonOptionWithBoolSelectorFieldClass : public CommonOptionWithSelectorFieldClass<LibObjT>
 {
 private:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
-
     using typename CommonOptionWithSelectorFieldClass<
         LibObjT>::_ThisCommonOptionWithSelectorFieldClass;
 
 public:
-    using Shared =
-        internal::SharedFieldClass<CommonOptionWithBoolSelectorFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonOptionWithBoolSelectorFieldClass<LibObjT>, LibObjT>;
 
-    explicit CommonOptionWithBoolSelectorFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonOptionWithBoolSelectorFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonOptionWithSelectorFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isOptionWithBoolSelector());
@@ -1337,28 +1645,33 @@ public:
 
     template <typename OtherLibObjT>
     CommonOptionWithBoolSelectorFieldClass(
-        const CommonOptionWithBoolSelectorFieldClass<OtherLibObjT>& fc) noexcept :
+        const CommonOptionWithBoolSelectorFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonOptionWithSelectorFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonOptionWithBoolSelectorFieldClass<LibObjT>&
-    operator=(const CommonOptionWithBoolSelectorFieldClass<OtherLibObjT>& fc) noexcept
+    CommonOptionWithBoolSelectorFieldClass
+    operator=(const CommonOptionWithBoolSelectorFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonOptionWithSelectorFieldClass::operator=(fc);
         return *this;
     }
 
+    CommonOptionWithBoolSelectorFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonOptionWithBoolSelectorFieldClass<const bt_field_class> {*this};
+    }
+
     bool selectorIsReversed() const noexcept
     {
         return bt_field_class_option_with_selector_field_bool_selector_is_reversed(
-            this->_libObjPtr());
+            this->libObjPtr());
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -1369,10 +1682,28 @@ using ConstOptionWithBoolSelectorFieldClass =
 
 namespace internal {
 
+struct OptionWithBoolSelectorFieldClassTypeDescr
+{
+    using Const = ConstOptionWithBoolSelectorFieldClass;
+    using NonConst = OptionWithBoolSelectorFieldClass;
+};
+
+template <>
+struct TypeDescr<OptionWithBoolSelectorFieldClass> :
+    public OptionWithBoolSelectorFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstOptionWithBoolSelectorFieldClass> :
+    public OptionWithBoolSelectorFieldClassTypeDescr
+{
+};
+
 template <typename RangeSetT>
 struct CommonOptionWithIntegerSelectorFieldClassSpec;
 
-// Functions specific to option field classes with unsigned integer ranges
+/* Functions specific to option field classes with unsigned integer ranges */
 template <>
 struct CommonOptionWithIntegerSelectorFieldClassSpec<ConstUnsignedIntegerRangeSet> final
 {
@@ -1384,7 +1715,7 @@ struct CommonOptionWithIntegerSelectorFieldClassSpec<ConstUnsignedIntegerRangeSe
     }
 };
 
-// Functions specific to option field classes with signed ranges
+/* Functions specific to option field classes with signed ranges */
 template <>
 struct CommonOptionWithIntegerSelectorFieldClassSpec<ConstSignedIntegerRangeSet> final
 {
@@ -1396,27 +1727,21 @@ struct CommonOptionWithIntegerSelectorFieldClassSpec<ConstSignedIntegerRangeSet>
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT, typename RangeSetT>
 class CommonOptionWithIntegerSelectorFieldClass : public CommonOptionWithSelectorFieldClass<LibObjT>
 {
 private:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
-
     using typename CommonOptionWithSelectorFieldClass<
         LibObjT>::_ThisCommonOptionWithSelectorFieldClass;
 
-    using _ThisCommonOptionWithIntegerSelectorFieldClass =
-        CommonOptionWithIntegerSelectorFieldClass<LibObjT, RangeSetT>;
-
 public:
-    using Shared =
-        internal::SharedFieldClass<_ThisCommonOptionWithIntegerSelectorFieldClass, LibObjT>;
-
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonOptionWithIntegerSelectorFieldClass, LibObjT>;
     using RangeSet = RangeSetT;
 
-    explicit CommonOptionWithIntegerSelectorFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonOptionWithIntegerSelectorFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonOptionWithSelectorFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isOptionWithIntegerSelector());
@@ -1424,14 +1749,14 @@ public:
 
     template <typename OtherLibObjT>
     CommonOptionWithIntegerSelectorFieldClass(
-        const CommonOptionWithIntegerSelectorFieldClass<OtherLibObjT, RangeSetT>& fc) noexcept :
+        const CommonOptionWithIntegerSelectorFieldClass<OtherLibObjT, RangeSetT> fc) noexcept :
         _ThisCommonOptionWithSelectorFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonOptionWithIntegerSelectorFieldClass&
-    operator=(const CommonOptionWithIntegerSelectorFieldClass<OtherLibObjT, RangeSetT>& fc) noexcept
+    CommonOptionWithIntegerSelectorFieldClass
+    operator=(const CommonOptionWithIntegerSelectorFieldClass<OtherLibObjT, RangeSetT> fc) noexcept
     {
         _ThisCommonOptionWithSelectorFieldClass::operator=(fc);
         return *this;
@@ -1440,12 +1765,12 @@ public:
     RangeSet ranges() const noexcept
     {
         return RangeSet {internal::CommonOptionWithIntegerSelectorFieldClassSpec<RangeSetT>::ranges(
-            this->_libObjPtr())};
+            this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -1463,10 +1788,46 @@ using ConstOptionWithSignedIntegerSelectorFieldClass =
 
 namespace internal {
 
+struct OptionWithUnsignedIntegerSelectorFieldClassTypeDescr
+{
+    using Const = ConstOptionWithUnsignedIntegerSelectorFieldClass;
+    using NonConst = OptionWithUnsignedIntegerSelectorFieldClass;
+};
+
+template <>
+struct TypeDescr<OptionWithUnsignedIntegerSelectorFieldClass> :
+    public OptionWithUnsignedIntegerSelectorFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstOptionWithUnsignedIntegerSelectorFieldClass> :
+    public OptionWithUnsignedIntegerSelectorFieldClassTypeDescr
+{
+};
+
+struct OptionWithSignedIntegerSelectorFieldClassTypeDescr
+{
+    using Const = ConstOptionWithSignedIntegerSelectorFieldClass;
+    using NonConst = OptionWithSignedIntegerSelectorFieldClass;
+};
+
+template <>
+struct TypeDescr<OptionWithSignedIntegerSelectorFieldClass> :
+    public OptionWithSignedIntegerSelectorFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstOptionWithSignedIntegerSelectorFieldClass> :
+    public OptionWithSignedIntegerSelectorFieldClassTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonVariantFieldClassOptionSpec;
 
-// Functions specific to mutable variant field class options
+/* Functions specific to mutable variant field class options */
 template <>
 struct CommonVariantFieldClassOptionSpec<bt_field_class_variant_option> final
 {
@@ -1474,9 +1835,14 @@ struct CommonVariantFieldClassOptionSpec<bt_field_class_variant_option> final
     {
         return bt_field_class_variant_option_borrow_field_class(libObjPtr);
     }
+
+    static bt_value *userAttributes(bt_field_class_variant_option * const libObjPtr) noexcept
+    {
+        return bt_field_class_variant_option_borrow_user_attributes(libObjPtr);
+    }
 };
 
-// Functions specific to constant variant field class options
+/* Functions specific to constant variant field class options */
 template <>
 struct CommonVariantFieldClassOptionSpec<const bt_field_class_variant_option> final
 {
@@ -1485,55 +1851,77 @@ struct CommonVariantFieldClassOptionSpec<const bt_field_class_variant_option> fi
     {
         return bt_field_class_variant_option_borrow_field_class_const(libObjPtr);
     }
+
+    static const bt_value *
+    userAttributes(const bt_field_class_variant_option * const libObjPtr) noexcept
+    {
+        return bt_field_class_variant_option_borrow_user_attributes_const(libObjPtr);
+    }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class CommonVariantFieldClassOption : public internal::BorrowedObj<LibObjT>
+class CommonVariantFieldClassOption : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-
-    using _FieldClass =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstFieldClass, FieldClass>::type;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
+    using _FieldClass = internal::DepFc<LibObjT>;
 
 public:
-    explicit CommonVariantFieldClassOption(const _LibObjPtr libObjPtr) noexcept :
-        _ThisBorrowedObj {libObjPtr}
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using UserAttributes = internal::DepUserAttrs<LibObjT>;
+
+    explicit CommonVariantFieldClassOption(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonVariantFieldClassOption(const CommonVariantFieldClassOption<OtherLibObjT>& fc) noexcept :
-        _ThisBorrowedObj {fc}
+    CommonVariantFieldClassOption(const CommonVariantFieldClassOption<OtherLibObjT> fc) noexcept :
+        _ThisBorrowedObject {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonVariantFieldClassOption<LibObjT>&
-    operator=(const CommonVariantFieldClassOption<OtherLibObjT>& fc) noexcept
+    CommonVariantFieldClassOption
+    operator=(const CommonVariantFieldClassOption<OtherLibObjT> fc) noexcept
     {
-        _ThisBorrowedObj::operator=(fc);
+        _ThisBorrowedObject::operator=(fc);
         return *this;
     }
 
-    bpstd::string_view name() const noexcept
+    CommonVariantFieldClassOption<const bt_field_class_variant_option> asConst() const noexcept
     {
-        return bt_field_class_variant_option_get_name(this->_libObjPtr());
+        return CommonVariantFieldClassOption<const bt_field_class_variant_option> {*this};
     }
 
-    ConstFieldClass fieldClass() const noexcept
+    bt2c::CStringView name() const noexcept
     {
-        return ConstFieldClass {internal::CommonVariantFieldClassOptionSpec<
-            const bt_field_class_variant_option>::fieldClass(this->_libObjPtr())};
+        return bt_field_class_variant_option_get_name(this->libObjPtr());
     }
 
-    _FieldClass fieldClass() noexcept
+    _FieldClass fieldClass() const noexcept
     {
         return _FieldClass {
-            internal::CommonVariantFieldClassOptionSpec<LibObjT>::fieldClass(this->_libObjPtr())};
+            internal::CommonVariantFieldClassOptionSpec<LibObjT>::fieldClass(this->libObjPtr())};
+    }
+
+    template <typename LibValT>
+    CommonVariantFieldClassOption
+    userAttributes(const CommonMapValue<LibValT> userAttrs) const noexcept
+    {
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstVariantFieldClassOption`.");
+
+        bt_field_class_variant_option_set_user_attributes(this->libObjPtr(), userAttrs.libObjPtr());
+        return *this;
+    }
+
+    UserAttributes userAttributes() const noexcept
+    {
+        return UserAttributes {internal::CommonVariantFieldClassOptionSpec<LibObjT>::userAttributes(
+            this->libObjPtr())};
     }
 };
 
@@ -1544,10 +1932,26 @@ using ConstVariantFieldClassOption =
 
 namespace internal {
 
+struct VariantFieldClassOptionTypeDescr
+{
+    using Const = ConstVariantFieldClassOption;
+    using NonConst = VariantFieldClassOption;
+};
+
+template <>
+struct TypeDescr<VariantFieldClassOption> : public VariantFieldClassOptionTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstVariantFieldClassOption> : public VariantFieldClassOptionTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct ConstVariantWithIntegerSelectorFieldClassOptionSpec;
 
-// Functions specific to variant field class options with unsigned integer selector
+/* Functions specific to variant field class options with unsigned integer selector */
 template <>
 struct ConstVariantWithIntegerSelectorFieldClassOptionSpec<
     const bt_field_class_variant_with_selector_field_integer_unsigned_option>
@@ -1570,7 +1974,7 @@ struct ConstVariantWithIntegerSelectorFieldClassOptionSpec<
     }
 };
 
-// Functions specific to variant field class options with signed integer selector
+/* Functions specific to variant field class options with signed integer selector */
 template <>
 struct ConstVariantWithIntegerSelectorFieldClassOptionSpec<
     const bt_field_class_variant_with_selector_field_integer_signed_option>
@@ -1593,49 +1997,50 @@ struct ConstVariantWithIntegerSelectorFieldClassOptionSpec<
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class ConstVariantWithIntegerSelectorFieldClassOption : public internal::BorrowedObj<LibObjT>
+class ConstVariantWithIntegerSelectorFieldClassOption : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
     using _Spec = internal::ConstVariantWithIntegerSelectorFieldClassOptionSpec<LibObjT>;
 
 public:
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+
     using RangeSet = typename std::conditional<
         std::is_same<
             LibObjT,
             const bt_field_class_variant_with_selector_field_integer_unsigned_option>::value,
         ConstUnsignedIntegerRangeSet, ConstSignedIntegerRangeSet>::type;
 
-    explicit ConstVariantWithIntegerSelectorFieldClassOption(const _LibObjPtr libObjPtr) noexcept :
-        _ThisBorrowedObj {libObjPtr}
+    explicit ConstVariantWithIntegerSelectorFieldClassOption(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
     ConstVariantWithIntegerSelectorFieldClassOption(
-        const ConstVariantWithIntegerSelectorFieldClassOption<OtherLibObjT>& fc) noexcept :
-        _ThisBorrowedObj {fc}
+        const ConstVariantWithIntegerSelectorFieldClassOption<OtherLibObjT> fc) noexcept :
+        _ThisBorrowedObject {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    ConstVariantWithIntegerSelectorFieldClassOption<LibObjT>&
-    operator=(const ConstVariantWithIntegerSelectorFieldClassOption<OtherLibObjT>& fc) noexcept
+    ConstVariantWithIntegerSelectorFieldClassOption
+    operator=(const ConstVariantWithIntegerSelectorFieldClassOption<OtherLibObjT> fc) noexcept
     {
-        _ThisBorrowedObj::operator=(fc);
+        _ThisBorrowedObject::operator=(fc);
         return *this;
     }
 
     ConstVariantFieldClassOption asBaseOption() const noexcept
     {
-        return ConstVariantFieldClassOption {_Spec::asBaseOption(this->_libObjPtr())};
+        return ConstVariantFieldClassOption {_Spec::asBaseOption(this->libObjPtr())};
     }
 
-    bpstd::string_view name() const noexcept
+    bt2c::CStringView name() const noexcept
     {
         return this->asBaseOption().name();
     }
@@ -1647,7 +2052,7 @@ public:
 
     RangeSet ranges() const noexcept
     {
-        return RangeSet {_Spec::ranges(this->_libObjPtr())};
+        return RangeSet {_Spec::ranges(this->libObjPtr())};
     }
 };
 
@@ -1664,7 +2069,7 @@ namespace internal {
 template <typename LibObjT>
 struct CommonVariantFieldClassSpec;
 
-// Functions specific to mutable variant field classes
+/* Functions specific to mutable variant field classes */
 template <>
 struct CommonVariantFieldClassSpec<bt_field_class> final
 {
@@ -1681,7 +2086,7 @@ struct CommonVariantFieldClassSpec<bt_field_class> final
     }
 };
 
-// Functions specific to constant variant field classes
+/* Functions specific to constant variant field classes */
 template <>
 struct CommonVariantFieldClassSpec<const bt_field_class> final
 {
@@ -1698,7 +2103,7 @@ struct CommonVariantFieldClassSpec<const bt_field_class> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonVariantFieldClass : public CommonFieldClass<LibObjT>
@@ -1707,112 +2112,107 @@ private:
     using typename CommonFieldClass<LibObjT>::_ThisCommonFieldClass;
 
 protected:
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
     using _ThisCommonVariantFieldClass = CommonVariantFieldClass<LibObjT>;
 
 public:
-    using Shared = internal::SharedFieldClass<CommonVariantFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonVariantFieldClass<LibObjT>, LibObjT>;
+    using Iterator = BorrowedObjectIterator<CommonVariantFieldClass>;
 
     using Option =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstVariantFieldClassOption,
-                                  VariantFieldClassOption>::type;
+        internal::DepType<LibObjT, VariantFieldClassOption, ConstVariantFieldClassOption>;
 
-    explicit CommonVariantFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonVariantFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isVariant());
     }
 
     template <typename OtherLibObjT>
-    CommonVariantFieldClass(const CommonVariantFieldClass<OtherLibObjT>& fc) noexcept :
+    CommonVariantFieldClass(const CommonVariantFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonVariantFieldClass&
-    operator=(const CommonVariantFieldClass<OtherLibObjT>& fc) noexcept
+    CommonVariantFieldClass operator=(const CommonVariantFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonFieldClass::operator=(fc);
         return *this;
     }
 
-    std::uint64_t size() const noexcept
-    {
-        return bt_field_class_variant_get_option_count(this->_libObjPtr());
-    }
-
-    ConstVariantFieldClassOption operator[](const std::uint64_t index) const noexcept
+    CommonVariantFieldClass<const bt_field_class> asConst() const noexcept
     {
-        return ConstVariantFieldClassOption {
-            internal::CommonVariantFieldClassSpec<const bt_field_class>::optionByIndex(
-                this->_libObjPtr(), index)};
+        return CommonVariantFieldClass<const bt_field_class> {*this};
     }
 
-    Option operator[](const std::uint64_t index) noexcept
+    std::uint64_t length() const noexcept
     {
-        return Option {internal::CommonVariantFieldClassSpec<LibObjT>::optionByIndex(
-            this->_libObjPtr(), index)};
+        return bt_field_class_variant_get_option_count(this->libObjPtr());
     }
 
-    nonstd::optional<ConstVariantFieldClassOption>
-    operator[](const char * const name) const noexcept
+    Iterator begin() const noexcept
     {
-        const auto libObjPtr =
-            internal::CommonVariantFieldClassSpec<const bt_field_class>::optionByName(
-                this->_libObjPtr(), name);
-
-        if (libObjPtr) {
-            return ConstVariantFieldClassOption {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return Iterator {*this, 0};
     }
 
-    nonstd::optional<ConstVariantFieldClassOption>
-    operator[](const std::string& name) const noexcept
+    Iterator end() const noexcept
     {
-        return (*this)[name.data()];
+        return Iterator {*this, this->length()};
     }
 
-    nonstd::optional<Option> operator[](const char * const name) noexcept
+    Option operator[](const std::uint64_t index) const noexcept
     {
-        const auto libObjPtr =
-            internal::CommonVariantFieldClassSpec<LibObjT>::optionByName(this->_libObjPtr(), name);
-
-        if (libObjPtr) {
-            return Option {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return Option {internal::CommonVariantFieldClassSpec<LibObjT>::optionByIndex(
+            this->libObjPtr(), index)};
     }
 
-    nonstd::optional<Option> operator[](const std::string& name) noexcept
+    OptionalBorrowedObject<Option> operator[](const bt2c::CStringView name) const noexcept
     {
-        return (*this)[name.data()];
+        return internal::CommonVariantFieldClassSpec<LibObjT>::optionByName(this->libObjPtr(),
+                                                                            name);
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using VariantFieldClass = CommonVariantFieldClass<bt_field_class>;
 using ConstVariantFieldClass = CommonVariantFieldClass<const bt_field_class>;
 
+namespace internal {
+
+struct VariantFieldClassTypeDescr
+{
+    using Const = ConstVariantFieldClass;
+    using NonConst = VariantFieldClass;
+};
+
+template <>
+struct TypeDescr<VariantFieldClass> : public VariantFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstVariantFieldClass> : public VariantFieldClassTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonVariantWithoutSelectorFieldClass : public CommonVariantFieldClass<LibObjT>
 {
 private:
     using typename CommonVariantFieldClass<LibObjT>::_ThisCommonVariantFieldClass;
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
 
 public:
-    using Shared =
-        internal::SharedFieldClass<CommonVariantWithoutSelectorFieldClass<LibObjT>, LibObjT>;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonVariantWithoutSelectorFieldClass<LibObjT>, LibObjT>;
 
-    explicit CommonVariantWithoutSelectorFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonVariantWithoutSelectorFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonVariantFieldClass {libObjPtr}
     {
         BT_ASSERT_DBG(this->isVariantWithoutSelector());
@@ -1820,40 +2220,56 @@ public:
 
     template <typename OtherLibObjT>
     CommonVariantWithoutSelectorFieldClass(
-        const CommonVariantWithoutSelectorFieldClass<OtherLibObjT>& fc) noexcept :
+        const CommonVariantWithoutSelectorFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonVariantFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonVariantWithoutSelectorFieldClass<LibObjT>&
-    operator=(const CommonVariantWithoutSelectorFieldClass<OtherLibObjT>& fc) noexcept
+    CommonVariantWithoutSelectorFieldClass
+    operator=(const CommonVariantWithoutSelectorFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonVariantFieldClass::operator=(fc);
         return *this;
     }
 
-    void appendOption(const char * const name, const FieldClass& fc)
+    CommonVariantWithoutSelectorFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonVariantWithoutSelectorFieldClass<const bt_field_class> {*this};
+    }
+
+    CommonVariantWithoutSelectorFieldClass appendOption(const char * const name,
+                                                        const FieldClass fc) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstVariantWithoutSelectorFieldClass`.");
 
         const auto status = bt_field_class_variant_without_selector_append_option(
-            this->_libObjPtr(), name, fc._libObjPtr());
+            this->libObjPtr(), name, fc.libObjPtr());
 
         if (status ==
             BT_FIELD_CLASS_VARIANT_WITHOUT_SELECTOR_FIELD_APPEND_OPTION_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
+
+        return *this;
     }
 
-    void appendOption(const std::string& name, const FieldClass& fc)
+    CommonVariantWithoutSelectorFieldClass appendOption(const bt2c::CStringView name,
+                                                        const FieldClass fc) const
     {
-        this->appendOption(name.data(), fc);
+        return this->appendOption(name.data(), fc);
+    }
+
+    CommonVariantWithoutSelectorFieldClass appendOption(const bt2s::optional<std::string>& name,
+                                                        const FieldClass fc) const
+    {
+        return this->appendOption(name ? name->data() : nullptr, fc);
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -1863,10 +2279,28 @@ using ConstVariantWithoutSelectorFieldClass =
 
 namespace internal {
 
+struct VariantWithoutSelectorFieldClassTypeDescr
+{
+    using Const = ConstVariantWithoutSelectorFieldClass;
+    using NonConst = VariantWithoutSelectorFieldClass;
+};
+
+template <>
+struct TypeDescr<VariantWithoutSelectorFieldClass> :
+    public VariantWithoutSelectorFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstVariantWithoutSelectorFieldClass> :
+    public VariantWithoutSelectorFieldClassTypeDescr
+{
+};
+
 template <typename OptionT>
 struct CommonVariantWithIntegerSelectorFieldClassSpec;
 
-// Functions specific to variant field classes with unsigned integer selector
+/* Functions specific to variant field classes with unsigned integer selector */
 template <>
 struct CommonVariantWithIntegerSelectorFieldClassSpec<
     ConstVariantWithUnsignedIntegerSelectorFieldClassOption>
@@ -1896,7 +2330,7 @@ struct CommonVariantWithIntegerSelectorFieldClassSpec<
     }
 };
 
-// Functions specific to variant field classes with signed integer selector
+/* Functions specific to variant field classes with signed integer selector */
 template <>
 struct CommonVariantWithIntegerSelectorFieldClassSpec<
     ConstVariantWithSignedIntegerSelectorFieldClassOption>
@@ -1926,97 +2360,159 @@ struct CommonVariantWithIntegerSelectorFieldClassSpec<
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
-template <typename LibObjT, typename OptionT>
-class CommonVariantWithIntegerSelectorFieldClass : public CommonVariantFieldClass<LibObjT>
+template <typename LibObjT>
+class CommonVariantWithSelectorFieldClass : public CommonVariantFieldClass<LibObjT>
 {
 private:
     using typename CommonVariantFieldClass<LibObjT>::_ThisCommonVariantFieldClass;
-    using typename CommonFieldClass<LibObjT>::_LibObjPtr;
-
-    using _ThisCommonVariantWithIntegerSelectorFieldClass =
-        CommonVariantWithIntegerSelectorFieldClass<LibObjT, OptionT>;
 
-    using _Spec = internal::CommonVariantWithIntegerSelectorFieldClassSpec<OptionT>;
+protected:
+    using _ThisCommonVariantWithSelectorFieldClass = CommonVariantWithSelectorFieldClass<LibObjT>;
 
 public:
-    using Shared =
-        internal::SharedFieldClass<_ThisCommonVariantWithIntegerSelectorFieldClass, LibObjT>;
-
-    using Option = OptionT;
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<_ThisCommonVariantWithSelectorFieldClass, LibObjT>;
 
-    explicit CommonVariantWithIntegerSelectorFieldClass(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonVariantWithSelectorFieldClass(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonVariantFieldClass {libObjPtr}
     {
-        BT_ASSERT_DBG(this->isVariant());
+        BT_ASSERT_DBG(this->isVariantWithSelector());
     }
 
     template <typename OtherLibObjT>
-    CommonVariantWithIntegerSelectorFieldClass(
-        const CommonVariantWithIntegerSelectorFieldClass<OtherLibObjT, OptionT>& fc) noexcept :
+    CommonVariantWithSelectorFieldClass(
+        const CommonVariantWithSelectorFieldClass<OtherLibObjT> fc) noexcept :
         _ThisCommonVariantFieldClass {fc}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonVariantWithIntegerSelectorFieldClass&
-    operator=(const CommonVariantWithIntegerSelectorFieldClass<OtherLibObjT, OptionT>& fc) noexcept
+    CommonVariantWithSelectorFieldClass
+    operator=(const CommonVariantWithSelectorFieldClass<OtherLibObjT> fc) noexcept
     {
         _ThisCommonVariantFieldClass::operator=(fc);
         return *this;
     }
 
+    CommonVariantWithSelectorFieldClass<const bt_field_class> asConst() const noexcept
+    {
+        return CommonVariantWithSelectorFieldClass<const bt_field_class> {*this};
+    }
+
     ConstFieldPath selectorFieldPath() const noexcept
     {
         return ConstFieldPath {
-            bt_field_class_option_with_selector_field_borrow_selector_field_path_const(
-                this->_libObjPtr())};
+            bt_field_class_variant_with_selector_field_borrow_selector_field_path_const(
+                this->libObjPtr())};
     }
 
-    Option operator[](const std::uint64_t index) const noexcept
+    Shared shared() const noexcept
+    {
+        return Shared::createWithRef(*this);
+    }
+};
+
+using VariantWithSelectorFieldClass = CommonVariantWithSelectorFieldClass<bt_field_class>;
+using ConstVariantWithSelectorFieldClass =
+    CommonVariantWithSelectorFieldClass<const bt_field_class>;
+
+template <typename LibObjT, typename OptionT>
+class CommonVariantWithIntegerSelectorFieldClass :
+    public CommonVariantWithSelectorFieldClass<LibObjT>
+{
+private:
+    using typename CommonVariantWithSelectorFieldClass<
+        LibObjT>::_ThisCommonVariantWithSelectorFieldClass;
+
+    using _Spec = internal::CommonVariantWithIntegerSelectorFieldClassSpec<OptionT>;
+
+public:
+    using typename CommonFieldClass<LibObjT>::LibObjPtr;
+    using Shared = SharedFieldClass<CommonVariantWithIntegerSelectorFieldClass, LibObjT>;
+    using Option = OptionT;
+
+    using Iterator =
+        BorrowedObjectIterator<CommonVariantWithIntegerSelectorFieldClass<LibObjT, Option>>;
+
+    explicit CommonVariantWithIntegerSelectorFieldClass(const LibObjPtr libObjPtr) noexcept :
+        _ThisCommonVariantWithSelectorFieldClass {libObjPtr}
     {
-        return Option {_Spec::optionByIndex(this->_libObjPtr(), index)};
+        BT_ASSERT_DBG(this->isVariantWithIntegerSelector());
     }
 
-    nonstd::optional<Option> operator[](const char * const name) const noexcept
+    template <typename OtherLibObjT>
+    CommonVariantWithIntegerSelectorFieldClass(
+        const CommonVariantWithIntegerSelectorFieldClass<OtherLibObjT, OptionT> fc) noexcept :
+        _ThisCommonVariantWithSelectorFieldClass {fc}
     {
-        const auto libObjPtr = _Spec::optionByName(this->_libObjPtr(), name);
+    }
 
-        if (libObjPtr) {
-            return Option {libObjPtr};
-        }
+    template <typename OtherLibObjT>
+    CommonVariantWithIntegerSelectorFieldClass
+    operator=(const CommonVariantWithIntegerSelectorFieldClass<OtherLibObjT, OptionT> fc) noexcept
+    {
+        _ThisCommonVariantWithSelectorFieldClass::operator=(fc);
+        return *this;
+    }
 
-        return nonstd::nullopt;
+    Option operator[](const std::uint64_t index) const noexcept
+    {
+        return Option {_Spec::optionByIndex(this->libObjPtr(), index)};
     }
 
-    nonstd::optional<Option> operator[](const std::string& name) const noexcept
+    OptionalBorrowedObject<Option> operator[](const bt2c::CStringView name) const noexcept
     {
-        return (*this)[name.data()];
+        return _Spec::optionByName(this->libObjPtr(), name);
     }
 
-    void appendOption(const char * const name, const FieldClass& fc,
-                      const typename Option::RangeSet& ranges)
+    CommonVariantWithIntegerSelectorFieldClass
+    appendOption(const char * const name, const FieldClass fc,
+                 const typename Option::RangeSet ranges) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(
+            !std::is_const<LibObjT>::value,
+            "Not available with `bt2::ConstVariantWithUnsignedIntegerSelectorFieldClass`.");
 
         const auto status =
-            _Spec::appendOption(this->_libObjPtr(), name, fc._libObjPtr(), ranges._libObjPtr());
+            _Spec::appendOption(this->libObjPtr(), name, fc.libObjPtr(), ranges.libObjPtr());
 
         if (status ==
             BT_FIELD_CLASS_VARIANT_WITH_SELECTOR_FIELD_APPEND_OPTION_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
+
+        return *this;
     }
 
-    void appendOption(const std::string& name, const FieldClass& fc)
+    CommonVariantWithIntegerSelectorFieldClass
+    appendOption(const bt2c::CStringView name, const FieldClass fc,
+                 const typename Option::RangeSet ranges) const
     {
-        this->appendOption(name.data(), fc);
+        return this->appendOption(name.data(), fc, ranges);
+    }
+
+    CommonVariantWithIntegerSelectorFieldClass
+    appendOption(const bt2s::optional<std::string>& name, const FieldClass fc,
+                 const typename Option::RangeSet ranges) const
+    {
+        return this->appendOption(name ? name->data() : nullptr, fc, ranges);
+    }
+
+    Iterator begin() const noexcept
+    {
+        return Iterator {*this, 0};
+    }
+
+    Iterator end() const noexcept
+    {
+        return Iterator {*this, this->length()};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -2033,18 +2529,62 @@ using VariantWithSignedIntegerSelectorFieldClass = CommonVariantWithIntegerSelec
 using ConstVariantWithSignedIntegerSelectorFieldClass = CommonVariantWithIntegerSelectorFieldClass<
     const bt_field_class, ConstVariantWithSignedIntegerSelectorFieldClassOption>;
 
+namespace internal {
+
+struct VariantWithUnsignedIntegerSelectorFieldClassTypeDescr
+{
+    using Const = ConstVariantWithUnsignedIntegerSelectorFieldClass;
+    using NonConst = VariantWithUnsignedIntegerSelectorFieldClass;
+};
+
+template <>
+struct TypeDescr<VariantWithUnsignedIntegerSelectorFieldClass> :
+    public VariantWithUnsignedIntegerSelectorFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstVariantWithUnsignedIntegerSelectorFieldClass> :
+    public VariantWithUnsignedIntegerSelectorFieldClassTypeDescr
+{
+};
+
+struct VariantWithSignedIntegerSelectorFieldClassTypeDescr
+{
+    using Const = ConstVariantWithSignedIntegerSelectorFieldClass;
+    using NonConst = VariantWithSignedIntegerSelectorFieldClass;
+};
+
+template <>
+struct TypeDescr<VariantWithSignedIntegerSelectorFieldClass> :
+    public VariantWithSignedIntegerSelectorFieldClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstVariantWithSignedIntegerSelectorFieldClass> :
+    public VariantWithSignedIntegerSelectorFieldClassTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 CommonBitArrayFieldClass<LibObjT> CommonFieldClass<LibObjT>::asBitArray() const noexcept
 {
-    BT_ASSERT_DBG(this->isBitArray());
-    return CommonBitArrayFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonBitArrayFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonIntegerFieldClass<LibObjT> CommonFieldClass<LibObjT>::asInteger() const noexcept
 {
-    BT_ASSERT_DBG(this->isInteger());
-    return CommonIntegerFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonIntegerFieldClass<LibObjT> {this->libObjPtr()};
+}
+
+template <typename LibObjT>
+CommonBaseEnumerationFieldClass<LibObjT> CommonFieldClass<LibObjT>::asEnumeration() const noexcept
+{
+    return CommonBaseEnumerationFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
@@ -2053,7 +2593,7 @@ CommonFieldClass<LibObjT>::asUnsignedEnumeration() const noexcept
 {
     BT_ASSERT_DBG(this->isUnsignedEnumeration());
     return CommonEnumerationFieldClass<LibObjT, ConstUnsignedEnumerationFieldClassMapping> {
-        this->_libObjPtr()};
+        this->libObjPtr()};
 }
 
 template <typename LibObjT>
@@ -2062,59 +2602,52 @@ CommonFieldClass<LibObjT>::asSignedEnumeration() const noexcept
 {
     BT_ASSERT_DBG(this->isSignedEnumeration());
     return CommonEnumerationFieldClass<LibObjT, ConstSignedEnumerationFieldClassMapping> {
-        this->_libObjPtr()};
+        this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonStructureFieldClass<LibObjT> CommonFieldClass<LibObjT>::asStructure() const noexcept
 {
-    BT_ASSERT_DBG(this->isStructure());
-    return CommonStructureFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonStructureFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonArrayFieldClass<LibObjT> CommonFieldClass<LibObjT>::asArray() const noexcept
 {
-    BT_ASSERT_DBG(this->isArray());
-    return CommonArrayFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonArrayFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonStaticArrayFieldClass<LibObjT> CommonFieldClass<LibObjT>::asStaticArray() const noexcept
 {
-    BT_ASSERT_DBG(this->isStaticArray());
-    return CommonStaticArrayFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonStaticArrayFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonDynamicArrayWithLengthFieldClass<LibObjT>
 CommonFieldClass<LibObjT>::asDynamicArrayWithLength() const noexcept
 {
-    BT_ASSERT_DBG(this->isDynamicArrayWithLength());
-    return CommonDynamicArrayWithLengthFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonDynamicArrayWithLengthFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonOptionFieldClass<LibObjT> CommonFieldClass<LibObjT>::asOption() const noexcept
 {
-    BT_ASSERT_DBG(this->isOption());
-    return CommonOptionFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonOptionFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonOptionWithSelectorFieldClass<LibObjT>
 CommonFieldClass<LibObjT>::asOptionWithSelector() const noexcept
 {
-    BT_ASSERT_DBG(this->isOptionWithSelector());
-    return CommonOptionWithSelectorFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonOptionWithSelectorFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonOptionWithBoolSelectorFieldClass<LibObjT>
 CommonFieldClass<LibObjT>::asOptionWithBoolSelector() const noexcept
 {
-    BT_ASSERT_DBG(this->isOptionWithBoolSelector());
-    return CommonOptionWithBoolSelectorFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonOptionWithBoolSelectorFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
@@ -2123,7 +2656,7 @@ CommonFieldClass<LibObjT>::asOptionWithUnsignedIntegerSelector() const noexcept
 {
     BT_ASSERT_DBG(this->isOptionWithUnsignedIntegerSelector());
     return CommonOptionWithIntegerSelectorFieldClass<LibObjT, ConstUnsignedIntegerRangeSet> {
-        this->_libObjPtr()};
+        this->libObjPtr()};
 }
 
 template <typename LibObjT>
@@ -2132,22 +2665,27 @@ CommonFieldClass<LibObjT>::asOptionWithSignedIntegerSelector() const noexcept
 {
     BT_ASSERT_DBG(this->isOptionWithSignedIntegerSelector());
     return CommonOptionWithIntegerSelectorFieldClass<LibObjT, ConstSignedIntegerRangeSet> {
-        this->_libObjPtr()};
+        this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonVariantFieldClass<LibObjT> CommonFieldClass<LibObjT>::asVariant() const noexcept
 {
-    BT_ASSERT_DBG(this->isVariant());
-    return CommonVariantFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonVariantFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonVariantWithoutSelectorFieldClass<LibObjT>
 CommonFieldClass<LibObjT>::asVariantWithoutSelector() const noexcept
 {
-    BT_ASSERT_DBG(this->isVariantWithoutSelector());
-    return CommonVariantWithoutSelectorFieldClass<LibObjT> {this->_libObjPtr()};
+    return CommonVariantWithoutSelectorFieldClass<LibObjT> {this->libObjPtr()};
+}
+
+template <typename LibObjT>
+CommonVariantWithSelectorFieldClass<LibObjT>
+CommonFieldClass<LibObjT>::asVariantWithSelector() const noexcept
+{
+    return CommonVariantWithSelectorFieldClass<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
@@ -2157,7 +2695,7 @@ CommonFieldClass<LibObjT>::asVariantWithUnsignedIntegerSelector() const noexcept
 {
     BT_ASSERT_DBG(this->isVariantWithUnsignedIntegerSelector());
     return CommonVariantWithIntegerSelectorFieldClass<
-        LibObjT, ConstVariantWithUnsignedIntegerSelectorFieldClassOption> {this->_libObjPtr()};
+        LibObjT, ConstVariantWithUnsignedIntegerSelectorFieldClassOption> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
@@ -2167,9 +2705,9 @@ CommonFieldClass<LibObjT>::asVariantWithSignedIntegerSelector() const noexcept
 {
     BT_ASSERT_DBG(this->isVariantWithSignedIntegerSelector());
     return CommonVariantWithIntegerSelectorFieldClass<
-        LibObjT, ConstVariantWithSignedIntegerSelectorFieldClassOption> {this->_libObjPtr()};
+        LibObjT, ConstVariantWithSignedIntegerSelectorFieldClassOption> {this->libObjPtr()};
 }
 
-} // namespace bt2
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_FIELD_CLASS_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_FIELD_CLASS_HPP */
index b91d91135ba95aed791ba64853a6d241abf327a5..ed44ad4bda9a25a099c6645dd5f2a4020d178e06 100644 (file)
@@ -8,10 +8,15 @@
 #define BABELTRACE_CPP_COMMON_BT2_FIELD_PATH_HPP
 
 #include <cstdint>
+
 #include <babeltrace2/babeltrace.h>
 
 #include "common/assert.h"
-#include "internal/borrowed-obj.hpp"
+#include "cpp-common/vendor/wise-enum/wise_enum.h"
+
+#include "borrowed-object-iterator.hpp"
+#include "borrowed-object.hpp"
+#include "shared-object.hpp"
 
 namespace bt2 {
 
@@ -19,26 +24,17 @@ class ConstIndexFieldPathItem;
 
 enum class FieldPathItemType
 {
-    INDEX = BT_FIELD_PATH_ITEM_TYPE_INDEX,
-    CURRENT_ARRAY_ELEMENT = BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT,
-    CURRENT_OPTION_CONTENT = BT_FIELD_PATH_ITEM_TYPE_CURRENT_OPTION_CONTENT,
+    Index = BT_FIELD_PATH_ITEM_TYPE_INDEX,
+    CurrentArrayElement = BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT,
+    CurrentOptionContent = BT_FIELD_PATH_ITEM_TYPE_CURRENT_OPTION_CONTENT,
 };
 
-class ConstFieldPathItem : public internal::BorrowedObj<const bt_field_path_item>
+class ConstFieldPathItem : public BorrowedObject<const bt_field_path_item>
 {
 public:
-    explicit ConstFieldPathItem(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
-    {
-    }
-
-    ConstFieldPathItem(const ConstFieldPathItem& fpItem) noexcept : _ThisBorrowedObj {fpItem}
-    {
-    }
-
-    ConstFieldPathItem& operator=(const ConstFieldPathItem& fpItem) noexcept
+    explicit ConstFieldPathItem(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
     {
-        _ThisBorrowedObj::operator=(fpItem);
-        return *this;
     }
 
     FieldPathItemType type() const noexcept
@@ -66,109 +62,99 @@ public:
 private:
     bt_field_path_item_type _libType() const noexcept
     {
-        return bt_field_path_item_get_type(this->_libObjPtr());
+        return bt_field_path_item_get_type(this->libObjPtr());
     }
 };
 
 class ConstIndexFieldPathItem final : public ConstFieldPathItem
 {
 public:
-    explicit ConstIndexFieldPathItem(const _LibObjPtr libObjPtr) noexcept :
+    explicit ConstIndexFieldPathItem(const LibObjPtr libObjPtr) noexcept :
         ConstFieldPathItem {libObjPtr}
     {
         BT_ASSERT_DBG(this->isIndex());
     }
 
-    ConstIndexFieldPathItem(const ConstIndexFieldPathItem& fpItem) noexcept :
-        ConstFieldPathItem {fpItem}
-    {
-    }
-
-    ConstIndexFieldPathItem& operator=(const ConstIndexFieldPathItem& fpItem) noexcept
-    {
-        ConstFieldPathItem::operator=(fpItem);
-        return *this;
-    }
-
     std::uint64_t index() const noexcept
     {
-        return bt_field_path_item_index_get_index(this->_libObjPtr());
+        return bt_field_path_item_index_get_index(this->libObjPtr());
     }
 };
 
 inline ConstIndexFieldPathItem ConstFieldPathItem::asIndex() const noexcept
 {
-    BT_ASSERT_DBG(this->isIndex());
-    return ConstIndexFieldPathItem {this->_libObjPtr()};
+    return ConstIndexFieldPathItem {this->libObjPtr()};
 }
 
 namespace internal {
 
 struct FieldPathRefFuncs final
 {
-    static void get(const bt_field_path * const libObjPtr)
+    static void get(const bt_field_path * const libObjPtr) noexcept
     {
         bt_field_path_get_ref(libObjPtr);
     }
 
-    static void put(const bt_field_path * const libObjPtr)
+    static void put(const bt_field_path * const libObjPtr) noexcept
     {
         bt_field_path_put_ref(libObjPtr);
     }
 };
 
-} // namespace internal
+} /* namespace internal */
+
+/* clang-format off */
 
-class ConstFieldPath final : public internal::BorrowedObj<const bt_field_path>
+WISE_ENUM_CLASS(FieldPathScope,
+    (PacketContext, BT_FIELD_PATH_SCOPE_PACKET_CONTEXT),
+    (EventCommonContext, BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT),
+    (EventSpecificContext, BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT),
+    (EventPayload, BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD))
+
+/* clang-format on */
+
+class ConstFieldPath final : public BorrowedObject<const bt_field_path>
 {
 public:
-    using Shared =
-        internal::SharedObj<ConstFieldPath, const bt_field_path, internal::FieldPathRefFuncs>;
+    using Shared = SharedObject<ConstFieldPath, const bt_field_path, internal::FieldPathRefFuncs>;
+    using Iterator = BorrowedObjectIterator<ConstFieldPath>;
 
-    enum class Scope
-    {
-        PACKET_CONTEXT = BT_FIELD_PATH_SCOPE_PACKET_CONTEXT,
-        EVENT_COMMON_CONTEXT = BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT,
-        EVENT_SPECIFIC_CONTEXT = BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT,
-        EVENT_PAYLOAD = BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD,
-    };
-
-    explicit ConstFieldPath(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit ConstFieldPath(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
-    ConstFieldPath(const ConstFieldPath& clkSnapshot) noexcept : _ThisBorrowedObj {clkSnapshot}
+    FieldPathScope rootScope() const noexcept
     {
+        return static_cast<FieldPathScope>(bt_field_path_get_root_scope(this->libObjPtr()));
     }
 
-    ConstFieldPath& operator=(const ConstFieldPath& clkSnapshot) noexcept
+    std::uint64_t length() const noexcept
     {
-        _ThisBorrowedObj::operator=(clkSnapshot);
-        return *this;
+        return bt_field_path_get_item_count(this->libObjPtr());
     }
 
-    Scope rootScope() const noexcept
+    ConstFieldPathItem operator[](const std::uint64_t index) const noexcept
     {
-        return static_cast<Scope>(bt_field_path_get_root_scope(this->_libObjPtr()));
+        return ConstFieldPathItem {
+            bt_field_path_borrow_item_by_index_const(this->libObjPtr(), index)};
     }
 
-    std::uint64_t size() const noexcept
+    Iterator begin() const noexcept
     {
-        return bt_field_path_get_item_count(this->_libObjPtr());
+        return Iterator {*this, 0};
     }
 
-    ConstFieldPathItem operator[](const std::uint64_t index) const noexcept
+    Iterator end() const noexcept
     {
-        return ConstFieldPathItem {
-            bt_field_path_borrow_item_by_index_const(this->_libObjPtr(), index)};
+        return Iterator {*this, this->length()};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
-} // namespace bt2
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_FIELD_PATH_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_FIELD_PATH_HPP */
index c62368e5d2c45fb8cd2df9ca4ccafffbcb4ba6cc..05e1c24422125ee4364c35d9e07ca2699dbc1052 100644 (file)
@@ -7,15 +7,19 @@
 #ifndef BABELTRACE_CPP_COMMON_BT2_FIELD_HPP
 #define BABELTRACE_CPP_COMMON_BT2_FIELD_HPP
 
-#include <type_traits>
 #include <cstdint>
+#include <type_traits>
+
 #include <babeltrace2/babeltrace.h>
 
 #include "common/assert.h"
-#include "internal/borrowed-obj.hpp"
-#include "cpp-common/optional.hpp"
-#include "cpp-common/string_view.hpp"
+#include "cpp-common/bt2c/c-string-view.hpp"
+
+#include "borrowed-object.hpp"
 #include "field-class.hpp"
+#include "internal/utils.hpp"
+#include "optional-borrowed-object.hpp"
+#include "raw-value-proxy.hpp"
 
 namespace bt2 {
 
@@ -66,7 +70,7 @@ namespace internal {
 template <typename LibObjT>
 struct CommonFieldSpec;
 
-// Functions specific to mutable fields
+/* Functions specific to mutable fields */
 template <>
 struct CommonFieldSpec<bt_field> final
 {
@@ -76,7 +80,7 @@ struct CommonFieldSpec<bt_field> final
     }
 };
 
-// Functions specific to constant fields
+/* Functions specific to constant fields */
 template <>
 struct CommonFieldSpec<const bt_field> final
 {
@@ -86,51 +90,50 @@ struct CommonFieldSpec<const bt_field> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class CommonField : public internal::BorrowedObj<LibObjT>
+class CommonField : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
 
 protected:
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
     using _ThisCommonField = CommonField<LibObjT>;
 
 public:
-    using Class =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstFieldClass, FieldClass>::type;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Class = internal::DepFc<LibObjT>;
 
-    explicit CommonField(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonField(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonField(const CommonField<OtherLibObjT>& val) noexcept : _ThisBorrowedObj {val}
+    CommonField(const CommonField<OtherLibObjT> val) noexcept : _ThisBorrowedObject {val}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonField& operator=(const CommonField<OtherLibObjT>& val) noexcept
+    _ThisCommonField operator=(const CommonField<OtherLibObjT> val) noexcept
     {
-        _ThisBorrowedObj::operator=(val);
+        _ThisBorrowedObject::operator=(val);
         return *this;
     }
 
-    FieldClassType classType() const noexcept
+    CommonField<const bt_field> asConst() const noexcept
     {
-        return static_cast<FieldClassType>(bt_field_get_class_type(this->_libObjPtr()));
+        return CommonField<const bt_field> {*this};
     }
 
-    ConstFieldClass cls() const noexcept
+    FieldClassType classType() const noexcept
     {
-        return ConstFieldClass {internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+        return static_cast<FieldClassType>(bt_field_get_class_type(this->libObjPtr()));
     }
 
-    Class cls() noexcept
+    Class cls() const noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
     bool isBool() const noexcept
@@ -203,6 +206,12 @@ public:
         return this->cls().isVariant();
     }
 
+    template <typename FieldT>
+    FieldT as() const noexcept
+    {
+        return FieldT {this->libObjPtr()};
+    }
+
     CommonBoolField<LibObjT> asBool() const noexcept;
     CommonBitArrayField<LibObjT> asBitArray() const noexcept;
     CommonUnsignedIntegerField<LibObjT> asUnsignedInteger() const noexcept;
@@ -222,106 +231,156 @@ public:
 using Field = CommonField<bt_field>;
 using ConstField = CommonField<const bt_field>;
 
+namespace internal {
+
+struct FieldTypeDescr
+{
+    using Const = ConstField;
+    using NonConst = Field;
+};
+
+template <>
+struct TypeDescr<Field> : public FieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstField> : public FieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonBoolField final : public CommonField<LibObjT>
 {
 private:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using typename CommonField<LibObjT>::_ThisCommonField;
 
 public:
+    using typename CommonField<LibObjT>::LibObjPtr;
     using Value = bool;
 
-    explicit CommonBoolField(const _LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
+    explicit CommonBoolField(const LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isBool());
     }
 
     template <typename OtherLibObjT>
-    CommonBoolField(const CommonBoolField<OtherLibObjT>& val) noexcept : _ThisCommonField {val}
+    CommonBoolField(const CommonBoolField<OtherLibObjT> val) noexcept : _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonBoolField<LibObjT>& operator=(const CommonBoolField<OtherLibObjT>& val) noexcept
+    CommonBoolField<LibObjT> operator=(const CommonBoolField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    CommonBoolField<LibObjT>& operator=(const Value val) noexcept
+    CommonBoolField<const bt_field> asConst() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return CommonBoolField<const bt_field> {*this};
+    }
 
-        bt_field_bool_set_value(this->_libObjPtr(), static_cast<bt_bool>(val));
-        return *this;
+    RawValueProxy<CommonBoolField> operator*() const noexcept
+    {
+        return RawValueProxy<CommonBoolField> {*this};
     }
 
-    Value value() const noexcept
+    CommonBoolField value(const Value val) const noexcept
     {
-        return static_cast<Value>(bt_field_bool_get_value(this->_libObjPtr()));
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstBoolField`.");
+
+        bt_field_bool_set_value(this->libObjPtr(), static_cast<bt_bool>(val));
+        return *this;
     }
 
-    operator Value() const noexcept
+    Value value() const noexcept
     {
-        return this->value();
+        return static_cast<Value>(bt_field_bool_get_value(this->libObjPtr()));
     }
 };
 
 using BoolField = CommonBoolField<bt_field>;
 using ConstBoolField = CommonBoolField<const bt_field>;
 
+namespace internal {
+
+struct BoolFieldTypeDescr
+{
+    using Const = ConstBoolField;
+    using NonConst = BoolField;
+};
+
+template <>
+struct TypeDescr<BoolField> : public BoolFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstBoolField> : public BoolFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonBitArrayField final : public CommonField<LibObjT>
 {
 private:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using typename CommonField<LibObjT>::_ThisCommonField;
 
 public:
-    using Class = typename std::conditional<std::is_const<LibObjT>::value, ConstBitArrayFieldClass,
-                                            BitArrayFieldClass>::type;
+    using typename CommonField<LibObjT>::LibObjPtr;
+    using Class = internal::DepType<LibObjT, BitArrayFieldClass, ConstBitArrayFieldClass>;
 
-    explicit CommonBitArrayField(const _LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
+    explicit CommonBitArrayField(const LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isBitArray());
     }
 
     template <typename OtherLibObjT>
-    CommonBitArrayField(const CommonBitArrayField<OtherLibObjT>& val) noexcept :
+    CommonBitArrayField(const CommonBitArrayField<OtherLibObjT> val) noexcept :
         _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonBitArrayField<LibObjT>& operator=(const CommonBitArrayField<OtherLibObjT>& val) noexcept
+    CommonBitArrayField<LibObjT> operator=(const CommonBitArrayField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
+    CommonBitArrayField<const bt_field> asConst() const noexcept
+    {
+        return CommonBitArrayField<const bt_field> {*this};
+    }
+
     ConstBitArrayFieldClass cls() const noexcept
     {
         return ConstBitArrayFieldClass {
-            internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+            internal::CommonFieldSpec<const bt_field>::cls(this->libObjPtr())};
     }
 
     Class cls() noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
-    CommonBitArrayField<LibObjT>& operator=(const std::uint64_t bits) noexcept
+    CommonBitArrayField valueAsInteger(const std::uint64_t bits) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstBitArrayField`.");
 
-        bt_field_bit_array_set_value_as_integer(this->_libObjPtr(), bits);
+        bt_field_bit_array_set_value_as_integer(this->libObjPtr(), bits);
         return *this;
     }
 
     std::uint64_t valueAsInteger() const noexcept
     {
-        return bt_field_bit_array_get_value_as_integer(this->_libObjPtr());
+        return bt_field_bit_array_get_value_as_integer(this->libObjPtr());
     }
 
     bool bitValue(const std::uint64_t index) const noexcept
@@ -334,173 +393,223 @@ public:
 using BitArrayField = CommonBitArrayField<bt_field>;
 using ConstBitArrayField = CommonBitArrayField<const bt_field>;
 
+namespace internal {
+
+struct BitArrayFieldTypeDescr
+{
+    using Const = ConstBitArrayField;
+    using NonConst = BitArrayField;
+};
+
+template <>
+struct TypeDescr<BitArrayField> : public BitArrayFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstBitArrayField> : public BitArrayFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
-class CommonUnsignedIntegerField final : public CommonField<LibObjT>
+class CommonUnsignedIntegerField : public CommonField<LibObjT>
 {
 private:
     using typename CommonField<LibObjT>::_ThisCommonField;
 
 protected:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using _ThisCommonUnsignedIntegerField = CommonUnsignedIntegerField<LibObjT>;
 
 public:
+    using typename CommonField<LibObjT>::LibObjPtr;
     using Value = std::uint64_t;
+    using Class = internal::DepType<LibObjT, IntegerFieldClass, ConstIntegerFieldClass>;
 
-    using Class = typename std::conditional<std::is_const<LibObjT>::value, ConstIntegerFieldClass,
-                                            IntegerFieldClass>::type;
-
-    explicit CommonUnsignedIntegerField(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonUnsignedIntegerField(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isUnsignedInteger());
     }
 
     template <typename OtherLibObjT>
-    CommonUnsignedIntegerField(const CommonUnsignedIntegerField<OtherLibObjT>& val) noexcept :
+    CommonUnsignedIntegerField(const CommonUnsignedIntegerField<OtherLibObjT> val) noexcept :
         _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonUnsignedIntegerField&
-    operator=(const CommonUnsignedIntegerField<OtherLibObjT>& val) noexcept
+    _ThisCommonUnsignedIntegerField
+    operator=(const CommonUnsignedIntegerField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    ConstIntegerFieldClass cls() const noexcept
+    CommonUnsignedIntegerField<const bt_field> asConst() const noexcept
     {
-        return ConstIntegerFieldClass {
-            internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+        return CommonUnsignedIntegerField<const bt_field> {*this};
     }
 
-    Class cls() noexcept
+    Class cls() const noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
-    CommonUnsignedIntegerField<LibObjT>& operator=(const Value val) noexcept
+    RawValueProxy<CommonUnsignedIntegerField> operator*() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
-
-        bt_field_integer_unsigned_set_value(this->_libObjPtr(), val);
-        return *this;
+        return RawValueProxy<CommonUnsignedIntegerField> {*this};
     }
 
-    Value value() const noexcept
+    CommonUnsignedIntegerField value(const Value val) const noexcept
     {
-        return bt_field_integer_unsigned_get_value(this->_libObjPtr());
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstUnsignedIntegerField`.");
+
+        bt_field_integer_unsigned_set_value(this->libObjPtr(), val);
+        return *this;
     }
 
-    operator Value() const noexcept
+    Value value() const noexcept
     {
-        return this->value();
+        return bt_field_integer_unsigned_get_value(this->libObjPtr());
     }
 };
 
 using UnsignedIntegerField = CommonUnsignedIntegerField<bt_field>;
 using ConstUnsignedIntegerField = CommonUnsignedIntegerField<const bt_field>;
 
+namespace internal {
+
+struct UnsignedIntegerFieldTypeDescr
+{
+    using Const = ConstUnsignedIntegerField;
+    using NonConst = UnsignedIntegerField;
+};
+
+template <>
+struct TypeDescr<UnsignedIntegerField> : public UnsignedIntegerFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstUnsignedIntegerField> : public UnsignedIntegerFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
-class CommonSignedIntegerField final : public CommonField<LibObjT>
+class CommonSignedIntegerField : public CommonField<LibObjT>
 {
 private:
     using typename CommonField<LibObjT>::_ThisCommonField;
 
 protected:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using _ThisCommonSignedIntegerField = CommonSignedIntegerField<LibObjT>;
 
 public:
-    using Value = std::uint64_t;
+    using typename CommonField<LibObjT>::LibObjPtr;
+    using Value = std::int64_t;
+    using Class = internal::DepType<LibObjT, IntegerFieldClass, ConstIntegerFieldClass>;
 
-    using Class = typename std::conditional<std::is_const<LibObjT>::value, ConstIntegerFieldClass,
-                                            IntegerFieldClass>::type;
-
-    explicit CommonSignedIntegerField(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonSignedIntegerField(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isSignedInteger());
     }
 
     template <typename OtherLibObjT>
-    CommonSignedIntegerField(const CommonSignedIntegerField<OtherLibObjT>& val) noexcept :
+    CommonSignedIntegerField(const CommonSignedIntegerField<OtherLibObjT> val) noexcept :
         _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonSignedIntegerField&
-    operator=(const CommonSignedIntegerField<OtherLibObjT>& val) noexcept
+    _ThisCommonSignedIntegerField
+    operator=(const CommonSignedIntegerField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    ConstIntegerFieldClass cls() const noexcept
+    CommonSignedIntegerField<const bt_field> asConst() const noexcept
     {
-        return ConstIntegerFieldClass {
-            internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+        return CommonSignedIntegerField<const bt_field> {*this};
     }
 
-    Class cls() noexcept
+    Class cls() const noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
-    CommonSignedIntegerField<LibObjT>& operator=(const Value val) noexcept
+    RawValueProxy<CommonSignedIntegerField> operator*() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
-
-        bt_field_integer_signed_set_value(this->_libObjPtr(), val);
-        return *this;
+        return RawValueProxy<CommonSignedIntegerField> {*this};
     }
 
-    Value value() const noexcept
+    CommonSignedIntegerField value(const Value val) const noexcept
     {
-        return bt_field_integer_signed_get_value(this->_libObjPtr());
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstSignedIntegerField`.");
+
+        bt_field_integer_signed_set_value(this->libObjPtr(), val);
+        return *this;
     }
 
-    operator Value() const noexcept
+    Value value() const noexcept
     {
-        return this->value();
+        return bt_field_integer_signed_get_value(this->libObjPtr());
     }
 };
 
 using SignedIntegerField = CommonSignedIntegerField<bt_field>;
 using ConstSignedIntegerField = CommonSignedIntegerField<const bt_field>;
 
+namespace internal {
+
+struct SignedIntegerFieldTypeDescr
+{
+    using Const = ConstSignedIntegerField;
+    using NonConst = SignedIntegerField;
+};
+
+template <>
+struct TypeDescr<SignedIntegerField> : public SignedIntegerFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstSignedIntegerField> : public SignedIntegerFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 class EnumerationFieldClassMappingLabels
 {
 public:
     explicit EnumerationFieldClassMappingLabels(
         const bt_field_class_enumeration_mapping_label_array labels, const std::uint64_t size) :
         _mLabels {labels},
-        _mSize {size}
+        _mLen {size}
     {
     }
 
-    EnumerationFieldClassMappingLabels(const EnumerationFieldClassMappingLabels&) noexcept =
-        default;
-
-    EnumerationFieldClassMappingLabels&
-    operator=(const EnumerationFieldClassMappingLabels&) noexcept = default;
-
-    std::uint64_t size() const noexcept
+    std::uint64_t length() const noexcept
     {
-        return _mSize;
+        return _mLen;
     }
 
-    bpstd::string_view operator[](const std::uint64_t index) const noexcept
+    bt2c::CStringView operator[](const std::uint64_t index) const noexcept
     {
         return _mLabels[index];
     }
 
 private:
     bt_field_class_enumeration_mapping_label_array _mLabels;
-    std::uint64_t _mSize;
+    std::uint64_t _mLen;
 };
 
 template <typename LibObjT>
@@ -508,54 +617,53 @@ class CommonUnsignedEnumerationField final : public CommonUnsignedIntegerField<L
 {
 private:
     using typename CommonUnsignedIntegerField<LibObjT>::_ThisCommonUnsignedIntegerField;
-    using typename CommonField<LibObjT>::_LibObjPtr;
 
 public:
-    using Class =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstUnsignedEnumerationFieldClass,
-                                  UnsignedEnumerationFieldClass>::type;
+    using typename CommonField<LibObjT>::LibObjPtr;
 
-    explicit CommonUnsignedEnumerationField(const _LibObjPtr libObjPtr) noexcept :
+    using Class = internal::DepType<LibObjT, UnsignedEnumerationFieldClass,
+                                    ConstUnsignedEnumerationFieldClass>;
+
+    explicit CommonUnsignedEnumerationField(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonUnsignedIntegerField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isUnsignedEnumeration());
     }
 
     template <typename OtherLibObjT>
-    CommonUnsignedEnumerationField(const CommonUnsignedEnumerationField<OtherLibObjT>& val) noexcept
+    CommonUnsignedEnumerationField(const CommonUnsignedEnumerationField<OtherLibObjT> val) noexcept
         :
         _ThisCommonUnsignedIntegerField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonUnsignedEnumerationField<LibObjT>&
-    operator=(const CommonUnsignedEnumerationField<OtherLibObjT>& val) noexcept
+    CommonUnsignedEnumerationField<LibObjT>
+    operator=(const CommonUnsignedEnumerationField<OtherLibObjT> val) noexcept
     {
         _ThisCommonUnsignedIntegerField::operator=(val);
         return *this;
     }
 
-    ConstUnsignedEnumerationFieldClass cls() const noexcept
+    CommonUnsignedEnumerationField<const bt_field> asConst() const noexcept
     {
-        return ConstUnsignedEnumerationFieldClass {
-            internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+        return CommonUnsignedEnumerationField<const bt_field> {*this};
     }
 
-    Class cls() noexcept
+    Class cls() const noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
     EnumerationFieldClassMappingLabels labels() const
     {
         bt_field_class_enumeration_mapping_label_array labelArray;
         std::uint64_t count;
-        const auto status = bt_field_enumeration_unsigned_get_mapping_labels(this->_libObjPtr(),
+        const auto status = bt_field_enumeration_unsigned_get_mapping_labels(this->libObjPtr(),
                                                                              &labelArray, &count);
 
         if (status == BT_FIELD_ENUMERATION_GET_MAPPING_LABELS_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
 
         return EnumerationFieldClassMappingLabels {labelArray, count};
@@ -565,47 +673,66 @@ public:
 using UnsignedEnumerationField = CommonUnsignedEnumerationField<bt_field>;
 using ConstUnsignedEnumerationField = CommonUnsignedEnumerationField<const bt_field>;
 
+namespace internal {
+
+struct UnsignedEnumerationFieldTypeDescr
+{
+    using Const = ConstUnsignedEnumerationField;
+    using NonConst = UnsignedEnumerationField;
+};
+
+template <>
+struct TypeDescr<UnsignedEnumerationField> : public UnsignedEnumerationFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstUnsignedEnumerationField> : public UnsignedEnumerationFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonSignedEnumerationField final : public CommonSignedIntegerField<LibObjT>
 {
 private:
     using typename CommonSignedIntegerField<LibObjT>::_ThisCommonSignedIntegerField;
-    using typename CommonField<LibObjT>::_LibObjPtr;
 
 public:
+    using typename CommonField<LibObjT>::LibObjPtr;
+
     using Class =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstSignedEnumerationFieldClass,
-                                  SignedEnumerationFieldClass>::type;
+        internal::DepType<LibObjT, SignedEnumerationFieldClass, ConstSignedEnumerationFieldClass>;
 
-    explicit CommonSignedEnumerationField(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonSignedEnumerationField(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonSignedIntegerField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isSignedEnumeration());
     }
 
     template <typename OtherLibObjT>
-    CommonSignedEnumerationField(const CommonSignedEnumerationField<OtherLibObjT>& val) noexcept :
+    CommonSignedEnumerationField(const CommonSignedEnumerationField<OtherLibObjT> val) noexcept :
         _ThisCommonSignedIntegerField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonSignedEnumerationField<LibObjT>&
-    operator=(const CommonSignedEnumerationField<OtherLibObjT>& val) noexcept
+    CommonSignedEnumerationField<LibObjT>
+    operator=(const CommonSignedEnumerationField<OtherLibObjT> val) noexcept
     {
         _ThisCommonSignedIntegerField::operator=(val);
         return *this;
     }
 
-    ConstSignedEnumerationFieldClass cls() const noexcept
+    CommonSignedEnumerationField<const bt_field> asConst() const noexcept
     {
-        return ConstSignedEnumerationFieldClass {
-            internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+        return CommonSignedEnumerationField<const bt_field> {*this};
     }
 
-    Class cls() noexcept
+    Class cls() const noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
     EnumerationFieldClassMappingLabels labels() const
@@ -613,10 +740,10 @@ public:
         bt_field_class_enumeration_mapping_label_array labelArray;
         std::uint64_t count;
         const auto status =
-            bt_field_enumeration_signed_get_mapping_labels(this->_libObjPtr(), &labelArray, &count);
+            bt_field_enumeration_signed_get_mapping_labels(this->libObjPtr(), &labelArray, &count);
 
         if (status == BT_FIELD_ENUMERATION_GET_MAPPING_LABELS_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
 
         return EnumerationFieldClassMappingLabels {labelArray, count};
@@ -626,165 +753,276 @@ public:
 using SignedEnumerationField = CommonSignedEnumerationField<bt_field>;
 using ConstSignedEnumerationField = CommonSignedEnumerationField<const bt_field>;
 
+namespace internal {
+
+struct SignedEnumerationFieldTypeDescr
+{
+    using Const = ConstSignedEnumerationField;
+    using NonConst = SignedEnumerationField;
+};
+
+template <>
+struct TypeDescr<SignedEnumerationField> : public SignedEnumerationFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstSignedEnumerationField> : public SignedEnumerationFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonSinglePrecisionRealField final : public CommonField<LibObjT>
 {
 private:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using typename CommonField<LibObjT>::_ThisCommonField;
 
 public:
+    using typename CommonField<LibObjT>::LibObjPtr;
     using Value = float;
 
-    explicit CommonSinglePrecisionRealField(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonSinglePrecisionRealField(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isSinglePrecisionReal());
     }
 
     template <typename OtherLibObjT>
-    CommonSinglePrecisionRealField(const CommonSinglePrecisionRealField<OtherLibObjT>& val) noexcept
+    CommonSinglePrecisionRealField(const CommonSinglePrecisionRealField<OtherLibObjT> val) noexcept
         :
         _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonSinglePrecisionRealField<LibObjT>&
-    operator=(const CommonSinglePrecisionRealField<OtherLibObjT>& val) noexcept
+    CommonSinglePrecisionRealField<LibObjT>
+    operator=(const CommonSinglePrecisionRealField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    CommonSinglePrecisionRealField<LibObjT>& operator=(const Value val) noexcept
+    CommonSinglePrecisionRealField<const bt_field> asConst() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return CommonSinglePrecisionRealField<const bt_field> {*this};
+    }
 
-        bt_field_real_single_precision_set_value(this->_libObjPtr(), val);
-        return *this;
+    RawValueProxy<CommonSinglePrecisionRealField> operator*() const noexcept
+    {
+        return RawValueProxy<CommonSinglePrecisionRealField> {*this};
     }
 
-    Value value() const noexcept
+    CommonSinglePrecisionRealField value(const Value val) const noexcept
     {
-        return bt_field_real_single_precision_get_value(this->_libObjPtr());
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstSinglePrecisionRealField`.");
+
+        bt_field_real_single_precision_set_value(this->libObjPtr(), val);
+        return *this;
     }
 
-    operator Value() const noexcept
+    Value value() const noexcept
     {
-        return this->value();
+        return bt_field_real_single_precision_get_value(this->libObjPtr());
     }
 };
 
 using SinglePrecisionRealField = CommonSinglePrecisionRealField<bt_field>;
 using ConstSinglePrecisionRealField = CommonSinglePrecisionRealField<const bt_field>;
 
+namespace internal {
+
+struct SinglePrecisionRealFieldTypeDescr
+{
+    using Const = ConstSinglePrecisionRealField;
+    using NonConst = SinglePrecisionRealField;
+};
+
+template <>
+struct TypeDescr<SinglePrecisionRealField> : public SinglePrecisionRealFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstSinglePrecisionRealField> : public SinglePrecisionRealFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonDoublePrecisionRealField final : public CommonField<LibObjT>
 {
 private:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using typename CommonField<LibObjT>::_ThisCommonField;
 
 public:
+    using typename CommonField<LibObjT>::LibObjPtr;
     using Value = double;
 
-    explicit CommonDoublePrecisionRealField(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonDoublePrecisionRealField(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isDoublePrecisionReal());
     }
 
     template <typename OtherLibObjT>
-    CommonDoublePrecisionRealField(const CommonDoublePrecisionRealField<OtherLibObjT>& val) noexcept
+    CommonDoublePrecisionRealField(const CommonDoublePrecisionRealField<OtherLibObjT> val) noexcept
         :
         _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonDoublePrecisionRealField<LibObjT>&
-    operator=(const CommonDoublePrecisionRealField<OtherLibObjT>& val) noexcept
+    CommonDoublePrecisionRealField<LibObjT>
+    operator=(const CommonDoublePrecisionRealField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    CommonDoublePrecisionRealField<LibObjT>& operator=(const Value val) noexcept
+    CommonDoublePrecisionRealField<const bt_field> asConst() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return CommonDoublePrecisionRealField<const bt_field> {*this};
+    }
 
-        bt_field_real_single_precision_set_value(this->_libObjPtr(), val);
-        return *this;
+    RawValueProxy<CommonDoublePrecisionRealField> operator*() const noexcept
+    {
+        return RawValueProxy<CommonDoublePrecisionRealField> {*this};
     }
 
-    Value value() const noexcept
+    CommonDoublePrecisionRealField value(const Value val) const noexcept
     {
-        return bt_field_real_single_precision_get_value(this->_libObjPtr());
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstDoublePrecisionRealField`.");
+
+        bt_field_real_double_precision_set_value(this->libObjPtr(), val);
+        return *this;
     }
 
-    operator Value() const noexcept
+    Value value() const noexcept
     {
-        return this->value();
+        return bt_field_real_double_precision_get_value(this->libObjPtr());
     }
 };
 
 using DoublePrecisionRealField = CommonDoublePrecisionRealField<bt_field>;
 using ConstDoublePrecisionRealField = CommonDoublePrecisionRealField<const bt_field>;
 
+namespace internal {
+
+struct DoublePrecisionRealFieldTypeDescr
+{
+    using Const = ConstDoublePrecisionRealField;
+    using NonConst = DoublePrecisionRealField;
+};
+
+template <>
+struct TypeDescr<DoublePrecisionRealField> : public DoublePrecisionRealFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstDoublePrecisionRealField> : public DoublePrecisionRealFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonStringField final : public CommonField<LibObjT>
 {
 private:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using typename CommonField<LibObjT>::_ThisCommonField;
 
 public:
-    explicit CommonStringField(const _LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
+    using typename CommonField<LibObjT>::LibObjPtr;
+    using Value = bt2c::CStringView;
+
+    explicit CommonStringField(const LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isString());
     }
 
     template <typename OtherLibObjT>
-    CommonStringField(const CommonStringField<OtherLibObjT>& val) noexcept : _ThisCommonField {val}
+    CommonStringField(const CommonStringField<OtherLibObjT> val) noexcept : _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStringField<LibObjT>& operator=(const CommonStringField<OtherLibObjT>& val) noexcept
+    CommonStringField<LibObjT> operator=(const CommonStringField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    CommonStringField<LibObjT>& operator=(const char * const val) noexcept
+    CommonStringField<const bt_field> asConst() const noexcept
+    {
+        return CommonStringField<const bt_field> {*this};
+    }
+
+    RawValueProxy<CommonStringField> operator*() const noexcept
+    {
+        return RawValueProxy<CommonStringField> {*this};
+    }
+
+    CommonStringField value(const Value val) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStringField`.");
 
-        const auto status = bt_field_string_set_value(this->_libObjPtr(), val);
+        const auto status = bt_field_string_set_value(this->libObjPtr(), *val);
 
         if (status == BT_FIELD_STRING_SET_VALUE_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
+        }
+
+        return *this;
+    }
+
+    CommonStringField append(const bt2c::CStringView begin, const std::uint64_t len) const
+    {
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStringField`.");
+
+        const auto status = bt_field_string_append_with_length(this->libObjPtr(), begin, len);
+
+        if (status == BT_FIELD_STRING_APPEND_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
         }
 
         return *this;
     }
 
-    CommonStringField<LibObjT>& operator=(const std::string& val) noexcept
+    CommonStringField append(const bt2c::CStringView val) const
     {
-        return *this = val.data();
+        return this->append(val, std::strlen(val));
     }
 
-    void clear() noexcept
+    CommonStringField append(const std::string& val) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return this->append(val.data(), val.size());
+    }
+
+    CommonStringField clear() const noexcept
+    {
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStringField`.");
+
+        bt_field_string_clear(this->libObjPtr());
+        return *this;
+    }
 
-        bt_field_string_clear(this->_libObjPtr());
+    Value value() const noexcept
+    {
+        return bt_field_string_get_value(this->libObjPtr());
     }
 
-    bpstd::string_view value() const noexcept
+    std::uint64_t length() const noexcept
     {
-        return bt_field_string_get_value(this->_libObjPtr());
+        return bt_field_string_get_length(this->libObjPtr());
     }
 };
 
@@ -793,10 +1031,26 @@ using ConstStringField = CommonStringField<const bt_field>;
 
 namespace internal {
 
+struct StringFieldTypeDescr
+{
+    using Const = ConstStringField;
+    using NonConst = StringField;
+};
+
+template <>
+struct TypeDescr<StringField> : public StringFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstStringField> : public StringFieldTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonStructureFieldSpec;
 
-// Functions specific to mutable structure fields
+/* Functions specific to mutable structure fields */
 template <>
 struct CommonStructureFieldSpec<bt_field> final
 {
@@ -812,7 +1066,7 @@ struct CommonStructureFieldSpec<bt_field> final
     }
 };
 
-// Functions specific to constant structure fields
+/* Functions specific to constant structure fields */
 template <>
 struct CommonStructureFieldSpec<const bt_field> final
 {
@@ -829,98 +1083,61 @@ struct CommonStructureFieldSpec<const bt_field> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonStructureField final : public CommonField<LibObjT>
 {
 private:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using typename CommonField<LibObjT>::_ThisCommonField;
     using _Spec = internal::CommonStructureFieldSpec<LibObjT>;
 
 public:
-    using Class = typename std::conditional<std::is_const<LibObjT>::value, ConstStructureFieldClass,
-                                            StructureFieldClass>::type;
+    using typename CommonField<LibObjT>::LibObjPtr;
+    using Class = internal::DepType<LibObjT, StructureFieldClass, ConstStructureFieldClass>;
 
-    explicit CommonStructureField(const _LibObjPtr libObjPtr) noexcept :
-        _ThisCommonField {libObjPtr}
+    explicit CommonStructureField(const LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isStructure());
     }
 
     template <typename OtherLibObjT>
-    CommonStructureField(const CommonStructureField<OtherLibObjT>& val) noexcept :
+    CommonStructureField(const CommonStructureField<OtherLibObjT> val) noexcept :
         _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStructureField<LibObjT>& operator=(const CommonStructureField<OtherLibObjT>& val) noexcept
+    CommonStructureField<LibObjT> operator=(const CommonStructureField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    ConstStructureFieldClass cls() const noexcept
+    CommonStructureField<const bt_field> asConst() const noexcept
     {
-        return ConstStructureFieldClass {
-            internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+        return CommonStructureField<const bt_field> {*this};
     }
 
-    Class cls() noexcept
+    Class cls() const noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
-    std::uint64_t size() const noexcept
-    {
-        return this->cls().size();
-    }
-
-    ConstField operator[](const std::uint64_t index) const noexcept
-    {
-        return ConstField {internal::CommonStructureFieldSpec<const bt_field>::memberFieldByIndex(
-            this->_libObjPtr(), index)};
-    }
-
-    CommonField<LibObjT> operator[](const std::uint64_t index) noexcept
-    {
-        return CommonField<LibObjT> {_Spec::memberFieldByIndex(this->_libObjPtr(), index)};
-    }
-
-    nonstd::optional<ConstField> operator[](const char * const name) const noexcept
-    {
-        const auto libObjPtr =
-            internal::CommonStructureFieldSpec<const bt_field>::memberFieldByName(
-                this->_libObjPtr(), name);
-
-        if (libObjPtr) {
-            return ConstField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
-    }
-
-    nonstd::optional<ConstField> operator[](const std::string& name) const noexcept
+    std::uint64_t length() const noexcept
     {
-        return (*this)[name.data()];
+        return this->cls().length();
     }
 
-    nonstd::optional<CommonField<LibObjT>> operator[](const char * const name) noexcept
+    CommonField<LibObjT> operator[](const std::uint64_t index) const noexcept
     {
-        const auto libObjPtr = _Spec::memberFieldByName(this->_libObjPtr(), name);
-
-        if (libObjPtr) {
-            return CommonField<LibObjT> {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return CommonField<LibObjT> {_Spec::memberFieldByIndex(this->libObjPtr(), index)};
     }
 
-    nonstd::optional<CommonField<LibObjT>> operator[](const std::string& name) noexcept
+    OptionalBorrowedObject<CommonField<LibObjT>>
+    operator[](const bt2c::CStringView name) const noexcept
     {
-        return (*this)[name.data()];
+        return _Spec::memberFieldByName(this->libObjPtr(), name);
     }
 };
 
@@ -929,10 +1146,26 @@ using ConstStructureField = CommonStructureField<const bt_field>;
 
 namespace internal {
 
+struct StructureFieldTypeDescr
+{
+    using Const = ConstStructureField;
+    using NonConst = StructureField;
+};
+
+template <>
+struct TypeDescr<StructureField> : public StructureFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstStructureField> : public StructureFieldTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonArrayFieldSpec;
 
-// Functions specific to mutable array fields
+/* Functions specific to mutable array fields */
 template <>
 struct CommonArrayFieldSpec<bt_field> final
 {
@@ -943,7 +1176,7 @@ struct CommonArrayFieldSpec<bt_field> final
     }
 };
 
-// Functions specific to constant array fields
+/* Functions specific to constant array fields */
 template <>
 struct CommonArrayFieldSpec<const bt_field> final
 {
@@ -954,7 +1187,7 @@ struct CommonArrayFieldSpec<const bt_field> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonArrayField : public CommonField<LibObjT>
@@ -964,103 +1197,124 @@ private:
     using _Spec = internal::CommonArrayFieldSpec<LibObjT>;
 
 protected:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using _ThisCommonArrayField = CommonArrayField<LibObjT>;
 
 public:
-    using Class = typename std::conditional<std::is_const<LibObjT>::value, ConstArrayFieldClass,
-                                            ArrayFieldClass>::type;
+    using typename CommonField<LibObjT>::LibObjPtr;
+    using Class = internal::DepType<LibObjT, ArrayFieldClass, ConstArrayFieldClass>;
 
-    explicit CommonArrayField(const _LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
+    explicit CommonArrayField(const LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isArray());
     }
 
     template <typename OtherLibObjT>
-    CommonArrayField(const CommonArrayField<OtherLibObjT>& val) noexcept : _ThisCommonField {val}
+    CommonArrayField(const CommonArrayField<OtherLibObjT> val) noexcept : _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonArrayField& operator=(const CommonArrayField<OtherLibObjT>& val) noexcept
+    _ThisCommonArrayField operator=(const CommonArrayField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    ConstArrayFieldClass cls() const noexcept
+    CommonArrayField<const bt_field> asConst() const noexcept
     {
-        return ConstArrayFieldClass {
-            internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+        return CommonArrayField<const bt_field> {*this};
     }
 
-    Class cls() noexcept
+    Class cls() const noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
     std::uint64_t length() const noexcept
     {
-        return bt_field_array_get_length(this->_libObjPtr());
+        return bt_field_array_get_length(this->libObjPtr());
     }
 
-    ConstField operator[](const std::uint64_t index) const noexcept
+    CommonField<LibObjT> operator[](const std::uint64_t index) const noexcept
     {
-        return ConstField {internal::CommonArrayFieldSpec<const bt_field>::elementFieldByIndex(
-            this->_libObjPtr(), index)};
-    }
-
-    CommonField<LibObjT> operator[](const std::uint64_t index) noexcept
-    {
-        return CommonField<LibObjT> {_Spec::elementFieldByIndex(this->_libObjPtr(), index)};
+        return CommonField<LibObjT> {_Spec::elementFieldByIndex(this->libObjPtr(), index)};
     }
 };
 
 using ArrayField = CommonArrayField<bt_field>;
 using ConstArrayField = CommonArrayField<const bt_field>;
 
+namespace internal {
+
+struct ArrayFieldTypeDescr
+{
+    using Const = ConstArrayField;
+    using NonConst = ArrayField;
+};
+
+template <>
+struct TypeDescr<ArrayField> : public ArrayFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstArrayField> : public ArrayFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonDynamicArrayField : public CommonArrayField<LibObjT>
 {
 private:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using typename CommonArrayField<LibObjT>::_ThisCommonArrayField;
 
 public:
-    explicit CommonDynamicArrayField(const _LibObjPtr libObjPtr) noexcept :
+    using typename CommonField<LibObjT>::LibObjPtr;
+
+    explicit CommonDynamicArrayField(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonArrayField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isDynamicArray());
     }
 
     template <typename OtherLibObjT>
-    CommonDynamicArrayField(const CommonDynamicArrayField<OtherLibObjT>& val) noexcept :
+    CommonDynamicArrayField(const CommonDynamicArrayField<OtherLibObjT> val) noexcept :
         _ThisCommonArrayField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonDynamicArrayField<LibObjT>&
-    operator=(const CommonDynamicArrayField<OtherLibObjT>& val) noexcept
+    CommonDynamicArrayField<LibObjT>
+    operator=(const CommonDynamicArrayField<OtherLibObjT> val) noexcept
     {
         _ThisCommonArrayField::operator=(val);
         return *this;
     }
 
+    CommonDynamicArrayField<const bt_field> asConst() const noexcept
+    {
+        return CommonDynamicArrayField<const bt_field> {*this};
+    }
+
     std::uint64_t length() const noexcept
     {
         return _ThisCommonArrayField::length();
     }
 
-    void length(const std::uint64_t length)
+    CommonDynamicArrayField length(const std::uint64_t length) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstDynamicArrayField`.");
 
-        const auto status = bt_field_array_dynamic_set_length(this->_libObjPtr(), length);
+        const auto status = bt_field_array_dynamic_set_length(this->libObjPtr(), length);
 
         if (status == BT_FIELD_DYNAMIC_ARRAY_SET_LENGTH_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
+
+        return *this;
     }
 };
 
@@ -1069,10 +1323,26 @@ using ConstDynamicArrayField = CommonDynamicArrayField<const bt_field>;
 
 namespace internal {
 
+struct DynamicArrayFieldTypeDescr
+{
+    using Const = ConstDynamicArrayField;
+    using NonConst = DynamicArrayField;
+};
+
+template <>
+struct TypeDescr<DynamicArrayField> : public DynamicArrayFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstDynamicArrayField> : public DynamicArrayFieldTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonOptionFieldSpec;
 
-// Functions specific to mutable option fields
+/* Functions specific to mutable option fields */
 template <>
 struct CommonOptionFieldSpec<bt_field> final
 {
@@ -1082,7 +1352,7 @@ struct CommonOptionFieldSpec<bt_field> final
     }
 };
 
-// Functions specific to constant option fields
+/* Functions specific to constant option fields */
 template <>
 struct CommonOptionFieldSpec<const bt_field> final
 {
@@ -1092,53 +1362,53 @@ struct CommonOptionFieldSpec<const bt_field> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonOptionField : public CommonField<LibObjT>
 {
 private:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using typename CommonField<LibObjT>::_ThisCommonField;
     using _Spec = internal::CommonOptionFieldSpec<LibObjT>;
 
 public:
-    using Class = typename std::conditional<std::is_const<LibObjT>::value, ConstOptionFieldClass,
-                                            OptionFieldClass>::type;
+    using typename CommonField<LibObjT>::LibObjPtr;
+    using Class = internal::DepType<LibObjT, OptionFieldClass, ConstOptionFieldClass>;
 
-    explicit CommonOptionField(const _LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
+    explicit CommonOptionField(const LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isOption());
     }
 
     template <typename OtherLibObjT>
-    CommonOptionField(const CommonOptionField<OtherLibObjT>& val) noexcept : _ThisCommonField {val}
+    CommonOptionField(const CommonOptionField<OtherLibObjT> val) noexcept : _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonOptionField<LibObjT>& operator=(const CommonOptionField<OtherLibObjT>& val) noexcept
+    CommonOptionField<LibObjT> operator=(const CommonOptionField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    ConstOptionFieldClass cls() const noexcept
+    CommonOptionField<const bt_field> asConst() const noexcept
     {
-        return ConstOptionFieldClass {
-            internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+        return CommonOptionField<const bt_field> {*this};
     }
 
-    Class cls() noexcept
+    Class cls() const noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
-    void hasField(const bool hasField) noexcept
+    CommonOptionField hasField(const bool hasField) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstOptionField`.");
 
-        bt_field_option_set_has_field(this->_libObjPtr(), static_cast<bt_bool>(hasField));
+        bt_field_option_set_has_field(this->libObjPtr(), static_cast<bt_bool>(hasField));
+        return *this;
     }
 
     bool hasField() const noexcept
@@ -1146,27 +1416,9 @@ public:
         return this->field();
     }
 
-    nonstd::optional<ConstField> field() const noexcept
+    OptionalBorrowedObject<CommonField<LibObjT>> field() const noexcept
     {
-        const auto libObjPtr =
-            internal::CommonOptionFieldSpec<const bt_field>::field(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return ConstField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
-    }
-
-    nonstd::optional<CommonField<LibObjT>> field() noexcept
-    {
-        const auto libObjPtr = _Spec::field(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return CommonField<LibObjT> {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::field(this->libObjPtr());
     }
 };
 
@@ -1175,10 +1427,26 @@ using ConstOptionField = CommonOptionField<const bt_field>;
 
 namespace internal {
 
+struct OptionFieldTypeDescr
+{
+    using Const = ConstOptionField;
+    using NonConst = OptionField;
+};
+
+template <>
+struct TypeDescr<OptionField> : public OptionFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstOptionField> : public OptionFieldTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonVariantFieldSpec;
 
-// Functions specific to mutable variant fields
+/* Functions specific to mutable variant fields */
 template <>
 struct CommonVariantFieldSpec<bt_field> final
 {
@@ -1188,7 +1456,7 @@ struct CommonVariantFieldSpec<bt_field> final
     }
 };
 
-// Functions specific to constant variant fields
+/* Functions specific to constant variant fields */
 template <>
 struct CommonVariantFieldSpec<const bt_field> final
 {
@@ -1198,174 +1466,175 @@ struct CommonVariantFieldSpec<const bt_field> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonVariantField : public CommonField<LibObjT>
 {
 private:
-    using typename CommonField<LibObjT>::_LibObjPtr;
     using typename CommonField<LibObjT>::_ThisCommonField;
     using _Spec = internal::CommonVariantFieldSpec<LibObjT>;
 
 public:
-    using Class = typename std::conditional<std::is_const<LibObjT>::value, ConstVariantFieldClass,
-                                            VariantFieldClass>::type;
+    using typename CommonField<LibObjT>::LibObjPtr;
+    using Class = internal::DepType<LibObjT, VariantFieldClass, ConstVariantFieldClass>;
 
-    explicit CommonVariantField(const _LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
+    explicit CommonVariantField(const LibObjPtr libObjPtr) noexcept : _ThisCommonField {libObjPtr}
     {
         BT_ASSERT_DBG(this->isVariant());
     }
 
     template <typename OtherLibObjT>
-    CommonVariantField(const CommonVariantField<OtherLibObjT>& val) noexcept :
-        _ThisCommonField {val}
+    CommonVariantField(const CommonVariantField<OtherLibObjT> val) noexcept : _ThisCommonField {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonVariantField<LibObjT>& operator=(const CommonVariantField<OtherLibObjT>& val) noexcept
+    CommonVariantField<LibObjT> operator=(const CommonVariantField<OtherLibObjT> val) noexcept
     {
         _ThisCommonField::operator=(val);
         return *this;
     }
 
-    ConstVariantFieldClass cls() const noexcept
+    CommonVariantField<const bt_field> asConst() const noexcept
     {
-        return ConstVariantFieldClass {
-            internal::CommonFieldSpec<const bt_field>::cls(this->_libObjPtr())};
+        return CommonVariantField<const bt_field> {*this};
     }
 
-    Class cls() noexcept
+    Class cls() const noexcept
     {
-        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->_libObjPtr())};
+        return Class {internal::CommonFieldSpec<LibObjT>::cls(this->libObjPtr())};
     }
 
-    void selectOption(const std::uint64_t index) noexcept
+    CommonVariantField selectOption(const std::uint64_t index) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstVariantField`.");
 
-        static_cast<void>(bt_field_variant_select_option_by_index(this->_libObjPtr(), index));
-    }
+        const auto status = bt_field_variant_select_option_by_index(this->libObjPtr(), index);
 
-    ConstField selectedOptionField() const noexcept
-    {
-        return ConstField {internal::CommonVariantFieldSpec<const bt_field>::selectedOptionField(
-            this->_libObjPtr())};
+        BT_ASSERT_DBG(status == BT_FIELD_VARIANT_SELECT_OPTION_STATUS_OK);
+        return *this;
     }
 
-    CommonField<LibObjT> selectedOptionField() noexcept
+    CommonField<LibObjT> selectedOptionField() const noexcept
     {
-        return CommonField<LibObjT> {_Spec::selectedOptionField(this->_libObjPtr())};
+        return CommonField<LibObjT> {_Spec::selectedOptionField(this->libObjPtr())};
     }
 
     std::uint64_t selectedOptionIndex() const noexcept
     {
-        return bt_field_variant_get_selected_option_index(this->_libObjPtr());
+        return bt_field_variant_get_selected_option_index(this->libObjPtr());
     }
 };
 
 using VariantField = CommonVariantField<bt_field>;
 using ConstVariantField = CommonVariantField<const bt_field>;
 
+namespace internal {
+
+struct VariantFieldTypeDescr
+{
+    using Const = ConstVariantField;
+    using NonConst = VariantField;
+};
+
+template <>
+struct TypeDescr<VariantField> : public VariantFieldTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstVariantField> : public VariantFieldTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 CommonBoolField<LibObjT> CommonField<LibObjT>::asBool() const noexcept
 {
-    BT_ASSERT_DBG(this->isBool());
-    return CommonBoolField<LibObjT> {this->_libObjPtr()};
+    return CommonBoolField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonBitArrayField<LibObjT> CommonField<LibObjT>::asBitArray() const noexcept
 {
-    BT_ASSERT_DBG(this->isBitArray());
-    return CommonBitArrayField<LibObjT> {this->_libObjPtr()};
+    return CommonBitArrayField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonUnsignedIntegerField<LibObjT> CommonField<LibObjT>::asUnsignedInteger() const noexcept
 {
-    BT_ASSERT_DBG(this->isUnsignedInteger());
-    return CommonUnsignedIntegerField<LibObjT> {this->_libObjPtr()};
+    return CommonUnsignedIntegerField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonSignedIntegerField<LibObjT> CommonField<LibObjT>::asSignedInteger() const noexcept
 {
-    BT_ASSERT_DBG(this->isSignedInteger());
-    return CommonSignedIntegerField<LibObjT> {this->_libObjPtr()};
+    return CommonSignedIntegerField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonUnsignedEnumerationField<LibObjT> CommonField<LibObjT>::asUnsignedEnumeration() const noexcept
 {
-    BT_ASSERT_DBG(this->isUnsignedEnumeration());
-    return CommonUnsignedEnumerationField<LibObjT> {this->_libObjPtr()};
+    return CommonUnsignedEnumerationField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonSignedEnumerationField<LibObjT> CommonField<LibObjT>::asSignedEnumeration() const noexcept
 {
-    BT_ASSERT_DBG(this->isSignedEnumeration());
-    return CommonSignedEnumerationField<LibObjT> {this->_libObjPtr()};
+    return CommonSignedEnumerationField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonSinglePrecisionRealField<LibObjT> CommonField<LibObjT>::asSinglePrecisionReal() const noexcept
 {
-    BT_ASSERT_DBG(this->isSinglePrecisionReal());
-    return CommonSinglePrecisionRealField<LibObjT> {this->_libObjPtr()};
+    return CommonSinglePrecisionRealField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonDoublePrecisionRealField<LibObjT> CommonField<LibObjT>::asDoublePrecisionReal() const noexcept
 {
-    BT_ASSERT_DBG(this->isDoublePrecisionReal());
-    return CommonDoublePrecisionRealField<LibObjT> {this->_libObjPtr()};
+    return CommonDoublePrecisionRealField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonStringField<LibObjT> CommonField<LibObjT>::asString() const noexcept
 {
-    BT_ASSERT_DBG(this->isString());
-    return CommonStringField<LibObjT> {this->_libObjPtr()};
+    return CommonStringField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonStructureField<LibObjT> CommonField<LibObjT>::asStructure() const noexcept
 {
-    BT_ASSERT_DBG(this->isStructure());
-    return CommonStructureField<LibObjT> {this->_libObjPtr()};
+    return CommonStructureField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonArrayField<LibObjT> CommonField<LibObjT>::asArray() const noexcept
 {
-    BT_ASSERT_DBG(this->isArray());
-    return CommonArrayField<LibObjT> {this->_libObjPtr()};
+    return CommonArrayField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonDynamicArrayField<LibObjT> CommonField<LibObjT>::asDynamicArray() const noexcept
 {
-    BT_ASSERT_DBG(this->isDynamicArray());
-    return CommonDynamicArrayField<LibObjT> {this->_libObjPtr()};
+    return CommonDynamicArrayField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonOptionField<LibObjT> CommonField<LibObjT>::asOption() const noexcept
 {
-    BT_ASSERT_DBG(this->isOption());
-    return CommonOptionField<LibObjT> {this->_libObjPtr()};
+    return CommonOptionField<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonVariantField<LibObjT> CommonField<LibObjT>::asVariant() const noexcept
 {
-    BT_ASSERT_DBG(this->isVariant());
-    return CommonVariantField<LibObjT> {this->_libObjPtr()};
+    return CommonVariantField<LibObjT> {this->libObjPtr()};
 }
 
-} // namespace bt2
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_FIELD_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_FIELD_HPP */
diff --git a/src/cpp-common/bt2/graph.hpp b/src/cpp-common/bt2/graph.hpp
new file mode 100644 (file)
index 0000000..e2bcf77
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2024 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_GRAPH_HPP
+#define BABELTRACE_CPP_COMMON_BT2_GRAPH_HPP
+
+#include <cstdint>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "borrowed-object.hpp"
+#include "component-class.hpp"
+#include "exc.hpp"
+#include "shared-object.hpp"
+
+namespace bt2 {
+namespace internal {
+
+struct GraphRefFuncs final
+{
+    static void get(const bt_graph * const libObjPtr) noexcept
+    {
+        bt_graph_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_graph * const libObjPtr) noexcept
+    {
+        bt_graph_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+class Graph final : public BorrowedObject<bt_graph>
+{
+public:
+    using Shared = SharedObject<Graph, bt_graph, internal::GraphRefFuncs>;
+
+    explicit Graph(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    static Shared create(const std::uint64_t mipVersion)
+    {
+        const auto libObjPtr = bt_graph_create(mipVersion);
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return Shared::createWithoutRef(libObjPtr);
+    }
+
+    ConstSourceComponent addComponent(const ConstSourceComponentClass componentClass,
+                                      const bt2c::CStringView name,
+                                      const OptionalBorrowedObject<ConstMapValue> params = {},
+                                      const LoggingLevel loggingLevel = LoggingLevel::None) const
+    {
+        return this->_addComponent<ConstSourceComponent>(
+            componentClass, name, params, static_cast<void *>(nullptr), loggingLevel,
+            bt_graph_add_source_component_with_initialize_method_data);
+    }
+
+    template <typename InitDataT>
+    ConstSourceComponent addComponent(const ConstSourceComponentClass componentClass,
+                                      const bt2c::CStringView name, InitDataT&& initData,
+                                      const OptionalBorrowedObject<ConstMapValue> params = {},
+                                      const LoggingLevel loggingLevel = LoggingLevel::None) const
+    {
+        return this->_addComponent<ConstSourceComponent>(
+            componentClass, name, params, &initData, loggingLevel,
+            bt_graph_add_source_component_with_initialize_method_data);
+    }
+
+    ConstFilterComponent addComponent(const ConstFilterComponentClass componentClass,
+                                      const bt2c::CStringView name,
+                                      const OptionalBorrowedObject<ConstMapValue> params = {},
+                                      const LoggingLevel loggingLevel = LoggingLevel::None) const
+    {
+        return this->_addComponent<ConstFilterComponent>(
+            componentClass, name, params, static_cast<void *>(nullptr), loggingLevel,
+            bt_graph_add_filter_component_with_initialize_method_data);
+    }
+
+    template <typename InitDataT>
+    ConstFilterComponent addComponent(const ConstFilterComponentClass componentClass,
+                                      const bt2c::CStringView name, InitDataT&& initData,
+                                      const OptionalBorrowedObject<ConstMapValue> params = {},
+                                      const LoggingLevel loggingLevel = LoggingLevel::None) const
+    {
+        return this->_addComponent<ConstFilterComponent>(
+            componentClass, name, params, &initData, loggingLevel,
+            bt_graph_add_filter_component_with_initialize_method_data);
+    }
+
+    ConstSinkComponent addComponent(const ConstSinkComponentClass componentClass,
+                                    const bt2c::CStringView name,
+                                    const OptionalBorrowedObject<ConstMapValue> params = {},
+                                    const LoggingLevel loggingLevel = LoggingLevel::None) const
+    {
+        return this->_addComponent<ConstSinkComponent>(
+            componentClass, name, params, static_cast<void *>(nullptr), loggingLevel,
+            bt_graph_add_sink_component_with_initialize_method_data);
+    }
+
+    template <typename InitDataT>
+    ConstSinkComponent addComponent(const ConstSinkComponentClass componentClass,
+                                    const bt2c::CStringView name, InitDataT&& initData,
+                                    const OptionalBorrowedObject<ConstMapValue> params = {},
+                                    const LoggingLevel loggingLevel = LoggingLevel::None) const
+    {
+        return this->_addComponent<ConstSinkComponent>(
+            componentClass, name, params, &initData, loggingLevel,
+            bt_graph_add_sink_component_with_initialize_method_data);
+    }
+
+    Graph connectPorts(const ConstOutputPort outputPort, const ConstInputPort inputPort) const
+    {
+        const auto status = bt_graph_connect_ports(this->libObjPtr(), outputPort.libObjPtr(),
+                                                   inputPort.libObjPtr(), nullptr);
+
+        if (status == BT_GRAPH_CONNECT_PORTS_STATUS_ERROR) {
+            throw Error {};
+        } else if (status == BT_GRAPH_CONNECT_PORTS_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
+        }
+
+        return *this;
+    }
+
+    Graph runOnce() const
+    {
+        const auto status = bt_graph_run_once(this->libObjPtr());
+
+        if (status == BT_GRAPH_RUN_ONCE_STATUS_ERROR) {
+            throw Error {};
+        } else if (status == BT_GRAPH_RUN_ONCE_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
+        } else if (status == BT_GRAPH_RUN_ONCE_STATUS_AGAIN) {
+            throw TryAgain {};
+        }
+
+        return *this;
+    }
+
+    Graph run() const
+    {
+        const auto status = bt_graph_run(this->libObjPtr());
+
+        if (status == BT_GRAPH_RUN_STATUS_ERROR) {
+            throw Error {};
+        } else if (status == BT_GRAPH_RUN_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
+        } else if (status == BT_GRAPH_RUN_STATUS_AGAIN) {
+            throw TryAgain {};
+        }
+
+        return *this;
+    }
+
+private:
+    template <typename ConstComponentT, typename ConstComponentClassT, typename InitDataT,
+              typename AddFuncT>
+    ConstComponentT
+    _addComponent(const ConstComponentClassT componentClass, const bt2c::CStringView name,
+                  const OptionalBorrowedObject<ConstMapValue> params, InitDataT * const initData,
+                  const LoggingLevel loggingLevel, AddFuncT&& addFunc) const
+    {
+        typename ConstComponentT::LibObjPtr libObjPtr = nullptr;
+
+        const auto status = addFunc(this->libObjPtr(), componentClass.libObjPtr(), name,
+                                    params ? params->libObjPtr() : nullptr,
+                                    const_cast<void *>(static_cast<const void *>(initData)),
+                                    static_cast<bt_logging_level>(loggingLevel), &libObjPtr);
+
+        if (status == BT_GRAPH_ADD_COMPONENT_STATUS_ERROR) {
+            throw Error {};
+        } else if (status == BT_GRAPH_ADD_COMPONENT_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
+        }
+
+        return ConstComponentT {libObjPtr};
+    }
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_GRAPH_HPP */
index a56a4566f051eb37c0c166888b46233e1f797706..090520bc601cd1fecc7b538d1a92c899767641ef 100644 (file)
@@ -9,15 +9,17 @@
 
 #include <cstdint>
 #include <type_traits>
+
 #include <babeltrace2/babeltrace.h>
 
-#include "internal/borrowed-obj.hpp"
-#include "internal/utils.hpp"
+#include "borrowed-object-iterator.hpp"
+#include "borrowed-object.hpp"
+#include "exc.hpp"
 #include "integer-range.hpp"
-#include "lib-error.hpp"
+#include "internal/utils.hpp"
+#include "shared-object.hpp"
 
 namespace bt2 {
-
 namespace internal {
 
 template <typename LibObjT>
@@ -26,39 +28,39 @@ struct IntegerRangeSetRefFuncs;
 template <>
 struct IntegerRangeSetRefFuncs<const bt_integer_range_set_unsigned> final
 {
-    static void get(const bt_integer_range_set_unsigned * const libObjPtr)
+    static void get(const bt_integer_range_set_unsigned * const libObjPtr) noexcept
     {
         bt_integer_range_set_unsigned_get_ref(libObjPtr);
     }
 
-    static void put(const bt_integer_range_set_unsigned * const libObjPtr)
+    static void put(const bt_integer_range_set_unsigned * const libObjPtr) noexcept
     {
-        bt_integer_range_set_unsigned_get_ref(libObjPtr);
+        bt_integer_range_set_unsigned_put_ref(libObjPtr);
     }
 };
 
 template <>
 struct IntegerRangeSetRefFuncs<const bt_integer_range_set_signed> final
 {
-    static void get(const bt_integer_range_set_signed * const libObjPtr)
+    static void get(const bt_integer_range_set_signed * const libObjPtr) noexcept
     {
         bt_integer_range_set_signed_get_ref(libObjPtr);
     }
 
-    static void put(const bt_integer_range_set_signed * const libObjPtr)
+    static void put(const bt_integer_range_set_signed * const libObjPtr) noexcept
     {
-        bt_integer_range_set_signed_get_ref(libObjPtr);
+        bt_integer_range_set_signed_put_ref(libObjPtr);
     }
 };
 
 template <typename LibObjT>
 struct CommonIntegerRangeSetSpec;
 
-// Functions specific to unsigned integer range sets
+/* Functions specific to unsigned integer range sets */
 template <>
 struct CommonIntegerRangeSetSpec<const bt_integer_range_set_unsigned> final
 {
-    static std::uint64_t size(const bt_integer_range_set_unsigned * const libRangePtr) noexcept
+    static std::uint64_t length(const bt_integer_range_set_unsigned * const libRangePtr) noexcept
     {
         return bt_integer_range_set_get_range_count(
             bt_integer_range_set_unsigned_as_range_set_const(libRangePtr));
@@ -91,11 +93,11 @@ struct CommonIntegerRangeSetSpec<const bt_integer_range_set_unsigned> final
     }
 };
 
-// Functions specific to signed integer range sets
+/* Functions specific to signed integer range sets */
 template <>
 struct CommonIntegerRangeSetSpec<const bt_integer_range_set_signed> final
 {
-    static std::uint64_t size(const bt_integer_range_set_signed * const libRangePtr) noexcept
+    static std::uint64_t length(const bt_integer_range_set_signed * const libRangePtr) noexcept
     {
         return bt_integer_range_set_get_range_count(
             bt_integer_range_set_signed_as_range_set_const(libRangePtr));
@@ -127,59 +129,31 @@ struct CommonIntegerRangeSetSpec<const bt_integer_range_set_signed> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class ConstVariantWithIntegerSelectorFieldClassOption;
-
-template <typename LibObjT, typename RangeSetT>
-class CommonVariantWithIntegerSelectorFieldClass;
-
-template <typename LibObjT>
-class CommonTraceClass;
-
-template <typename LibObjT>
-class CommonIntegerRangeSet final : public internal::BorrowedObj<LibObjT>
+class CommonIntegerRangeSet final : public BorrowedObject<LibObjT>
 {
-    // Allow operator==() to call `other._libObjPtr()`
-    friend class CommonIntegerRangeSet<bt_integer_range_set_unsigned>;
-    friend class CommonIntegerRangeSet<const bt_integer_range_set_unsigned>;
-    friend class CommonIntegerRangeSet<bt_integer_range_set_signed>;
-    friend class CommonIntegerRangeSet<const bt_integer_range_set_signed>;
-
-    // Allow appendOption() to call `ranges._libObjPtr()`
-    friend class CommonVariantWithIntegerSelectorFieldClass<
-        bt_field_class,
-        ConstVariantWithIntegerSelectorFieldClassOption<
-            const bt_field_class_variant_with_selector_field_integer_unsigned_option>>;
-
-    friend class CommonVariantWithIntegerSelectorFieldClass<
-        bt_field_class,
-        ConstVariantWithIntegerSelectorFieldClassOption<
-            const bt_field_class_variant_with_selector_field_integer_signed_option>>;
-
-    // Allow create*FieldClass() to call `ranges._libObjPtr()`
-    friend class CommonTraceClass<bt_trace_class>;
-
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
     using _ConstLibObjT = typename std::add_const<LibObjT>::type;
-    using _RefFuncs = internal::IntegerRangeSetRefFuncs<_ConstLibObjT>;
     using _Spec = internal::CommonIntegerRangeSetSpec<_ConstLibObjT>;
-    using _ThisCommonIntegerRangeSet = CommonIntegerRangeSet<LibObjT>;
 
 public:
-    using Shared = internal::SharedObj<_ThisCommonIntegerRangeSet, LibObjT, _RefFuncs>;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+
+    using Shared = SharedObject<CommonIntegerRangeSet, LibObjT,
+                                internal::IntegerRangeSetRefFuncs<_ConstLibObjT>>;
 
     using Range = typename std::conditional<
         std::is_same<_ConstLibObjT, const bt_integer_range_set_unsigned>::value,
         ConstUnsignedIntegerRange, ConstSignedIntegerRange>::type;
 
     using Value = typename Range::Value;
+    using Iterator = BorrowedObjectIterator<CommonIntegerRangeSet>;
 
-    explicit CommonIntegerRangeSet(const _LibObjPtr libObjPtr) noexcept :
-        _ThisBorrowedObj {libObjPtr}
+    explicit CommonIntegerRangeSet(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
     {
     }
 
@@ -188,59 +162,77 @@ public:
         const auto libObjPtr = _Spec::create();
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Shared {_ThisCommonIntegerRangeSet {libObjPtr}};
+        return CommonIntegerRangeSet::Shared::createWithoutRef(libObjPtr);
     }
 
     template <typename OtherLibObjT>
-    CommonIntegerRangeSet(const CommonIntegerRangeSet<OtherLibObjT>& rangeSet) noexcept :
-        _ThisBorrowedObj {rangeSet}
+    CommonIntegerRangeSet(const CommonIntegerRangeSet<OtherLibObjT> rangeSet) noexcept :
+        _ThisBorrowedObject {rangeSet}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonIntegerRangeSet&
-    operator=(const CommonIntegerRangeSet<OtherLibObjT>& rangeSet) noexcept
+    CommonIntegerRangeSet operator=(const CommonIntegerRangeSet<OtherLibObjT> rangeSet) noexcept
     {
-        _ThisBorrowedObj::operator=(rangeSet);
+        _ThisBorrowedObject::operator=(rangeSet);
         return *this;
     }
 
+    _ConstLibObjT asConst() const noexcept
+    {
+        return _ConstLibObjT {*this};
+    }
+
     template <typename OtherLibObjT>
-    bool operator==(const CommonIntegerRangeSet<OtherLibObjT>& other) const noexcept
+    bool operator==(const CommonIntegerRangeSet<OtherLibObjT> other) const noexcept
     {
-        return _Spec::isEqual(this->_libObjPtr(), other._libObjPtr());
+        return _Spec::isEqual(this->libObjPtr(), other.libObjPtr());
     }
 
     template <typename OtherLibObjT>
-    bool operator!=(const CommonIntegerRangeSet<OtherLibObjT>& other) const noexcept
+    bool operator!=(const CommonIntegerRangeSet<OtherLibObjT> other) const noexcept
     {
         return !(*this == other);
     }
 
-    void addRange(const Value lower, const Value upper)
+    CommonIntegerRangeSet addRange(const Value lower, const Value upper) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(
+            !std::is_const<LibObjT>::value,
+            "Not available with `bt2::ConstUnsignedIntegerRangeSet` or `bt2::ConstSignedIntegerRangeSet`.");
 
-        const auto status = _Spec::addRange(this->_libObjPtr(), lower, upper);
+        const auto status = _Spec::addRange(this->libObjPtr(), lower, upper);
 
         if (status == BT_INTEGER_RANGE_SET_ADD_RANGE_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
+
+        return *this;
     }
 
-    std::uint64_t size() const noexcept
+    std::uint64_t length() const noexcept
     {
-        return _Spec::size(this->_libObjPtr());
+        return _Spec::length(this->libObjPtr());
     }
 
     Range operator[](const std::uint64_t index) const noexcept
     {
-        return Range {_Spec::rangeByIndex(this->_libObjPtr(), index)};
+        return Range {_Spec::rangeByIndex(this->libObjPtr(), index)};
+    }
+
+    Iterator begin() const noexcept
+    {
+        return Iterator {*this, 0};
+    }
+
+    Iterator end() const noexcept
+    {
+        return Iterator {*this, this->length()};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -249,6 +241,41 @@ using ConstUnsignedIntegerRangeSet = CommonIntegerRangeSet<const bt_integer_rang
 using SignedIntegerRangeSet = CommonIntegerRangeSet<bt_integer_range_set_signed>;
 using ConstSignedIntegerRangeSet = CommonIntegerRangeSet<const bt_integer_range_set_signed>;
 
-} // namespace bt2
+namespace internal {
+
+struct UnsignedIntegerRangeSetTypeDescr
+{
+    using Const = ConstUnsignedIntegerRangeSet;
+    using NonConst = UnsignedIntegerRangeSet;
+};
+
+template <>
+struct TypeDescr<UnsignedIntegerRangeSet> : public UnsignedIntegerRangeSetTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstUnsignedIntegerRangeSet> : public UnsignedIntegerRangeSetTypeDescr
+{
+};
+
+struct SignedIntegerRangeSetTypeDescr
+{
+    using Const = ConstSignedIntegerRangeSet;
+    using NonConst = SignedIntegerRangeSet;
+};
+
+template <>
+struct TypeDescr<SignedIntegerRangeSet> : public SignedIntegerRangeSetTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstSignedIntegerRangeSet> : public SignedIntegerRangeSetTypeDescr
+{
+};
+
+} /* namespace internal */
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_INTEGER_RANGE_SET_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_INTEGER_RANGE_SET_HPP */
index 7e94e9e037c42bbcefe0711056f692f94dc05b22..6f449403098dd95294ab6a84d9c0839c9f2f8ecc 100644 (file)
@@ -7,20 +7,20 @@
 #ifndef BABELTRACE_CPP_COMMON_BT2_INTEGER_RANGE_HPP
 #define BABELTRACE_CPP_COMMON_BT2_INTEGER_RANGE_HPP
 
-#include <type_traits>
 #include <cstdint>
+#include <type_traits>
+
 #include <babeltrace2/babeltrace.h>
 
-#include "internal/borrowed-obj.hpp"
+#include "borrowed-object.hpp"
 
 namespace bt2 {
-
 namespace internal {
 
 template <typename ValueT>
 struct ConstIntegerRangeSpec;
 
-// Functions specific to unsigned integer ranges
+/* Functions specific to unsigned integer ranges */
 template <>
 struct ConstIntegerRangeSpec<const bt_integer_range_unsigned> final
 {
@@ -41,7 +41,7 @@ struct ConstIntegerRangeSpec<const bt_integer_range_unsigned> final
     }
 };
 
-// Functions specific to signed integer ranges
+/* Functions specific to signed integer ranges */
 template <>
 struct ConstIntegerRangeSpec<const bt_integer_range_signed> final
 {
@@ -62,61 +62,51 @@ struct ConstIntegerRangeSpec<const bt_integer_range_signed> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class ConstIntegerRange final : public internal::BorrowedObj<LibObjT>
+class ConstIntegerRange final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ThisConstIntegerRange = ConstIntegerRange<LibObjT>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
 
 public:
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+
     using Value =
         typename std::conditional<std::is_same<LibObjT, const bt_integer_range_unsigned>::value,
                                   std::uint64_t, std::int64_t>::type;
 
 public:
-    explicit ConstIntegerRange(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
-    {
-    }
-
-    ConstIntegerRange(const _ThisConstIntegerRange& range) noexcept : _ThisBorrowedObj {range}
-    {
-    }
-
-    _ThisConstIntegerRange& operator=(const _ThisConstIntegerRange& range) noexcept
+    explicit ConstIntegerRange(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
-        _ThisBorrowedObj::operator=(range);
-        return *this;
     }
 
-    bool operator==(const _ThisConstIntegerRange& other) const noexcept
+    bool operator==(const ConstIntegerRange& other) const noexcept
     {
-        return internal::ConstIntegerRangeSpec<LibObjT>::isEqual(this->_libObjPtr(),
-                                                                 other._libObjPtr());
+        return internal::ConstIntegerRangeSpec<LibObjT>::isEqual(this->libObjPtr(),
+                                                                 other.libObjPtr());
     }
 
-    bool operator!=(const _ThisConstIntegerRange& other) const noexcept
+    bool operator!=(const ConstIntegerRange& other) const noexcept
     {
         return !(*this == other);
     }
 
     Value lower() const noexcept
     {
-        return internal::ConstIntegerRangeSpec<LibObjT>::lower(this->_libObjPtr());
+        return internal::ConstIntegerRangeSpec<LibObjT>::lower(this->libObjPtr());
     }
 
     Value upper() const noexcept
     {
-        return internal::ConstIntegerRangeSpec<LibObjT>::upper(this->_libObjPtr());
+        return internal::ConstIntegerRangeSpec<LibObjT>::upper(this->libObjPtr());
     }
 };
 
 using ConstUnsignedIntegerRange = ConstIntegerRange<const bt_integer_range_unsigned>;
 using ConstSignedIntegerRange = ConstIntegerRange<const bt_integer_range_signed>;
 
-} // namespace bt2
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_INTEGER_RANGE_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_INTEGER_RANGE_HPP */
diff --git a/src/cpp-common/bt2/internal/borrowed-obj.hpp b/src/cpp-common/bt2/internal/borrowed-obj.hpp
deleted file mode 100644 (file)
index 89feb62..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2019-2020 (c) Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: MIT
- */
-
-#ifndef BABELTRACE_CPP_COMMON_BT2_INTERNAL_BORROWED_OBJ_HPP
-#define BABELTRACE_CPP_COMMON_BT2_INTERNAL_BORROWED_OBJ_HPP
-
-#include <type_traits>
-
-#include "common/assert.h"
-
-namespace bt2 {
-namespace internal {
-
-template <typename ObjT, typename LibObjT, typename RefFuncsT>
-class SharedObj;
-
-/*
- * An instance of this class wraps a pointer to a libbabeltrace2 object
- * of type `LibObjT` without managing any reference counting.
- *
- * This is an abstract base class for any libbabeltrace2 object wrapper.
- *
- * `LibObjT` is the direct libbabeltrace2 object type, for example
- * `bt_stream_class` or `const bt_value`.
- *
- * Methods of a derived class can call _libObjPtr() to access the
- * libbabeltrace2 object pointer.
- */
-template <typename LibObjT>
-class BorrowedObj
-{
-    static_assert(!std::is_pointer<LibObjT>::value, "`LibObjT` must not be a pointer");
-
-    /*
-     * This makes it possible for a `BorrowedObj<const bt_something>`
-     * instance to get assigned an instance of
-     * `BorrowedObj<bt_something>` (copy constructor and assignment
-     * operator).
-     *
-     * C++ forbids the other way around.
-     */
-    template <typename AnyLibObjT>
-    friend class BorrowedObj;
-
-    /*
-     * This is to allow a `SharedObj<_ThisBorrowedObj, LibObjT, ...>`
-     * instance containing a `BorrowedObj<LibObjT>` instance to access
-     * _libObjPtr() in order to increment/decrement its libbabeltrace2
-     * reference count.
-     */
-    template <typename ObjT, typename AnyLibObjT, typename RefFuncsT>
-    friend class SharedObj;
-
-protected:
-    // libbabeltrace2 object pointer
-    using _LibObjPtr = LibObjT *;
-
-    // This complete borrowed object
-    using _ThisBorrowedObj = BorrowedObj<LibObjT>;
-
-    /*
-     * Builds a borrowed object to wrap the libbabeltrace2 object
-     * pointer `libObjPtr`.
-     *
-     * `libObjPtr` must not be `nullptr`.
-     */
-    explicit BorrowedObj(const _LibObjPtr libObjPtr) noexcept : _mLibObjPtr {libObjPtr}
-    {
-        BT_ASSERT(libObjPtr);
-    }
-
-    /*
-     * Generic copy constructor.
-     *
-     * This converting constructor accepts both an instance of
-     * `_ThisBorrowedObj` and an instance (`other`) of
-     * `BorrowedObj<ConstLibObjT>`, where `ConstLibObjT` is the `const`
-     * version of `LibObjT`, if applicable.
-     *
-     * This makes it possible for a `BorrowedObj<const bt_something>`
-     * instance to be built from an instance of
-     * `BorrowedObj<bt_something>`. C++ forbids the other way around.
-     */
-    template <typename OtherLibObjT>
-    BorrowedObj(const BorrowedObj<OtherLibObjT>& other) noexcept : BorrowedObj {other._mLibObjPtr}
-    {
-    }
-
-    /*
-     * Generic assignment operator.
-     *
-     * This operator accepts both an instance of
-     * `_ThisBorrowedObj` and an instance (`other`) of
-     * `BorrowedObj<ConstLibObjT>`, where `ConstLibObjT` is the `const`
-     * version of `LibObjT`, if applicable.
-     *
-     * This makes it possible for a `BorrowedObj<const bt_something>`
-     * instance to get assigned an instance of
-     * `BorrowedObj<bt_something>`. C++ forbids the other way around.
-     */
-    template <typename OtherLibObjT>
-    _ThisBorrowedObj& operator=(const BorrowedObj<OtherLibObjT>& other) noexcept
-    {
-        _mLibObjPtr = other._mLibObjPtr;
-        return *this;
-    }
-
-    // Wrapped libbabeltrace2 object pointer
-    _LibObjPtr _libObjPtr() const noexcept
-    {
-        return _mLibObjPtr;
-    }
-
-private:
-    _LibObjPtr _mLibObjPtr;
-};
-
-} // namespace internal
-} // namespace bt2
-
-#endif // BABELTRACE_CPP_COMMON_BT2_INTERNAL_BORROWED_OBJ_HPP
diff --git a/src/cpp-common/bt2/internal/comp-cls-bridge.hpp b/src/cpp-common/bt2/internal/comp-cls-bridge.hpp
new file mode 100644 (file)
index 0000000..88d8711
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 2024 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_INTERNAL_COMP_CLS_BRIDGE_HPP
+#define BABELTRACE_CPP_COMMON_BT2_INTERNAL_COMP_CLS_BRIDGE_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "logging/log-api.h"
+
+#include "../integer-range-set.hpp"
+#include "../logging.hpp"
+#include "../private-query-executor.hpp"
+#include "../self-component-class.hpp"
+#include "../self-component-port.hpp"
+#include "../self-message-iterator-configuration.hpp"
+#include "../self-message-iterator.hpp"
+#include "../value.hpp"
+
+namespace bt2 {
+namespace internal {
+
+constexpr bt2c::CStringView unhandledExcLogStr() noexcept
+{
+    return "Unhandled exception.";
+}
+
+constexpr bt2c::CStringView unhandledExcLogTag() noexcept
+{
+    return "PLUGIN-DEV-HPP";
+}
+
+/*
+ * Base class of any component class bridge.
+ *
+ * `UserCompClsT` is the actual C++ user component class and `TypesT`
+ * is a structure offering the following specific types:
+ *
+ * `LibSelfCompCls`:
+ *     Self component class library type.
+ *
+ * `LibSelfComp`:
+ *     Self component library type.
+ *
+ * `LibSelfCompCfg`:
+ *     Self component configuration library type.
+ *
+ * `SelfComp`:
+ *     Self component type.
+ */
+template <typename UserCompClsT, typename TypesT>
+class CompClsBridge
+{
+private:
+    using _LibSelfCompPtr = typename TypesT::LibSelfComp *;
+
+public:
+    static UserCompClsT& userCompFromLibSelfCompPtr(const _LibSelfCompPtr libSelfCompPtr) noexcept
+    {
+        return typename TypesT::SelfComp {libSelfCompPtr}.template data<UserCompClsT>();
+    }
+
+    static bt_component_class_initialize_method_status init(const _LibSelfCompPtr libSelfCompPtr,
+                                                            typename TypesT::LibSelfCompCfg *,
+                                                            const bt_value * const libParamsPtr,
+                                                            void * const initData) noexcept
+    {
+        const auto selfComp = typename TypesT::SelfComp {libSelfCompPtr};
+
+        try {
+            const auto comp =
+                new UserCompClsT {selfComp, ConstMapValue {libParamsPtr},
+                                  static_cast<typename UserCompClsT::InitData *>(initData)};
+
+            selfComp.data(*comp);
+        } catch (const std::bad_alloc&) {
+            return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(BT_LOG_WARNING, static_cast<int>(selfComp.loggingLevel()),
+                                 unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+        }
+
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+    }
+
+    static void finalize(const _LibSelfCompPtr libSelfCompPtr) noexcept
+    {
+        delete &userCompFromLibSelfCompPtr(libSelfCompPtr);
+    }
+
+    static bt_component_class_get_supported_mip_versions_method_status
+    getSupportedMipVersions(typename TypesT::LibSelfCompCls * const libSelfCompClsPtr,
+                            const bt_value * const libParamsPtr, void *,
+                            const bt_logging_level logLevel,
+                            bt_integer_range_set_unsigned * const libSupportedVersionsPtr) noexcept
+    {
+        try {
+            UserCompClsT::getSupportedMipVersions(
+                SelfComponentClass {libSelfCompClsPtr}, ConstMapValue {libParamsPtr},
+                static_cast<LoggingLevel>(logLevel),
+                UnsignedIntegerRangeSet {libSupportedVersionsPtr});
+            return BT_COMPONENT_CLASS_GET_SUPPORTED_MIP_VERSIONS_METHOD_STATUS_OK;
+        } catch (const std::bad_alloc&) {
+            return BT_COMPONENT_CLASS_GET_SUPPORTED_MIP_VERSIONS_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_COMPONENT_CLASS_GET_SUPPORTED_MIP_VERSIONS_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(BT_LOG_WARNING, static_cast<int>(logLevel), unhandledExcLogTag(),
+                                 unhandledExcLogStr());
+            return BT_COMPONENT_CLASS_GET_SUPPORTED_MIP_VERSIONS_METHOD_STATUS_ERROR;
+        }
+    }
+
+    static bt_component_class_query_method_status
+    query(typename TypesT::LibSelfCompCls * const libSelfCompClsPtr,
+          bt_private_query_executor * const libPrivQueryExecPtr, const char * const object,
+          const bt_value * const libParamsPtr, void * const data,
+          const bt_value ** const libResultPtr) noexcept
+    {
+        const auto privQueryExec = PrivateQueryExecutor {libPrivQueryExecPtr};
+
+        try {
+            auto result = UserCompClsT::query(
+                SelfComponentClass {libSelfCompClsPtr}, privQueryExec, object,
+                ConstValue {libParamsPtr}, static_cast<typename UserCompClsT::QueryData *>(data));
+
+            *libResultPtr = result.release().libObjPtr();
+            return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+        } catch (const TryAgain&) {
+            return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
+        } catch (const UnknownObject&) {
+            return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
+        } catch (const std::bad_alloc&) {
+            return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(BT_LOG_WARNING, static_cast<int>(privQueryExec.loggingLevel()),
+                                 unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+        }
+    }
+};
+
+template <typename SpecCompClsBridgeT, typename TypesT>
+struct CompClsBridgeWithInputPorts
+{
+    static bt_component_class_port_connected_method_status
+    inputPortConnected(typename TypesT::LibSelfComp * const libSelfCompPtr,
+                       bt_self_component_port_input * const libSelfCompPortPtr,
+                       const bt_port_output * const libOtherPortPtr) noexcept
+    {
+        try {
+            SpecCompClsBridgeT::userCompFromLibSelfCompPtr(libSelfCompPtr)
+                .inputPortConnected(SelfComponentInputPort {libSelfCompPortPtr},
+                                    ConstOutputPort {libOtherPortPtr});
+        } catch (const std::bad_alloc&) {
+            return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING,
+                static_cast<int>(typename TypesT::SelfComp {libSelfCompPtr}.loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_ERROR;
+        }
+
+        return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_OK;
+    }
+};
+
+template <typename SpecCompClsBridgeT, typename TypesT>
+struct CompClsBridgeWithOutputPorts
+{
+    static bt_component_class_port_connected_method_status
+    outputPortConnected(typename TypesT::LibSelfComp * const libSelfCompPtr,
+                        bt_self_component_port_output * const libSelfCompPortPtr,
+                        const bt_port_input * const libOtherPortPtr) noexcept
+    {
+        try {
+            SpecCompClsBridgeT::userCompFromLibSelfCompPtr(libSelfCompPtr)
+                .outputPortConnected(SelfComponentOutputPort {libSelfCompPortPtr},
+                                     ConstInputPort {libOtherPortPtr});
+        } catch (const std::bad_alloc&) {
+            return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING,
+                static_cast<int>(typename TypesT::SelfComp {libSelfCompPtr}.loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_ERROR;
+        }
+
+        return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_OK;
+    }
+};
+
+struct SrcCompClsTypes final
+{
+    using LibSelfCompCls = bt_self_component_class_source;
+    using LibSelfComp = bt_self_component_source;
+    using LibSelfCompCfg = bt_self_component_source_configuration;
+    using SelfComp = SelfSourceComponent;
+};
+
+template <typename UserCompClsT>
+class SrcCompClsBridge final :
+    public CompClsBridge<UserCompClsT, SrcCompClsTypes>,
+    public CompClsBridgeWithOutputPorts<SrcCompClsBridge<UserCompClsT>, SrcCompClsTypes>
+{
+};
+
+struct FltCompClsTypes final
+{
+    using LibSelfCompCls = bt_self_component_class_filter;
+    using LibSelfComp = bt_self_component_filter;
+    using LibSelfCompCfg = bt_self_component_filter_configuration;
+    using SelfComp = SelfFilterComponent;
+};
+
+template <typename UserCompClsT>
+class FltCompClsBridge final :
+    public CompClsBridge<UserCompClsT, FltCompClsTypes>,
+    public CompClsBridgeWithInputPorts<FltCompClsBridge<UserCompClsT>, FltCompClsTypes>,
+    public CompClsBridgeWithOutputPorts<FltCompClsBridge<UserCompClsT>, FltCompClsTypes>
+{
+};
+
+struct SinkCompClsTypes final
+{
+    using LibSelfCompCls = bt_self_component_class_sink;
+    using LibSelfComp = bt_self_component_sink;
+    using LibSelfCompCfg = bt_self_component_sink_configuration;
+    using SelfComp = SelfSinkComponent;
+};
+
+template <typename UserCompClsT>
+class SinkCompClsBridge final :
+    public CompClsBridge<UserCompClsT, SinkCompClsTypes>,
+    public CompClsBridgeWithInputPorts<SinkCompClsBridge<UserCompClsT>, SinkCompClsTypes>
+{
+public:
+    using CompClsBridge<UserCompClsT, SinkCompClsTypes>::userCompFromLibSelfCompPtr;
+
+    static bt_component_class_sink_consume_method_status
+    consume(bt_self_component_sink * const libSelfCompPtr) noexcept
+    {
+        try {
+            if (userCompFromLibSelfCompPtr(libSelfCompPtr).consume()) {
+                return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+            } else {
+                return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_END;
+            }
+        } catch (const TryAgain&) {
+            return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_AGAIN;
+        } catch (const std::bad_alloc&) {
+            return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING, static_cast<int>(SelfSinkComponent {libSelfCompPtr}.loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+        }
+    }
+
+    static bt_component_class_sink_graph_is_configured_method_status
+    graphIsConfigured(bt_self_component_sink * const libSelfCompPtr) noexcept
+    {
+        try {
+            userCompFromLibSelfCompPtr(libSelfCompPtr).graphIsConfigured();
+            return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK;
+        } catch (const std::bad_alloc&) {
+            return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING, static_cast<int>(SelfSinkComponent {libSelfCompPtr}.loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_ERROR;
+        }
+    }
+};
+
+template <typename UserMsgIterT>
+class MsgIterClsBridge final
+{
+public:
+    static UserMsgIterT&
+    userMsgIterFromLibSelfMsgIterPtr(bt_self_message_iterator * const libSelfMsgIterPtr) noexcept
+    {
+        return SelfMessageIterator {libSelfMsgIterPtr}.data<UserMsgIterT>();
+    }
+
+    static bt_message_iterator_class_initialize_method_status
+    init(bt_self_message_iterator * const libSelfMsgIterPtr,
+         bt_self_message_iterator_configuration * const libSelfMsgIterConfigPtr,
+         bt_self_component_port_output * const libSelfCompPortPtr) noexcept
+    {
+        const auto selfMsgIter = SelfMessageIterator {libSelfMsgIterPtr};
+
+        try {
+            const auto msgIter = new UserMsgIterT {
+                selfMsgIter, SelfMessageIteratorConfiguration {libSelfMsgIterConfigPtr},
+                SelfComponentOutputPort {libSelfCompPortPtr}};
+
+            selfMsgIter.data(*msgIter);
+        } catch (const std::bad_alloc&) {
+            return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING,
+                static_cast<int>(
+                    SelfMessageIterator {libSelfMsgIterPtr}.component().loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+        }
+
+        return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
+    }
+
+    static void finalize(bt_self_message_iterator * const libSelfMsgIterPtr) noexcept
+    {
+        delete &userMsgIterFromLibSelfMsgIterPtr(libSelfMsgIterPtr);
+    }
+
+    static bt_message_iterator_class_next_method_status
+    next(bt_self_message_iterator * const libSelfMsgIterPtr, bt_message_array_const libMsgsPtr,
+         const uint64_t capacity, uint64_t * const count) noexcept
+    {
+        try {
+            auto msgArray = ConstMessageArray::wrapEmpty(libMsgsPtr, capacity);
+            auto& msgIter = userMsgIterFromLibSelfMsgIterPtr(libSelfMsgIterPtr);
+
+            msgIter.next(msgArray);
+            *count = msgArray.release();
+
+            if (G_LIKELY(*count > 0)) {
+                return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+            } else {
+                return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
+            }
+        } catch (const TryAgain&) {
+            return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
+        } catch (const std::bad_alloc&) {
+            return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING,
+                static_cast<int>(
+                    SelfMessageIterator {libSelfMsgIterPtr}.component().loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+        }
+    }
+
+    static bt_message_iterator_class_can_seek_beginning_method_status
+    canSeekBeginning(bt_self_message_iterator * const libSelfMsgIterPtr,
+                     bt_bool * const canSeek) noexcept
+    {
+        try {
+            *canSeek = static_cast<bt_bool>(
+                userMsgIterFromLibSelfMsgIterPtr(libSelfMsgIterPtr).canSeekBeginning());
+        } catch (const TryAgain&) {
+            return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_BEGINNING_METHOD_STATUS_AGAIN;
+        } catch (const std::bad_alloc&) {
+            return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_BEGINNING_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_BEGINNING_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING,
+                static_cast<int>(
+                    SelfMessageIterator {libSelfMsgIterPtr}.component().loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_BEGINNING_METHOD_STATUS_ERROR;
+        }
+
+        return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_BEGINNING_METHOD_STATUS_OK;
+    }
+
+    static bt_message_iterator_class_seek_beginning_method_status
+    seekBeginning(bt_self_message_iterator * const libSelfMsgIterPtr) noexcept
+    {
+        try {
+            userMsgIterFromLibSelfMsgIterPtr(libSelfMsgIterPtr).seekBeginning();
+        } catch (const TryAgain&) {
+            return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_AGAIN;
+        } catch (const std::bad_alloc&) {
+            return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING,
+                static_cast<int>(
+                    SelfMessageIterator {libSelfMsgIterPtr}.component().loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_ERROR;
+        }
+
+        return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_OK;
+    }
+
+    static bt_message_iterator_class_can_seek_ns_from_origin_method_status
+    canSeekNsFromOrigin(bt_self_message_iterator * const libSelfMsgIterPtr,
+                        const std::int64_t nsFromOrigin, bt_bool * const canSeek) noexcept
+    {
+        try {
+            *canSeek = static_cast<bt_bool>(userMsgIterFromLibSelfMsgIterPtr(libSelfMsgIterPtr)
+                                                .canSeekNsFromOrigin(nsFromOrigin));
+        } catch (const TryAgain&) {
+            return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_AGAIN;
+        } catch (const std::bad_alloc&) {
+            return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING,
+                static_cast<int>(
+                    SelfMessageIterator {libSelfMsgIterPtr}.component().loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_ERROR;
+        }
+
+        return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_OK;
+    }
+
+    static bt_message_iterator_class_seek_ns_from_origin_method_status
+    seekNsFromOrigin(bt_self_message_iterator * const libSelfMsgIterPtr,
+                     const std::int64_t nsFromOrigin) noexcept
+    {
+        try {
+            userMsgIterFromLibSelfMsgIterPtr(libSelfMsgIterPtr).seekNsFromOrigin(nsFromOrigin);
+        } catch (const TryAgain&) {
+            return BT_MESSAGE_ITERATOR_CLASS_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_AGAIN;
+        } catch (const std::bad_alloc&) {
+            return BT_MESSAGE_ITERATOR_CLASS_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_MEMORY_ERROR;
+        } catch (const Error&) {
+            return BT_MESSAGE_ITERATOR_CLASS_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_ERROR;
+        } catch (...) {
+            BT_LOG_WRITE_CUR_LVL(
+                BT_LOG_WARNING,
+                static_cast<int>(
+                    SelfMessageIterator {libSelfMsgIterPtr}.component().loggingLevel()),
+                unhandledExcLogTag(), unhandledExcLogStr());
+            return BT_MESSAGE_ITERATOR_CLASS_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_ERROR;
+        }
+
+        return BT_MESSAGE_ITERATOR_CLASS_SEEK_NS_FROM_ORIGIN_METHOD_STATUS_OK;
+    }
+};
+
+} /* namespace internal */
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_INTERNAL_COMP_CLS_BRIDGE_HPP */
diff --git a/src/cpp-common/bt2/internal/shared-obj.hpp b/src/cpp-common/bt2/internal/shared-obj.hpp
deleted file mode 100644 (file)
index 5451be5..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (c) 2019-2020 Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: MIT
- */
-
-#ifndef BABELTRACE_CPP_COMMON_BT2_INTERNAL_SHARED_OBJ_HPP
-#define BABELTRACE_CPP_COMMON_BT2_INTERNAL_SHARED_OBJ_HPP
-
-#include "common/assert.h"
-#include "cpp-common/optional.hpp"
-
-namespace bt2 {
-namespace internal {
-
-/*
- * An instance of this class wraps an optional instance of `ObjT` and
- * manages the reference counting of the underlying libbabeltrace2
- * object.
- *
- * When you move a shared object, it becomes invalid, in that
- * operator*() and operator->() will either fail to assert in debug mode
- * or trigger a segmentation fault.
- *
- * `LibObjT` is the direct libbabeltrace2 object type, for example
- * `bt_stream_class` or `const bt_value`.
- *
- * RefFuncsT::get() must accept a `const LibObjT *` value and increment
- * its reference count.
- *
- * RefFuncsT::put() must accept a `const LibObjT *` value and decrement
- * its reference count.
- */
-template <typename ObjT, typename LibObjT, typename RefFuncsT>
-class SharedObj final
-{
-    /*
-     * This makes it possible for a
-     * `SharedObj<Something, bt_something, ...>` instance to get
-     * assigned an instance of
-     * `SharedObj<SpecificSomething, bt_something, ...>` (copy/move
-     * constructor and assignment operator), given that
-     * `SpecificSomething` inherits `Something`.
-     */
-    template <typename AnyObjT, typename AnyLibObjT, typename AnyRefFuncsT>
-    friend class SharedObj;
-
-public:
-    // This complete shared object
-    using ThisSharedObj = SharedObj<ObjT, LibObjT, RefFuncsT>;
-
-    /*
-     * Builds a shared object from `obj` without an initial reference.
-     *
-     * Use this constructor to build a shared object wrapping a newly
-     * created libbabeltrace2 object.
-     *
-     * Use createWithInitialRef() to build a shared object having an
-     * initial reference count.
-     */
-    explicit SharedObj(const ObjT& obj) noexcept : _mObj {obj}
-    {
-    }
-
-    /*
-     * Builds a shared object from `obj` with an initial reference.
-     *
-     * Use this constructor to build a shared object wrapping a newly
-     * created libbabeltrace2 object.
-     */
-    static ThisSharedObj createWithInitialRef(const ObjT& obj) noexcept
-    {
-        ThisSharedObj sharedObj {obj};
-
-        sharedObj._getRef();
-        return sharedObj;
-    }
-
-    /*
-     * Generic copy constructor.
-     *
-     * See the `friend class SharedObj` comment above.
-     */
-    template <typename OtherObjT, typename OtherLibObjT>
-    SharedObj(const SharedObj<OtherObjT, OtherLibObjT, RefFuncsT>& other) noexcept :
-        _mObj {other._mObj}
-    {
-        this->_getRef();
-    }
-
-    /*
-     * Generic move constructor.
-     *
-     * See the `friend class SharedObj` comment above.
-     */
-    template <typename OtherObjT, typename OtherLibObjT>
-    SharedObj(SharedObj<OtherObjT, OtherLibObjT, RefFuncsT>&& other) noexcept : _mObj {other._mObj}
-    {
-        // Reset moved-from object
-        other._reset();
-    }
-
-    /*
-     * Generic copy assignment operator.
-     *
-     * See the `friend class SharedObj` comment above.
-     */
-    template <typename OtherObjT, typename OtherLibObjT>
-    ThisSharedObj& operator=(const SharedObj<OtherObjT, OtherLibObjT, RefFuncsT>& other) noexcept
-    {
-        // Put current object's reference
-        this->_putRef();
-
-        // Set new current object and get a reference
-        _mObj = other._mObj;
-        this->_getRef();
-
-        return *this;
-    }
-
-    /*
-     * Generic move assignment operator.
-     *
-     * See the `friend class SharedObj` comment above.
-     */
-    template <typename OtherObjT, typename OtherLibObjT>
-    ThisSharedObj& operator=(SharedObj<OtherObjT, OtherLibObjT, RefFuncsT>&& other) noexcept
-    {
-        // Put current object's reference
-        this->_putRef();
-
-        // Set new current object
-        _mObj = other._mObj;
-
-        // Reset moved-from object
-        other._reset();
-
-        return *this;
-    }
-
-    ~SharedObj()
-    {
-        this->_putRef();
-    }
-
-    ObjT& operator*() noexcept
-    {
-        BT_ASSERT_DBG(_mObj);
-        return *_mObj;
-    }
-
-    const ObjT& operator*() const noexcept
-    {
-        BT_ASSERT_DBG(_mObj);
-        return *_mObj;
-    }
-
-    ObjT *operator->() noexcept
-    {
-        BT_ASSERT_DBG(_mObj);
-        return &*_mObj;
-    }
-
-    const ObjT *operator->() const noexcept
-    {
-        BT_ASSERT_DBG(_mObj);
-        return &*_mObj;
-    }
-
-private:
-    /*
-     * Resets this shared object.
-     *
-     * To be used when moving it.
-     */
-    void _reset() noexcept
-    {
-        _mObj.reset();
-    }
-
-    /*
-     * Gets a new reference using the configured libbabeltrace2
-     * reference incrementation function.
-     */
-    void _getRef() const noexcept
-    {
-        if (_mObj) {
-            RefFuncsT::get(_mObj->_libObjPtr());
-        }
-    }
-
-    /*
-     * Puts a reference using the configured libbabeltrace2 reference
-     * decrementation function.
-     */
-    void _putRef() const noexcept
-    {
-        if (_mObj) {
-            RefFuncsT::put(_mObj->_libObjPtr());
-        }
-    }
-
-    nonstd::optional<ObjT> _mObj;
-};
-
-} // namespace internal
-} // namespace bt2
-
-#endif // BABELTRACE_CPP_COMMON_BT2_INTERNAL_SHARED_OBJ_HPP
index d429f3bc745e036d17eade113c3e7f98230ba977..acb6e72d2185d0cd9016277cbe49d51727850cc7 100644 (file)
@@ -9,20 +9,55 @@
 
 #include <type_traits>
 
-#include "../lib-error.hpp"
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2/exc.hpp"
 
 namespace bt2 {
+
+template <typename>
+class CommonMapValue;
+
+template <typename>
+class CommonFieldClass;
+
+template <typename>
+class CommonPacket;
+
+template <typename>
+class CommonStream;
+
 namespace internal {
 
 template <typename LibObjPtrT>
 void validateCreatedObjPtr(const LibObjPtrT libOjbPtr)
 {
     if (!libOjbPtr) {
-        throw LibMemoryError {};
+        throw MemoryError {};
     }
 }
 
-} // namespace internal
-} // namespace bt2
+template <typename LibObjT, typename DepObjT, typename ConstDepObjT>
+using DepType =
+    typename std::conditional<std::is_const<LibObjT>::value, ConstDepObjT, DepObjT>::type;
+
+template <typename LibObjT>
+using DepUserAttrs = DepType<LibObjT, CommonMapValue<bt_value>, CommonMapValue<const bt_value>>;
+
+template <typename LibObjT>
+using DepFc =
+    DepType<LibObjT, CommonFieldClass<bt_field_class>, CommonFieldClass<const bt_field_class>>;
+
+template <typename LibObjT>
+using DepPacket = DepType<LibObjT, CommonPacket<bt_packet>, CommonPacket<const bt_packet>>;
+
+template <typename LibObjT>
+using DepStream = DepType<LibObjT, CommonStream<bt_stream>, CommonStream<const bt_stream>>;
+
+template <typename ObjT>
+struct TypeDescr;
+
+} /* namespace internal */
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_INTERNAL_UTILS_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_INTERNAL_UTILS_HPP */
diff --git a/src/cpp-common/bt2/lib-error.hpp b/src/cpp-common/bt2/lib-error.hpp
deleted file mode 100644 (file)
index 646b7fc..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: MIT
- */
-
-#ifndef BABELTRACE_CPP_COMMON_BT2_LIB_ERROR_HPP
-#define BABELTRACE_CPP_COMMON_BT2_LIB_ERROR_HPP
-
-#include <string>
-#include <stdexcept>
-
-namespace bt2 {
-
-/*
- * Any library error.
- */
-class LibError : public std::runtime_error
-{
-public:
-    explicit LibError(const std::string& msg = "Error") : std::runtime_error {msg}
-    {
-    }
-};
-
-/*
- * Memory error.
- */
-class LibMemoryError : public LibError
-{
-public:
-    LibMemoryError() : LibError {"Memory error"}
-    {
-    }
-};
-
-/*
- * Overflow error.
- */
-class LibOverflowError : public LibError
-{
-public:
-    LibOverflowError() : LibError {"Overflow error"}
-    {
-    }
-};
-
-} // namespace bt2
-
-#endif // BABELTRACE_CPP_COMMON_BT2_LIB_ERROR_HPP
index 471ac4775a18aaf673ae712a87e750cc5f3cabf4..d8c8a480b5751c18dd8035d090e51cb7a068d596 100644 (file)
@@ -9,19 +9,27 @@
 
 #include <babeltrace2/babeltrace.h>
 
+#include "common/macros.h"
+
 namespace bt2 {
 
+/* Avoid `-Wshadow` error on GCC, conflicting with `bt2::Error` */
+BT_DIAG_PUSH
+BT_DIAG_IGNORE_SHADOW
+
 enum class LoggingLevel
 {
-    TRACE = BT_LOGGING_LEVEL_TRACE,
-    DEBUG = BT_LOGGING_LEVEL_DEBUG,
-    INFO = BT_LOGGING_LEVEL_INFO,
-    WARNING = BT_LOGGING_LEVEL_WARNING,
-    ERROR = BT_LOGGING_LEVEL_ERROR,
-    FATAL = BT_LOGGING_LEVEL_FATAL,
-    NONE = BT_LOGGING_LEVEL_NONE,
+    Trace = BT_LOGGING_LEVEL_TRACE,
+    Debug = BT_LOGGING_LEVEL_DEBUG,
+    Info = BT_LOGGING_LEVEL_INFO,
+    Warning = BT_LOGGING_LEVEL_WARNING,
+    Error = BT_LOGGING_LEVEL_ERROR,
+    Fatal = BT_LOGGING_LEVEL_FATAL,
+    None = BT_LOGGING_LEVEL_NONE,
 };
 
-} // namespace bt2
+BT_DIAG_POP
+
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_LOGGING_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_LOGGING_HPP */
diff --git a/src/cpp-common/bt2/message-array.hpp b/src/cpp-common/bt2/message-array.hpp
new file mode 100644 (file)
index 0000000..76b3491
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_MESSAGE_ARRAY_HPP
+#define BABELTRACE_CPP_COMMON_BT2_MESSAGE_ARRAY_HPP
+
+#include <algorithm>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+
+#include "message.hpp"
+
+namespace bt2 {
+
+class ConstMessageArray;
+
+class ConstMessageArrayIterator final
+{
+    friend class ConstMessageArray;
+
+public:
+    using difference_type = std::ptrdiff_t;
+    using value_type = ConstMessage;
+    using pointer = value_type *;
+    using reference = value_type&;
+    using iterator_category = std::input_iterator_tag;
+
+private:
+    explicit ConstMessageArrayIterator(const ConstMessageArray& msgArray,
+                                       const uint64_t idx) noexcept :
+        _mMsgArray {&msgArray},
+        _mIdx {idx}
+    {
+    }
+
+public:
+    ConstMessageArrayIterator& operator++() noexcept
+    {
+        ++_mIdx;
+        return *this;
+    }
+
+    ConstMessageArrayIterator operator++(int) noexcept
+    {
+        const auto tmp = *this;
+
+        ++(*this);
+        return tmp;
+    }
+
+    bool operator==(const ConstMessageArrayIterator& other) const noexcept
+    {
+        BT_ASSERT_DBG(_mMsgArray == other._mMsgArray);
+        return _mIdx == other._mIdx;
+    }
+
+    bool operator!=(const ConstMessageArrayIterator& other) const noexcept
+    {
+        return !(*this == other);
+    }
+
+    ConstMessage operator*() noexcept;
+
+private:
+    const ConstMessageArray *_mMsgArray;
+    uint64_t _mIdx;
+};
+
+/*
+ * A wrapper of `bt_message_array_const`, either:
+ *
+ * Containing existing messages:
+ *     Use ConstMessageArray::wrapExisting().
+ *
+ *     Example:
+ *
+ *         bt_message_array_const libMsgs;
+ *         uint64_t count;
+ *
+ *         if (bt_message_iterator_next(myIter, &libMsgs, &count) !=
+ *                 BT_MESSAGE_ITERATOR_NEXT_STATUS_OK) {
+ *             // Handle special status
+ *         }
+ *
+ *         const auto msgs = bt2::ConstMessageArray::wrapExisting(libMsgs,
+ *                                                                count);
+ *
+ *         // At this point `msgs` manages `libMsgs`
+ *
+ * An empty one which you need to fill:
+ *     Use ConstMessageArray::wrapEmpty().
+ *
+ *     Use the release() method to move the ownership of the wrapped
+ *     library array to you.
+ *
+ *     Example:
+ *
+ *         bt_message_iterator_class_next_method_status myNext(
+ *                 bt_self_message_iterator * const libSelfMsgIter,
+ *                 const bt_message_array_const libMsgs,
+ *                 const uint64_t capacity, uint64_t * const count)
+ *         {
+ *             auto msgs = bt2::ConstMessageArray::wrapEmpty(libMsgs,
+ *                                                           capacity);
+ *
+ *             // Create messages and append with `msgs.append(...)`
+ *
+ *             *count = msgs.release();
+ *             return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+ *         }
+ *
+ * In both cases, the returned message array wrapper is always the sole
+ * owner of the wrapped library array: it's not a simple passive view.
+ * The destructor puts the references of the contained messages.
+ */
+class ConstMessageArray final
+{
+private:
+    explicit ConstMessageArray(const bt_message_array_const libArrayPtr, const std::uint64_t length,
+                               const std::uint64_t capacity) noexcept :
+        _mLibArrayPtr {libArrayPtr},
+        _mLen {length}, _mCap {capacity}
+    {
+        BT_ASSERT_DBG(length <= capacity);
+        BT_ASSERT_DBG(capacity > 0);
+    }
+
+public:
+    using Iterator = ConstMessageArrayIterator;
+
+    ~ConstMessageArray()
+    {
+        this->_putMsgRefs();
+    }
+
+    /*
+     * Not available because there's no underlying message array to
+     * copy to.
+     */
+    ConstMessageArray(const ConstMessageArray&) = delete;
+
+    /*
+     * Not available because we don't need it yet.
+     */
+    ConstMessageArray& operator=(const ConstMessageArray&) = delete;
+
+    ConstMessageArray(ConstMessageArray&& other) noexcept :
+        ConstMessageArray {other._mLibArrayPtr, other._mLen, other._mCap}
+    {
+        other._reset();
+    }
+
+    ConstMessageArray& operator=(ConstMessageArray&& other) noexcept
+    {
+        /* Put existing message references */
+        this->_putMsgRefs();
+
+        /* Move other members to this message array */
+        _mLibArrayPtr = other._mLibArrayPtr;
+        _mLen = other._mLen;
+        _mCap = other._mCap;
+
+        /* Reset other message array */
+        other._reset();
+
+        return *this;
+    }
+
+    /*
+     * Wraps an existing library array `libArrayPtr`, known to contain
+     * `length` messages.
+     *
+     * CAUTION: The ownership of the existing messages contained in
+     * `libArrayPtr` is _moved_ to the returned `ConstMessageArray`
+     * instance.
+     *
+     * This is similar to what the constructor of `std::shared_ptr`
+     * does. Do NOT wrap the same library array twice.
+     */
+    static ConstMessageArray wrapExisting(const bt_message_array_const libArrayPtr,
+                                          const std::uint64_t length) noexcept
+    {
+        return ConstMessageArray {libArrayPtr, length, length};
+    }
+
+    /*
+     * Wraps an existing library array `libArrayPtr`, known to be empty,
+     * with a capacity of `capacity` messages.
+     */
+    static ConstMessageArray wrapEmpty(const bt_message_array_const libArrayPtr,
+                                       const std::uint64_t capacity) noexcept
+    {
+        return ConstMessageArray {libArrayPtr, 0, capacity};
+    }
+
+    std::uint64_t length() const noexcept
+    {
+        return _mLen;
+    }
+
+    std::uint64_t capacity() const noexcept
+    {
+        return _mCap;
+    }
+
+    bool isEmpty() const noexcept
+    {
+        return _mLen == 0;
+    }
+
+    bool isFull() const noexcept
+    {
+        return _mLen == _mCap;
+    }
+
+    ConstMessageArray& append(ConstMessage::Shared message) noexcept
+    {
+        BT_ASSERT_DBG(!this->isFull());
+
+        /* Move reference to underlying array */
+        _mLibArrayPtr[_mLen] = message.release().libObjPtr();
+        ++_mLen;
+        return *this;
+    }
+
+    /*
+     * Transfers the ownership of the wrapped library array to the
+     * caller, returning the number of contained messages (array
+     * length).
+     */
+    std::uint64_t release() noexcept
+    {
+        const auto len = _mLen;
+
+        this->_reset();
+        return len;
+    }
+
+    ConstMessage operator[](const std::uint64_t index) const noexcept
+    {
+        BT_ASSERT_DBG(index < _mLen);
+        return ConstMessage {_mLibArrayPtr[index]};
+    }
+
+    Iterator begin() const noexcept
+    {
+        return Iterator {*this, 0};
+    }
+
+    Iterator end() const noexcept
+    {
+        return Iterator {*this, this->length()};
+    }
+
+private:
+    void _reset() noexcept
+    {
+        /*
+         * This means this array is pretty much dead, and any call to
+         * append() or operator[]() will make assertions fail.
+         *
+         * That being said, you may still move another array to this
+         * one.
+         */
+        _mLen = 0;
+        _mCap = 0;
+    }
+
+    /*
+     * Decrements the reference count of all the contained messages.
+     */
+    void _putMsgRefs() noexcept
+    {
+        std::for_each(&_mLibArrayPtr[0], &_mLibArrayPtr[_mLen], [](const bt_message * const msg) {
+            bt_message_put_ref(msg);
+        });
+    }
+
+    /* Underlying array which is generally owned by the library */
+    bt_message_array_const _mLibArrayPtr;
+
+    /* Length (count of contained messages) */
+    std::uint64_t _mLen;
+
+    /* Capacity (maximum length) */
+    std::uint64_t _mCap;
+};
+
+inline ConstMessage ConstMessageArrayIterator::operator*() noexcept
+{
+    return (*_mMsgArray)[_mIdx];
+}
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_MESSAGE_ARRAY_HPP */
diff --git a/src/cpp-common/bt2/message-iterator.hpp b/src/cpp-common/bt2/message-iterator.hpp
new file mode 100644 (file)
index 0000000..36c144d
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_MESSAGE_ITERATOR_HPP
+#define BABELTRACE_CPP_COMMON_BT2_MESSAGE_ITERATOR_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/common.h"
+#include "cpp-common/bt2s/optional.hpp"
+
+#include "component-port.hpp"
+#include "exc.hpp"
+#include "message-array.hpp"
+#include "shared-object.hpp"
+
+namespace bt2 {
+namespace internal {
+
+struct MessageIteratorRefFuncs final
+{
+    static void get(const bt_message_iterator * const libObjPtr) noexcept
+    {
+        bt_message_iterator_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_message_iterator * const libObjPtr) noexcept
+    {
+        bt_message_iterator_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+class MessageIterator final : public BorrowedObject<bt_message_iterator>
+{
+public:
+    using Shared =
+        SharedObject<MessageIterator, bt_message_iterator, internal::MessageIteratorRefFuncs>;
+
+    explicit MessageIterator(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    ConstComponent component() const noexcept
+    {
+        return ConstComponent {bt_message_iterator_borrow_component(this->libObjPtr())};
+    }
+
+    bt2s::optional<ConstMessageArray> next() const
+    {
+        bt_message_array_const libMsgsPtr;
+        std::uint64_t count;
+        const auto status = bt_message_iterator_next(this->libObjPtr(), &libMsgsPtr, &count);
+
+        switch (status) {
+        case BT_MESSAGE_ITERATOR_NEXT_STATUS_OK:
+            /* Caller becomes the owner of the contained messages */
+            return ConstMessageArray::wrapExisting(libMsgsPtr, count);
+        case BT_MESSAGE_ITERATOR_NEXT_STATUS_END:
+            return bt2s::nullopt;
+        case BT_MESSAGE_ITERATOR_NEXT_STATUS_AGAIN:
+            throw TryAgain {};
+        case BT_MESSAGE_ITERATOR_NEXT_STATUS_MEMORY_ERROR:
+            throw MemoryError {};
+        case BT_MESSAGE_ITERATOR_NEXT_STATUS_ERROR:
+            throw Error {};
+        default:
+            bt_common_abort();
+        }
+    }
+
+    bool canSeekBeginning() const
+    {
+        bt_bool canSeek;
+
+        const auto status = bt_message_iterator_can_seek_beginning(this->libObjPtr(), &canSeek);
+
+        switch (status) {
+        case BT_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_STATUS_OK:
+            return static_cast<bool>(canSeek);
+        case BT_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_STATUS_AGAIN:
+            throw TryAgain {};
+        case BT_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_STATUS_MEMORY_ERROR:
+            throw MemoryError {};
+        case BT_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_STATUS_ERROR:
+            throw Error {};
+        default:
+            bt_common_abort();
+        }
+    }
+
+    MessageIterator seekBeginning() const
+    {
+        const auto status = bt_message_iterator_seek_beginning(this->libObjPtr());
+
+        switch (status) {
+        case BT_MESSAGE_ITERATOR_SEEK_BEGINNING_STATUS_OK:
+            break;
+        case BT_MESSAGE_ITERATOR_SEEK_BEGINNING_STATUS_AGAIN:
+            throw TryAgain {};
+        case BT_MESSAGE_ITERATOR_SEEK_BEGINNING_STATUS_MEMORY_ERROR:
+            throw MemoryError {};
+        case BT_MESSAGE_ITERATOR_SEEK_BEGINNING_STATUS_ERROR:
+            throw Error {};
+        default:
+            bt_common_abort();
+        }
+
+        return *this;
+    }
+
+    bool canSeekNsFromOrigin(const std::int64_t nsFromOrigin) const
+    {
+        bt_bool canSeek;
+
+        const auto status =
+            bt_message_iterator_can_seek_ns_from_origin(this->libObjPtr(), nsFromOrigin, &canSeek);
+
+        switch (status) {
+        case BT_MESSAGE_ITERATOR_CAN_SEEK_NS_FROM_ORIGIN_STATUS_OK:
+            return static_cast<bool>(canSeek);
+        case BT_MESSAGE_ITERATOR_CAN_SEEK_NS_FROM_ORIGIN_STATUS_AGAIN:
+            throw TryAgain {};
+        case BT_MESSAGE_ITERATOR_CAN_SEEK_NS_FROM_ORIGIN_STATUS_MEMORY_ERROR:
+            throw MemoryError {};
+        case BT_MESSAGE_ITERATOR_CAN_SEEK_NS_FROM_ORIGIN_STATUS_ERROR:
+            throw Error {};
+        default:
+            bt_common_abort();
+        }
+    }
+
+    MessageIterator seekNsFromOrigin(const std::int64_t nsFromOrigin) const
+    {
+        const auto status =
+            bt_message_iterator_seek_ns_from_origin(this->libObjPtr(), nsFromOrigin);
+
+        switch (status) {
+        case BT_MESSAGE_ITERATOR_SEEK_NS_FROM_ORIGIN_STATUS_OK:
+            break;
+        case BT_MESSAGE_ITERATOR_SEEK_NS_FROM_ORIGIN_STATUS_AGAIN:
+            throw TryAgain {};
+        case BT_MESSAGE_ITERATOR_SEEK_NS_FROM_ORIGIN_STATUS_MEMORY_ERROR:
+            throw MemoryError {};
+        case BT_MESSAGE_ITERATOR_SEEK_NS_FROM_ORIGIN_STATUS_ERROR:
+            throw Error {};
+        default:
+            bt_common_abort();
+        }
+
+        return *this;
+    }
+
+    bool canSeekForward() const noexcept
+    {
+        return static_cast<bool>(bt_message_iterator_can_seek_forward(this->libObjPtr()));
+    }
+
+    Shared shared() const noexcept
+    {
+        return Shared::createWithRef(*this);
+    }
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_MESSAGE_ITERATOR_HPP */
index 65404f3c270ca5453a4d0cf9ee1805a435366e21..77321778ad0394e6e81482400219a8f0731524e5 100644 (file)
@@ -7,41 +7,43 @@
 #ifndef BABELTRACE_CPP_COMMON_BT2_MESSAGE_HPP
 #define BABELTRACE_CPP_COMMON_BT2_MESSAGE_HPP
 
-#include <type_traits>
 #include <cstdint>
-#include <functional>
+#include <type_traits>
+
 #include <babeltrace2/babeltrace.h>
 
 #include "common/assert.h"
-#include "common/common.h"
-#include "internal/borrowed-obj.hpp"
-#include "internal/shared-obj.hpp"
+#include "cpp-common/bt2/clock-snapshot.hpp"
+#include "cpp-common/bt2/trace-ir.hpp"
+#include "cpp-common/bt2s/optional.hpp"
+#include "cpp-common/vendor/wise-enum/wise_enum.h"
+
+#include "borrowed-object.hpp"
 #include "internal/utils.hpp"
-#include "cpp-common/optional.hpp"
-#include "cpp-common/string_view.hpp"
-#include "lib-error.hpp"
+#include "optional-borrowed-object.hpp"
+#include "shared-object.hpp"
+#include "trace-ir.hpp"
 
 namespace bt2 {
-
 namespace internal {
 
 struct MessageRefFuncs final
 {
-    static void get(const bt_message * const libObjPtr)
+    static void get(const bt_message * const libObjPtr) noexcept
     {
         bt_message_get_ref(libObjPtr);
     }
 
-    static void put(const bt_message * const libObjPtr)
+    static void put(const bt_message * const libObjPtr) noexcept
     {
         bt_message_put_ref(libObjPtr);
     }
 };
 
-template <typename ObjT, typename LibObjT>
-using SharedMessage = internal::SharedObj<ObjT, LibObjT, internal::MessageRefFuncs>;
+} /* namespace internal */
 
-} // namespace internal
+template <typename ObjT, typename LibObjT>
+using SharedMessage = SharedObject<ObjT, LibObjT, internal::MessageRefFuncs>;
 
 template <typename LibObjT>
 class CommonStreamBeginningMessage;
@@ -67,95 +69,108 @@ class CommonDiscardedPacketsMessage;
 template <typename LibObjT>
 class CommonMessageIteratorInactivityMessage;
 
-enum class MessageType
-{
-    STREAM_BEGINNING = BT_MESSAGE_TYPE_STREAM_BEGINNING,
-    STREAM_END = BT_MESSAGE_TYPE_STREAM_END,
-    EVENT = BT_MESSAGE_TYPE_EVENT,
-    PACKET_BEGINNING = BT_MESSAGE_TYPE_PACKET_BEGINNING,
-    PACKET_END = BT_MESSAGE_TYPE_PACKET_END,
-    DISCARDED_EVENTS = BT_MESSAGE_TYPE_DISCARDED_EVENTS,
-    DISCARDED_PACKETS = BT_MESSAGE_TYPE_DISCARDED_PACKETS,
-    MESSAGE_ITERATOR_INACTIVITY = BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY,
-};
+/* clang-format off */
+
+WISE_ENUM_CLASS(MessageType,
+    (StreamBeginning, BT_MESSAGE_TYPE_STREAM_BEGINNING),
+    (StreamEnd, BT_MESSAGE_TYPE_STREAM_END),
+    (Event, BT_MESSAGE_TYPE_EVENT),
+    (PacketBeginning, BT_MESSAGE_TYPE_PACKET_BEGINNING),
+    (PacketEnd, BT_MESSAGE_TYPE_PACKET_END),
+    (DiscardedEvents, BT_MESSAGE_TYPE_DISCARDED_EVENTS),
+    (DiscardedPackets, BT_MESSAGE_TYPE_DISCARDED_PACKETS),
+    (MessageIteratorInactivity, BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY));
+
+/* clang-format on */
 
 template <typename LibObjT>
-class CommonMessage : public internal::BorrowedObj<LibObjT>
+class CommonMessage : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
 
 protected:
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
     using _ThisCommonMessage = CommonMessage<LibObjT>;
 
 public:
-    using Shared = internal::SharedMessage<CommonMessage<LibObjT>, LibObjT>;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedMessage<CommonMessage<LibObjT>, LibObjT>;
 
-    explicit CommonMessage(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonMessage(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonMessage(const CommonMessage<OtherLibObjT>& val) noexcept : _ThisBorrowedObj {val}
+    CommonMessage(const CommonMessage<OtherLibObjT> val) noexcept : _ThisBorrowedObject {val}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonMessage& operator=(const CommonMessage<OtherLibObjT>& val) noexcept
+    _ThisCommonMessage operator=(const CommonMessage<OtherLibObjT> val) noexcept
     {
-        _ThisBorrowedObj::operator=(val);
+        _ThisBorrowedObject::operator=(val);
         return *this;
     }
 
+    CommonMessage<const bt_message> asConst() const noexcept
+    {
+        return CommonMessage<const bt_message> {*this};
+    }
+
     MessageType type() const noexcept
     {
-        return static_cast<MessageType>(bt_message_get_type(this->_libObjPtr()));
+        return static_cast<MessageType>(bt_message_get_type(this->libObjPtr()));
     }
 
     bool isStreamBeginning() const noexcept
     {
-        return this->type() == MessageType::STREAM_BEGINNING;
+        return this->type() == MessageType::StreamBeginning;
     }
 
     bool isStreamEnd() const noexcept
     {
-        return this->type() == MessageType::STREAM_END;
+        return this->type() == MessageType::StreamEnd;
     }
 
     bool isEvent() const noexcept
     {
-        return this->type() == MessageType::EVENT;
+        return this->type() == MessageType::Event;
     }
 
     bool isPacketBeginning() const noexcept
     {
-        return this->type() == MessageType::PACKET_BEGINNING;
+        return this->type() == MessageType::PacketBeginning;
     }
 
     bool isPacketEnd() const noexcept
     {
-        return this->type() == MessageType::PACKET_END;
+        return this->type() == MessageType::PacketEnd;
     }
 
     bool isDiscardedEvents() const noexcept
     {
-        return this->type() == MessageType::DISCARDED_EVENTS;
+        return this->type() == MessageType::DiscardedEvents;
     }
 
     bool isDiscardedPackets() const noexcept
     {
-        return this->type() == MessageType::DISCARDED_PACKETS;
+        return this->type() == MessageType::DiscardedPackets;
     }
 
     bool isMessageIteratorInactivity() const noexcept
     {
-        return this->type() == MessageType::MESSAGE_ITERATOR_INACTIVITY;
+        return this->type() == MessageType::MessageIteratorInactivity;
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
+    }
+
+    template <typename MessageT>
+    MessageT as() const noexcept
+    {
+        return MessageT {this->libObjPtr()};
     }
 
     CommonStreamBeginningMessage<LibObjT> asStreamBeginning() const noexcept;
@@ -173,10 +188,26 @@ using ConstMessage = CommonMessage<const bt_message>;
 
 namespace internal {
 
+struct MessageTypeDescr
+{
+    using Const = ConstMessage;
+    using NonConst = Message;
+};
+
+template <>
+struct TypeDescr<Message> : public MessageTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstMessage> : public MessageTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonStreamBeginningMessageSpec;
 
-// Functions specific to mutable stream beginning messages
+/* Functions specific to mutable stream beginning messages */
 template <>
 struct CommonStreamBeginningMessageSpec<bt_message> final
 {
@@ -186,7 +217,7 @@ struct CommonStreamBeginningMessageSpec<bt_message> final
     }
 };
 
-// Functions specific to constant stream beginning messages
+/* Functions specific to constant stream beginning messages */
 template <>
 struct CommonStreamBeginningMessageSpec<const bt_message> final
 {
@@ -196,77 +227,81 @@ struct CommonStreamBeginningMessageSpec<const bt_message> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonStreamBeginningMessage final : public CommonMessage<LibObjT>
 {
 private:
-    using typename CommonMessage<LibObjT>::_LibObjPtr;
     using typename CommonMessage<LibObjT>::_ThisCommonMessage;
-
-    using _Stream =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonStream<const bt_stream>,
-                                  CommonStream<bt_stream>>::type;
+    using _Stream = internal::DepStream<LibObjT>;
 
 public:
-    using Shared = internal::SharedMessage<CommonStreamBeginningMessage<LibObjT>, LibObjT>;
+    using typename CommonMessage<LibObjT>::LibObjPtr;
+    using Shared = SharedMessage<CommonStreamBeginningMessage<LibObjT>, LibObjT>;
 
-    explicit CommonStreamBeginningMessage(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonStreamBeginningMessage(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonMessage {libObjPtr}
     {
         BT_ASSERT_DBG(this->isStreamBeginning());
     }
 
     template <typename OtherLibObjT>
-    CommonStreamBeginningMessage(const CommonStreamBeginningMessage<OtherLibObjT>& val) noexcept :
+    CommonStreamBeginningMessage(const CommonStreamBeginningMessage<OtherLibObjT> val) noexcept :
         _ThisCommonMessage {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStreamBeginningMessage<LibObjT>&
-    operator=(const CommonStreamBeginningMessage<OtherLibObjT>& val) noexcept
+    CommonStreamBeginningMessage<LibObjT>
+    operator=(const CommonStreamBeginningMessage<OtherLibObjT> val) noexcept
     {
         _ThisCommonMessage::operator=(val);
         return *this;
     }
 
-    ConstStream stream() const noexcept
+    CommonStreamBeginningMessage<const bt_message> asConst() const noexcept
     {
-        return ConstStream {internal::CommonStreamBeginningMessageSpec<const bt_message>::stream(
-            this->_libObjPtr())};
+        return CommonStreamBeginningMessage<const bt_message> {*this};
     }
 
-    _Stream stream() noexcept
+    _Stream stream() const noexcept
     {
         return _Stream {
-            internal::CommonStreamBeginningMessageSpec<LibObjT>::stream(this->_libObjPtr())};
+            internal::CommonStreamBeginningMessageSpec<LibObjT>::stream(this->libObjPtr())};
+    }
+
+    OptionalBorrowedObject<ConstClockClass> streamClassDefaultClockClass() const noexcept
+    {
+        return bt_message_stream_beginning_borrow_stream_class_default_clock_class_const(
+            this->libObjPtr());
     }
 
-    void defaultClockSnapshot(const std::uint64_t val) noexcept
+    CommonStreamBeginningMessage defaultClockSnapshot(const std::uint64_t val) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamBeginningMessage`.");
 
-        bt_message_stream_beginning_set_default_clock_snapshot(this->_libObjPtr(), val);
+        bt_message_stream_beginning_set_default_clock_snapshot(this->libObjPtr(), val);
+        return *this;
     }
 
-    nonstd::optional<ConstClockSnapshot> defaultClockSnapshot() const noexcept
+    OptionalBorrowedObject<ConstClockSnapshot> defaultClockSnapshot() const noexcept
     {
         const bt_clock_snapshot *libObjPtr;
         const auto state = bt_message_stream_beginning_borrow_default_clock_snapshot_const(
-            this->_libObjPtr(), &libObjPtr);
+            this->libObjPtr(), &libObjPtr);
 
         if (state == BT_MESSAGE_STREAM_CLOCK_SNAPSHOT_STATE_KNOWN) {
-            return ConstClockSnapshot {libObjPtr};
+            return libObjPtr;
         }
 
-        return nonstd::nullopt;
+        return {};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -275,10 +310,26 @@ using ConstStreamBeginningMessage = CommonStreamBeginningMessage<const bt_messag
 
 namespace internal {
 
+struct StreamBeginningMessageTypeDescr
+{
+    using Const = ConstStreamBeginningMessage;
+    using NonConst = StreamBeginningMessage;
+};
+
+template <>
+struct TypeDescr<StreamBeginningMessage> : public StreamBeginningMessageTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstStreamBeginningMessage> : public StreamBeginningMessageTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonStreamEndMessageSpec;
 
-// Functions specific to mutable stream end messages
+/* Functions specific to mutable stream end messages */
 template <>
 struct CommonStreamEndMessageSpec<bt_message> final
 {
@@ -288,7 +339,7 @@ struct CommonStreamEndMessageSpec<bt_message> final
     }
 };
 
-// Functions specific to constant stream end messages
+/* Functions specific to constant stream end messages */
 template <>
 struct CommonStreamEndMessageSpec<const bt_message> final
 {
@@ -298,76 +349,80 @@ struct CommonStreamEndMessageSpec<const bt_message> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonStreamEndMessage final : public CommonMessage<LibObjT>
 {
 private:
-    using typename CommonMessage<LibObjT>::_LibObjPtr;
     using typename CommonMessage<LibObjT>::_ThisCommonMessage;
-
-    using _Stream =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonStream<const bt_stream>,
-                                  CommonStream<bt_stream>>::type;
+    using _Stream = internal::DepStream<LibObjT>;
 
 public:
-    using Shared = internal::SharedMessage<CommonStreamEndMessage<LibObjT>, LibObjT>;
+    using typename CommonMessage<LibObjT>::LibObjPtr;
+    using Shared = SharedMessage<CommonStreamEndMessage<LibObjT>, LibObjT>;
 
-    explicit CommonStreamEndMessage(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonStreamEndMessage(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonMessage {libObjPtr}
     {
         BT_ASSERT_DBG(this->isStreamEnd());
     }
 
     template <typename OtherLibObjT>
-    CommonStreamEndMessage(const CommonStreamEndMessage<OtherLibObjT>& val) noexcept :
+    CommonStreamEndMessage(const CommonStreamEndMessage<OtherLibObjT> val) noexcept :
         _ThisCommonMessage {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStreamEndMessage<LibObjT>&
-    operator=(const CommonStreamEndMessage<OtherLibObjT>& val) noexcept
+    CommonStreamEndMessage<LibObjT>
+    operator=(const CommonStreamEndMessage<OtherLibObjT> val) noexcept
     {
         _ThisCommonMessage::operator=(val);
         return *this;
     }
 
-    ConstStream stream() const noexcept
+    CommonStreamEndMessage<const bt_message> asConst() const noexcept
+    {
+        return CommonStreamEndMessage<const bt_message> {*this};
+    }
+
+    _Stream stream() const noexcept
     {
-        return ConstStream {
-            internal::CommonStreamEndMessageSpec<const bt_message>::stream(this->_libObjPtr())};
+        return _Stream {internal::CommonStreamEndMessageSpec<LibObjT>::stream(this->libObjPtr())};
     }
 
-    _Stream stream() noexcept
+    OptionalBorrowedObject<ConstClockClass> streamClassDefaultClockClass() const noexcept
     {
-        return _Stream {internal::CommonStreamEndMessageSpec<LibObjT>::stream(this->_libObjPtr())};
+        return bt_message_stream_end_borrow_stream_class_default_clock_class_const(
+            this->libObjPtr());
     }
 
-    void defaultClockSnapshot(const std::uint64_t val) noexcept
+    CommonStreamEndMessage defaultClockSnapshot(const std::uint64_t val) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamEndMessage`.");
 
-        bt_message_stream_end_set_default_clock_snapshot(this->_libObjPtr(), val);
+        bt_message_stream_end_set_default_clock_snapshot(this->libObjPtr(), val);
+        return *this;
     }
 
-    nonstd::optional<ConstClockSnapshot> defaultClockSnapshot() const noexcept
+    OptionalBorrowedObject<ConstClockSnapshot> defaultClockSnapshot() const noexcept
     {
         const bt_clock_snapshot *libObjPtr;
         const auto state = bt_message_stream_end_borrow_default_clock_snapshot_const(
-            this->_libObjPtr(), &libObjPtr);
+            this->libObjPtr(), &libObjPtr);
 
         if (state == BT_MESSAGE_STREAM_CLOCK_SNAPSHOT_STATE_KNOWN) {
-            return ConstClockSnapshot {libObjPtr};
+            return libObjPtr;
         }
 
-        return nonstd::nullopt;
+        return {};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -376,10 +431,26 @@ using ConstStreamEndMessage = CommonStreamEndMessage<const bt_message>;
 
 namespace internal {
 
+struct StreamEndMessageTypeDescr
+{
+    using Const = ConstStreamEndMessage;
+    using NonConst = StreamEndMessage;
+};
+
+template <>
+struct TypeDescr<StreamEndMessage> : public StreamEndMessageTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstStreamEndMessage> : public StreamEndMessageTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonPacketBeginningMessageSpec;
 
-// Functions specific to mutable packet beginning messages
+/* Functions specific to mutable packet beginning messages */
 template <>
 struct CommonPacketBeginningMessageSpec<bt_message> final
 {
@@ -389,7 +460,7 @@ struct CommonPacketBeginningMessageSpec<bt_message> final
     }
 };
 
-// Functions specific to constant packet beginning messages
+/* Functions specific to constant packet beginning messages */
 template <>
 struct CommonPacketBeginningMessageSpec<const bt_message> final
 {
@@ -399,72 +470,76 @@ struct CommonPacketBeginningMessageSpec<const bt_message> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonPacketBeginningMessage final : public CommonMessage<LibObjT>
 {
 private:
-    using typename CommonMessage<LibObjT>::_LibObjPtr;
     using typename CommonMessage<LibObjT>::_ThisCommonMessage;
-
-    using _Packet =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonPacket<const bt_packet>,
-                                  CommonPacket<bt_packet>>::type;
+    using _Packet = internal::DepPacket<LibObjT>;
 
 public:
-    using Shared = internal::SharedMessage<CommonPacketBeginningMessage<LibObjT>, LibObjT>;
+    using typename CommonMessage<LibObjT>::LibObjPtr;
+    using Shared = SharedMessage<CommonPacketBeginningMessage<LibObjT>, LibObjT>;
 
-    explicit CommonPacketBeginningMessage(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonPacketBeginningMessage(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonMessage {libObjPtr}
     {
         BT_ASSERT_DBG(this->isPacketBeginning());
     }
 
     template <typename OtherLibObjT>
-    CommonPacketBeginningMessage(const CommonPacketBeginningMessage<OtherLibObjT>& val) noexcept :
+    CommonPacketBeginningMessage(const CommonPacketBeginningMessage<OtherLibObjT> val) noexcept :
         _ThisCommonMessage {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonPacketBeginningMessage<LibObjT>&
-    operator=(const CommonPacketBeginningMessage<OtherLibObjT>& val) noexcept
+    CommonPacketBeginningMessage<LibObjT>
+    operator=(const CommonPacketBeginningMessage<OtherLibObjT> val) noexcept
     {
         _ThisCommonMessage::operator=(val);
         return *this;
     }
 
-    ConstPacket packet() const noexcept
+    CommonPacketBeginningMessage<const bt_message> asConst() const noexcept
     {
-        return ConstPacket {internal::CommonPacketBeginningMessageSpec<const bt_message>::packet(
-            this->_libObjPtr())};
+        return CommonPacketBeginningMessage<const bt_message> {*this};
     }
 
-    _Packet packet() noexcept
+    _Packet packet() const noexcept
     {
         return _Packet {
-            internal::CommonPacketBeginningMessageSpec<LibObjT>::packet(this->_libObjPtr())};
+            internal::CommonPacketBeginningMessageSpec<LibObjT>::packet(this->libObjPtr())};
     }
 
-    void defaultClockSnapshot(const std::uint64_t val) noexcept
+    OptionalBorrowedObject<ConstClockClass> streamClassDefaultClockClass() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return bt_message_packet_beginning_borrow_stream_class_default_clock_class_const(
+            this->libObjPtr());
+    }
 
-        bt_message_packet_beginning_set_default_clock_snapshot(this->_libObjPtr(), val);
+    CommonPacketBeginningMessage defaultClockSnapshot(const std::uint64_t val) const noexcept
+    {
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstPacketBeginningMessage`.");
+
+        bt_message_packet_beginning_set_default_clock_snapshot(this->libObjPtr(), val);
+        return *this;
     }
 
     ConstClockSnapshot defaultClockSnapshot() const noexcept
     {
         const auto libObjPtr =
-            bt_message_packet_beginning_borrow_default_clock_snapshot_const(this->_libObjPtr());
+            bt_message_packet_beginning_borrow_default_clock_snapshot_const(this->libObjPtr());
 
         return ConstClockSnapshot {libObjPtr};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -473,10 +548,26 @@ using ConstPacketBeginningMessage = CommonPacketBeginningMessage<const bt_messag
 
 namespace internal {
 
+struct PacketBeginningMessageTypeDescr
+{
+    using Const = ConstPacketBeginningMessage;
+    using NonConst = PacketBeginningMessage;
+};
+
+template <>
+struct TypeDescr<PacketBeginningMessage> : public PacketBeginningMessageTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstPacketBeginningMessage> : public PacketBeginningMessageTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonPacketEndMessageSpec;
 
-// Functions specific to mutable packet end messages
+/* Functions specific to mutable packet end messages */
 template <>
 struct CommonPacketEndMessageSpec<bt_message> final
 {
@@ -486,7 +577,7 @@ struct CommonPacketEndMessageSpec<bt_message> final
     }
 };
 
-// Functions specific to constant packet end messages
+/* Functions specific to constant packet end messages */
 template <>
 struct CommonPacketEndMessageSpec<const bt_message> final
 {
@@ -496,71 +587,75 @@ struct CommonPacketEndMessageSpec<const bt_message> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonPacketEndMessage final : public CommonMessage<LibObjT>
 {
 private:
-    using typename CommonMessage<LibObjT>::_LibObjPtr;
     using typename CommonMessage<LibObjT>::_ThisCommonMessage;
-
-    using _Packet =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonPacket<const bt_packet>,
-                                  CommonPacket<bt_packet>>::type;
+    using _Packet = internal::DepPacket<LibObjT>;
 
 public:
-    using Shared = internal::SharedMessage<CommonPacketEndMessage<LibObjT>, LibObjT>;
+    using typename CommonMessage<LibObjT>::LibObjPtr;
+    using Shared = SharedMessage<CommonPacketEndMessage<LibObjT>, LibObjT>;
 
-    explicit CommonPacketEndMessage(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonPacketEndMessage(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonMessage {libObjPtr}
     {
         BT_ASSERT_DBG(this->isPacketEnd());
     }
 
     template <typename OtherLibObjT>
-    CommonPacketEndMessage(const CommonPacketEndMessage<OtherLibObjT>& val) noexcept :
+    CommonPacketEndMessage(const CommonPacketEndMessage<OtherLibObjT> val) noexcept :
         _ThisCommonMessage {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonPacketEndMessage<LibObjT>&
-    operator=(const CommonPacketEndMessage<OtherLibObjT>& val) noexcept
+    CommonPacketEndMessage<LibObjT>
+    operator=(const CommonPacketEndMessage<OtherLibObjT> val) noexcept
     {
         _ThisCommonMessage::operator=(val);
         return *this;
     }
 
-    ConstPacket packet() const noexcept
+    CommonPacketEndMessage<const bt_message> asConst() const noexcept
+    {
+        return CommonPacketEndMessage<const bt_message> {*this};
+    }
+
+    _Packet packet() const noexcept
     {
-        return ConstPacket {
-            internal::CommonPacketEndMessageSpec<const bt_message>::packet(this->_libObjPtr())};
+        return _Packet {internal::CommonPacketEndMessageSpec<LibObjT>::packet(this->libObjPtr())};
     }
 
-    _Packet packet() noexcept
+    OptionalBorrowedObject<ConstClockClass> streamClassDefaultClockClass() const noexcept
     {
-        return _Packet {internal::CommonPacketEndMessageSpec<LibObjT>::packet(this->_libObjPtr())};
+        return bt_message_packet_end_borrow_stream_class_default_clock_class_const(
+            this->libObjPtr());
     }
 
-    void defaultClockSnapshot(const std::uint64_t val) noexcept
+    CommonPacketEndMessage defaultClockSnapshot(const std::uint64_t val) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstPacketEndMessage`.");
 
-        bt_message_packet_end_set_default_clock_snapshot(this->_libObjPtr(), val);
+        bt_message_packet_end_set_default_clock_snapshot(this->libObjPtr(), val);
+        return *this;
     }
 
     ConstClockSnapshot defaultClockSnapshot() const noexcept
     {
         const auto libObjPtr =
-            bt_message_packet_end_borrow_default_clock_snapshot_const(this->_libObjPtr());
+            bt_message_packet_end_borrow_default_clock_snapshot_const(this->libObjPtr());
 
         return ConstClockSnapshot {libObjPtr};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -569,10 +664,26 @@ using ConstPacketEndMessage = CommonPacketEndMessage<const bt_message>;
 
 namespace internal {
 
+struct PacketEndMessageTypeDescr
+{
+    using Const = ConstPacketEndMessage;
+    using NonConst = PacketEndMessage;
+};
+
+template <>
+struct TypeDescr<PacketEndMessage> : public PacketEndMessageTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstPacketEndMessage> : public PacketEndMessageTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonEventMessageSpec;
 
-// Functions specific to mutable event messages
+/* Functions specific to mutable event messages */
 template <>
 struct CommonEventMessageSpec<bt_message> final
 {
@@ -582,7 +693,7 @@ struct CommonEventMessageSpec<bt_message> final
     }
 };
 
-// Functions specific to constant event messages
+/* Functions specific to constant event messages */
 template <>
 struct CommonEventMessageSpec<const bt_message> final
 {
@@ -592,63 +703,63 @@ struct CommonEventMessageSpec<const bt_message> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonEventMessage final : public CommonMessage<LibObjT>
 {
 private:
-    using typename CommonMessage<LibObjT>::_LibObjPtr;
     using typename CommonMessage<LibObjT>::_ThisCommonMessage;
-
-    using _Event =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonEvent<const bt_event>,
-                                  CommonEvent<bt_event>>::type;
+    using _Event = internal::DepType<LibObjT, CommonEvent<bt_event>, CommonEvent<const bt_event>>;
 
 public:
-    using Shared = internal::SharedMessage<CommonEventMessage<LibObjT>, LibObjT>;
+    using typename CommonMessage<LibObjT>::LibObjPtr;
+    using Shared = SharedMessage<CommonEventMessage<LibObjT>, LibObjT>;
 
-    explicit CommonEventMessage(const _LibObjPtr libObjPtr) noexcept :
-        _ThisCommonMessage {libObjPtr}
+    explicit CommonEventMessage(const LibObjPtr libObjPtr) noexcept : _ThisCommonMessage {libObjPtr}
     {
         BT_ASSERT_DBG(this->isEvent());
     }
 
     template <typename OtherLibObjT>
-    CommonEventMessage(const CommonEventMessage<OtherLibObjT>& val) noexcept :
+    CommonEventMessage(const CommonEventMessage<OtherLibObjT> val) noexcept :
         _ThisCommonMessage {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonEventMessage<LibObjT>& operator=(const CommonEventMessage<OtherLibObjT>& val) noexcept
+    CommonEventMessage<LibObjT> operator=(const CommonEventMessage<OtherLibObjT> val) noexcept
     {
         _ThisCommonMessage::operator=(val);
         return *this;
     }
 
-    ConstEvent event() const noexcept
+    CommonEventMessage<const bt_message> asConst() const noexcept
     {
-        return ConstEvent {
-            internal::CommonEventMessageSpec<const bt_message>::event(this->_libObjPtr())};
+        return CommonEventMessage<const bt_message> {*this};
     }
 
-    _Event event() noexcept
+    _Event event() const noexcept
     {
-        return _Event {internal::CommonEventMessageSpec<LibObjT>::event(this->_libObjPtr())};
+        return _Event {internal::CommonEventMessageSpec<LibObjT>::event(this->libObjPtr())};
+    }
+
+    OptionalBorrowedObject<ConstClockClass> streamClassDefaultClockClass() const noexcept
+    {
+        return bt_message_event_borrow_stream_class_default_clock_class_const(this->libObjPtr());
     }
 
     ConstClockSnapshot defaultClockSnapshot() const noexcept
     {
         const auto libObjPtr =
-            bt_message_event_borrow_default_clock_snapshot_const(this->_libObjPtr());
+            bt_message_event_borrow_default_clock_snapshot_const(this->libObjPtr());
 
         return ConstClockSnapshot {libObjPtr};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -657,10 +768,26 @@ using ConstEventMessage = CommonEventMessage<const bt_message>;
 
 namespace internal {
 
+struct EventMessageTypeDescr
+{
+    using Const = ConstEventMessage;
+    using NonConst = EventMessage;
+};
+
+template <>
+struct TypeDescr<EventMessage> : public EventMessageTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstEventMessage> : public EventMessageTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonDiscardedEventsMessageSpec;
 
-// Functions specific to mutable discarded events messages
+/* Functions specific to mutable discarded events messages */
 template <>
 struct CommonDiscardedEventsMessageSpec<bt_message> final
 {
@@ -670,7 +797,7 @@ struct CommonDiscardedEventsMessageSpec<bt_message> final
     }
 };
 
-// Functions specific to constant discarded events messages
+/* Functions specific to constant discarded events messages */
 template <>
 struct CommonDiscardedEventsMessageSpec<const bt_message> final
 {
@@ -680,59 +807,61 @@ struct CommonDiscardedEventsMessageSpec<const bt_message> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonDiscardedEventsMessage final : public CommonMessage<LibObjT>
 {
 private:
-    using typename CommonMessage<LibObjT>::_LibObjPtr;
     using typename CommonMessage<LibObjT>::_ThisCommonMessage;
-
-    using _Stream =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonStream<const bt_stream>,
-                                  CommonStream<bt_stream>>::type;
+    using _Stream = internal::DepStream<LibObjT>;
 
 public:
-    using Shared = internal::SharedMessage<CommonDiscardedEventsMessage<LibObjT>, LibObjT>;
+    using typename CommonMessage<LibObjT>::LibObjPtr;
+    using Shared = SharedMessage<CommonDiscardedEventsMessage<LibObjT>, LibObjT>;
 
-    explicit CommonDiscardedEventsMessage(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonDiscardedEventsMessage(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonMessage {libObjPtr}
     {
         BT_ASSERT_DBG(this->isDiscardedEvents());
     }
 
     template <typename OtherLibObjT>
-    CommonDiscardedEventsMessage(const CommonDiscardedEventsMessage<OtherLibObjT>& val) noexcept :
+    CommonDiscardedEventsMessage(const CommonDiscardedEventsMessage<OtherLibObjT> val) noexcept :
         _ThisCommonMessage {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonDiscardedEventsMessage<LibObjT>&
-    operator=(const CommonDiscardedEventsMessage<OtherLibObjT>& val) noexcept
+    CommonDiscardedEventsMessage<LibObjT>
+    operator=(const CommonDiscardedEventsMessage<OtherLibObjT> val) noexcept
     {
         _ThisCommonMessage::operator=(val);
         return *this;
     }
 
-    ConstStream stream() const noexcept
+    CommonDiscardedEventsMessage<const bt_message> asConst() const noexcept
     {
-        return ConstStream {internal::CommonDiscardedEventsMessageSpec<const bt_message>::stream(
-            this->_libObjPtr())};
+        return CommonDiscardedEventsMessage<const bt_message> {*this};
     }
 
-    _Stream stream() noexcept
+    _Stream stream() const noexcept
     {
         return _Stream {
-            internal::CommonDiscardedEventsMessageSpec<LibObjT>::stream(this->_libObjPtr())};
+            internal::CommonDiscardedEventsMessageSpec<LibObjT>::stream(this->libObjPtr())};
+    }
+
+    OptionalBorrowedObject<ConstClockClass> streamClassDefaultClockClass() const noexcept
+    {
+        return bt_message_discarded_events_borrow_stream_class_default_clock_class_const(
+            this->libObjPtr());
     }
 
     ConstClockSnapshot beginningDefaultClockSnapshot() const noexcept
     {
         const auto libObjPtr =
             bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
-                this->_libObjPtr());
+                this->libObjPtr());
 
         return ConstClockSnapshot {libObjPtr};
     }
@@ -740,33 +869,34 @@ public:
     ConstClockSnapshot endDefaultClockSnapshot() const noexcept
     {
         const auto libObjPtr =
-            bt_message_discarded_events_borrow_end_default_clock_snapshot_const(this->_libObjPtr());
+            bt_message_discarded_events_borrow_end_default_clock_snapshot_const(this->libObjPtr());
 
         return ConstClockSnapshot {libObjPtr};
     }
 
-    void count(const std::uint64_t count) noexcept
+    CommonDiscardedEventsMessage count(const std::uint64_t count) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstDiscardedEventsMessage`.");
 
-        bt_message_discarded_events_set_count(this->_libObjPtr(), count);
+        bt_message_discarded_events_set_count(this->libObjPtr(), count);
+        return *this;
     }
 
-    nonstd::optional<std::uint64_t> count() const noexcept
+    bt2s::optional<std::uint64_t> count() const noexcept
     {
         std::uint64_t count;
-        const auto avail = bt_message_discarded_events_get_count(this->_libObjPtr(), &count);
 
-        if (avail) {
+        if (bt_message_discarded_events_get_count(this->libObjPtr(), &count)) {
             return count;
         }
 
-        return nonstd::nullopt;
+        return bt2s::nullopt;
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -775,10 +905,26 @@ using ConstDiscardedEventsMessage = CommonDiscardedEventsMessage<const bt_messag
 
 namespace internal {
 
+struct DiscardedEventsMessageTypeDescr
+{
+    using Const = ConstDiscardedEventsMessage;
+    using NonConst = DiscardedEventsMessage;
+};
+
+template <>
+struct TypeDescr<DiscardedEventsMessage> : public DiscardedEventsMessageTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstDiscardedEventsMessage> : public DiscardedEventsMessageTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonDiscardedPacketsMessageSpec;
 
-// Functions specific to mutable discarded packets messages
+/* Functions specific to mutable discarded packets messages */
 template <>
 struct CommonDiscardedPacketsMessageSpec<bt_message> final
 {
@@ -788,7 +934,7 @@ struct CommonDiscardedPacketsMessageSpec<bt_message> final
     }
 };
 
-// Functions specific to constant discarded packets messages
+/* Functions specific to constant discarded packets messages */
 template <>
 struct CommonDiscardedPacketsMessageSpec<const bt_message> final
 {
@@ -798,111 +944,133 @@ struct CommonDiscardedPacketsMessageSpec<const bt_message> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonDiscardedPacketsMessage final : public CommonMessage<LibObjT>
 {
 private:
-    using typename CommonMessage<LibObjT>::_LibObjPtr;
     using typename CommonMessage<LibObjT>::_ThisCommonMessage;
-
-    using _Stream =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonStream<const bt_stream>,
-                                  CommonStream<bt_stream>>::type;
+    using _Stream = internal::DepStream<LibObjT>;
 
 public:
-    using Shared = internal::SharedMessage<CommonDiscardedPacketsMessage<LibObjT>, LibObjT>;
+    using typename CommonMessage<LibObjT>::LibObjPtr;
+    using Shared = SharedMessage<CommonDiscardedPacketsMessage<LibObjT>, LibObjT>;
 
-    explicit CommonDiscardedPacketsMessage(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonDiscardedPacketsMessage(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonMessage {libObjPtr}
     {
         BT_ASSERT_DBG(this->isDiscardedPackets());
     }
 
     template <typename OtherLibObjT>
-    CommonDiscardedPacketsMessage(const CommonDiscardedPacketsMessage<OtherLibObjT>& val) noexcept :
+    CommonDiscardedPacketsMessage(const CommonDiscardedPacketsMessage<OtherLibObjT> val) noexcept :
         _ThisCommonMessage {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonDiscardedPacketsMessage<LibObjT>&
-    operator=(const CommonDiscardedPacketsMessage<OtherLibObjT>& val) noexcept
+    CommonDiscardedPacketsMessage<LibObjT>
+    operator=(const CommonDiscardedPacketsMessage<OtherLibObjT> val) noexcept
     {
         _ThisCommonMessage::operator=(val);
         return *this;
     }
 
-    ConstStream stream() const noexcept
+    CommonDiscardedPacketsMessage<const bt_message> asConst() const noexcept
     {
-        return ConstStream {internal::CommonDiscardedPacketsMessageSpec<const bt_message>::stream(
-            this->_libObjPtr())};
+        return CommonDiscardedPacketsMessage<const bt_message> {*this};
     }
 
-    _Stream stream() noexcept
+    _Stream stream() const noexcept
     {
         return _Stream {
-            internal::CommonDiscardedPacketsMessageSpec<LibObjT>::stream(this->_libObjPtr())};
+            internal::CommonDiscardedPacketsMessageSpec<LibObjT>::stream(this->libObjPtr())};
+    }
+
+    OptionalBorrowedObject<ConstClockClass> streamClassDefaultClockClass() const noexcept
+    {
+        return bt_message_discarded_packets_borrow_stream_class_default_clock_class_const(
+            this->libObjPtr());
     }
 
     ConstClockSnapshot beginningDefaultClockSnapshot() const noexcept
     {
         const auto libObjPtr =
             bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
-                this->_libObjPtr());
+                this->libObjPtr());
 
         return ConstClockSnapshot {libObjPtr};
     }
 
     ConstClockSnapshot endDefaultClockSnapshot() const noexcept
     {
-        const auto libObjPtr = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(
-            this->_libObjPtr());
+        const auto libObjPtr =
+            bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(this->libObjPtr());
 
         return ConstClockSnapshot {libObjPtr};
     }
 
-    void count(const std::uint64_t count) noexcept
+    CommonDiscardedPacketsMessage count(const std::uint64_t count) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstDiscardedPacketsMessage`.");
 
-        bt_message_discarded_packets_set_count(this->_libObjPtr(), count);
+        bt_message_discarded_packets_set_count(this->libObjPtr(), count);
+        return *this;
     }
 
-    nonstd::optional<std::uint64_t> count() const noexcept
+    bt2s::optional<std::uint64_t> count() const noexcept
     {
         std::uint64_t count;
-        const auto avail = bt_message_discarded_packets_get_count(this->_libObjPtr(), &count);
 
-        if (avail) {
+        if (bt_message_discarded_packets_get_count(this->libObjPtr(), &count)) {
             return count;
         }
 
-        return nonstd::nullopt;
+        return bt2s::nullopt;
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using DiscardedPacketsMessage = CommonDiscardedPacketsMessage<bt_message>;
 using ConstDiscardedPacketsMessage = CommonDiscardedPacketsMessage<const bt_message>;
 
+namespace internal {
+
+struct DiscardedPacketsMessageTypeDescr
+{
+    using Const = ConstDiscardedPacketsMessage;
+    using NonConst = DiscardedPacketsMessage;
+};
+
+template <>
+struct TypeDescr<DiscardedPacketsMessage> : public DiscardedPacketsMessageTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstDiscardedPacketsMessage> : public DiscardedPacketsMessageTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonMessageIteratorInactivityMessage final : public CommonMessage<LibObjT>
 {
 private:
-    using typename CommonMessage<LibObjT>::_LibObjPtr;
     using typename CommonMessage<LibObjT>::_ThisCommonMessage;
 
 public:
-    using Shared =
-        internal::SharedMessage<CommonMessageIteratorInactivityMessage<LibObjT>, LibObjT>;
+    using typename CommonMessage<LibObjT>::LibObjPtr;
+    using Shared = SharedMessage<CommonMessageIteratorInactivityMessage<LibObjT>, LibObjT>;
 
-    explicit CommonMessageIteratorInactivityMessage(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonMessageIteratorInactivityMessage(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonMessage {libObjPtr}
     {
         BT_ASSERT_DBG(this->isMessageIteratorInactivity());
@@ -910,30 +1078,35 @@ public:
 
     template <typename OtherLibObjT>
     CommonMessageIteratorInactivityMessage(
-        const CommonMessageIteratorInactivityMessage<OtherLibObjT>& val) noexcept :
+        const CommonMessageIteratorInactivityMessage<OtherLibObjT> val) noexcept :
         _ThisCommonMessage {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonMessageIteratorInactivityMessage<LibObjT>&
-    operator=(const CommonMessageIteratorInactivityMessage<OtherLibObjT>& val) noexcept
+    CommonMessageIteratorInactivityMessage<LibObjT>
+    operator=(const CommonMessageIteratorInactivityMessage<OtherLibObjT> val) noexcept
     {
         _ThisCommonMessage::operator=(val);
         return *this;
     }
 
+    CommonMessageIteratorInactivityMessage<const bt_message> asConst() const noexcept
+    {
+        return CommonMessageIteratorInactivityMessage<const bt_message> {*this};
+    }
+
     ConstClockSnapshot clockSnapshot() const noexcept
     {
         const auto libObjPtr =
-            bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(this->_libObjPtr());
+            bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(this->libObjPtr());
 
         return ConstClockSnapshot {libObjPtr};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -941,56 +1114,77 @@ using MessageIteratorInactivityMessage = CommonMessageIteratorInactivityMessage<
 using ConstMessageIteratorInactivityMessage =
     CommonMessageIteratorInactivityMessage<const bt_message>;
 
+namespace internal {
+
+struct MessageIteratorInactivityMessageTypeDescr
+{
+    using Const = ConstMessageIteratorInactivityMessage;
+    using NonConst = MessageIteratorInactivityMessage;
+};
+
+template <>
+struct TypeDescr<MessageIteratorInactivityMessage> :
+    public MessageIteratorInactivityMessageTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstMessageIteratorInactivityMessage> :
+    public MessageIteratorInactivityMessageTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 CommonStreamBeginningMessage<LibObjT> CommonMessage<LibObjT>::asStreamBeginning() const noexcept
 {
-    BT_ASSERT_DBG(this->isStreamBeginning());
-    return CommonStreamBeginningMessage<LibObjT> {this->_libObjPtr()};
+    return CommonStreamBeginningMessage<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonStreamEndMessage<LibObjT> CommonMessage<LibObjT>::asStreamEnd() const noexcept
 {
-    BT_ASSERT_DBG(this->isStreamEnd());
-    return CommonStreamEndMessage<LibObjT> {this->_libObjPtr()};
+    return CommonStreamEndMessage<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonPacketBeginningMessage<LibObjT> CommonMessage<LibObjT>::asPacketBeginning() const noexcept
 {
-    BT_ASSERT_DBG(this->isPacketBeginning());
-    return CommonPacketBeginningMessage<LibObjT> {this->_libObjPtr()};
+    return CommonPacketBeginningMessage<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonPacketEndMessage<LibObjT> CommonMessage<LibObjT>::asPacketEnd() const noexcept
 {
-    BT_ASSERT_DBG(this->isPacketEnd());
-    return CommonPacketEndMessage<LibObjT> {this->_libObjPtr()};
+    return CommonPacketEndMessage<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonEventMessage<LibObjT> CommonMessage<LibObjT>::asEvent() const noexcept
 {
-    BT_ASSERT_DBG(this->isEvent());
-    return CommonEventMessage<LibObjT> {this->_libObjPtr()};
+    return CommonEventMessage<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonDiscardedEventsMessage<LibObjT> CommonMessage<LibObjT>::asDiscardedEvents() const noexcept
 {
-    BT_ASSERT_DBG(this->isDiscardedEvents());
-    return CommonDiscardedEventsMessage<LibObjT> {this->_libObjPtr()};
+    return CommonDiscardedEventsMessage<LibObjT> {this->libObjPtr()};
+}
+
+template <typename LibObjT>
+CommonDiscardedPacketsMessage<LibObjT> CommonMessage<LibObjT>::asDiscardedPackets() const noexcept
+{
+    return CommonDiscardedPacketsMessage<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonMessageIteratorInactivityMessage<LibObjT>
 CommonMessage<LibObjT>::asMessageIteratorInactivity() const noexcept
 {
-    BT_ASSERT_DBG(this->isMessageIteratorInactivity());
-    return CommonMessageIteratorInactivityMessage<LibObjT> {this->_libObjPtr()};
+    return CommonMessageIteratorInactivityMessage<LibObjT> {this->libObjPtr()};
 }
 
-} // namespace bt2
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_MESSAGE_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_MESSAGE_HPP */
diff --git a/src/cpp-common/bt2/optional-borrowed-object.hpp b/src/cpp-common/bt2/optional-borrowed-object.hpp
new file mode 100644 (file)
index 0000000..a967c27
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_OPTIONAL_BORROWED_OBJECT_HPP
+#define BABELTRACE_CPP_COMMON_BT2_OPTIONAL_BORROWED_OBJECT_HPP
+
+#include <type_traits>
+
+#include "borrowed-object-proxy.hpp"
+
+namespace bt2 {
+
+/*
+ * An instance of this class template manages an optional contained
+ * borrowed object of type `ObjT`, that is, a borrowed object that may
+ * or may not be present.
+ *
+ * Such an object considers that a `nullptr` libbabeltrace2 object
+ * pointer means none. Therefore, using an `OptionalBorrowedObject`
+ * isn't more costly, in time and space, as using a libbabeltrace2
+ * object pointer in C, but offers the typical C++ optional interface.
+ *
+ * There's no `bt2s::nullopt` equivalent: just use the default
+ * constructor or call reset().
+ *
+ * There are a constructors an assignment operators which accept an
+ * instance of another wrapper object or of another optional borrowed
+ * object. For those, static assertions make sure that the assignment
+ * would work with borrowed objects, for example:
+ *
+ *     auto sharedIntVal = createValue(23L);
+ *
+ *     OptionalBorrowedObject<Value> myVal {*sharedIntVal};
+ *
+ * This is needed because `OptionalBorrowedObject` only keeps a
+ * libbabeltrace2 pointer (`_mLibObjPtr`), therefore it doesn't
+ * automatically know the relation between wrapper classes.
+ */
+template <typename ObjT>
+class OptionalBorrowedObject final
+{
+public:
+    using Obj = ObjT;
+    using LibObjPtr = typename ObjT::LibObjPtr;
+
+    /*
+     * Builds an optional borrowed object without an object.
+     *
+     * Intentionally not explicit.
+     */
+    OptionalBorrowedObject() noexcept = default;
+
+    /*
+     * Builds an optional borrowed object with an instance of `ObjT`
+     * wrapping the libbabeltrace2 pointer `libObjPtr`.
+     *
+     * Intentionally not explicit.
+     */
+    OptionalBorrowedObject(const LibObjPtr libObjPtr) noexcept : _mLibObjPtr {libObjPtr}
+    {
+    }
+
+    /*
+     * Builds an optional borrowed object with an instance of `ObjT`
+     * constructed from `obj`.
+     *
+     * It must be possible to construct an instance of `ObjT` with an
+     * instance of `OtherObjT`.
+     *
+     * Intentionally not explicit.
+     */
+    template <typename OtherObjT>
+    OptionalBorrowedObject(const OtherObjT obj) noexcept : _mLibObjPtr {obj.libObjPtr()}
+    {
+        static_assert(std::is_constructible<ObjT, OtherObjT>::value,
+                      "`ObjT` is constructible with an instance of `OtherObjT`.");
+    }
+
+    /*
+     * Builds an optional borrowed object from `optObj`, with or without
+     * an object.
+     *
+     * It must be possible to construct an instance of `ObjT` with an
+     * instance of `OtherObjT`.
+     *
+     * Intentionally not explicit.
+     */
+    template <typename OtherObjT>
+    OptionalBorrowedObject(const OptionalBorrowedObject<OtherObjT> optObj) noexcept :
+        _mLibObjPtr {optObj.libObjPtr()}
+    {
+        static_assert(std::is_constructible<ObjT, OtherObjT>::value,
+                      "`ObjT` is constructible with an instance of `OtherObjT`.");
+    }
+
+    /*
+     * Makes this optional borrowed object have an instance of `ObjT`
+     * wrapping the libbabeltrace2 pointer `libObjPtr`.
+     */
+    OptionalBorrowedObject& operator=(const LibObjPtr libObjPtr) noexcept
+    {
+        _mLibObjPtr = libObjPtr;
+        return *this;
+    }
+
+    /*
+     * Makes this optional borrowed object have an instance of `ObjT`
+     * constructed from `obj`.
+     *
+     * It must be possible to construct an instance of `ObjT` with an
+     * instance of `OtherObjT`.
+     */
+    template <typename OtherObjT>
+    OptionalBorrowedObject& operator=(const ObjT obj) noexcept
+    {
+        static_assert(std::is_constructible<ObjT, OtherObjT>::value,
+                      "`ObjT` is constructible with an instance of `OtherObjT`.");
+        _mLibObjPtr = obj.libObjPtr();
+        return *this;
+    }
+
+    /*
+     * Sets this optional borrowed object to `optObj`.
+     *
+     * It must be possible to construct an instance of `ObjT` with an
+     * instance of `OtherObjT`.
+     */
+    template <typename OtherObjT>
+    OptionalBorrowedObject& operator=(const OptionalBorrowedObject<ObjT> optObj) noexcept
+    {
+        static_assert(std::is_constructible<ObjT, OtherObjT>::value,
+                      "`ObjT` is constructible with an instance of `OtherObjT`.");
+        _mLibObjPtr = optObj.libObjPtr();
+        return *this;
+    }
+
+    /* Wrapped libbabeltrace2 object pointer (may be `nullptr`) */
+    LibObjPtr libObjPtr() const noexcept
+    {
+        return _mLibObjPtr;
+    }
+
+    ObjT object() const noexcept
+    {
+        return ObjT {_mLibObjPtr};
+    }
+
+    ObjT operator*() const noexcept
+    {
+        return this->object();
+    }
+
+    /*
+     * We want to return the address of an `ObjT` instance here, but we
+     * only have a library pointer (`_mLibObjPtr`) because an `ObjT`
+     * instance may not wrap `nullptr`.
+     *
+     * Therefore, return a proxy object which holds an internal
+     * `ObjT` instance and implements operator->() itself.
+     *
+     * In other words, doing `myOptObj->something()` is equivalent to
+     * calling `myOptObj.operator->().operator->()->something()`.
+     */
+    BorrowedObjectProxy<ObjT> operator->() const noexcept
+    {
+        return BorrowedObjectProxy<ObjT> {_mLibObjPtr};
+    }
+
+    bool hasObject() const noexcept
+    {
+        return _mLibObjPtr;
+    }
+
+    explicit operator bool() const noexcept
+    {
+        return this->hasObject();
+    }
+
+    void reset() noexcept
+    {
+        _mLibObjPtr = nullptr;
+    }
+
+private:
+    typename ObjT::LibObjPtr _mLibObjPtr = nullptr;
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_OPTIONAL_BORROWED_OBJECT_HPP */
diff --git a/src/cpp-common/bt2/plugin-dev.hpp b/src/cpp-common/bt2/plugin-dev.hpp
new file mode 100644 (file)
index 0000000..5b3e2ed
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2023 Simon Marchi <simon.marchi@efficios.com>
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_PLUGIN_DEV_HPP
+#define BABELTRACE_CPP_COMMON_BT2_PLUGIN_DEV_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "internal/comp-cls-bridge.hpp" /* IWYU pragma: keep */
+
+#define BT_CPP_PLUGIN_SOURCE_COMPONENT_CLASS_WITH_ID(_pluginId, _componentClassId, _name,          \
+                                                     _userComponentClass)                          \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_WITH_ID(                                                      \
+        _pluginId, _componentClassId, _name,                                                       \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::next);              \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_INITIALIZE_METHOD_WITH_ID(                                    \
+        _pluginId, _componentClassId, bt2::internal::SrcCompClsBridge<_userComponentClass>::init); \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(                                      \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SrcCompClsBridge<_userComponentClass>::finalize);                           \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_GET_SUPPORTED_MIP_VERSIONS_METHOD_WITH_ID(                    \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SrcCompClsBridge<_userComponentClass>::getSupportedMipVersions);            \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_OUTPUT_PORT_CONNECTED_METHOD_WITH_ID(                         \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SrcCompClsBridge<_userComponentClass>::outputPortConnected);                \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_QUERY_METHOD_WITH_ID(                                         \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SrcCompClsBridge<_userComponentClass>::query);                              \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_WITH_ID(             \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::init);              \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD_WITH_ID(               \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::finalize);          \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHODS_WITH_ID(        \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::seekBeginning,      \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::canSeekBeginning);  \
+    BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_SEEK_NS_FROM_ORIGIN_METHODS_WITH_ID(   \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::seekNsFromOrigin,   \
+        bt2::internal::MsgIterClsBridge<                                                           \
+            _userComponentClass::MessageIterator>::canSeekNsFromOrigin);
+
+#define BT_CPP_PLUGIN_FILTER_COMPONENT_CLASS_WITH_ID(_pluginId, _componentClassId, _name,          \
+                                                     _userComponentClass)                          \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_WITH_ID(                                                      \
+        _pluginId, _componentClassId, _name,                                                       \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::next);              \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_INITIALIZE_METHOD_WITH_ID(                                    \
+        _pluginId, _componentClassId, bt2::internal::FltCompClsBridge<_userComponentClass>::init); \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(                                      \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::FltCompClsBridge<_userComponentClass>::finalize);                           \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_GET_SUPPORTED_MIP_VERSIONS_METHOD_WITH_ID(                    \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::FltCompClsBridge<_userComponentClass>::getSupportedMipVersions);            \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_INPUT_PORT_CONNECTED_METHOD_WITH_ID(                          \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::FltCompClsBridge<_userComponentClass>::inputPortConnected);                 \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_OUTPUT_PORT_CONNECTED_METHOD_WITH_ID(                         \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::FltCompClsBridge<_userComponentClass>::outputPortConnected);                \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_QUERY_METHOD_WITH_ID(                                         \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::FltCompClsBridge<_userComponentClass>::query);                              \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_WITH_ID(             \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::init);              \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD_WITH_ID(               \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::finalize);          \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHODS_WITH_ID(        \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::seekBeginning,      \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::canSeekBeginning);  \
+    BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_SEEK_NS_FROM_ORIGIN_METHODS_WITH_ID(   \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::MsgIterClsBridge<_userComponentClass::MessageIterator>::seekNsFromOrigin,   \
+        bt2::internal::MsgIterClsBridge<                                                           \
+            _userComponentClass::MessageIterator>::canSeekNsFromOrigin);
+
+#define BT_CPP_PLUGIN_SINK_COMPONENT_CLASS_WITH_ID(_pluginId, _componentClassId, _name,            \
+                                                   _userComponentClass)                            \
+    BT_PLUGIN_SINK_COMPONENT_CLASS_WITH_ID(                                                        \
+        _pluginId, _componentClassId, _name,                                                       \
+        bt2::internal::SinkCompClsBridge<_userComponentClass>::consume);                           \
+    BT_PLUGIN_SINK_COMPONENT_CLASS_INITIALIZE_METHOD_WITH_ID(                                      \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SinkCompClsBridge<_userComponentClass>::init);                              \
+    BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(                                        \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SinkCompClsBridge<_userComponentClass>::finalize);                          \
+    BT_PLUGIN_SINK_COMPONENT_CLASS_GET_SUPPORTED_MIP_VERSIONS_METHOD_WITH_ID(                      \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SinkCompClsBridge<_userComponentClass>::getSupportedMipVersions);           \
+    BT_PLUGIN_SINK_COMPONENT_CLASS_INPUT_PORT_CONNECTED_METHOD_WITH_ID(                            \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SinkCompClsBridge<_userComponentClass>::inputPortConnected);                \
+    BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD_WITH_ID(                             \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SinkCompClsBridge<_userComponentClass>::graphIsConfigured);                 \
+    BT_PLUGIN_SINK_COMPONENT_CLASS_QUERY_METHOD_WITH_ID(                                           \
+        _pluginId, _componentClassId,                                                              \
+        bt2::internal::SinkCompClsBridge<_userComponentClass>::query);
+
+#define BT_CPP_PLUGIN_SOURCE_COMPONENT_CLASS(_name, _userComponentClass)                           \
+    BT_CPP_PLUGIN_SOURCE_COMPONENT_CLASS_WITH_ID(auto, _name, #_name, _userComponentClass)
+
+#define BT_CPP_PLUGIN_FILTER_COMPONENT_CLASS(_name, _userComponentClass)                           \
+    BT_CPP_PLUGIN_FILTER_COMPONENT_CLASS_WITH_ID(auto, _name, #_name, _userComponentClass)
+
+#define BT_CPP_PLUGIN_SINK_COMPONENT_CLASS(_name, _userComponentClass)                             \
+    BT_CPP_PLUGIN_SINK_COMPONENT_CLASS_WITH_ID(auto, _name, #_name, _userComponentClass)
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_PLUGIN_DEV_HPP */
diff --git a/src/cpp-common/bt2/plugin-load.hpp b/src/cpp-common/bt2/plugin-load.hpp
new file mode 100644 (file)
index 0000000..00b0510
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2024 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_PLUGIN_LOAD_HPP
+#define BABELTRACE_CPP_COMMON_BT2_PLUGIN_LOAD_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/common.h"
+#include "cpp-common/bt2c/c-string-view.hpp"
+
+#include "exc.hpp"
+#include "plugin-set.hpp"
+#include "plugin.hpp"
+
+namespace bt2 {
+
+inline ConstPlugin::Shared
+findPlugin(const bt2c::CStringView name, const bool findInStdEnvVar = true,
+           const bool findInUserDir = true, const bool findInSysDir = true,
+           const bool findInStatic = true, const bool failOnLoadError = false)
+{
+    const bt_plugin *plugin;
+    const auto status = bt_plugin_find(name, findInStdEnvVar, findInUserDir, findInSysDir,
+                                       findInStatic, failOnLoadError, &plugin);
+
+    if (status == BT_PLUGIN_FIND_STATUS_MEMORY_ERROR) {
+        throw MemoryError {};
+    } else if (status == BT_PLUGIN_FIND_STATUS_ERROR) {
+        throw Error {};
+    } else if (status == BT_PLUGIN_FIND_STATUS_NOT_FOUND) {
+        return ConstPlugin::Shared {};
+    }
+
+    return ConstPlugin::Shared::createWithoutRef(plugin);
+}
+
+inline ConstPluginSet::Shared findAllPluginsFromDir(const bt2c::CStringView path,
+                                                    const bool recurse, const bool failOnLoadError)
+{
+    const bt_plugin_set *pluginSet;
+
+    switch (bt_plugin_find_all_from_dir(path, recurse, failOnLoadError, &pluginSet)) {
+    case BT_PLUGIN_FIND_ALL_FROM_DIR_STATUS_OK:
+        return ConstPluginSet::Shared::createWithoutRef(pluginSet);
+    case BT_PLUGIN_FIND_ALL_FROM_DIR_STATUS_NOT_FOUND:
+        return ConstPluginSet::Shared {};
+    case BT_PLUGIN_FIND_ALL_FROM_DIR_STATUS_MEMORY_ERROR:
+        throw MemoryError {};
+    case BT_PLUGIN_FIND_ALL_FROM_DIR_STATUS_ERROR:
+        throw Error {};
+    }
+
+    bt_common_abort();
+}
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_PLUGIN_LOAD_HPP */
diff --git a/src/cpp-common/bt2/plugin-set.hpp b/src/cpp-common/bt2/plugin-set.hpp
new file mode 100644 (file)
index 0000000..57ce92e
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_PLUGIN_SET_HPP
+#define BABELTRACE_CPP_COMMON_BT2_PLUGIN_SET_HPP
+
+#include <cstdint>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "borrowed-object.hpp"
+#include "shared-object.hpp"
+
+namespace bt2 {
+namespace internal {
+
+struct PluginSetRefFuncs
+{
+    static void get(const bt_plugin_set * const libObjPtr) noexcept
+    {
+        bt_plugin_set_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_plugin_set * const libObjPtr) noexcept
+    {
+        bt_plugin_set_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+class ConstPluginSet final : public BorrowedObject<const bt_plugin_set>
+{
+public:
+    using Shared = SharedObject<ConstPluginSet, const bt_plugin_set, internal::PluginSetRefFuncs>;
+
+    explicit ConstPluginSet(const bt_plugin_set * const plugin_set) :
+        _ThisBorrowedObject {plugin_set}
+    {
+    }
+
+    std::uint64_t length() const noexcept
+    {
+        return bt_plugin_set_get_plugin_count(this->libObjPtr());
+    }
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_PLUGIN_SET_HPP */
diff --git a/src/cpp-common/bt2/plugin.hpp b/src/cpp-common/bt2/plugin.hpp
new file mode 100644 (file)
index 0000000..36d6b6b
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2024 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_PLUGIN_HPP
+#define BABELTRACE_CPP_COMMON_BT2_PLUGIN_HPP
+
+#include <cstdint>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/bt2s/optional.hpp"
+
+#include "borrowed-object-iterator.hpp"
+#include "borrowed-object.hpp"
+#include "component-class.hpp"
+#include "shared-object.hpp"
+
+namespace bt2 {
+
+template <typename PluginSpecCompClsT>
+class ConstPluginComponentClasses final : public BorrowedObject<const bt_plugin>
+{
+public:
+    using Iterator = BorrowedObjectIterator<ConstPluginComponentClasses<PluginSpecCompClsT>>;
+
+    explicit ConstPluginComponentClasses(const LibObjPtr libPluginPtr) noexcept :
+        _ThisBorrowedObject {libPluginPtr}
+    {
+    }
+
+    std::uint64_t length() const noexcept
+    {
+        return PluginSpecCompClsT::getCompClsCount(this->libObjPtr());
+    }
+
+    Iterator begin() const noexcept
+    {
+        return Iterator {*this, 0};
+    }
+
+    Iterator end() const noexcept
+    {
+        return Iterator {*this, this->length()};
+    }
+
+    typename PluginSpecCompClsT::CompCls operator[](const std::uint64_t index) const noexcept
+    {
+        return PluginSpecCompClsT::borrowCompClsByIndex(this->libObjPtr(), index);
+    }
+
+    OptionalBorrowedObject<typename PluginSpecCompClsT::CompCls>
+    operator[](const bt2c::CStringView name) const noexcept
+    {
+        return PluginSpecCompClsT::borrowCompClsByName(this->libObjPtr(), name);
+    }
+};
+
+namespace internal {
+
+struct PluginRefFuncs final
+{
+    static void get(const bt_plugin * const libObjPtr) noexcept
+    {
+        bt_plugin_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_plugin * const libObjPtr) noexcept
+    {
+        bt_plugin_put_ref(libObjPtr);
+    }
+};
+
+struct PluginSourceCompClsFuncs final
+{
+    using CompCls = ConstSourceComponentClass;
+
+    static std::uint64_t getCompClsCount(const bt_plugin * const plugin) noexcept
+    {
+        return bt_plugin_get_source_component_class_count(plugin);
+    }
+
+    static const bt_component_class_source *borrowCompClsByIndex(const bt_plugin * const plugin,
+                                                                 const std::uint64_t index) noexcept
+    {
+        return bt_plugin_borrow_source_component_class_by_index_const(plugin, index);
+    }
+
+    static const bt_component_class_source *borrowCompClsByName(const bt_plugin * const plugin,
+                                                                const char *name) noexcept
+    {
+        return bt_plugin_borrow_source_component_class_by_name_const(plugin, name);
+    }
+};
+
+struct PluginFilterCompClsFuncs final
+{
+    using CompCls = ConstFilterComponentClass;
+
+    static std::uint64_t getCompClsCount(const bt_plugin * const plugin) noexcept
+    {
+        return bt_plugin_get_filter_component_class_count(plugin);
+    }
+
+    static const bt_component_class_filter *borrowCompClsByIndex(const bt_plugin * const plugin,
+                                                                 const std::uint64_t index) noexcept
+    {
+        return bt_plugin_borrow_filter_component_class_by_index_const(plugin, index);
+    }
+
+    static const bt_component_class_filter *borrowCompClsByName(const bt_plugin * const plugin,
+                                                                const char *name) noexcept
+    {
+        return bt_plugin_borrow_filter_component_class_by_name_const(plugin, name);
+    }
+};
+
+struct PluginSinkCompClsFuncs final
+{
+    using CompCls = ConstSinkComponentClass;
+
+    static std::uint64_t getCompClsCount(const bt_plugin * const plugin) noexcept
+    {
+        return bt_plugin_get_sink_component_class_count(plugin);
+    }
+
+    static const bt_component_class_sink *borrowCompClsByIndex(const bt_plugin * const plugin,
+                                                               const std::uint64_t index) noexcept
+    {
+        return bt_plugin_borrow_sink_component_class_by_index_const(plugin, index);
+    }
+
+    static const bt_component_class_sink *borrowCompClsByName(const bt_plugin * const plugin,
+                                                              const char *name) noexcept
+    {
+        return bt_plugin_borrow_sink_component_class_by_name_const(plugin, name);
+    }
+};
+
+} /* namespace internal */
+
+class ConstPlugin final : public BorrowedObject<const bt_plugin>
+{
+public:
+    using SourceComponementClasses =
+        ConstPluginComponentClasses<internal::PluginSourceCompClsFuncs>;
+    using FilterComponementClasses =
+        ConstPluginComponentClasses<internal::PluginFilterCompClsFuncs>;
+    using SinkComponementClasses = ConstPluginComponentClasses<internal::PluginSinkCompClsFuncs>;
+
+    class Version final
+    {
+    public:
+        explicit Version(const unsigned int major, const unsigned int minor,
+                         const unsigned int patch, const bt2c::CStringView extra) noexcept :
+            _mMajor {major},
+            _mMinor {minor}, _mPatch {patch}, _mExtra {extra}
+        {
+        }
+
+        unsigned int major() const noexcept
+        {
+            return _mMajor;
+        }
+
+        unsigned int minor() const noexcept
+        {
+            return _mMinor;
+        }
+
+        unsigned int patch() const noexcept
+        {
+            return _mPatch;
+        }
+
+        bt2c::CStringView extra() const noexcept
+        {
+            return _mExtra;
+        }
+
+    private:
+        unsigned int _mMajor, _mMinor, _mPatch;
+        bt2c::CStringView _mExtra;
+    };
+
+    using Shared = SharedObject<ConstPlugin, const bt_plugin, internal::PluginRefFuncs>;
+
+    explicit ConstPlugin(const LibObjPtr plugin) : _ThisBorrowedObject {plugin}
+    {
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return bt_plugin_get_name(this->libObjPtr());
+    }
+
+    bt2c::CStringView description() const noexcept
+    {
+        return bt_plugin_get_description(this->libObjPtr());
+    }
+
+    bt2c::CStringView author() const noexcept
+    {
+        return bt_plugin_get_author(this->libObjPtr());
+    }
+
+    bt2c::CStringView license() const noexcept
+    {
+        return bt_plugin_get_license(this->libObjPtr());
+    }
+
+    bt2c::CStringView path() const noexcept
+    {
+        return bt_plugin_get_path(this->libObjPtr());
+    }
+
+    bt2s::optional<Version> version() const noexcept
+    {
+        unsigned int major, minor, patch;
+        const char *extra;
+
+        if (bt_plugin_get_version(this->libObjPtr(), &major, &minor, &patch, &extra) ==
+            BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) {
+            return bt2s::nullopt;
+        }
+
+        return Version {major, minor, patch, extra};
+    }
+
+    SourceComponementClasses sourceComponentClasses() const noexcept
+    {
+        return SourceComponementClasses {this->libObjPtr()};
+    }
+
+    FilterComponementClasses filterComponentClasses() const noexcept
+    {
+        return FilterComponementClasses {this->libObjPtr()};
+    }
+
+    SinkComponementClasses sinkComponentClasses() const noexcept
+    {
+        return SinkComponementClasses {this->libObjPtr()};
+    }
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_PLUGIN_HPP */
diff --git a/src/cpp-common/bt2/private-query-executor.hpp b/src/cpp-common/bt2/private-query-executor.hpp
new file mode 100644 (file)
index 0000000..95264a1
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_PRIVATE_QUERY_EXECUTOR_HPP
+#define BABELTRACE_CPP_COMMON_BT2_PRIVATE_QUERY_EXECUTOR_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "logging.hpp"
+
+#include "borrowed-object.hpp"
+
+namespace bt2 {
+
+class PrivateQueryExecutor final : public BorrowedObject<bt_private_query_executor>
+{
+public:
+    explicit PrivateQueryExecutor(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    LoggingLevel loggingLevel() const noexcept
+    {
+        return static_cast<LoggingLevel>(bt_query_executor_get_logging_level(
+            bt_private_query_executor_as_query_executor_const(this->libObjPtr())));
+    }
+
+    bool isInterrupted() const noexcept
+    {
+        return static_cast<bool>(bt_query_executor_is_interrupted(
+            bt_private_query_executor_as_query_executor_const(this->libObjPtr())));
+    }
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_PRIVATE_QUERY_EXECUTOR_HPP */
diff --git a/src/cpp-common/bt2/query-executor.hpp b/src/cpp-common/bt2/query-executor.hpp
new file mode 100644 (file)
index 0000000..4b3fb26
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2024 EfficiOS, Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_QUERY_EXECUTOR_HPP
+#define BABELTRACE_CPP_COMMON_BT2_QUERY_EXECUTOR_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+
+#include "borrowed-object.hpp"
+#include "component-class.hpp"
+#include "shared-object.hpp"
+#include "value.hpp"
+
+namespace bt2 {
+namespace internal {
+
+struct QueryExecutorRefFuncs final
+{
+    static void get(const bt_query_executor * const libObjPtr) noexcept
+    {
+        bt_query_executor_get_ref(libObjPtr);
+    }
+
+    static void put(const bt_query_executor * const libObjPtr) noexcept
+    {
+        bt_query_executor_put_ref(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+template <typename LibObjT>
+class CommonQueryExecutor final : public BorrowedObject<LibObjT>
+{
+private:
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
+
+public:
+    using typename _ThisBorrowedObject::LibObjPtr;
+    using Shared = SharedObject<CommonQueryExecutor, LibObjT, internal::QueryExecutorRefFuncs>;
+
+    explicit CommonQueryExecutor(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonQueryExecutor(const CommonQueryExecutor<OtherLibObjT> queryExec) noexcept :
+        _ThisBorrowedObject {queryExec}
+    {
+    }
+
+    template <typename OtherLibObjT>
+    CommonQueryExecutor operator=(const CommonQueryExecutor<OtherLibObjT> queryExec) noexcept
+    {
+        _ThisBorrowedObject::operator=(queryExec);
+        return *this;
+    }
+
+    static Shared create(const ConstComponentClass compCls, const bt2c::CStringView objectName,
+                         const OptionalBorrowedObject<ConstMapValue> params = {})
+    {
+        return CommonQueryExecutor::_create(compCls, objectName, params,
+                                            static_cast<void *>(nullptr));
+    }
+
+    template <typename QueryDataT>
+    static Shared create(const ConstComponentClass compCls, const bt2c::CStringView objectName,
+                         QueryDataT& queryData,
+                         const OptionalBorrowedObject<ConstMapValue> params = {})
+    {
+        return CommonQueryExecutor::_create(compCls, objectName, params, &queryData);
+    }
+
+    ConstValue::Shared query() const
+    {
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstQueryExecutor`.");
+
+        const bt_value *res;
+        const auto status = bt_query_executor_query(this->libObjPtr(), &res);
+
+        if (status == BT_QUERY_EXECUTOR_QUERY_STATUS_ERROR) {
+            throw Error {};
+        } else if (status == BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
+        } else if (status == BT_QUERY_EXECUTOR_QUERY_STATUS_AGAIN) {
+            throw TryAgain {};
+        } else if (status == BT_QUERY_EXECUTOR_QUERY_STATUS_UNKNOWN_OBJECT) {
+            throw UnknownObject {};
+        }
+
+        return ConstValue::Shared::createWithoutRef(res);
+    }
+
+private:
+    template <typename QueryDataT>
+    static Shared _create(const ConstComponentClass compCls, const bt2c::CStringView objectName,
+                          const OptionalBorrowedObject<ConstMapValue> params,
+                          QueryDataT * const queryData)
+    {
+        const auto libObjPtr = bt_query_executor_create_with_method_data(
+            compCls.libObjPtr(), objectName, params ? params->libObjPtr() : nullptr,
+            const_cast<void *>(static_cast<const void *>(queryData)));
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return Shared::createWithoutRef(libObjPtr);
+    }
+};
+
+using QueryExecutor = CommonQueryExecutor<bt_query_executor>;
+using ConstQueryExecutor = CommonQueryExecutor<const bt_query_executor>;
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_QUERY_EXECUTOR_HPP  */
diff --git a/src/cpp-common/bt2/raw-value-proxy.hpp b/src/cpp-common/bt2/raw-value-proxy.hpp
new file mode 100644 (file)
index 0000000..89f1a6a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_RAW_VALUE_PROXY_HPP
+#define BABELTRACE_CPP_COMMON_BT2_RAW_VALUE_PROXY_HPP
+
+namespace bt2 {
+
+template <typename ObjT>
+class RawValueProxy
+{
+private:
+    using _RawVal = typename ObjT::Value;
+
+public:
+    explicit RawValueProxy(const ObjT obj) : _mObj {obj}
+    {
+    }
+
+    RawValueProxy& operator=(const _RawVal& rawVal)
+    {
+        _mObj.value(rawVal);
+        return *this;
+    }
+
+    operator _RawVal() const noexcept
+    {
+        return _mObj.value();
+    }
+
+private:
+    ObjT _mObj;
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_RAW_VALUE_PROXY_HPP */
diff --git a/src/cpp-common/bt2/self-component-class.hpp b/src/cpp-common/bt2/self-component-class.hpp
new file mode 100644 (file)
index 0000000..9a0275f
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_SELF_COMPONENT_CLASS_HPP
+#define BABELTRACE_CPP_COMMON_BT2_SELF_COMPONENT_CLASS_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+
+#include "borrowed-object.hpp"
+
+namespace bt2 {
+
+class SelfComponentClass final : public BorrowedObject<bt_self_component_class>
+{
+public:
+    explicit SelfComponentClass(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    explicit SelfComponentClass(bt_self_component_class_source * const libObjPtr) noexcept :
+        _ThisBorrowedObject {bt_self_component_class_source_as_self_component_class(libObjPtr)}
+    {
+    }
+
+    explicit SelfComponentClass(bt_self_component_class_filter * const libObjPtr) noexcept :
+        _ThisBorrowedObject {bt_self_component_class_filter_as_self_component_class(libObjPtr)}
+    {
+    }
+
+    explicit SelfComponentClass(bt_self_component_class_sink * const libObjPtr) noexcept :
+        _ThisBorrowedObject {bt_self_component_class_sink_as_self_component_class(libObjPtr)}
+    {
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return bt_component_class_get_name(this->_libCompClsPtr());
+    }
+
+    bt2c::CStringView description() const noexcept
+    {
+        return bt_component_class_get_description(this->_libCompClsPtr());
+    }
+
+    bt2c::CStringView help() const noexcept
+    {
+        return bt_component_class_get_help(this->_libCompClsPtr());
+    }
+
+private:
+    const bt_component_class *_libCompClsPtr() const noexcept
+    {
+        return bt_self_component_class_as_component_class(this->libObjPtr());
+    }
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_SELF_COMPONENT_CLASS_HPP */
diff --git a/src/cpp-common/bt2/self-component-port.hpp b/src/cpp-common/bt2/self-component-port.hpp
new file mode 100644 (file)
index 0000000..10a73bb
--- /dev/null
@@ -0,0 +1,756 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_SELF_COMPONENT_PORT_HPP
+#define BABELTRACE_CPP_COMMON_BT2_SELF_COMPONENT_PORT_HPP
+
+#include <cstdint>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "logging.hpp"
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+
+#include "borrowed-object-iterator.hpp"
+#include "borrowed-object.hpp"
+#include "component-port.hpp"
+#include "message-iterator.hpp"
+
+namespace bt2 {
+
+class SelfSourceComponent;
+class SelfFilterComponent;
+class SelfSinkComponent;
+
+class SelfComponent final : public BorrowedObject<bt_self_component>
+{
+public:
+    explicit SelfComponent(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    explicit SelfComponent(bt_self_component_source * const libObjPtr) noexcept :
+        _ThisBorrowedObject {bt_self_component_source_as_self_component(libObjPtr)}
+    {
+    }
+
+    explicit SelfComponent(bt_self_component_filter * const libObjPtr) noexcept :
+        _ThisBorrowedObject {bt_self_component_filter_as_self_component(libObjPtr)}
+    {
+    }
+
+    explicit SelfComponent(bt_self_component_sink * const libObjPtr) noexcept :
+        _ThisBorrowedObject {bt_self_component_sink_as_self_component(libObjPtr)}
+    {
+    }
+
+    /* Not `explicit` to make them behave like copy constructors */
+    SelfComponent(SelfSourceComponent other) noexcept;
+    SelfComponent(SelfFilterComponent other) noexcept;
+    SelfComponent(SelfSinkComponent other) noexcept;
+
+    SelfComponent operator=(SelfSourceComponent other) noexcept;
+    SelfComponent operator=(SelfFilterComponent other) noexcept;
+    SelfComponent operator=(SelfSinkComponent other) noexcept;
+
+    ConstComponent asConstComponent() const noexcept
+    {
+        return ConstComponent {bt_self_component_as_component(this->libObjPtr())};
+    }
+
+    bool isSource() const noexcept
+    {
+        return this->asConstComponent().isSource();
+    }
+
+    bool isFilter() const noexcept
+    {
+        return this->asConstComponent().isFilter();
+    }
+
+    bool isSink() const noexcept
+    {
+        return this->asConstComponent().isSink();
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return this->asConstComponent().name();
+    }
+
+    LoggingLevel loggingLevel() const noexcept
+    {
+        return this->asConstComponent().loggingLevel();
+    }
+
+    std::uint64_t graphMipVersion() const noexcept
+    {
+        return bt_self_component_get_graph_mip_version(this->libObjPtr());
+    }
+
+    template <typename T>
+    T& data() const noexcept
+    {
+        return *static_cast<T *>(bt_self_component_get_data(this->libObjPtr()));
+    }
+
+    template <typename T>
+    SelfComponent data(T& obj) const noexcept
+    {
+        bt_self_component_set_data(this->libObjPtr(),
+                                   const_cast<void *>(static_cast<const void *>(&obj)));
+        return *this;
+    }
+
+    TraceClass::Shared createTraceClass() const
+    {
+        const auto libObjPtr = bt_trace_class_create(this->libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return TraceClass::Shared::createWithoutRef(libObjPtr);
+    }
+
+    ClockClass::Shared createClockClass() const
+    {
+        const auto libObjPtr = bt_clock_class_create(this->libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return ClockClass::Shared::createWithoutRef(libObjPtr);
+    }
+};
+
+namespace internal {
+
+template <typename LibObjT>
+class SelfSpecificComponent : public BorrowedObject<LibObjT>
+{
+private:
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
+
+public:
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+
+protected:
+    explicit SelfSpecificComponent(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    template <typename PortT, typename LibPortT, typename AddPortFuncT, typename DataT>
+    PortT _addPort(const char * const name, DataT * const data, AddPortFuncT&& func) const
+    {
+        LibPortT *libPortPtr;
+
+        const auto status = func(this->libObjPtr(), name,
+                                 const_cast<void *>(static_cast<const void *>(data)), &libPortPtr);
+
+        switch (status) {
+        case BT_SELF_COMPONENT_ADD_PORT_STATUS_OK:
+            return PortT {libPortPtr};
+        case BT_SELF_COMPONENT_ADD_PORT_STATUS_MEMORY_ERROR:
+            throw MemoryError {};
+        case BT_SELF_COMPONENT_ADD_PORT_STATUS_ERROR:
+            throw Error {};
+        default:
+            bt_common_abort();
+        }
+    }
+
+public:
+    bt2c::CStringView name() const noexcept
+    {
+        return this->_selfComponent().name();
+    }
+
+    LoggingLevel loggingLevel() const noexcept
+    {
+        return this->_selfComponent().loggingLevel();
+    }
+
+    std::uint64_t graphMipVersion() const noexcept
+    {
+        return this->_selfComponent().graphMipVersion();
+    }
+
+    template <typename T>
+    T& data() const noexcept
+    {
+        return this->_selfComponent().template data<T>();
+    }
+
+protected:
+    SelfComponent _selfComponent() const noexcept
+    {
+        return SelfComponent {this->libObjPtr()};
+    }
+};
+
+template <typename LibSelfCompT, typename LibSelfCompPortPtrT>
+struct SelfComponentPortsSpec;
+
+template <>
+struct SelfComponentPortsSpec<bt_self_component_source, bt_self_component_port_output> final
+{
+    static std::uint64_t portCount(bt_self_component_source * const libCompPtr) noexcept
+    {
+        return bt_component_source_get_output_port_count(
+            bt_self_component_source_as_component_source(libCompPtr));
+    }
+
+    static bt_self_component_port_output *portByIndex(bt_self_component_source * const libCompPtr,
+                                                      const std::uint64_t index) noexcept
+    {
+        return bt_self_component_source_borrow_output_port_by_index(libCompPtr, index);
+    }
+
+    static bt_self_component_port_output *portByName(bt_self_component_source * const libCompPtr,
+                                                     const char * const name) noexcept
+    {
+        return bt_self_component_source_borrow_output_port_by_name(libCompPtr, name);
+    }
+};
+
+template <>
+struct SelfComponentPortsSpec<bt_self_component_filter, bt_self_component_port_output> final
+{
+    static std::uint64_t portCount(bt_self_component_filter * const libCompPtr) noexcept
+    {
+        return bt_component_filter_get_output_port_count(
+            bt_self_component_filter_as_component_filter(libCompPtr));
+    }
+
+    static bt_self_component_port_output *portByIndex(bt_self_component_filter * const libCompPtr,
+                                                      const std::uint64_t index) noexcept
+    {
+        return bt_self_component_filter_borrow_output_port_by_index(libCompPtr, index);
+    }
+
+    static bt_self_component_port_output *portByName(bt_self_component_filter * const libCompPtr,
+                                                     const char * const name) noexcept
+    {
+        return bt_self_component_filter_borrow_output_port_by_name(libCompPtr, name);
+    }
+};
+
+template <>
+struct SelfComponentPortsSpec<bt_self_component_filter, bt_self_component_port_input> final
+{
+    static std::uint64_t portCount(bt_self_component_filter * const libCompPtr) noexcept
+    {
+        return bt_component_filter_get_input_port_count(
+            bt_self_component_filter_as_component_filter(libCompPtr));
+    }
+
+    static bt_self_component_port_input *portByIndex(bt_self_component_filter * const libCompPtr,
+                                                     const std::uint64_t index) noexcept
+    {
+        return bt_self_component_filter_borrow_input_port_by_index(libCompPtr, index);
+    }
+
+    static bt_self_component_port_input *portByName(bt_self_component_filter * const libCompPtr,
+                                                    const char * const name) noexcept
+    {
+        return bt_self_component_filter_borrow_input_port_by_name(libCompPtr, name);
+    }
+};
+
+template <>
+struct SelfComponentPortsSpec<bt_self_component_sink, bt_self_component_port_input> final
+{
+    static std::uint64_t portCount(bt_self_component_sink * const libCompPtr) noexcept
+    {
+        return bt_component_sink_get_input_port_count(
+            bt_self_component_sink_as_component_sink(libCompPtr));
+    }
+
+    static bt_self_component_port_input *portByIndex(bt_self_component_sink * const libCompPtr,
+                                                     const std::uint64_t index) noexcept
+    {
+        return bt_self_component_sink_borrow_input_port_by_index(libCompPtr, index);
+    }
+
+    static bt_self_component_port_input *portByName(bt_self_component_sink * const libCompPtr,
+                                                    const char * const name) noexcept
+    {
+        return bt_self_component_sink_borrow_input_port_by_name(libCompPtr, name);
+    }
+};
+
+} /* namespace internal */
+
+template <typename LibSelfCompPortT, typename LibPortT>
+class SelfComponentPort;
+
+template <typename LibSelfCompT, typename LibSelfCompPortT, typename LibPortT>
+class SelfComponentPorts final : public BorrowedObject<LibSelfCompT>
+{
+private:
+    using typename BorrowedObject<LibSelfCompT>::_ThisBorrowedObject;
+    using _Spec = internal::SelfComponentPortsSpec<LibSelfCompT, LibSelfCompPortT>;
+
+public:
+    using typename BorrowedObject<LibSelfCompT>::LibObjPtr;
+    using Port = SelfComponentPort<LibSelfCompPortT, LibPortT>;
+    using Iterator = BorrowedObjectIterator<SelfComponentPorts>;
+
+    explicit SelfComponentPorts(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    std::uint64_t length() const noexcept
+    {
+        return _Spec::portCount(this->libObjPtr());
+    }
+
+    Port operator[](std::uint64_t index) const noexcept;
+    Port operator[](bt2c::CStringView name) const noexcept;
+    Iterator begin() const noexcept;
+    Iterator end() const noexcept;
+    Port front() const noexcept;
+    Port back() const noexcept;
+};
+
+class SelfSourceComponent final : public internal::SelfSpecificComponent<bt_self_component_source>
+{
+    using _ThisSelfSpecificComponent = internal::SelfSpecificComponent<bt_self_component_source>;
+
+public:
+    using OutputPorts = SelfComponentPorts<bt_self_component_source, bt_self_component_port_output,
+                                           const bt_port_output>;
+
+    explicit SelfSourceComponent(bt_self_component_source * const libObjPtr) noexcept :
+        SelfSpecificComponent {libObjPtr}
+    {
+    }
+
+    ConstSourceComponent asConstComponent() const noexcept
+    {
+        return ConstSourceComponent {
+            bt_self_component_source_as_component_source(this->libObjPtr())};
+    }
+
+    using _ThisSelfSpecificComponent::data;
+
+    template <typename T>
+    SelfSourceComponent data(T& obj) const noexcept
+    {
+        this->_selfComponent().data(obj);
+        return *this;
+    }
+
+    template <typename DataT>
+    OutputPorts::Port addOutputPort(bt2c::CStringView name, DataT& data) const;
+
+    OutputPorts::Port addOutputPort(bt2c::CStringView name) const;
+
+    OutputPorts outputPorts() const noexcept;
+
+private:
+    template <typename DataT>
+    OutputPorts::Port _addOutputPort(const char *name, DataT *data) const;
+};
+
+class SelfFilterComponent final : public internal::SelfSpecificComponent<bt_self_component_filter>
+{
+    using _ThisSelfSpecificComponent = internal::SelfSpecificComponent<bt_self_component_filter>;
+
+public:
+    using InputPorts = SelfComponentPorts<bt_self_component_filter, bt_self_component_port_input,
+                                          const bt_port_input>;
+    using OutputPorts = SelfComponentPorts<bt_self_component_filter, bt_self_component_port_output,
+                                           const bt_port_output>;
+
+    explicit SelfFilterComponent(bt_self_component_filter * const libObjPtr) noexcept :
+        SelfSpecificComponent {libObjPtr}
+    {
+    }
+
+    ConstFilterComponent asConstComponent() const noexcept
+    {
+        return ConstFilterComponent {
+            bt_self_component_filter_as_component_filter(this->libObjPtr())};
+    }
+
+    using _ThisSelfSpecificComponent::data;
+
+    template <typename T>
+    SelfFilterComponent data(T& obj) const noexcept
+    {
+        this->_selfComponent().data(obj);
+        return *this;
+    }
+
+    template <typename DataT>
+    InputPorts::Port addInputPort(bt2c::CStringView name, DataT& data) const;
+
+    InputPorts::Port addInputPort(bt2c::CStringView name) const;
+
+    InputPorts inputPorts() const noexcept;
+
+    template <typename DataT>
+    OutputPorts::Port addOutputPort(bt2c::CStringView name, DataT& data) const;
+
+    OutputPorts::Port addOutputPort(bt2c::CStringView name) const;
+
+    OutputPorts outputPorts() const noexcept;
+
+private:
+    template <typename DataT>
+    InputPorts::Port _addInputPort(const char *name, DataT *data) const;
+
+    template <typename DataT>
+    OutputPorts::Port _addOutputPort(const char *name, DataT *data) const;
+};
+
+class SelfSinkComponent final : public internal::SelfSpecificComponent<bt_self_component_sink>
+{
+    using _ThisSelfSpecificComponent = internal::SelfSpecificComponent<bt_self_component_sink>;
+
+public:
+    using InputPorts = SelfComponentPorts<bt_self_component_sink, bt_self_component_port_input,
+                                          const bt_port_input>;
+
+    explicit SelfSinkComponent(bt_self_component_sink * const libObjPtr) noexcept :
+        SelfSpecificComponent {libObjPtr}
+    {
+    }
+
+    ConstSinkComponent asConstComponent() const noexcept
+    {
+        return ConstSinkComponent {bt_self_component_sink_as_component_sink(this->libObjPtr())};
+    }
+
+    using _ThisSelfSpecificComponent::data;
+
+    template <typename T>
+    SelfSinkComponent data(T& obj) const noexcept
+    {
+        this->_selfComponent().data(obj);
+        return *this;
+    }
+
+    MessageIterator::Shared createMessageIterator(InputPorts::Port port) const;
+
+    bool isInterrupted() const noexcept
+    {
+        return static_cast<bool>(bt_self_component_sink_is_interrupted(this->libObjPtr()));
+    }
+
+    template <typename DataT>
+    InputPorts::Port addInputPort(bt2c::CStringView name, DataT& data) const;
+
+    InputPorts::Port addInputPort(bt2c::CStringView name) const;
+
+    InputPorts inputPorts() const noexcept;
+
+private:
+    template <typename DataT>
+    InputPorts::Port _addInputPort(const char *name, DataT *data) const;
+};
+
+inline SelfComponent::SelfComponent(const SelfSourceComponent other) noexcept :
+    SelfComponent {other.libObjPtr()}
+{
+}
+
+inline SelfComponent::SelfComponent(const SelfFilterComponent other) noexcept :
+    SelfComponent {other.libObjPtr()}
+{
+}
+
+inline SelfComponent::SelfComponent(const SelfSinkComponent other) noexcept :
+    SelfComponent {other.libObjPtr()}
+{
+}
+
+inline SelfComponent SelfComponent::operator=(const SelfSourceComponent other) noexcept
+{
+    *this = SelfComponent {other.libObjPtr()};
+    return *this;
+}
+
+inline SelfComponent SelfComponent::operator=(const SelfFilterComponent other) noexcept
+{
+    *this = SelfComponent {other.libObjPtr()};
+    return *this;
+}
+
+inline SelfComponent SelfComponent::operator=(const SelfSinkComponent other) noexcept
+{
+    *this = SelfComponent {other.libObjPtr()};
+    return *this;
+}
+
+namespace internal {
+
+template <typename LibObjT>
+struct SelfComponentPortSpec;
+
+/* Functions specific to self component input ports */
+template <>
+struct SelfComponentPortSpec<bt_self_component_port_input> final
+{
+    static bt_self_component_port *
+    asSelfCompPort(bt_self_component_port_input * const libObjPtr) noexcept
+    {
+        return bt_self_component_port_input_as_self_component_port(libObjPtr);
+    }
+
+    static const bt_port_input *
+    asConstPort(const bt_self_component_port_input * const libObjPtr) noexcept
+    {
+        return bt_self_component_port_input_as_port_input(libObjPtr);
+    }
+};
+
+/* Functions specific to self component output ports */
+template <>
+struct SelfComponentPortSpec<bt_self_component_port_output> final
+{
+    static bt_self_component_port *
+    asSelfCompPort(bt_self_component_port_output * const libObjPtr) noexcept
+    {
+        return bt_self_component_port_output_as_self_component_port(libObjPtr);
+    }
+
+    static const bt_port_output *
+    asConstPort(bt_self_component_port_output * const libObjPtr) noexcept
+    {
+        return bt_self_component_port_output_as_port_output(libObjPtr);
+    }
+};
+
+} /* namespace internal */
+
+template <typename LibSelfCompPortT, typename LibPortT>
+class SelfComponentPort final : public BorrowedObject<LibSelfCompPortT>
+{
+public:
+    using typename BorrowedObject<LibSelfCompPortT>::LibObjPtr;
+
+    explicit SelfComponentPort(const LibObjPtr libObjPtr) noexcept :
+        BorrowedObject<LibSelfCompPortT> {libObjPtr}
+    {
+    }
+
+    ConstPort<LibPortT> asConstPort() const noexcept
+    {
+        return ConstPort<LibPortT> {
+            internal::SelfComponentPortSpec<LibSelfCompPortT>::asConstPort(this->libObjPtr())};
+    }
+
+    bt2c::CStringView name() const noexcept
+    {
+        return this->asConstPort().name();
+    }
+
+    bool isConnected() const noexcept
+    {
+        return this->asConstPort().isConnected();
+    }
+
+    SelfComponent component() const noexcept
+    {
+        return SelfComponent {bt_self_component_port_borrow_component(this->_libSelfCompPortPtr())};
+    }
+
+    template <typename T>
+    T& data() const noexcept
+    {
+        return *static_cast<T *>(bt_self_component_port_get_data(this->_libSelfCompPortPtr()));
+    }
+
+private:
+    bt_self_component_port *_libSelfCompPortPtr() const noexcept
+    {
+        return internal::SelfComponentPortSpec<LibSelfCompPortT>::asSelfCompPort(this->libObjPtr());
+    }
+};
+
+template <typename LibSelfCompT, typename LibSelfCompPortT, typename LibPortT>
+typename SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::Port
+SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::operator[](
+    const std::uint64_t index) const noexcept
+{
+    return Port {_Spec::portByIndex(this->libObjPtr(), index)};
+}
+
+template <typename LibSelfCompT, typename LibSelfCompPortT, typename LibPortT>
+typename SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::Port
+SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::operator[](
+    const bt2c::CStringView name) const noexcept
+{
+    return Port {_Spec::portByName(this->libObjPtr(), name)};
+}
+
+template <typename LibSelfCompT, typename LibSelfCompPortT, typename LibPortT>
+typename SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::Iterator
+SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::begin() const noexcept
+{
+    return Iterator {*this, 0};
+}
+
+template <typename LibSelfCompT, typename LibSelfCompPortT, typename LibPortT>
+typename SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::Iterator
+SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::end() const noexcept
+{
+    return Iterator {*this, this->length()};
+}
+
+template <typename LibSelfCompT, typename LibSelfCompPortT, typename LibPortT>
+typename SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::Port
+SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::front() const noexcept
+{
+    return (*this)[0];
+}
+
+template <typename LibSelfCompT, typename LibSelfCompPortT, typename LibPortT>
+typename SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::Port
+SelfComponentPorts<LibSelfCompT, LibSelfCompPortT, LibPortT>::back() const noexcept
+{
+    return (*this)[this->length() - 1];
+}
+
+using SelfComponentInputPort = SelfComponentPort<bt_self_component_port_input, const bt_port_input>;
+
+using SelfComponentOutputPort =
+    SelfComponentPort<bt_self_component_port_output, const bt_port_output>;
+
+template <typename DataT>
+SelfSourceComponent::OutputPorts::Port SelfSourceComponent::_addOutputPort(const char * const name,
+                                                                           DataT * const data) const
+{
+    return this->_addPort<SelfSourceComponent::OutputPorts::Port, bt_self_component_port_output>(
+        name, data, bt_self_component_source_add_output_port);
+}
+
+template <typename DataT>
+SelfSourceComponent::OutputPorts::Port
+SelfSourceComponent::addOutputPort(const bt2c::CStringView name, DataT& data) const
+{
+    return this->_addOutputPort(name, &data);
+}
+
+inline SelfSourceComponent::OutputPorts::Port
+SelfSourceComponent::addOutputPort(const bt2c::CStringView name) const
+{
+    return this->_addOutputPort<void>(name, nullptr);
+}
+
+inline SelfSourceComponent::OutputPorts SelfSourceComponent::outputPorts() const noexcept
+{
+    return OutputPorts {this->libObjPtr()};
+}
+
+template <typename DataT>
+SelfFilterComponent::OutputPorts::Port SelfFilterComponent::_addOutputPort(const char * const name,
+                                                                           DataT * const data) const
+{
+    return this->_addPort<SelfFilterComponent::OutputPorts::Port, bt_self_component_port_output>(
+        name, data, bt_self_component_filter_add_output_port);
+}
+
+template <typename DataT>
+SelfFilterComponent::OutputPorts::Port
+SelfFilterComponent::addOutputPort(const bt2c::CStringView name, DataT& data) const
+{
+    return this->_addOutputPort(name, &data);
+}
+
+inline SelfFilterComponent::OutputPorts::Port
+SelfFilterComponent::addOutputPort(const bt2c::CStringView name) const
+{
+    return this->_addOutputPort<void>(name, nullptr);
+}
+
+inline SelfFilterComponent::OutputPorts SelfFilterComponent::outputPorts() const noexcept
+{
+    return OutputPorts {this->libObjPtr()};
+}
+
+template <typename DataT>
+SelfFilterComponent::InputPorts::Port SelfFilterComponent::_addInputPort(const char * const name,
+                                                                         DataT * const data) const
+{
+    return this->_addPort<SelfFilterComponent::InputPorts::Port, bt_self_component_port_input>(
+        name, data, bt_self_component_filter_add_input_port);
+}
+
+template <typename DataT>
+SelfFilterComponent::InputPorts::Port
+SelfFilterComponent::addInputPort(const bt2c::CStringView name, DataT& data) const
+{
+    return this->_addInputPort(name, &data);
+}
+
+inline SelfFilterComponent::InputPorts::Port
+SelfFilterComponent::addInputPort(const bt2c::CStringView name) const
+{
+    return this->_addInputPort<void>(name, nullptr);
+}
+
+inline SelfFilterComponent::InputPorts SelfFilterComponent::inputPorts() const noexcept
+{
+    return InputPorts {this->libObjPtr()};
+}
+
+inline MessageIterator::Shared
+SelfSinkComponent::createMessageIterator(const InputPorts::Port port) const
+{
+    bt_message_iterator *libMsgIterPtr = nullptr;
+
+    const auto status = bt_message_iterator_create_from_sink_component(
+        this->libObjPtr(), port.libObjPtr(), &libMsgIterPtr);
+
+    switch (status) {
+    case BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK:
+        return MessageIterator::Shared::createWithoutRef(libMsgIterPtr);
+    case BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_MEMORY_ERROR:
+        throw MemoryError {};
+    case BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_ERROR:
+        throw Error {};
+    default:
+        bt_common_abort();
+    }
+}
+
+template <typename DataT>
+SelfSinkComponent::InputPorts::Port SelfSinkComponent::_addInputPort(const char * const name,
+                                                                     DataT * const data) const
+{
+    return this->_addPort<SelfSinkComponent::InputPorts::Port, bt_self_component_port_input>(
+        name, data, bt_self_component_sink_add_input_port);
+}
+
+template <typename DataT>
+SelfSinkComponent::InputPorts::Port SelfSinkComponent::addInputPort(const bt2c::CStringView name,
+                                                                    DataT& data) const
+{
+    return this->_addInputPort(name, &data);
+}
+
+inline SelfSinkComponent::InputPorts::Port
+SelfSinkComponent::addInputPort(const bt2c::CStringView name) const
+{
+    return this->_addInputPort<void>(name, nullptr);
+}
+
+inline SelfSinkComponent::InputPorts SelfSinkComponent::inputPorts() const noexcept
+{
+    return InputPorts {this->libObjPtr()};
+}
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_SELF_COMPONENT_PORT_HPP */
diff --git a/src/cpp-common/bt2/self-message-iterator-configuration.hpp b/src/cpp-common/bt2/self-message-iterator-configuration.hpp
new file mode 100644 (file)
index 0000000..5205efa
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_SELF_MESSAGE_ITERATOR_CONFIGURATION_HPP
+#define BABELTRACE_CPP_COMMON_BT2_SELF_MESSAGE_ITERATOR_CONFIGURATION_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "borrowed-object.hpp"
+
+namespace bt2 {
+
+class SelfMessageIteratorConfiguration final :
+    public BorrowedObject<bt_self_message_iterator_configuration>
+{
+public:
+    explicit SelfMessageIteratorConfiguration(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    SelfMessageIteratorConfiguration canSeekForward(const bool canSeekForward) const noexcept
+    {
+        bt_self_message_iterator_configuration_set_can_seek_forward(
+            this->libObjPtr(), static_cast<bt_bool>(canSeekForward));
+        return *this;
+    }
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_SELF_MESSAGE_ITERATOR_CONFIGURATION_HPP */
diff --git a/src/cpp-common/bt2/self-message-iterator.hpp b/src/cpp-common/bt2/self-message-iterator.hpp
new file mode 100644 (file)
index 0000000..6095145
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_SELF_MESSAGE_ITERATOR_HPP
+#define BABELTRACE_CPP_COMMON_BT2_SELF_MESSAGE_ITERATOR_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/common.h"
+
+#include "borrowed-object.hpp"
+#include "message-iterator.hpp"
+#include "self-component-port.hpp"
+
+namespace bt2 {
+
+class SelfMessageIterator final : public BorrowedObject<bt_self_message_iterator>
+{
+public:
+    explicit SelfMessageIterator(const LibObjPtr libObjPtr) noexcept :
+        _ThisBorrowedObject {libObjPtr}
+    {
+    }
+
+    MessageIterator::Shared createMessageIterator(const SelfComponentInputPort port) const
+    {
+        bt_message_iterator *libMsgIterPtr = nullptr;
+        const auto status = bt_message_iterator_create_from_message_iterator(
+            this->libObjPtr(), port.libObjPtr(), &libMsgIterPtr);
+
+        switch (status) {
+        case BT_MESSAGE_ITERATOR_CREATE_FROM_MESSAGE_ITERATOR_STATUS_OK:
+            return MessageIterator::Shared::createWithoutRef(libMsgIterPtr);
+        case BT_MESSAGE_ITERATOR_CREATE_FROM_MESSAGE_ITERATOR_STATUS_MEMORY_ERROR:
+            throw MemoryError {};
+        case BT_MESSAGE_ITERATOR_CREATE_FROM_MESSAGE_ITERATOR_STATUS_ERROR:
+            throw Error {};
+        default:
+            bt_common_abort();
+        }
+    }
+
+    SelfComponent component() const noexcept
+    {
+        return SelfComponent {bt_self_message_iterator_borrow_component(this->libObjPtr())};
+    }
+
+    SelfComponentOutputPort port() const noexcept
+    {
+        return SelfComponentOutputPort {bt_self_message_iterator_borrow_port(this->libObjPtr())};
+    }
+
+    bool isInterrupted() const noexcept
+    {
+        return static_cast<bool>(bt_self_message_iterator_is_interrupted(this->libObjPtr()));
+    }
+
+    template <typename T>
+    T& data() const noexcept
+    {
+        return *static_cast<T *>(bt_self_message_iterator_get_data(this->libObjPtr()));
+    }
+
+    template <typename T>
+    SelfMessageIterator data(T& obj) const noexcept
+    {
+        bt_self_message_iterator_set_data(this->libObjPtr(),
+                                          const_cast<void *>(static_cast<const void *>(&obj)));
+        return *this;
+    }
+
+    StreamBeginningMessage::Shared createStreamBeginningMessage(const ConstStream stream) const
+    {
+        const auto libObjPtr =
+            bt_message_stream_beginning_create(this->libObjPtr(), stream.libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return StreamBeginningMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    StreamEndMessage::Shared createStreamEndMessage(const ConstStream stream) const
+    {
+        const auto libObjPtr = bt_message_stream_end_create(this->libObjPtr(), stream.libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return StreamEndMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    EventMessage::Shared createEventMessage(const ConstEventClass eventCls,
+                                            const ConstStream stream) const
+    {
+        const auto libObjPtr =
+            bt_message_event_create(this->libObjPtr(), eventCls.libObjPtr(), stream.libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return EventMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    EventMessage::Shared createEventMessage(const ConstEventClass eventCls,
+                                            const ConstStream stream,
+                                            const std::uint64_t clockSnapshotValue) const
+    {
+        const auto libObjPtr = bt_message_event_create_with_default_clock_snapshot(
+            this->libObjPtr(), eventCls.libObjPtr(), stream.libObjPtr(), clockSnapshotValue);
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return EventMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    EventMessage::Shared createEventMessage(const ConstEventClass eventCls,
+                                            const ConstPacket packet) const
+    {
+        const auto libObjPtr = bt_message_event_create_with_packet(
+            this->libObjPtr(), eventCls.libObjPtr(), packet.libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return EventMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    EventMessage::Shared createEventMessage(const ConstEventClass eventCls,
+                                            const ConstPacket packet,
+                                            const std::uint64_t clockSnapshotValue) const
+    {
+        const auto libObjPtr = bt_message_event_create_with_packet_and_default_clock_snapshot(
+            this->libObjPtr(), eventCls.libObjPtr(), packet.libObjPtr(), clockSnapshotValue);
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return EventMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    PacketBeginningMessage::Shared createPacketBeginningMessage(const ConstPacket packet) const
+    {
+        const auto libObjPtr =
+            bt_message_packet_beginning_create(this->libObjPtr(), packet.libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return PacketBeginningMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    PacketBeginningMessage::Shared
+    createPacketBeginningMessage(const ConstPacket packet,
+                                 const std::uint64_t clockSnapshotValue) const
+    {
+        const auto libObjPtr = bt_message_packet_beginning_create_with_default_clock_snapshot(
+            this->libObjPtr(), packet.libObjPtr(), clockSnapshotValue);
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return PacketBeginningMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    PacketEndMessage::Shared createPacketEndMessage(const ConstPacket packet) const
+    {
+        const auto libObjPtr = bt_message_packet_end_create(this->libObjPtr(), packet.libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return PacketEndMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    PacketEndMessage::Shared createPacketEndMessage(const ConstPacket packet,
+                                                    const std::uint64_t clockSnapshotValue) const
+    {
+        const auto libObjPtr = bt_message_packet_end_create_with_default_clock_snapshot(
+            this->libObjPtr(), packet.libObjPtr(), clockSnapshotValue);
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return PacketEndMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    DiscardedEventsMessage::Shared createDiscardedEventsMessage(const ConstStream stream)
+    {
+        const auto libObjPtr =
+            bt_message_discarded_events_create(this->libObjPtr(), stream.libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return DiscardedEventsMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    DiscardedEventsMessage::Shared
+    createDiscardedEventsMessage(const ConstStream stream,
+                                 const std::uint64_t beginningClockSnapshotValue,
+                                 const std::uint64_t endClockSnapshotValue) const
+    {
+        const auto libObjPtr = bt_message_discarded_events_create_with_default_clock_snapshots(
+            this->libObjPtr(), stream.libObjPtr(), beginningClockSnapshotValue,
+            endClockSnapshotValue);
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return DiscardedEventsMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    DiscardedPacketsMessage::Shared createDiscardedPacketsMessage(const ConstStream stream) const
+    {
+        const auto libObjPtr =
+            bt_message_discarded_packets_create(this->libObjPtr(), stream.libObjPtr());
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return DiscardedPacketsMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    DiscardedPacketsMessage::Shared
+    createDiscardedPacketsMessage(const ConstStream stream,
+                                  const std::uint64_t beginningClockSnapshotValue,
+                                  const std::uint64_t endClockSnapshotValue) const
+    {
+        const auto libObjPtr = bt_message_discarded_packets_create_with_default_clock_snapshots(
+            this->libObjPtr(), stream.libObjPtr(), beginningClockSnapshotValue,
+            endClockSnapshotValue);
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return DiscardedPacketsMessage::Shared::createWithoutRef(libObjPtr);
+    }
+
+    MessageIteratorInactivityMessage::Shared
+    createMessageIteratorInactivityMessage(const ConstClockClass clockClass,
+                                           const std::uint64_t clockSnapshotValue) const
+    {
+        const auto libObjPtr = bt_message_message_iterator_inactivity_create(
+            this->libObjPtr(), clockClass.libObjPtr(), clockSnapshotValue);
+
+        if (!libObjPtr) {
+            throw MemoryError {};
+        }
+
+        return MessageIteratorInactivityMessage::Shared::createWithoutRef(libObjPtr);
+    }
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_SELF_MESSAGE_ITERATOR_HPP */
diff --git a/src/cpp-common/bt2/shared-object.hpp b/src/cpp-common/bt2/shared-object.hpp
new file mode 100644 (file)
index 0000000..1f68906
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2019-2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_SHARED_OBJECT_HPP
+#define BABELTRACE_CPP_COMMON_BT2_SHARED_OBJECT_HPP
+
+#include <utility>
+
+#include "optional-borrowed-object.hpp"
+
+namespace bt2 {
+
+/*
+ * An instance of this class wraps an optional instance of `ObjT` and
+ * manages the reference counting of the underlying libbabeltrace2
+ * object.
+ *
+ * When you move a shared object, it becomes empty, in that operator*()
+ * and operator->() will either fail to assert in debug mode or trigger
+ * a segmentation fault.
+ *
+ * The default constructor builds an empty shared object. You may also
+ * call the reset() method to make a shared object empty. Check whether
+ * or not a shared object is empty with the `bool` operator.
+ *
+ * `LibObjT` is the direct libbabeltrace2 object type, for example
+ * `bt_stream_class` or `const bt_value`.
+ *
+ * RefFuncsT::get() must accept a `const LibObjT *` value and increment
+ * its reference count.
+ *
+ * RefFuncsT::put() must accept a `const LibObjT *` value and decrement
+ * its reference count.
+ */
+template <typename ObjT, typename LibObjT, typename RefFuncsT>
+class SharedObject final
+{
+    /*
+     * This makes it possible for a
+     * `SharedObject<Something, bt_something, ...>` instance to get
+     * assigned an instance of
+     * `SharedObject<SpecificSomething, bt_something, ...>` (copy/move
+     * constructors and assignment operators), given that
+     * `SpecificSomething` inherits `Something`.
+     */
+    template <typename, typename, typename>
+    friend class SharedObject;
+
+public:
+    /*
+     * Builds an empty shared object.
+     */
+    explicit SharedObject() noexcept
+    {
+    }
+
+private:
+    /*
+     * Builds a shared object from `obj` without getting a reference.
+     */
+    explicit SharedObject(const ObjT& obj) noexcept : _mObj {obj}
+    {
+    }
+
+    /*
+     * Common generic "copy" constructor.
+     *
+     * This constructor is meant to be delegated to by the copy
+     * constructor and the generic "copy" constructor.
+     *
+     * The second parameter, of type `int`, makes it possible to
+     * delegate by deduction as you can't explicit the template
+     * parameters when delegating to a constructor template.
+     */
+    template <typename OtherObjT, typename OtherLibObjT>
+    SharedObject(const SharedObject<OtherObjT, OtherLibObjT, RefFuncsT>& other, int) noexcept :
+        _mObj {other._mObj}
+    {
+        this->_getRef();
+    }
+
+    /*
+     * Common generic "move" constructor.
+     *
+     * See the comment of the common generic "copy" constructor above.
+     */
+    template <typename OtherObjT, typename OtherLibObjT>
+    SharedObject(SharedObject<OtherObjT, OtherLibObjT, RefFuncsT>&& other, int) noexcept :
+        _mObj {other._mObj}
+    {
+        /* Reset moved-from object */
+        other._reset();
+    }
+
+public:
+    /*
+     * Builds a shared object from `obj` without getting a reference.
+     */
+    static SharedObject createWithoutRef(const ObjT& obj) noexcept
+    {
+        return SharedObject {obj};
+    }
+
+    /*
+     * Builds a shared object from `libObjPtr` without getting a
+     * reference.
+     */
+    static SharedObject createWithoutRef(LibObjT * const libObjPtr) noexcept
+    {
+        return SharedObject::createWithoutRef(ObjT {libObjPtr});
+    }
+
+    /*
+     * Builds a shared object from `obj`, immediately getting a new
+     * reference.
+     */
+    static SharedObject createWithRef(const ObjT& obj) noexcept
+    {
+        SharedObject sharedObj {obj};
+
+        sharedObj._getRef();
+        return sharedObj;
+    }
+
+    /*
+     * Builds a shared object from `libObjPtr`, immediately getting a
+     * new reference.
+     */
+    static SharedObject createWithRef(LibObjT * const libObjPtr) noexcept
+    {
+        return SharedObject::createWithRef(ObjT {libObjPtr});
+    }
+
+    /*
+     * Copy constructor.
+     */
+    SharedObject(const SharedObject& other) noexcept : SharedObject {other, 0}
+    {
+    }
+
+    /*
+     * Move constructor.
+     */
+    SharedObject(SharedObject&& other) noexcept : SharedObject {std::move(other), 0}
+    {
+    }
+
+    /*
+     * Copy assignment operator.
+     */
+    SharedObject& operator=(const SharedObject& other) noexcept
+    {
+        /* Use generic "copy" assignment operator */
+        return this->operator=<ObjT, LibObjT>(other);
+    }
+
+    /*
+     * Move assignment operator.
+     */
+    SharedObject& operator=(SharedObject&& other) noexcept
+    {
+        /* Use generic "move" assignment operator */
+        return this->operator=<ObjT, LibObjT>(std::move(other));
+    }
+
+    /*
+     * Generic "copy" constructor.
+     *
+     * See the `friend class SharedObject` comment above.
+     */
+    template <typename OtherObjT, typename OtherLibObjT>
+    SharedObject(const SharedObject<OtherObjT, OtherLibObjT, RefFuncsT>& other) noexcept :
+        SharedObject {other, 0}
+    {
+    }
+
+    /*
+     * Generic "move" constructor.
+     *
+     * See the `friend class SharedObject` comment above.
+     */
+    template <typename OtherObjT, typename OtherLibObjT>
+    SharedObject(SharedObject<OtherObjT, OtherLibObjT, RefFuncsT>&& other) noexcept :
+        SharedObject {std::move(other), 0}
+    {
+    }
+
+    /*
+     * Generic "copy" assignment operator.
+     *
+     * See the `friend class SharedObject` comment above.
+     */
+    template <typename OtherObjT, typename OtherLibObjT>
+    SharedObject& operator=(const SharedObject<OtherObjT, OtherLibObjT, RefFuncsT>& other) noexcept
+    {
+        /* Put current object's reference */
+        this->_putRef();
+
+        /* Set new current object and get a reference */
+        _mObj = other._mObj;
+        this->_getRef();
+
+        return *this;
+    }
+
+    /*
+     * Generic "move" assignment operator.
+     *
+     * See the `friend class SharedObject` comment above.
+     */
+    template <typename OtherObjT, typename OtherLibObjT>
+    SharedObject& operator=(SharedObject<OtherObjT, OtherLibObjT, RefFuncsT>&& other) noexcept
+    {
+        /* Put current object's reference */
+        this->_putRef();
+
+        /* Set new current object */
+        _mObj = other._mObj;
+
+        /* Reset moved-from object */
+        other._reset();
+
+        return *this;
+    }
+
+    ~SharedObject()
+    {
+        this->_putRef();
+    }
+
+    ObjT operator*() const noexcept
+    {
+        return *_mObj;
+    }
+
+    BorrowedObjectProxy<ObjT> operator->() const noexcept
+    {
+        return _mObj.operator->();
+    }
+
+    explicit operator bool() const noexcept
+    {
+        return _mObj.hasObject();
+    }
+
+    /*
+     * Makes this shared object empty.
+     */
+    void reset() noexcept
+    {
+        if (_mObj) {
+            this->_putRef();
+            this->_reset();
+        }
+    }
+
+    /*
+     * Transfers the reference of the object which this shared object
+     * wrapper manages and returns it, making the caller become an
+     * active owner.
+     *
+     * This method makes this object empty.
+     */
+    ObjT release() noexcept
+    {
+        const auto obj = *_mObj;
+
+        this->_reset();
+        return obj;
+    }
+
+private:
+    /*
+     * Resets this shared object.
+     *
+     * To be used when moving it.
+     */
+    void _reset() noexcept
+    {
+        _mObj.reset();
+    }
+
+    /*
+     * Gets a new reference using the configured libbabeltrace2
+     * reference incrementation function.
+     */
+    void _getRef() const noexcept
+    {
+        RefFuncsT::get(_mObj.libObjPtr());
+    }
+
+    /*
+     * Puts a reference using the configured libbabeltrace2 reference
+     * decrementation function.
+     */
+    void _putRef() const noexcept
+    {
+        RefFuncsT::put(_mObj.libObjPtr());
+    }
+
+    OptionalBorrowedObject<ObjT> _mObj;
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_SHARED_OBJECT_HPP */
index 62dc04e62ff9931e57ceead74fd30822d6dfc91e..07c65924d931b4a27dd3d7da6402b8dbe10fccdd 100644 (file)
@@ -7,19 +7,23 @@
 #ifndef BABELTRACE_CPP_COMMON_BT2_TRACE_IR_HPP
 #define BABELTRACE_CPP_COMMON_BT2_TRACE_IR_HPP
 
-#include <type_traits>
 #include <cstdint>
+#include <type_traits>
+
 #include <babeltrace2/babeltrace.h>
 
-#include "internal/borrowed-obj.hpp"
-#include "cpp-common/optional.hpp"
-#include "cpp-common/string_view.hpp"
+#include "common/macros.h"
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/bt2s/optional.hpp"
+
+#include "borrowed-object.hpp"
 #include "clock-class.hpp"
-#include "clock-snapshot.hpp"
 #include "field-class.hpp"
 #include "field.hpp"
-#include "value.hpp"
 #include "internal/utils.hpp"
+#include "optional-borrowed-object.hpp"
+#include "shared-object.hpp"
+#include "value.hpp"
 
 namespace bt2 {
 
@@ -49,7 +53,7 @@ namespace internal {
 template <typename LibObjT>
 struct CommonEventSpec;
 
-// Functions specific to mutable events
+/* Functions specific to mutable events */
 template <>
 struct CommonEventSpec<bt_event> final
 {
@@ -84,7 +88,7 @@ struct CommonEventSpec<bt_event> final
     }
 };
 
-// Functions specific to constant events
+/* Functions specific to constant events */
 template <>
 struct CommonEventSpec<const bt_event> final
 {
@@ -119,120 +123,65 @@ struct CommonEventSpec<const bt_event> final
     }
 };
 
-} // namespace internal
+template <typename LibObjT>
+using DepStructField = DepType<LibObjT, StructureField, ConstStructureField>;
+
+} /* namespace internal */
 
 template <typename LibObjT>
-class CommonEvent final : public internal::BorrowedObj<LibObjT>
+class CommonEvent final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ConstSpec = internal::CommonEventSpec<const bt_event>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
     using _Spec = internal::CommonEventSpec<LibObjT>;
-
-    using _Packet =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonPacket<const bt_packet>,
-                                  CommonPacket<bt_packet>>::type;
-
-    using _Stream =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonStream<const bt_stream>,
-                                  CommonStream<bt_stream>>::type;
-
-    using _StructureField = typename std::conditional<std::is_const<LibObjT>::value,
-                                                      ConstStructureField, StructureField>::type;
+    using _Packet = internal::DepPacket<LibObjT>;
+    using _Stream = internal::DepStream<LibObjT>;
+    using _StructureField = internal::DepStructField<LibObjT>;
 
 public:
-    using Class = typename std::conditional<std::is_const<LibObjT>::value,
-                                            CommonEventClass<const bt_event_class>,
-                                            CommonEventClass<bt_event_class>>::type;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
 
-    explicit CommonEvent(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    using Class = internal::DepType<LibObjT, CommonEventClass<bt_event_class>,
+                                    CommonEventClass<const bt_event_class>>;
+
+    explicit CommonEvent(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonEvent(const CommonEvent<OtherLibObjT>& event) noexcept : _ThisBorrowedObj {event}
+    CommonEvent(const CommonEvent<OtherLibObjT> event) noexcept : _ThisBorrowedObject {event}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonEvent<LibObjT>& operator=(const CommonEvent<OtherLibObjT>& event) noexcept
+    CommonEvent<LibObjT> operator=(const CommonEvent<OtherLibObjT> event) noexcept
     {
-        _ThisBorrowedObj::operator=(event);
+        _ThisBorrowedObject::operator=(event);
         return *this;
     }
 
-    CommonEventClass<const bt_event_class> cls() const noexcept;
-    Class cls() noexcept;
-    CommonStream<const bt_stream> stream() const noexcept;
-    _Stream stream() noexcept;
-    nonstd::optional<CommonPacket<const bt_packet>> packet() const noexcept;
-    nonstd::optional<_Packet> packet() noexcept;
-
-    nonstd::optional<ConstStructureField> payloadField() const noexcept
+    CommonEvent<const bt_event> asConst() const noexcept
     {
-        const auto libObjPtr = _ConstSpec::payloadField(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return ConstStructureField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return CommonEvent<const bt_event> {*this};
     }
 
-    nonstd::optional<_StructureField> payloadField() noexcept
-    {
-        const auto libObjPtr = _Spec::payloadField(this->_libObjPtr());
+    Class cls() const noexcept;
+    _Stream stream() const noexcept;
+    OptionalBorrowedObject<_Packet> packet() const noexcept;
 
-        if (libObjPtr) {
-            return _StructureField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
-    }
-
-    nonstd::optional<ConstStructureField> specificContextField() const noexcept
+    OptionalBorrowedObject<_StructureField> payloadField() const noexcept
     {
-        const auto libObjPtr = _ConstSpec::specificContextField(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return ConstStructureField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::payloadField(this->libObjPtr());
     }
 
-    nonstd::optional<_StructureField> specificContextField() noexcept
+    OptionalBorrowedObject<_StructureField> specificContextField() const noexcept
     {
-        const auto libObjPtr = _Spec::specificContextField(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return _StructureField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::specificContextField(this->libObjPtr());
     }
 
-    nonstd::optional<ConstStructureField> commonContextField() const noexcept
+    OptionalBorrowedObject<_StructureField> commonContextField() const noexcept
     {
-        const auto libObjPtr = _ConstSpec::commonContextField(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return ConstStructureField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
-    }
-
-    nonstd::optional<_StructureField> commonContextField() noexcept
-    {
-        const auto libObjPtr = _Spec::commonContextField(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return _StructureField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::commonContextField(this->libObjPtr());
     }
 };
 
@@ -241,14 +190,30 @@ using ConstEvent = CommonEvent<const bt_event>;
 
 namespace internal {
 
+struct EventTypeDescr
+{
+    using Const = ConstEvent;
+    using NonConst = Event;
+};
+
+template <>
+struct TypeDescr<Event> : public EventTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstEvent> : public EventTypeDescr
+{
+};
+
 struct PacketRefFuncs final
 {
-    static void get(const bt_packet * const libObjPtr)
+    static void get(const bt_packet * const libObjPtr) noexcept
     {
         bt_packet_get_ref(libObjPtr);
     }
 
-    static void put(const bt_packet * const libObjPtr)
+    static void put(const bt_packet * const libObjPtr) noexcept
     {
         bt_packet_put_ref(libObjPtr);
     }
@@ -257,7 +222,7 @@ struct PacketRefFuncs final
 template <typename LibObjT>
 struct CommonPacketSpec;
 
-// Functions specific to mutable packets
+/* Functions specific to mutable packets */
 template <>
 struct CommonPacketSpec<bt_packet> final
 {
@@ -272,7 +237,7 @@ struct CommonPacketSpec<bt_packet> final
     }
 };
 
-// Functions specific to constant packets
+/* Functions specific to constant packets */
 template <>
 struct CommonPacketSpec<const bt_packet> final
 {
@@ -287,112 +252,95 @@ struct CommonPacketSpec<const bt_packet> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class CommonPacket final : public internal::BorrowedObj<LibObjT>
+class CommonPacket final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ConstSpec = internal::CommonPacketSpec<const bt_packet>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
     using _Spec = internal::CommonPacketSpec<LibObjT>;
-    using _ThisCommonPacket = CommonPacket<LibObjT>;
-
-    using _Stream =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonStream<const bt_stream>,
-                                  CommonStream<bt_stream>>::type;
-
-    using _StructureField = typename std::conditional<std::is_const<LibObjT>::value,
-                                                      ConstStructureField, StructureField>::type;
+    using _Stream = internal::DepStream<LibObjT>;
+    using _StructureField = internal::DepStructField<LibObjT>;
 
 public:
-    using Shared = internal::SharedObj<_ThisCommonPacket, LibObjT, internal::PacketRefFuncs>;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedObject<CommonPacket, LibObjT, internal::PacketRefFuncs>;
 
-    explicit CommonPacket(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonPacket(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonPacket(const CommonPacket<OtherLibObjT>& packet) noexcept : _ThisBorrowedObj {packet}
+    CommonPacket(const CommonPacket<OtherLibObjT> packet) noexcept : _ThisBorrowedObject {packet}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonPacket& operator=(const CommonPacket<OtherLibObjT>& packet) noexcept
+    CommonPacket operator=(const CommonPacket<OtherLibObjT> packet) noexcept
     {
-        _ThisBorrowedObj::operator=(packet);
+        _ThisBorrowedObject::operator=(packet);
         return *this;
     }
 
-    CommonStream<const bt_stream> stream() const noexcept;
-    _Stream stream() noexcept;
-
-    nonstd::optional<ConstStructureField> contextField() const noexcept
+    CommonPacket<const bt_packet> asConst() const noexcept
     {
-        const auto libObjPtr = _ConstSpec::contextField(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return ConstStructureField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return CommonPacket<const bt_packet> {*this};
     }
 
-    nonstd::optional<_StructureField> contextField() noexcept
-    {
-        const auto libObjPtr = _Spec::contextField(this->_libObjPtr());
+    _Stream stream() const noexcept;
 
-        if (libObjPtr) {
-            return _StructureField {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+    OptionalBorrowedObject<_StructureField> contextField() const noexcept
+    {
+        return _Spec::contextField(this->libObjPtr());
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using Packet = CommonPacket<bt_packet>;
 using ConstPacket = CommonPacket<const bt_packet>;
 
-template <typename LibObjT>
-nonstd::optional<ConstPacket> CommonEvent<LibObjT>::packet() const noexcept
-{
-    const auto libObjPtr = _ConstSpec::packet(this->_libObjPtr());
+namespace internal {
 
-    if (libObjPtr) {
-        return ConstPacket {libObjPtr};
-    }
+struct PacketTypeDescr
+{
+    using Const = ConstPacket;
+    using NonConst = Packet;
+};
 
-    return nonstd::nullopt;
-}
+template <>
+struct TypeDescr<Packet> : public PacketTypeDescr
+{
+};
 
-template <typename LibObjT>
-nonstd::optional<typename CommonEvent<LibObjT>::_Packet> CommonEvent<LibObjT>::packet() noexcept
+template <>
+struct TypeDescr<ConstPacket> : public PacketTypeDescr
 {
-    const auto libObjPtr = _Spec::packet(this->_libObjPtr());
+};
 
-    if (libObjPtr) {
-        return _Packet {libObjPtr};
-    }
+} /* namespace internal */
 
-    return nonstd::nullopt;
+template <typename LibObjT>
+OptionalBorrowedObject<typename CommonEvent<LibObjT>::_Packet>
+CommonEvent<LibObjT>::packet() const noexcept
+{
+    return _Spec::packet(this->libObjPtr());
 }
 
 namespace internal {
 
 struct StreamRefFuncs final
 {
-    static void get(const bt_stream * const libObjPtr)
+    static void get(const bt_stream * const libObjPtr) noexcept
     {
         bt_stream_get_ref(libObjPtr);
     }
 
-    static void put(const bt_stream * const libObjPtr)
+    static void put(const bt_stream * const libObjPtr) noexcept
     {
         bt_stream_put_ref(libObjPtr);
     }
@@ -401,7 +349,7 @@ struct StreamRefFuncs final
 template <typename LibObjT>
 struct CommonStreamSpec;
 
-// Functions specific to mutable streams
+/* Functions specific to mutable streams */
 template <>
 struct CommonStreamSpec<bt_stream> final
 {
@@ -421,7 +369,7 @@ struct CommonStreamSpec<bt_stream> final
     }
 };
 
-// Functions specific to constant streams
+/* Functions specific to constant streams */
 template <>
 struct CommonStreamSpec<const bt_stream> final
 {
@@ -441,156 +389,146 @@ struct CommonStreamSpec<const bt_stream> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class CommonStream final : public internal::BorrowedObj<LibObjT>
+class CommonStream final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ConstSpec = internal::CommonStreamSpec<const bt_stream>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
     using _Spec = internal::CommonStreamSpec<LibObjT>;
-    using _ThisCommonStream = CommonStream<LibObjT>;
-
-    using _Trace =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonTrace<const bt_trace>,
-                                  CommonTrace<bt_trace>>::type;
+    using _Trace = internal::DepType<LibObjT, CommonTrace<bt_trace>, CommonTrace<const bt_trace>>;
 
 public:
-    using Shared = internal::SharedObj<_ThisCommonStream, LibObjT, internal::StreamRefFuncs>;
-
-    using Class = typename std::conditional<std::is_const<LibObjT>::value,
-                                            CommonStreamClass<const bt_stream_class>,
-                                            CommonStreamClass<bt_stream_class>>::type;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedObject<CommonStream, LibObjT, internal::StreamRefFuncs>;
+    using UserAttributes = internal::DepUserAttrs<LibObjT>;
 
-    using UserAttributes =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstMapValue, MapValue>::type;
+    using Class = internal::DepType<LibObjT, CommonStreamClass<bt_stream_class>,
+                                    CommonStreamClass<const bt_stream_class>>;
 
-    explicit CommonStream(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonStream(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStream(const CommonStream<OtherLibObjT>& stream) noexcept : _ThisBorrowedObj {stream}
+    CommonStream(const CommonStream<OtherLibObjT> stream) noexcept : _ThisBorrowedObject {stream}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonStream& operator=(const CommonStream<OtherLibObjT>& stream) noexcept
+    CommonStream operator=(const CommonStream<OtherLibObjT> stream) noexcept
     {
-        _ThisBorrowedObj::operator=(stream);
+        _ThisBorrowedObject::operator=(stream);
         return *this;
     }
 
-    Packet::Shared createPacket()
+    CommonStream<const bt_stream> asConst() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return CommonStream<const bt_stream> {*this};
+    }
+
+    Packet::Shared createPacket() const
+    {
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstStream`.");
 
-        const auto libObjPtr = bt_packet_create(this->_libObjPtr());
+        const auto libObjPtr = bt_packet_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Packet::Shared {Packet {libObjPtr}};
+        return Packet::Shared::createWithoutRef(libObjPtr);
     }
 
-    CommonStreamClass<const bt_stream_class> cls() const noexcept;
-    Class cls() noexcept;
-    CommonTrace<const bt_trace> trace() const noexcept;
-    _Trace trace() noexcept;
+    Class cls() const noexcept;
+    _Trace trace() const noexcept;
 
     std::uint64_t id() const noexcept
     {
-        return bt_stream_get_id(this->_libObjPtr());
+        return bt_stream_get_id(this->libObjPtr());
     }
 
-    void name(const char * const name)
+    CommonStream name(const bt2c::CStringView name) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstStream`.");
 
-        const auto status = bt_stream_set_name(this->_libObjPtr(), name);
+        const auto status = bt_stream_set_name(this->libObjPtr(), name);
 
         if (status == BT_STREAM_SET_NAME_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    void name(const std::string& name)
-    {
-        this->name(name.data());
+        return *this;
     }
 
-    nonstd::optional<bpstd::string_view> name() const noexcept
+    bt2c::CStringView name() const noexcept
     {
-        const auto name = bt_stream_get_name(this->_libObjPtr());
-
-        if (name) {
-            return name;
-        }
-
-        return nonstd::nullopt;
+        return bt_stream_get_name(this->libObjPtr());
     }
 
     template <typename LibValT>
-    void userAttributes(const CommonMapValue<LibValT>& userAttrs)
+    CommonStream userAttributes(const CommonMapValue<LibValT> userAttrs) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstStream`.");
 
-        bt_stream_set_user_attributes(this->_libObjPtr(), userAttrs._libObjPtr());
-    }
-
-    ConstMapValue userAttributes() const noexcept
-    {
-        return ConstMapValue {_ConstSpec::userAttributes(this->_libObjPtr())};
+        bt_stream_set_user_attributes(this->libObjPtr(), userAttrs.libObjPtr());
+        return *this;
     }
 
-    UserAttributes userAttributes() noexcept
+    UserAttributes userAttributes() const noexcept
     {
-        return UserAttributes {_Spec::userAttributes(this->_libObjPtr())};
+        return UserAttributes {_Spec::userAttributes(this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using Stream = CommonStream<bt_stream>;
 using ConstStream = CommonStream<const bt_stream>;
 
-template <typename LibObjT>
-ConstStream CommonEvent<LibObjT>::stream() const noexcept
+namespace internal {
+
+struct StreamTypeDescr
 {
-    return ConstStream {_ConstSpec::stream(this->_libObjPtr())};
-}
+    using Const = ConstStream;
+    using NonConst = Stream;
+};
 
-template <typename LibObjT>
-typename CommonEvent<LibObjT>::_Stream CommonEvent<LibObjT>::stream() noexcept
+template <>
+struct TypeDescr<Stream> : public StreamTypeDescr
 {
-    return _Stream {_Spec::stream(this->_libObjPtr())};
-}
+};
+
+template <>
+struct TypeDescr<ConstStream> : public StreamTypeDescr
+{
+};
+
+} /* namespace internal */
 
 template <typename LibObjT>
-ConstStream CommonPacket<LibObjT>::stream() const noexcept
+typename CommonEvent<LibObjT>::_Stream CommonEvent<LibObjT>::stream() const noexcept
 {
-    return ConstStream {_ConstSpec::stream(this->_libObjPtr())};
+    return _Stream {_Spec::stream(this->libObjPtr())};
 }
 
 template <typename LibObjT>
-typename CommonPacket<LibObjT>::_Stream CommonPacket<LibObjT>::stream() noexcept
+typename CommonPacket<LibObjT>::_Stream CommonPacket<LibObjT>::stream() const noexcept
 {
-    return _Stream {_Spec::stream(this->_libObjPtr())};
+    return _Stream {_Spec::stream(this->libObjPtr())};
 }
 
 namespace internal {
 
 struct TraceRefFuncs final
 {
-    static void get(const bt_trace * const libObjPtr)
+    static void get(const bt_trace * const libObjPtr) noexcept
     {
         bt_trace_get_ref(libObjPtr);
     }
 
-    static void put(const bt_trace * const libObjPtr)
+    static void put(const bt_trace * const libObjPtr) noexcept
     {
         bt_trace_put_ref(libObjPtr);
     }
@@ -599,7 +537,7 @@ struct TraceRefFuncs final
 template <typename LibObjT>
 struct CommonTraceSpec;
 
-// Functions specific to mutable traces
+/* Functions specific to mutable traces */
 template <>
 struct CommonTraceSpec<bt_trace> final
 {
@@ -624,7 +562,7 @@ struct CommonTraceSpec<bt_trace> final
     }
 };
 
-// Functions specific to constant traces
+/* Functions specific to constant traces */
 template <>
 struct CommonTraceSpec<const bt_trace> final
 {
@@ -651,185 +589,132 @@ struct CommonTraceSpec<const bt_trace> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class CommonTrace final : public internal::BorrowedObj<LibObjT>
+class CommonTrace final : public BorrowedObject<LibObjT>
 {
-    // Allow instantiate() to call `trace._libObjPtr()`
-    friend class CommonStreamClass<bt_stream_class>;
-
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ConstSpec = internal::CommonTraceSpec<const bt_trace>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
     using _Spec = internal::CommonTraceSpec<LibObjT>;
-    using _ThisCommonTrace = CommonTrace<LibObjT>;
-
-    using _Stream =
-        typename std::conditional<std::is_const<LibObjT>::value, CommonStream<const bt_stream>,
-                                  CommonStream<bt_stream>>::type;
+    using _Stream = internal::DepStream<LibObjT>;
 
 public:
-    using Shared = internal::SharedObj<_ThisCommonTrace, LibObjT, internal::TraceRefFuncs>;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedObject<CommonTrace, LibObjT, internal::TraceRefFuncs>;
+    using UserAttributes = internal::DepUserAttrs<LibObjT>;
 
-    using Class = typename std::conditional<std::is_const<LibObjT>::value,
-                                            CommonTraceClass<const bt_trace_class>,
-                                            CommonTraceClass<bt_trace_class>>::type;
-
-    using UserAttributes =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstMapValue, MapValue>::type;
+    using Class = internal::DepType<LibObjT, CommonTraceClass<bt_trace_class>,
+                                    CommonTraceClass<const bt_trace_class>>;
 
     struct ConstEnvironmentEntry
     {
-        bpstd::string_view name;
+        bt2c::CStringView name;
         ConstValue value;
     };
 
-    explicit CommonTrace(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonTrace(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonTrace(const CommonTrace<OtherLibObjT>& trace) noexcept : _ThisBorrowedObj {trace}
+    CommonTrace(const CommonTrace<OtherLibObjT> trace) noexcept : _ThisBorrowedObject {trace}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonTrace& operator=(const CommonTrace<OtherLibObjT>& trace) noexcept
+    CommonTrace operator=(const CommonTrace<OtherLibObjT> trace) noexcept
     {
-        _ThisBorrowedObj::operator=(trace);
+        _ThisBorrowedObject::operator=(trace);
         return *this;
     }
 
-    CommonTraceClass<const bt_trace_class> cls() const noexcept;
-    Class cls() noexcept;
+    CommonTrace<const bt_trace> asConst() const noexcept
+    {
+        return CommonTrace<const bt_trace> {*this};
+    }
+
+    Class cls() const noexcept;
 
-    void name(const char * const name)
+    CommonTrace name(const bt2c::CStringView name) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTrace`.");
 
-        const auto status = bt_trace_set_name(this->_libObjPtr(), name);
+        const auto status = bt_trace_set_name(this->libObjPtr(), name);
 
         if (status == BT_TRACE_SET_NAME_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    void name(const std::string& name)
-    {
-        this->name(name.data());
+        return *this;
     }
 
-    nonstd::optional<bpstd::string_view> name() const noexcept
+    bt2c::CStringView name() const noexcept
     {
-        const auto name = bt_trace_get_name(this->_libObjPtr());
-
-        if (name) {
-            return name;
-        }
-
-        return nonstd::nullopt;
+        return bt_trace_get_name(this->libObjPtr());
     }
 
-    void uuid(const std::uint8_t * const uuid) noexcept
+    CommonTrace uuid(const bt2c::UuidView& uuid) const noexcept
     {
-        bt_trace_set_uuid(this->_libObjPtr(), uuid);
+        bt_trace_set_uuid(this->libObjPtr(), uuid.begin());
+        return *this;
     }
 
-    nonstd::optional<bt2_common::UuidView> uuid() const noexcept
+    bt2s::optional<bt2c::UuidView> uuid() const noexcept
     {
-        const auto uuid = bt_trace_get_uuid(this->_libObjPtr());
+        const auto uuid = bt_trace_get_uuid(this->libObjPtr());
 
         if (uuid) {
-            return bt2_common::UuidView {uuid};
+            return bt2c::UuidView {uuid};
         }
 
-        return nonstd::nullopt;
+        return bt2s::nullopt;
     }
 
-    std::uint64_t size() const noexcept
+    std::uint64_t length() const noexcept
     {
-        return bt_trace_get_stream_count(this->_libObjPtr());
+        return bt_trace_get_stream_count(this->libObjPtr());
     }
 
-    ConstStream operator[](const std::uint64_t index) const noexcept
+    _Stream operator[](const std::uint64_t index) const noexcept
     {
-        return ConstStream {_ConstSpec::streamByIndex(this->_libObjPtr(), index)};
+        return _Stream {_Spec::streamByIndex(this->libObjPtr(), index)};
     }
 
-    _Stream operator[](const std::uint64_t index) noexcept
+    OptionalBorrowedObject<_Stream> streamById(const std::uint64_t id) const noexcept
     {
-        return _Stream {_Spec::streamByIndex(this->_libObjPtr(), index)};
-    }
-
-    nonstd::optional<ConstStream> streamById(const std::uint64_t id) const noexcept
-    {
-        const auto libObjPtr = _ConstSpec::streamById(this->_libObjPtr(), id);
-
-        if (libObjPtr) {
-            return ConstStream {libObjPtr};
-        }
-
-        return nonstd::nullopt;
-    }
-
-    nonstd::optional<_Stream> streamById(const std::uint64_t id) noexcept
-    {
-        const auto libObjPtr = _Spec::streamById(this->_libObjPtr(), id);
-
-        if (libObjPtr) {
-            return _Stream {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::streamById(this->libObjPtr(), id);
     }
 
-    void environmentEntry(const char * const name, const std::int64_t val)
+    CommonTrace environmentEntry(const bt2c::CStringView name, const std::int64_t val) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTrace`.");
 
-        const auto status = bt_trace_set_environment_entry_integer(this->_libObjPtr(), name, val);
+        const auto status = bt_trace_set_environment_entry_integer(this->libObjPtr(), name, val);
 
         if (status == BT_TRACE_SET_ENVIRONMENT_ENTRY_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    void environmentEntry(const std::string& name, const std::int64_t val)
-    {
-        this->environmentEntry(name.data(), val);
+        return *this;
     }
 
-    void environmentEntry(const char * const name, const char * const val)
+    CommonTrace environmentEntry(const bt2c::CStringView name, const bt2c::CStringView val) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTrace`.");
 
-        const auto status = bt_trace_set_environment_entry_string(this->_libObjPtr(), name, val);
+        const auto status = bt_trace_set_environment_entry_string(this->libObjPtr(), name, val);
 
         if (status == BT_TRACE_SET_ENVIRONMENT_ENTRY_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    void environmentEntry(const std::string& name, const char * const val)
-    {
-        this->environmentEntry(name.data(), val);
-    }
-
-    void environmentEntry(const char * const name, const std::string& val)
-    {
-        this->environmentEntry(name, val.data());
-    }
-
-    void environmentEntry(const std::string& name, const std::string& val)
-    {
-        this->environmentEntry(name.data(), val.data());
+        return *this;
     }
 
     std::uint64_t environmentSize() const noexcept
     {
-        return bt_trace_get_environment_entry_count(this->_libObjPtr());
+        return bt_trace_get_environment_entry_count(this->libObjPtr());
     }
 
     ConstEnvironmentEntry environmentEntry(const std::uint64_t index) const noexcept
@@ -837,77 +722,75 @@ public:
         const char *name;
         const bt_value *libObjPtr;
 
-        bt_trace_borrow_environment_entry_by_index_const(this->_libObjPtr(), index, &name,
+        bt_trace_borrow_environment_entry_by_index_const(this->libObjPtr(), index, &name,
                                                          &libObjPtr);
         return ConstEnvironmentEntry {name, ConstValue {libObjPtr}};
     }
 
-    nonstd::optional<ConstValue> environmentEntry(const char * const name) const noexcept
-    {
-        const auto libObjPtr =
-            bt_trace_borrow_environment_entry_value_by_name_const(this->_libObjPtr(), name);
-
-        if (libObjPtr) {
-            return ConstValue {libObjPtr};
-        }
-
-        return nonstd::nullopt;
-    }
-
-    nonstd::optional<ConstValue> environmentEntry(const std::string& name) const noexcept
+    OptionalBorrowedObject<ConstValue> environmentEntry(const bt2c::CStringView name) const noexcept
     {
-        return this->environmentEntry(name.data());
+        return bt_trace_borrow_environment_entry_value_by_name_const(this->libObjPtr(), name);
     }
 
     template <typename LibValT>
-    void userAttributes(const CommonMapValue<LibValT>& userAttrs)
+    CommonTrace userAttributes(const CommonMapValue<LibValT> userAttrs) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
-
-        bt_trace_set_user_attributes(this->_libObjPtr(), userAttrs._libObjPtr());
-    }
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTrace`.");
 
-    ConstMapValue userAttributes() const noexcept
-    {
-        return ConstMapValue {_ConstSpec::userAttributes(this->_libObjPtr())};
+        bt_trace_set_user_attributes(this->libObjPtr(), userAttrs.libObjPtr());
+        return *this;
     }
 
-    UserAttributes userAttributes() noexcept
+    UserAttributes userAttributes() const noexcept
     {
-        return UserAttributes {_Spec::userAttributes(this->_libObjPtr())};
+        return UserAttributes {_Spec::userAttributes(this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using Trace = CommonTrace<bt_trace>;
 using ConstTrace = CommonTrace<const bt_trace>;
 
-template <typename LibObjT>
-ConstTrace CommonStream<LibObjT>::trace() const noexcept
+namespace internal {
+
+struct TraceTypeDescr
 {
-    return ConstTrace {_ConstSpec::trace(this->_libObjPtr())};
-}
+    using Const = ConstTrace;
+    using NonConst = Trace;
+};
+
+template <>
+struct TypeDescr<Trace> : public TraceTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstTrace> : public TraceTypeDescr
+{
+};
+
+} /* namespace internal */
 
 template <typename LibObjT>
-typename CommonStream<LibObjT>::_Trace CommonStream<LibObjT>::trace() noexcept
+typename CommonStream<LibObjT>::_Trace CommonStream<LibObjT>::trace() const noexcept
 {
-    return _Trace {_Spec::trace(this->_libObjPtr())};
+    return _Trace {_Spec::trace(this->libObjPtr())};
 }
 
 namespace internal {
 
 struct EventClassRefFuncs final
 {
-    static void get(const bt_event_class * const libObjPtr)
+    static void get(const bt_event_class * const libObjPtr) noexcept
     {
         bt_event_class_get_ref(libObjPtr);
     }
 
-    static void put(const bt_event_class * const libObjPtr)
+    static void put(const bt_event_class * const libObjPtr) noexcept
     {
         bt_event_class_put_ref(libObjPtr);
     }
@@ -916,7 +799,7 @@ struct EventClassRefFuncs final
 template <typename LibObjT>
 struct CommonEventClassSpec;
 
-// Functions specific to mutable event classes
+/* Functions specific to mutable event classes */
 template <>
 struct CommonEventClassSpec<bt_event_class> final
 {
@@ -941,7 +824,7 @@ struct CommonEventClassSpec<bt_event_class> final
     }
 };
 
-// Functions specific to constant event classes
+/* Functions specific to constant event classes */
 template <>
 struct CommonEventClassSpec<const bt_event_class> final
 {
@@ -967,268 +850,234 @@ struct CommonEventClassSpec<const bt_event_class> final
     }
 };
 
-} // namespace internal
+template <typename LibObjT>
+using DepStructFc = DepType<LibObjT, StructureFieldClass, ConstStructureFieldClass>;
+
+} /* namespace internal */
+
+/* Avoid `-Wshadow` error on GCC, conflicting with `bt2::Error` */
+BT_DIAG_PUSH
+BT_DIAG_IGNORE_SHADOW
+
+enum class EventClassLogLevel
+{
+    Emergency = BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY,
+    Alert = BT_EVENT_CLASS_LOG_LEVEL_ALERT,
+    Critical = BT_EVENT_CLASS_LOG_LEVEL_CRITICAL,
+    Error = BT_EVENT_CLASS_LOG_LEVEL_ERROR,
+    Warning = BT_EVENT_CLASS_LOG_LEVEL_WARNING,
+    Notice = BT_EVENT_CLASS_LOG_LEVEL_NOTICE,
+    Info = BT_EVENT_CLASS_LOG_LEVEL_INFO,
+    DebugSystem = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM,
+    DebugProgram = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM,
+    DebugProcess = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS,
+    DebugModule = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE,
+    DebugUnit = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT,
+    DebugFunction = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION,
+    DebugLine = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE,
+    Debug = BT_EVENT_CLASS_LOG_LEVEL_DEBUG,
+};
+
+BT_DIAG_POP
 
 template <typename LibObjT>
-class CommonEventClass final : public internal::BorrowedObj<LibObjT>
+class CommonEventClass final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ConstSpec = internal::CommonEventClassSpec<const bt_event_class>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
     using _Spec = internal::CommonEventClassSpec<LibObjT>;
-    using _ThisCommonEventClass = CommonEventClass<LibObjT>;
-
-    using _StreamClass = typename std::conditional<std::is_const<LibObjT>::value,
-                                                   CommonStreamClass<const bt_stream_class>,
-                                                   CommonStreamClass<bt_stream_class>>::type;
+    using _StructureFieldClass = internal::DepStructFc<LibObjT>;
 
-    using _StructureFieldClass =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstStructureFieldClass,
-                                  StructureFieldClass>::type;
+    using _StreamClass = internal::DepType<LibObjT, CommonStreamClass<bt_stream_class>,
+                                           CommonStreamClass<const bt_stream_class>>;
 
 public:
-    using Shared =
-        internal::SharedObj<_ThisCommonEventClass, LibObjT, internal::EventClassRefFuncs>;
-
-    using UserAttributes =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstMapValue, MapValue>::type;
-
-    enum class LogLevel
-    {
-        EMERGENCY = BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY,
-        ALERT = BT_EVENT_CLASS_LOG_LEVEL_ALERT,
-        CRITICAL = BT_EVENT_CLASS_LOG_LEVEL_CRITICAL,
-        ERR = BT_EVENT_CLASS_LOG_LEVEL_ERROR,
-        WARNING = BT_EVENT_CLASS_LOG_LEVEL_WARNING,
-        NOTICE = BT_EVENT_CLASS_LOG_LEVEL_NOTICE,
-        INFO = BT_EVENT_CLASS_LOG_LEVEL_INFO,
-        DEBUG_SYSTEM = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM,
-        DEBUG_PROGRAM = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM,
-        DEBUG_PROC = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS,
-        DEBUG_MODULE = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE,
-        DEBUG_UNIT = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT,
-        DEBUG_FUNCTION = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION,
-        DEBUG_LINE = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE,
-        DEBUG = BT_EVENT_CLASS_LOG_LEVEL_DEBUG,
-    };
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedObject<CommonEventClass, LibObjT, internal::EventClassRefFuncs>;
+    using UserAttributes = internal::DepUserAttrs<LibObjT>;
 
-    explicit CommonEventClass(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonEventClass(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonEventClass(const CommonEventClass<OtherLibObjT>& eventClass) noexcept :
-        _ThisBorrowedObj {eventClass}
+    CommonEventClass(const CommonEventClass<OtherLibObjT> eventClass) noexcept :
+        _ThisBorrowedObject {eventClass}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonEventClass& operator=(const CommonEventClass<OtherLibObjT>& eventClass) noexcept
+    CommonEventClass operator=(const CommonEventClass<OtherLibObjT> eventClass) noexcept
     {
-        _ThisBorrowedObj::operator=(eventClass);
+        _ThisBorrowedObject::operator=(eventClass);
         return *this;
     }
 
-    CommonStreamClass<const bt_stream_class> streamClass() const noexcept;
-    _StreamClass streamClass() noexcept;
+    CommonEventClass<const bt_event_class> asConst() const noexcept
+    {
+        return CommonEventClass<const bt_event_class> {*this};
+    }
+
+    _StreamClass streamClass() const noexcept;
 
     std::uint64_t id() const noexcept
     {
-        return bt_event_class_get_id(this->_libObjPtr());
+        return bt_event_class_get_id(this->libObjPtr());
     }
 
-    void name(const char * const name)
+    CommonEventClass name(const bt2c::CStringView name) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstEventClass`.");
 
-        const auto status = bt_event_class_set_name(this->_libObjPtr(), name);
+        const auto status = bt_event_class_set_name(this->libObjPtr(), name);
 
         if (status == BT_EVENT_CLASS_SET_NAME_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    void name(const std::string& name)
-    {
-        this->name(name.data());
+        return *this;
     }
 
-    nonstd::optional<bpstd::string_view> name() const noexcept
+    bt2c::CStringView name() const noexcept
     {
-        const auto name = bt_event_class_get_name(this->_libObjPtr());
-
-        if (name) {
-            return name;
-        }
-
-        return nonstd::nullopt;
+        return bt_event_class_get_name(this->libObjPtr());
     }
 
-    void logLevel(const LogLevel logLevel) noexcept
+    CommonEventClass logLevel(const EventClassLogLevel logLevel) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstEventClass`.");
 
-        bt_event_class_set_log_level(this->_libObjPtr(),
+        bt_event_class_set_log_level(this->libObjPtr(),
                                      static_cast<bt_event_class_log_level>(logLevel));
+        return *this;
     }
 
-    nonstd::optional<LogLevel> logLevel() const noexcept
+    bt2s::optional<EventClassLogLevel> logLevel() const noexcept
     {
         bt_event_class_log_level libLogLevel;
-        const auto avail = bt_event_class_get_log_level(this->_libObjPtr(), &libLogLevel);
 
-        if (avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) {
-            return static_cast<LogLevel>(libLogLevel);
+        if (bt_event_class_get_log_level(this->libObjPtr(), &libLogLevel)) {
+            return static_cast<EventClassLogLevel>(libLogLevel);
         }
 
-        return nonstd::nullopt;
+        return bt2s::nullopt;
     }
 
-    void emfUri(const char * const emfUri)
+    CommonEventClass emfUri(const bt2c::CStringView emfUri) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstEventClass`.");
 
-        const auto status = bt_event_class_set_emf_uri(this->_libObjPtr(), emfUri);
+        const auto status = bt_event_class_set_emf_uri(this->libObjPtr(), emfUri);
 
         if (status == BT_EVENT_CLASS_SET_EMF_URI_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    void emfUri(const std::string& emfUri)
-    {
-        this->emfUri(emfUri.data());
+        return *this;
     }
 
-    nonstd::optional<bpstd::string_view> emfUri() const noexcept
+    bt2c::CStringView emfUri() const noexcept
     {
-        const auto emfUri = bt_event_class_get_emf_uri(this->_libObjPtr());
-
-        if (emfUri) {
-            return emfUri;
-        }
-
-        return nonstd::nullopt;
+        return bt_event_class_get_emf_uri(this->libObjPtr());
     }
 
-    void payloadFieldClass(const StructureFieldClass& fc)
+    CommonEventClass payloadFieldClass(const StructureFieldClass fc) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstEventClass`.");
 
         const auto status =
-            bt_event_class_set_payload_field_class(this->_libObjPtr(), fc._libObjPtr());
+            bt_event_class_set_payload_field_class(this->libObjPtr(), fc.libObjPtr());
 
         if (status == BT_EVENT_CLASS_SET_FIELD_CLASS_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    nonstd::optional<ConstStructureFieldClass> payloadFieldClass() const noexcept
-    {
-        const auto libObjPtr = _ConstSpec::payloadFieldClass(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return ConstStructureFieldClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return *this;
     }
 
-    nonstd::optional<_StructureFieldClass> payloadFieldClass() noexcept
+    OptionalBorrowedObject<_StructureFieldClass> payloadFieldClass() const noexcept
     {
-        const auto libObjPtr = _Spec::payloadFieldClass(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return _StructureFieldClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::payloadFieldClass(this->libObjPtr());
     }
 
-    void specificContextFieldClass(const StructureFieldClass& fc)
+    CommonEventClass specificContextFieldClass(const StructureFieldClass fc) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstEventClass`.");
 
         const auto status =
-            bt_event_class_set_specific_context_field_class(this->_libObjPtr(), fc._libObjPtr());
+            bt_event_class_set_specific_context_field_class(this->libObjPtr(), fc.libObjPtr());
 
         if (status == BT_EVENT_CLASS_SET_FIELD_CLASS_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    nonstd::optional<ConstStructureFieldClass> specificContextFieldClass() const noexcept
-    {
-        const auto libObjPtr = _ConstSpec::specificContextFieldClass(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return ConstStructureFieldClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return *this;
     }
 
-    nonstd::optional<_StructureFieldClass> specificContextFieldClass() noexcept
+    OptionalBorrowedObject<_StructureFieldClass> specificContextFieldClass() const noexcept
     {
-        const auto libObjPtr = _Spec::specificContextFieldClass(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return _StructureFieldClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::specificContextFieldClass(this->libObjPtr());
     }
 
     template <typename LibValT>
-    void userAttributes(const CommonMapValue<LibValT>& userAttrs)
+    CommonEventClass userAttributes(const CommonMapValue<LibValT> userAttrs) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstEventClass`.");
 
-        bt_event_class_set_user_attributes(this->_libObjPtr(), userAttrs._libObjPtr());
-    }
-
-    ConstMapValue userAttributes() const noexcept
-    {
-        return ConstMapValue {_ConstSpec::userAttributes(this->_libObjPtr())};
+        bt_event_class_set_user_attributes(this->libObjPtr(), userAttrs.libObjPtr());
+        return *this;
     }
 
-    UserAttributes userAttributes() noexcept
+    UserAttributes userAttributes() const noexcept
     {
-        return UserAttributes {_Spec::userAttributes(this->_libObjPtr())};
+        return UserAttributes {_Spec::userAttributes(this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using EventClass = CommonEventClass<bt_event_class>;
 using ConstEventClass = CommonEventClass<const bt_event_class>;
 
-template <typename LibObjT>
-ConstEventClass CommonEvent<LibObjT>::cls() const noexcept
+namespace internal {
+
+struct EventClassTypeDescr
 {
-    return ConstEventClass {_ConstSpec::cls(this->_libObjPtr())};
-}
+    using Const = ConstEventClass;
+    using NonConst = EventClass;
+};
+
+template <>
+struct TypeDescr<EventClass> : public EventClassTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstEventClass> : public EventClassTypeDescr
+{
+};
+
+} /* namespace internal */
 
 template <typename LibObjT>
-typename CommonEvent<LibObjT>::Class CommonEvent<LibObjT>::cls() noexcept
+typename CommonEvent<LibObjT>::Class CommonEvent<LibObjT>::cls() const noexcept
 {
-    return Class {_Spec::cls(this->_libObjPtr())};
+    return Class {_Spec::cls(this->libObjPtr())};
 }
 
 namespace internal {
 
 struct StreamClassRefFuncs final
 {
-    static void get(const bt_stream_class * const libObjPtr)
+    static void get(const bt_stream_class * const libObjPtr) noexcept
     {
         bt_stream_class_get_ref(libObjPtr);
     }
 
-    static void put(const bt_stream_class * const libObjPtr)
+    static void put(const bt_stream_class * const libObjPtr) noexcept
     {
         bt_stream_class_put_ref(libObjPtr);
     }
@@ -1237,7 +1086,7 @@ struct StreamClassRefFuncs final
 template <typename LibObjT>
 struct CommonStreamClassSpec;
 
-// Functions specific to mutable stream classes
+/* Functions specific to mutable stream classes */
 template <>
 struct CommonStreamClassSpec<bt_stream_class> final
 {
@@ -1279,7 +1128,7 @@ struct CommonStreamClassSpec<bt_stream_class> final
     }
 };
 
-// Functions specific to constant stream classes
+/* Functions specific to constant stream classes */
 template <>
 struct CommonStreamClassSpec<const bt_stream_class> final
 {
@@ -1323,427 +1172,368 @@ struct CommonStreamClassSpec<const bt_stream_class> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class CommonStreamClass final : public internal::BorrowedObj<LibObjT>
+class CommonStreamClass final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ConstSpec = internal::CommonStreamClassSpec<const bt_stream_class>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
     using _Spec = internal::CommonStreamClassSpec<LibObjT>;
-    using _ThisCommonStreamClass = CommonStreamClass<LibObjT>;
-
-    using _TraceClass = typename std::conditional<std::is_const<LibObjT>::value,
-                                                  CommonTraceClass<const bt_trace_class>,
-                                                  CommonTraceClass<bt_trace_class>>::type;
+    using _StructureFieldClass = internal::DepStructFc<LibObjT>;
 
-    using _EventClass = typename std::conditional<std::is_const<LibObjT>::value,
-                                                  CommonEventClass<const bt_event_class>,
-                                                  CommonEventClass<bt_event_class>>::type;
+    using _TraceClass = internal::DepType<LibObjT, CommonTraceClass<bt_trace_class>,
+                                          CommonTraceClass<const bt_trace_class>>;
 
-    using _StructureFieldClass =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstStructureFieldClass,
-                                  StructureFieldClass>::type;
+    using _EventClass = internal::DepType<LibObjT, CommonEventClass<bt_event_class>,
+                                          CommonEventClass<const bt_event_class>>;
 
-    using _ClockClass =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstClockClass, ClockClass>::type;
+    using _ClockClass = internal::DepType<LibObjT, ClockClass, ConstClockClass>;
 
 public:
-    using Shared =
-        internal::SharedObj<_ThisCommonStreamClass, LibObjT, internal::StreamClassRefFuncs>;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedObject<CommonStreamClass, LibObjT, internal::StreamClassRefFuncs>;
+    using UserAttributes = internal::DepUserAttrs<LibObjT>;
 
-    using UserAttributes =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstMapValue, MapValue>::type;
-
-    explicit CommonStreamClass(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonStreamClass(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStreamClass(const CommonStreamClass<OtherLibObjT>& streamClass) noexcept :
-        _ThisBorrowedObj {streamClass}
+    CommonStreamClass(const CommonStreamClass<OtherLibObjT> streamClass) noexcept :
+        _ThisBorrowedObject {streamClass}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonStreamClass& operator=(const CommonStreamClass<OtherLibObjT>& streamClass) noexcept
+    CommonStreamClass operator=(const CommonStreamClass<OtherLibObjT> streamClass) noexcept
     {
-        _ThisBorrowedObj::operator=(streamClass);
+        _ThisBorrowedObject::operator=(streamClass);
         return *this;
     }
 
-    Stream::Shared instantiate(const Trace& trace)
+    CommonStreamClass<const bt_stream_class> asConst() const noexcept
+    {
+        return CommonStreamClass<const bt_stream_class> {*this};
+    }
+
+    Stream::Shared instantiate(const Trace trace) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-        const auto libObjPtr = bt_stream_create(this->_libObjPtr(), trace._libObjPtr());
+        const auto libObjPtr = bt_stream_create(this->libObjPtr(), trace.libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Stream::Shared {Stream {libObjPtr}};
+        return Stream::Shared::createWithoutRef(libObjPtr);
     }
 
-    Stream::Shared instantiate(const Trace& trace, const std::uint64_t id)
+    Stream::Shared instantiate(const Trace trace, const std::uint64_t id) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-        const auto libObjPtr = bt_stream_create_with_id(this->_libObjPtr(), trace._libObjPtr(), id);
+        const auto libObjPtr = bt_stream_create_with_id(this->libObjPtr(), trace.libObjPtr(), id);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Stream::Shared {Stream {libObjPtr}};
+        return Stream::Shared::createWithoutRef(libObjPtr);
     }
 
-    EventClass::Shared createEventClass()
+    EventClass::Shared createEventClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-        const auto libObjPtr = bt_event_class_create(this->_libObjPtr());
+        const auto libObjPtr = bt_event_class_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return EventClass::Shared {EventClass {libObjPtr}};
+        return EventClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    EventClass::Shared createEventClass(const std::uint64_t id)
+    EventClass::Shared createEventClass(const std::uint64_t id) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-        const auto libObjPtr = bt_event_class_create_with_id(this->_libObjPtr(), id);
+        const auto libObjPtr = bt_event_class_create_with_id(this->libObjPtr(), id);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return EventClass::Shared {EventClass {libObjPtr}};
+        return EventClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    CommonTraceClass<const bt_trace_class> traceClass() const noexcept;
-    _TraceClass traceClass() noexcept;
+    _TraceClass traceClass() const noexcept;
 
     std::uint64_t id() const noexcept
     {
-        return bt_stream_class_get_id(this->_libObjPtr());
+        return bt_stream_class_get_id(this->libObjPtr());
     }
 
-    void name(const char * const name)
+    CommonStreamClass name(const bt2c::CStringView name) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-        const auto status = bt_stream_class_set_name(this->_libObjPtr(), name);
+        const auto status = bt_stream_class_set_name(this->libObjPtr(), name);
 
         if (status == BT_STREAM_CLASS_SET_NAME_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
-    }
 
-    void name(const std::string& name)
-    {
-        this->name(name.data());
+        return *this;
     }
 
-    nonstd::optional<bpstd::string_view> name() const noexcept
+    bt2c::CStringView name() const noexcept
     {
-        const auto name = bt_stream_class_get_name(this->_libObjPtr());
-
-        if (name) {
-            return name;
-        }
-
-        return nonstd::nullopt;
+        return bt_stream_class_get_name(this->libObjPtr());
     }
 
-    void assignsAutomaticEventClassId(const bool val) noexcept
+    CommonStreamClass assignsAutomaticEventClassId(const bool val) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-        bt_stream_class_set_assigns_automatic_event_class_id(this->_libObjPtr(),
+        bt_stream_class_set_assigns_automatic_event_class_id(this->libObjPtr(),
                                                              static_cast<bt_bool>(val));
+        return *this;
     }
 
     bool assignsAutomaticEventClassId() const noexcept
     {
         return static_cast<bool>(
-            bt_stream_class_assigns_automatic_event_class_id(this->_libObjPtr()));
+            bt_stream_class_assigns_automatic_event_class_id(this->libObjPtr()));
     }
 
-    void assignsAutomaticStreamId(const bool val) noexcept
+    CommonStreamClass assignsAutomaticStreamId(const bool val) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-        bt_stream_class_set_assigns_automatic_stream_id(this->_libObjPtr(),
+        bt_stream_class_set_assigns_automatic_stream_id(this->libObjPtr(),
                                                         static_cast<bt_bool>(val));
+        return *this;
     }
 
     bool assignsAutomaticStreamId() const noexcept
     {
-        return static_cast<bool>(bt_stream_class_assigns_automatic_stream_id(this->_libObjPtr()));
+        return static_cast<bool>(bt_stream_class_assigns_automatic_stream_id(this->libObjPtr()));
     }
 
-    void supportsPackets(const bool supportsPackets, const bool withBeginningDefaultClkSnapshot,
-                         const bool withEndDefaultClkSnapshot) noexcept
+    CommonStreamClass supportsPackets(const bool supportsPackets,
+                                      const bool withBeginningDefaultClkSnapshot,
+                                      const bool withEndDefaultClkSnapshot) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-        bt_stream_class_set_supports_packets(this->_libObjPtr(),
+        bt_stream_class_set_supports_packets(this->libObjPtr(),
                                              static_cast<bt_bool>(supportsPackets),
                                              static_cast<bt_bool>(withBeginningDefaultClkSnapshot),
                                              static_cast<bt_bool>(withEndDefaultClkSnapshot));
+        return *this;
     }
 
     bool supportsPackets() const noexcept
     {
-        return static_cast<bool>(bt_stream_class_supports_packets(this->_libObjPtr()));
+        return static_cast<bool>(bt_stream_class_supports_packets(this->libObjPtr()));
     }
 
     bool packetsHaveBeginningClockSnapshot() const noexcept
     {
         return static_cast<bool>(
-            bt_stream_class_packets_have_beginning_default_clock_snapshot(this->_libObjPtr()));
+            bt_stream_class_packets_have_beginning_default_clock_snapshot(this->libObjPtr()));
     }
 
     bool packetsHaveEndClockSnapshot() const noexcept
     {
         return static_cast<bool>(
-            bt_stream_class_packets_have_end_default_clock_snapshot(this->_libObjPtr()));
+            bt_stream_class_packets_have_end_default_clock_snapshot(this->libObjPtr()));
     }
 
-    void supportsDiscardedEvents(const bool supportsDiscardedEvents,
-                                 const bool withDefaultClkSnapshots) noexcept
+    CommonStreamClass supportsDiscardedEvents(const bool supportsDiscardedEvents,
+                                              const bool withDefaultClkSnapshots) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
         bt_stream_class_set_supports_discarded_events(
-            this->_libObjPtr(), static_cast<bt_bool>(supportsPackets),
+            this->libObjPtr(), static_cast<bt_bool>(supportsDiscardedEvents),
             static_cast<bt_bool>(withDefaultClkSnapshots));
+        return *this;
     }
 
     bool supportsDiscardedEvents() const noexcept
     {
-        return static_cast<bool>(bt_stream_class_supports_discarded_events(this->_libObjPtr()));
+        return static_cast<bool>(bt_stream_class_supports_discarded_events(this->libObjPtr()));
     }
 
     bool discardedEventsHaveDefaultClockSnapshots() const noexcept
     {
         return static_cast<bool>(
-            bt_stream_class_discarded_events_have_default_clock_snapshots(this->_libObjPtr()));
+            bt_stream_class_discarded_events_have_default_clock_snapshots(this->libObjPtr()));
     }
 
-    void supportsDiscardedPackets(const bool supportsDiscardedPackets,
-                                  const bool withDefaultClkSnapshots) noexcept
+    CommonStreamClass supportsDiscardedPackets(const bool supportsDiscardedPackets,
+                                               const bool withDefaultClkSnapshots) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
         bt_stream_class_set_supports_discarded_packets(
-            this->_libObjPtr(), static_cast<bt_bool>(supportsPackets),
+            this->libObjPtr(), static_cast<bt_bool>(supportsDiscardedPackets),
             static_cast<bt_bool>(withDefaultClkSnapshots));
+        return *this;
     }
 
     bool supportsDiscardedPackets() const noexcept
     {
-        return static_cast<bool>(bt_stream_class_supports_discarded_packets(this->_libObjPtr()));
+        return static_cast<bool>(bt_stream_class_supports_discarded_packets(this->libObjPtr()));
     }
 
     bool discardedPacketsHaveDefaultClockSnapshots() const noexcept
     {
         return static_cast<bool>(
-            bt_stream_class_discarded_packets_have_default_clock_snapshots(this->_libObjPtr()));
+            bt_stream_class_discarded_packets_have_default_clock_snapshots(this->libObjPtr()));
     }
 
-    void defaultClockClass(const ClockClass& clkCls)
+    CommonStreamClass defaultClockClass(const ClockClass clkCls) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
         const auto status =
-            bt_stream_class_set_default_clock_class(this->_libObjPtr(), clkCls._libObjPtr());
+            bt_stream_class_set_default_clock_class(this->libObjPtr(), clkCls.libObjPtr());
 
         BT_ASSERT(status == BT_STREAM_CLASS_SET_DEFAULT_CLOCK_CLASS_STATUS_OK);
+        return *this;
     }
 
-    nonstd::optional<ConstClockClass> defaultClockClass() const noexcept
-    {
-        const auto libObjPtr = _ConstSpec::defaultClockClass(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return ConstClockClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
-    }
-
-    nonstd::optional<_ClockClass> defaultClockClass() noexcept
-    {
-        const auto libObjPtr = _Spec::defaultClockClass(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return _ClockClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
-    }
-
-    std::uint64_t size() const noexcept
-    {
-        return bt_stream_class_get_event_class_count(this->_libObjPtr());
-    }
-
-    ConstEventClass operator[](const std::uint64_t index) const noexcept
+    OptionalBorrowedObject<_ClockClass> defaultClockClass() const noexcept
     {
-        return ConstEventClass {_ConstSpec::eventClassByIndex(this->_libObjPtr(), index)};
+        return _Spec::defaultClockClass(this->libObjPtr());
     }
 
-    _EventClass operator[](const std::uint64_t index) noexcept
+    std::uint64_t length() const noexcept
     {
-        return _EventClass {_Spec::eventClassByIndex(this->_libObjPtr(), index)};
+        return bt_stream_class_get_event_class_count(this->libObjPtr());
     }
 
-    nonstd::optional<ConstEventClass> eventClassById(const std::uint64_t id) const noexcept
+    _EventClass operator[](const std::uint64_t index) const noexcept
     {
-        const auto libObjPtr = _ConstSpec::eventClassById(this->_libObjPtr(), id);
-
-        if (libObjPtr) {
-            return ConstEventClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _EventClass {_Spec::eventClassByIndex(this->libObjPtr(), index)};
     }
 
-    nonstd::optional<_EventClass> eventClassById(const std::uint64_t id) noexcept
+    OptionalBorrowedObject<_EventClass> eventClassById(const std::uint64_t id) const noexcept
     {
-        const auto libObjPtr = _Spec::eventClassById(this->_libObjPtr(), id);
-
-        if (libObjPtr) {
-            return _EventClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::eventClassById(this->libObjPtr(), id);
     }
 
-    void packetContextFieldClass(const StructureFieldClass& fc)
+    CommonStreamClass packetContextFieldClass(const StructureFieldClass fc) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
         const auto status =
-            bt_stream_class_set_packet_context_field_class(this->_libObjPtr(), fc._libObjPtr());
+            bt_stream_class_set_packet_context_field_class(this->libObjPtr(), fc.libObjPtr());
 
-        if (status == BT_EVENT_CLASS_SET_FIELD_CLASS_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+        if (status == BT_STREAM_CLASS_SET_FIELD_CLASS_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
         }
-    }
 
-    nonstd::optional<ConstStructureFieldClass> packetContextFieldClass() const noexcept
-    {
-        const auto libObjPtr = _ConstSpec::packetContextFieldClass(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return ConstStructureFieldClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return *this;
     }
 
-    nonstd::optional<_StructureFieldClass> packetContextFieldClass() noexcept
+    OptionalBorrowedObject<_StructureFieldClass> packetContextFieldClass() const noexcept
     {
-        const auto libObjPtr = _Spec::packetContextFieldClass(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return _StructureFieldClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::packetContextFieldClass(this->libObjPtr());
     }
 
-    void eventCommonContextFieldClass(const StructureFieldClass& fc)
+    CommonStreamClass eventCommonContextFieldClass(const StructureFieldClass fc) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
-
-        const auto status = bt_stream_class_set_event_common_context_field_class(this->_libObjPtr(),
-                                                                                 fc._libObjPtr());
-
-        if (status == BT_EVENT_CLASS_SET_FIELD_CLASS_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
-        }
-    }
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-    nonstd::optional<ConstStructureFieldClass> eventCommonContextFieldClass() const noexcept
-    {
-        const auto libObjPtr = _ConstSpec::eventCommonContextFieldClass(this->_libObjPtr());
+        const auto status =
+            bt_stream_class_set_event_common_context_field_class(this->libObjPtr(), fc.libObjPtr());
 
-        if (libObjPtr) {
-            return ConstStructureFieldClass {libObjPtr};
+        if (status == BT_STREAM_CLASS_SET_FIELD_CLASS_STATUS_MEMORY_ERROR) {
+            throw MemoryError {};
         }
 
-        return nonstd::nullopt;
+        return *this;
     }
 
-    nonstd::optional<_StructureFieldClass> eventCommonContextFieldClass() noexcept
+    OptionalBorrowedObject<_StructureFieldClass> eventCommonContextFieldClass() const noexcept
     {
-        const auto libObjPtr = _Spec::eventCommonContextFieldClass(this->_libObjPtr());
-
-        if (libObjPtr) {
-            return _StructureFieldClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::eventCommonContextFieldClass(this->libObjPtr());
     }
 
     template <typename LibValT>
-    void userAttributes(const CommonMapValue<LibValT>& userAttrs)
+    CommonStreamClass userAttributes(const CommonMapValue<LibValT> userAttrs) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
-
-        bt_stream_class_set_user_attributes(this->_libObjPtr(), userAttrs._libObjPtr());
-    }
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStreamClass`.");
 
-    ConstMapValue userAttributes() const noexcept
-    {
-        return ConstMapValue {_ConstSpec::userAttributes(this->_libObjPtr())};
+        bt_stream_class_set_user_attributes(this->libObjPtr(), userAttrs.libObjPtr());
+        return *this;
     }
 
-    UserAttributes userAttributes() noexcept
+    UserAttributes userAttributes() const noexcept
     {
-        return UserAttributes {_Spec::userAttributes(this->_libObjPtr())};
+        return UserAttributes {_Spec::userAttributes(this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using StreamClass = CommonStreamClass<bt_stream_class>;
 using ConstStreamClass = CommonStreamClass<const bt_stream_class>;
 
-template <typename LibObjT>
-ConstStreamClass CommonEventClass<LibObjT>::streamClass() const noexcept
+namespace internal {
+
+struct StreamClassTypeDescr
 {
-    return ConstStreamClass {_ConstSpec::streamClass(this->_libObjPtr())};
-}
+    using Const = ConstStreamClass;
+    using NonConst = StreamClass;
+};
 
-template <typename LibObjT>
-typename CommonEventClass<LibObjT>::_StreamClass CommonEventClass<LibObjT>::streamClass() noexcept
+template <>
+struct TypeDescr<StreamClass> : public StreamClassTypeDescr
 {
-    return _StreamClass {_Spec::streamClass(this->_libObjPtr())};
-}
+};
+
+template <>
+struct TypeDescr<ConstStreamClass> : public StreamClassTypeDescr
+{
+};
+
+} /* namespace internal */
 
 template <typename LibObjT>
-ConstStreamClass CommonStream<LibObjT>::cls() const noexcept
+typename CommonEventClass<LibObjT>::_StreamClass
+CommonEventClass<LibObjT>::streamClass() const noexcept
 {
-    return ConstStreamClass {_ConstSpec::cls(this->_libObjPtr())};
+    return _StreamClass {_Spec::streamClass(this->libObjPtr())};
 }
 
 template <typename LibObjT>
-typename CommonStream<LibObjT>::Class CommonStream<LibObjT>::cls() noexcept
+typename CommonStream<LibObjT>::Class CommonStream<LibObjT>::cls() const noexcept
 {
-    return Class {_Spec::cls(this->_libObjPtr())};
+    return Class {_Spec::cls(this->libObjPtr())};
 }
 
 namespace internal {
 
 struct TraceClassRefFuncs final
 {
-    static void get(const bt_trace_class * const libObjPtr)
+    static void get(const bt_trace_class * const libObjPtr) noexcept
     {
         bt_trace_class_get_ref(libObjPtr);
     }
 
-    static void put(const bt_trace_class * const libObjPtr)
+    static void put(const bt_trace_class * const libObjPtr) noexcept
     {
         bt_trace_class_put_ref(libObjPtr);
     }
@@ -1752,7 +1542,7 @@ struct TraceClassRefFuncs final
 template <typename LibObjT>
 struct CommonTraceClassSpec;
 
-// Functions specific to mutable stream classes
+/* Functions specific to mutable stream classes */
 template <>
 struct CommonTraceClassSpec<bt_trace_class> final
 {
@@ -1774,7 +1564,7 @@ struct CommonTraceClassSpec<bt_trace_class> final
     }
 };
 
-// Functions specific to constant stream classes
+/* Functions specific to constant stream classes */
 template <>
 struct CommonTraceClassSpec<const bt_trace_class> final
 {
@@ -1796,412 +1586,392 @@ struct CommonTraceClassSpec<const bt_trace_class> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
-class CommonTraceClass final : public internal::BorrowedObj<LibObjT>
+class CommonTraceClass final : public BorrowedObject<LibObjT>
 {
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
-    using _ConstSpec = internal::CommonTraceClassSpec<const bt_trace_class>;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
+
     using _Spec = internal::CommonTraceClassSpec<LibObjT>;
-    using _ThisCommonTraceClass = CommonTraceClass<LibObjT>;
 
-    using _StreamClass = typename std::conditional<std::is_const<LibObjT>::value,
-                                                   CommonStreamClass<const bt_stream_class>,
-                                                   CommonStreamClass<bt_stream_class>>::type;
+    using _StreamClass = internal::DepType<LibObjT, CommonStreamClass<bt_stream_class>,
+                                           CommonStreamClass<const bt_stream_class>>;
 
 public:
-    using Shared =
-        internal::SharedObj<_ThisCommonTraceClass, LibObjT, internal::TraceClassRefFuncs>;
-
-    using UserAttributes =
-        typename std::conditional<std::is_const<LibObjT>::value, ConstMapValue, MapValue>::type;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedObject<CommonTraceClass, LibObjT, internal::TraceClassRefFuncs>;
+    using UserAttributes = internal::DepUserAttrs<LibObjT>;
 
-    explicit CommonTraceClass(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonTraceClass(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonTraceClass(const CommonTraceClass<OtherLibObjT>& traceClass) noexcept :
-        _ThisBorrowedObj {traceClass}
+    CommonTraceClass(const CommonTraceClass<OtherLibObjT> traceClass) noexcept :
+        _ThisBorrowedObject {traceClass}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonTraceClass& operator=(const CommonTraceClass<OtherLibObjT>& traceClass) noexcept
+    CommonTraceClass operator=(const CommonTraceClass<OtherLibObjT> traceClass) noexcept
     {
-        _ThisBorrowedObj::operator=(traceClass);
+        _ThisBorrowedObject::operator=(traceClass);
         return *this;
     }
 
-    Trace::Shared instantiate(const Trace& trace)
+    CommonTraceClass<const bt_trace_class> asConst() const noexcept
+    {
+        return CommonTraceClass<const bt_trace_class> {*this};
+    }
+
+    Trace::Shared instantiate() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_trace_create(this->_libObjPtr());
+        const auto libObjPtr = bt_trace_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Trace::Shared {Trace {libObjPtr}};
+        return Trace::Shared::createWithoutRef(libObjPtr);
     }
 
-    StreamClass::Shared createStreamClass()
+    StreamClass::Shared createStreamClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_stream_class_create(this->_libObjPtr());
+        const auto libObjPtr = bt_stream_class_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return StreamClass::Shared {StreamClass {libObjPtr}};
+        return StreamClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    StreamClass::Shared createStreamClass(const std::uint64_t id)
+    StreamClass::Shared createStreamClass(const std::uint64_t id) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_stream_class_create_with_id(this->_libObjPtr(), id);
+        const auto libObjPtr = bt_stream_class_create_with_id(this->libObjPtr(), id);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return StreamClass::Shared {StreamClass {libObjPtr}};
+        return StreamClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    FieldClass::Shared createBoolFieldClass()
+    FieldClass::Shared createBoolFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_bool_create(this->_libObjPtr());
+        const auto libObjPtr = bt_field_class_bool_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return FieldClass::Shared {FieldClass {libObjPtr}};
+        return FieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    BitArrayFieldClass::Shared createBitArrayFieldClass(const std::uint64_t length)
+    BitArrayFieldClass::Shared createBitArrayFieldClass(const std::uint64_t length) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_bit_array_create(this->_libObjPtr(), length);
+        const auto libObjPtr = bt_field_class_bit_array_create(this->libObjPtr(), length);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return BitArrayFieldClass::Shared {BitArrayFieldClass {libObjPtr}};
+        return BitArrayFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    IntegerFieldClass::Shared createUnsignedIntegerFieldClass()
+    IntegerFieldClass::Shared createUnsignedIntegerFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_integer_unsigned_create(this->_libObjPtr());
+        const auto libObjPtr = bt_field_class_integer_unsigned_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return IntegerFieldClass::Shared {IntegerFieldClass {libObjPtr}};
+        return IntegerFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    IntegerFieldClass::Shared createSignedIntegerFieldClass()
+    IntegerFieldClass::Shared createSignedIntegerFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_integer_signed_create(this->_libObjPtr());
+        const auto libObjPtr = bt_field_class_integer_signed_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return IntegerFieldClass::Shared {IntegerFieldClass {libObjPtr}};
+        return IntegerFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    UnsignedEnumerationFieldClass::Shared createUnsignedEnumerationFieldClass()
+    UnsignedEnumerationFieldClass::Shared createUnsignedEnumerationFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_enumeration_unsigned_create(this->_libObjPtr());
+        const auto libObjPtr = bt_field_class_enumeration_unsigned_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return UnsignedEnumerationFieldClass::Shared {UnsignedEnumerationFieldClass {libObjPtr}};
+        return UnsignedEnumerationFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    SignedEnumerationFieldClass::Shared createSignedEnumerationFieldClass()
+    SignedEnumerationFieldClass::Shared createSignedEnumerationFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_enumeration_signed_create(this->_libObjPtr());
+        const auto libObjPtr = bt_field_class_enumeration_signed_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return SignedEnumerationFieldClass::Shared {SignedEnumerationFieldClass {libObjPtr}};
+        return SignedEnumerationFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    FieldClass::Shared createSinglePrecisionRealFieldClass()
+    FieldClass::Shared createSinglePrecisionRealFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_real_single_precision_create(this->_libObjPtr());
+        const auto libObjPtr = bt_field_class_real_single_precision_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return FieldClass::Shared {FieldClass {libObjPtr}};
+        return FieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    FieldClass::Shared createDoublePrecisionRealFieldClass()
+    FieldClass::Shared createDoublePrecisionRealFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_real_double_precision_create(this->_libObjPtr());
+        const auto libObjPtr = bt_field_class_real_double_precision_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return FieldClass::Shared {FieldClass {libObjPtr}};
+        return FieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    FieldClass::Shared createStringFieldClass()
+    FieldClass::Shared createStringFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_string_create(this->_libObjPtr());
+        const auto libObjPtr = bt_field_class_string_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return FieldClass::Shared {FieldClass {libObjPtr}};
+        return FieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    StaticArrayFieldClass::Shared createStaticArrayFieldClass(const FieldClass& elementFieldClass,
-                                                              const std::uint64_t length)
+    StaticArrayFieldClass::Shared createStaticArrayFieldClass(const FieldClass elementFieldClass,
+                                                              const std::uint64_t length) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
         const auto libObjPtr = bt_field_class_array_static_create(
-            this->_libObjPtr(), elementFieldClass._libObjPtr(), length);
+            this->libObjPtr(), elementFieldClass.libObjPtr(), length);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return StaticArrayFieldClass::Shared {StaticArrayFieldClass {libObjPtr}};
+        return StaticArrayFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    ArrayFieldClass::Shared createDynamicArrayFieldClass(const FieldClass& elementFieldClass)
+    ArrayFieldClass::Shared createDynamicArrayFieldClass(const FieldClass elementFieldClass) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
         const auto libObjPtr = bt_field_class_array_dynamic_create(
-            this->_libObjPtr(), elementFieldClass._libObjPtr(), nullptr);
+            this->libObjPtr(), elementFieldClass.libObjPtr(), nullptr);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return ArrayFieldClass::Shared {ArrayFieldClass {libObjPtr}};
+        return ArrayFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
     DynamicArrayWithLengthFieldClass::Shared
-    createDynamicArrayFieldClass(const FieldClass& elementFieldClass,
-                                 const IntegerFieldClass& lengthFieldClass)
+    createDynamicArrayFieldClass(const FieldClass elementFieldClass,
+                                 const IntegerFieldClass lengthFieldClass) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
         const auto libObjPtr = bt_field_class_array_dynamic_create(
-            this->_libObjPtr(), elementFieldClass._libObjPtr(), lengthFieldClass._libObjPtr());
+            this->libObjPtr(), elementFieldClass.libObjPtr(), lengthFieldClass.libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return DynamicArrayWithLengthFieldClass::Shared {
-            DynamicArrayWithLengthFieldClass {libObjPtr}};
+        return DynamicArrayWithLengthFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    StructureFieldClass::Shared createStructureFieldClass(const std::uint64_t length)
+    StructureFieldClass::Shared createStructureFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_structure_create(this->_libObjPtr(), length);
+        const auto libObjPtr = bt_field_class_structure_create(this->libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return StructureFieldClass::Shared {StructureFieldClass {libObjPtr}};
+        return StructureFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    OptionFieldClass::Shared createOptionFieldClass(const FieldClass& optionalFieldClass)
+    OptionFieldClass::Shared createOptionFieldClass(const FieldClass optionalFieldClass) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
         const auto libObjPtr = bt_field_class_option_without_selector_create(
-            this->_libObjPtr(), optionalFieldClass._libObjPtr());
+            this->libObjPtr(), optionalFieldClass.libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return OptionFieldClass::Shared {OptionFieldClass {libObjPtr}};
+        return OptionFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
     OptionWithBoolSelectorFieldClass::Shared
-    createOptionWithBoolSelectorFieldClass(const FieldClass& optionalFieldClass,
-                                           const FieldClass& selectorFieldClass)
+    createOptionWithBoolSelectorFieldClass(const FieldClass optionalFieldClass,
+                                           const FieldClass selectorFieldClass) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
         const auto libObjPtr = bt_field_class_option_with_selector_field_bool_create(
-            this->_libObjPtr(), optionalFieldClass._libObjPtr(), selectorFieldClass._libObjPtr());
+            this->libObjPtr(), optionalFieldClass.libObjPtr(), selectorFieldClass.libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return OptionWithBoolSelectorFieldClass::Shared {
-            OptionWithBoolSelectorFieldClass {libObjPtr}};
+        return OptionWithBoolSelectorFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
     OptionWithUnsignedIntegerSelectorFieldClass::Shared
-    createOptionWithUnsignedIntegerSelectorFieldClass(const FieldClass& optionalFieldClass,
-                                                      const IntegerFieldClass& selectorFieldClass,
-                                                      const ConstUnsignedIntegerRangeSet& ranges)
+    createOptionWithUnsignedIntegerSelectorFieldClass(
+        const FieldClass optionalFieldClass, const IntegerFieldClass selectorFieldClass,
+        const ConstUnsignedIntegerRangeSet ranges) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
         const auto libObjPtr = bt_field_class_option_with_selector_field_integer_unsigned_create(
-            this->_libObjPtr(), optionalFieldClass._libObjPtr(), selectorFieldClass._libObjPtr(),
-            ranges._libObjPtr());
+            this->libObjPtr(), optionalFieldClass.libObjPtr(), selectorFieldClass.libObjPtr(),
+            ranges.libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return OptionWithUnsignedIntegerSelectorFieldClass::Shared {
-            OptionWithUnsignedIntegerSelectorFieldClass {libObjPtr}};
+        return OptionWithUnsignedIntegerSelectorFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
     OptionWithSignedIntegerSelectorFieldClass::Shared
-    createOptionWithSignedIntegerSelectorFieldClass(const FieldClass& optionalFieldClass,
-                                                    const IntegerFieldClass& selectorFieldClass,
-                                                    const ConstSignedIntegerRangeSet& ranges)
+    createOptionWithSignedIntegerSelectorFieldClass(const FieldClass optionalFieldClass,
+                                                    const IntegerFieldClass selectorFieldClass,
+                                                    const ConstSignedIntegerRangeSet ranges) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
         const auto libObjPtr = bt_field_class_option_with_selector_field_integer_signed_create(
-            this->_libObjPtr(), optionalFieldClass._libObjPtr(), selectorFieldClass._libObjPtr(),
-            ranges._libObjPtr());
+            this->libObjPtr(), optionalFieldClass.libObjPtr(), selectorFieldClass.libObjPtr(),
+            ranges.libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return OptionWithSignedIntegerSelectorFieldClass::Shared {
-            OptionWithSignedIntegerSelectorFieldClass {libObjPtr}};
+        return OptionWithSignedIntegerSelectorFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
-    VariantWithoutSelectorFieldClass::Shared createVariantFieldClass()
+    VariantWithoutSelectorFieldClass::Shared createVariantFieldClass() const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        const auto libObjPtr = bt_field_class_variant_create(this->_libObjPtr());
+        const auto libObjPtr = bt_field_class_variant_create(this->libObjPtr(), nullptr);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return VariantWithoutSelectorFieldClass::Shared {
-            VariantWithoutSelectorFieldClass {libObjPtr}};
+        return VariantWithoutSelectorFieldClass::Shared::createWithoutRef(libObjPtr);
     }
 
     VariantWithUnsignedIntegerSelectorFieldClass::Shared
-    createVariantWithUnsignedIntegerSelectorFieldClass(const IntegerFieldClass& selectorFieldClass)
+    createVariantWithUnsignedIntegerSelectorFieldClass(
+        const IntegerFieldClass selectorFieldClass) const
     {
-        return VariantWithUnsignedIntegerSelectorFieldClass::Shared {
-            VariantWithUnsignedIntegerSelectorFieldClass {
-                this->_createVariantWithIntegerSelectorFieldClass(selectorFieldClass)}};
+        return this->_createVariantWithIntegerSelectorFieldClass<
+            VariantWithUnsignedIntegerSelectorFieldClass>(selectorFieldClass);
     }
 
     VariantWithSignedIntegerSelectorFieldClass::Shared
-    createVariantWithSignedIntegerSelectorFieldClass(const IntegerFieldClass& selectorFieldClass)
+    createVariantWithSignedIntegerSelectorFieldClass(
+        const IntegerFieldClass selectorFieldClass) const
     {
-        return VariantWithSignedIntegerSelectorFieldClass::Shared {
-            VariantWithSignedIntegerSelectorFieldClass {
-                this->_createVariantWithIntegerSelectorFieldClass(selectorFieldClass)}};
+        return this->_createVariantWithIntegerSelectorFieldClass<
+            VariantWithSignedIntegerSelectorFieldClass>(selectorFieldClass);
     }
 
-    void assignsAutomaticStreamClassId(const bool val) noexcept
+    CommonTraceClass assignsAutomaticStreamClassId(const bool val) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        bt_trace_class_set_assigns_automatic_stream_class_id(this->_libObjPtr(),
+        bt_trace_class_set_assigns_automatic_stream_class_id(this->libObjPtr(),
                                                              static_cast<bt_bool>(val));
+        return *this;
     }
 
     bool assignsAutomaticStreamClassId() const noexcept
     {
         return static_cast<bool>(
-            bt_trace_class_assigns_automatic_stream_class_id(this->_libObjPtr()));
-    }
-
-    std::uint64_t size() const noexcept
-    {
-        return bt_trace_class_get_stream_class_count(this->_libObjPtr());
-    }
-
-    ConstStreamClass operator[](const std::uint64_t index) const noexcept
-    {
-        return ConstStreamClass {_ConstSpec::streamClassByIndex(this->_libObjPtr(), index)};
+            bt_trace_class_assigns_automatic_stream_class_id(this->libObjPtr()));
     }
 
-    _StreamClass operator[](const std::uint64_t index) noexcept
+    std::uint64_t length() const noexcept
     {
-        return _StreamClass {_Spec::streamClassByIndex(this->_libObjPtr(), index)};
+        return bt_trace_class_get_stream_class_count(this->libObjPtr());
     }
 
-    nonstd::optional<ConstStreamClass> streamClassById(const std::uint64_t id) const noexcept
+    _StreamClass operator[](const std::uint64_t index) const noexcept
     {
-        const auto libObjPtr = _ConstSpec::streamClassById(this->_libObjPtr(), id);
-
-        if (libObjPtr) {
-            return ConstStreamClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _StreamClass {_Spec::streamClassByIndex(this->libObjPtr(), index)};
     }
 
-    nonstd::optional<_StreamClass> streamClassById(const std::uint64_t id) noexcept
+    OptionalBorrowedObject<_StreamClass> streamClassById(const std::uint64_t id) const noexcept
     {
-        const auto libObjPtr = _Spec::streamClassById(this->_libObjPtr(), id);
-
-        if (libObjPtr) {
-            return _StreamClass {libObjPtr};
-        }
-
-        return nonstd::nullopt;
+        return _Spec::streamClassById(this->libObjPtr(), id);
     }
 
     template <typename LibValT>
-    void userAttributes(const CommonMapValue<LibValT>& userAttrs)
+    CommonTraceClass userAttributes(const CommonMapValue<LibValT> userAttrs) const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
-        bt_trace_class_set_user_attributes(this->_libObjPtr(), userAttrs._libObjPtr());
-    }
-
-    ConstMapValue userAttributes() const noexcept
-    {
-        return ConstMapValue {_ConstSpec::userAttributes(this->_libObjPtr())};
+        bt_trace_class_set_user_attributes(this->libObjPtr(), userAttrs.libObjPtr());
+        return *this;
     }
 
-    UserAttributes userAttributes() noexcept
+    UserAttributes userAttributes() const noexcept
     {
-        return UserAttributes {_Spec::userAttributes(this->_libObjPtr())};
+        return UserAttributes {_Spec::userAttributes(this->libObjPtr())};
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 
 private:
-    bt_field_class *
-    _createVariantWithIntegerSelectorFieldClass(const IntegerFieldClass& selectorFieldClass)
+    template <typename ObjT>
+    typename ObjT::Shared
+    _createVariantWithIntegerSelectorFieldClass(const IntegerFieldClass selectorFieldClass) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstTraceClass`.");
 
         const auto libObjPtr =
-            bt_field_class_variant_create(this->_libObjPtr(), selectorFieldClass._libObjPtr());
+            bt_field_class_variant_create(this->libObjPtr(), selectorFieldClass.libObjPtr());
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return libObjPtr;
+        return ObjT::Shared::createWithoutRef(libObjPtr);
     }
 };
 
 using TraceClass = CommonTraceClass<bt_trace_class>;
 using ConstTraceClass = CommonTraceClass<const bt_trace_class>;
 
-template <typename LibObjT>
-ConstTraceClass CommonStreamClass<LibObjT>::traceClass() const noexcept
+namespace internal {
+
+struct TraceClassTypeDescr
 {
-    return ConstTraceClass {_ConstSpec::traceClass(this->_libObjPtr())};
-}
+    using Const = ConstTraceClass;
+    using NonConst = TraceClass;
+};
 
-template <typename LibObjT>
-typename CommonStreamClass<LibObjT>::_TraceClass CommonStreamClass<LibObjT>::traceClass() noexcept
+template <>
+struct TypeDescr<TraceClass> : public TraceClassTypeDescr
 {
-    return _TraceClass {_Spec::traceClass(this->_libObjPtr())};
-}
+};
+
+template <>
+struct TypeDescr<ConstTraceClass> : public TraceClassTypeDescr
+{
+};
+
+} /* namespace internal */
 
 template <typename LibObjT>
-ConstTraceClass CommonTrace<LibObjT>::cls() const noexcept
+typename CommonStreamClass<LibObjT>::_TraceClass
+CommonStreamClass<LibObjT>::traceClass() const noexcept
 {
-    return ConstTraceClass {_ConstSpec::cls(this->_libObjPtr())};
+    return _TraceClass {_Spec::traceClass(this->libObjPtr())};
 }
 
 template <typename LibObjT>
-typename CommonTrace<LibObjT>::Class CommonTrace<LibObjT>::cls() noexcept
+typename CommonTrace<LibObjT>::Class CommonTrace<LibObjT>::cls() const noexcept
 {
-    return Class {_Spec::cls(this->_libObjPtr())};
+    return Class {_Spec::cls(this->libObjPtr())};
 }
 
-} // namespace bt2
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_TRACE_IR_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_TRACE_IR_HPP */
diff --git a/src/cpp-common/bt2/type-traits.hpp b/src/cpp-common/bt2/type-traits.hpp
new file mode 100644 (file)
index 0000000..53db333
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_TYPE_TRAITS_HPP
+#define BABELTRACE_CPP_COMMON_BT2_TYPE_TRAITS_HPP
+
+#include "internal/utils.hpp"
+
+namespace bt2 {
+
+template <typename ObjT>
+struct AddConst
+{
+    using Type = typename internal::TypeDescr<ObjT>::Const;
+};
+
+template <typename ObjT>
+struct RemoveConst
+{
+    using Type = typename internal::TypeDescr<ObjT>::NonConst;
+};
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_TYPE_TRAITS_HPP */
index 4b34a16ebf2038a57d944e47662b862d529935ba..9f7fdff5cc883b312e8fc71cf495d369a54507ca 100644 (file)
@@ -7,41 +7,45 @@
 #ifndef BABELTRACE_CPP_COMMON_BT2_VALUE_HPP
 #define BABELTRACE_CPP_COMMON_BT2_VALUE_HPP
 
-#include <type_traits>
 #include <cstdint>
 #include <functional>
+#include <type_traits>
+
 #include <babeltrace2/babeltrace.h>
 
 #include "common/assert.h"
 #include "common/common.h"
-#include "internal/borrowed-obj.hpp"
-#include "internal/shared-obj.hpp"
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/vendor/wise-enum/wise_enum.h"
+
+#include "borrowed-object-iterator.hpp"
+#include "borrowed-object.hpp"
+#include "exc.hpp"
 #include "internal/utils.hpp"
-#include "cpp-common/optional.hpp"
-#include "cpp-common/string_view.hpp"
-#include "lib-error.hpp"
+#include "optional-borrowed-object.hpp"
+#include "raw-value-proxy.hpp"
+#include "shared-object.hpp"
 
 namespace bt2 {
-
 namespace internal {
 
 struct ValueRefFuncs final
 {
-    static void get(const bt_value * const libObjPtr)
+    static void get(const bt_value * const libObjPtr) noexcept
     {
         bt_value_get_ref(libObjPtr);
     }
 
-    static void put(const bt_value * const libObjPtr)
+    static void put(const bt_value * const libObjPtr) noexcept
     {
         bt_value_put_ref(libObjPtr);
     }
 };
 
-template <typename ObjT, typename LibObjT>
-using SharedValue = internal::SharedObj<ObjT, LibObjT, internal::ValueRefFuncs>;
+} /* namespace internal */
 
-} // namespace internal
+template <typename ObjT, typename LibObjT>
+using SharedValue = SharedObject<ObjT, LibObjT, internal::ValueRefFuncs>;
 
 template <typename LibObjT>
 class CommonNullValue;
@@ -67,86 +71,81 @@ class CommonArrayValue;
 template <typename LibObjT>
 class CommonMapValue;
 
-enum class ValueType
-{
-    NUL = BT_VALUE_TYPE_NULL,
-    BOOL = BT_VALUE_TYPE_BOOL,
-    UNSIGNED_INTEGER = BT_VALUE_TYPE_UNSIGNED_INTEGER,
-    SIGNED_INTEGER = BT_VALUE_TYPE_SIGNED_INTEGER,
-    REAL = BT_VALUE_TYPE_REAL,
-    STRING = BT_VALUE_TYPE_STRING,
-    ARRAY = BT_VALUE_TYPE_ARRAY,
-    MAP = BT_VALUE_TYPE_MAP,
-};
-
-template <typename LibObjT>
-class CommonClockClass;
+/* clang-format off */
 
-template <typename LibObjT>
-class CommonFieldClass;
+WISE_ENUM_CLASS(ValueType,
+    (Null, BT_VALUE_TYPE_NULL),
+    (Bool, BT_VALUE_TYPE_BOOL),
+    (UnsignedInteger, BT_VALUE_TYPE_UNSIGNED_INTEGER),
+    (SignedInteger, BT_VALUE_TYPE_SIGNED_INTEGER),
+    (Real, BT_VALUE_TYPE_REAL),
+    (String, BT_VALUE_TYPE_STRING),
+    (Array, BT_VALUE_TYPE_ARRAY),
+    (Map, BT_VALUE_TYPE_MAP));
 
-template <typename LibObjT>
-class CommonTraceClass;
+/* clang-format on */
 
-template <typename LibObjT>
-class CommonStreamClass;
+template <typename ValueObjT>
+class CommonValueRawValueProxy final
+{
+public:
+    explicit CommonValueRawValueProxy(const ValueObjT obj) : _mObj {obj}
+    {
+    }
 
-template <typename LibObjT>
-class CommonEventClass;
+    CommonValueRawValueProxy& operator=(bool rawVal) noexcept;
+    CommonValueRawValueProxy& operator=(std::int64_t rawVal) noexcept;
+    CommonValueRawValueProxy& operator=(std::uint64_t rawVal) noexcept;
+    CommonValueRawValueProxy& operator=(double rawVal) noexcept;
+    CommonValueRawValueProxy& operator=(const char *rawVal);
+    CommonValueRawValueProxy& operator=(bt2c::CStringView rawVal);
+    operator bool() const noexcept;
+    operator std::int64_t() const noexcept;
+    operator std::uint64_t() const noexcept;
+    operator double() const noexcept;
+    operator bt2c::CStringView() const noexcept;
 
-template <typename LibObjT>
-class CommonStream;
+private:
+    ValueObjT _mObj;
+};
 
 template <typename LibObjT>
-class CommonValue : public internal::BorrowedObj<LibObjT>
+class CommonValue : public BorrowedObject<LibObjT>
 {
-    // Allow append() to call `val._libObjPtr()`
-    friend class CommonArrayValue<bt_value>;
-
-    // Allow insert() to call `val._libObjPtr()`
-    friend class CommonMapValue<bt_value>;
-
-    // Allow userAttributes() to call `val._libObjPtr()`
-    friend class CommonClockClass<bt_clock_class>;
-    friend class CommonFieldClass<bt_field_class>;
-    friend class CommonTraceClass<bt_trace_class>;
-    friend class CommonStreamClass<bt_stream_class>;
-    friend class CommonEventClass<bt_event_class>;
-    friend class CommonStream<bt_stream>;
-
-    // Allow operator==() to call `other._libObjPtr()`
-    friend class CommonValue<bt_value>;
-    friend class CommonValue<const bt_value>;
-
 private:
-    using typename internal::BorrowedObj<LibObjT>::_ThisBorrowedObj;
+    using typename BorrowedObject<LibObjT>::_ThisBorrowedObject;
 
 protected:
-    using typename internal::BorrowedObj<LibObjT>::_LibObjPtr;
     using _ThisCommonValue = CommonValue<LibObjT>;
 
 public:
-    using Shared = internal::SharedValue<CommonValue<LibObjT>, LibObjT>;
+    using typename BorrowedObject<LibObjT>::LibObjPtr;
+    using Shared = SharedValue<CommonValue<LibObjT>, LibObjT>;
 
-    explicit CommonValue(const _LibObjPtr libObjPtr) noexcept : _ThisBorrowedObj {libObjPtr}
+    explicit CommonValue(const LibObjPtr libObjPtr) noexcept : _ThisBorrowedObject {libObjPtr}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonValue(const CommonValue<OtherLibObjT>& val) noexcept : _ThisBorrowedObj {val}
+    CommonValue(const CommonValue<OtherLibObjT> val) noexcept : _ThisBorrowedObject {val}
     {
     }
 
     template <typename OtherLibObjT>
-    _ThisCommonValue& operator=(const CommonValue<OtherLibObjT>& val) noexcept
+    _ThisCommonValue operator=(const CommonValue<OtherLibObjT> val) noexcept
     {
-        _ThisBorrowedObj::operator=(val);
+        _ThisBorrowedObject::operator=(val);
         return *this;
     }
 
+    CommonValue<const bt_value> asConst() const noexcept
+    {
+        return CommonValue<const bt_value> {*this};
+    }
+
     ValueType type() const noexcept
     {
-        return static_cast<ValueType>(bt_value_get_type(this->_libObjPtr()));
+        return static_cast<ValueType>(bt_value_get_type(this->libObjPtr()));
     }
 
     bool isNull() const noexcept
@@ -195,20 +194,86 @@ public:
     }
 
     template <typename OtherLibObjT>
-    bool operator==(const CommonValue<OtherLibObjT>& other) const noexcept
+    bool operator==(const CommonValue<OtherLibObjT> other) const noexcept
     {
-        return static_cast<bool>(bt_value_is_equal(this->_libObjPtr(), other._libObjPtr()));
+        return static_cast<bool>(bt_value_is_equal(this->libObjPtr(), other.libObjPtr()));
     }
 
     template <typename OtherLibObjT>
-    bool operator!=(const CommonValue<OtherLibObjT>& other) const noexcept
+    bool operator!=(const CommonValue<OtherLibObjT> other) const noexcept
     {
         return !(*this == other);
     }
 
+    CommonValueRawValueProxy<CommonValue> operator*() const noexcept
+    {
+        return CommonValueRawValueProxy<CommonValue> {*this};
+    }
+
+    std::uint64_t arrayLength() const noexcept
+    {
+        return this->asArray().length();
+    }
+
+    bool arrayIsEmpty() const noexcept
+    {
+        return this->asArray().isEmpty();
+    }
+
+    CommonValue<LibObjT> operator[](const std::uint64_t index) const noexcept
+    {
+        return this->asArray()[index];
+    }
+
+    template <typename T>
+    void append(T&& elem) const
+    {
+        this->asArray().append(std::forward<T>(elem));
+    }
+
+    CommonArrayValue<bt_value> appendEmptyArray() const;
+    CommonMapValue<bt_value> appendEmptyMap() const;
+
+    std::uint64_t mapLength() const noexcept
+    {
+        return this->asMap().length();
+    }
+
+    bool mapIsEmpty() const noexcept
+    {
+        return this->asMap().isEmpty();
+    }
+
+    template <typename KeyT>
+    OptionalBorrowedObject<CommonValue<LibObjT>> operator[](KeyT&& key) const noexcept
+    {
+        return this->asMap()[std::forward<KeyT>(key)];
+    }
+
+    template <typename KeyT>
+    bool hasEntry(KeyT&& key) const noexcept
+    {
+        return this->asMap().hasEntry(std::forward<KeyT>(key));
+    }
+
+    template <typename KeyT, typename ValT>
+    void insert(KeyT&& key, ValT&& val) const
+    {
+        this->asMap().insert(std::forward<KeyT>(key), std::forward<ValT>(val));
+    }
+
+    CommonArrayValue<bt_value> insertEmptyArray(bt2c::CStringView key) const;
+    CommonMapValue<bt_value> insertEmptyMap(bt2c::CStringView key) const;
+
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
+    }
+
+    template <typename ValueT>
+    ValueT as() const noexcept
+    {
+        return ValueT {this->libObjPtr()};
     }
 
     CommonNullValue<LibObjT> asNull() const noexcept;
@@ -223,13 +288,111 @@ public:
 protected:
     bool _libTypeIs(const bt_value_type type) const noexcept
     {
-        return bt_value_type_is(bt_value_get_type(this->_libObjPtr()), type);
+        return bt_value_type_is(bt_value_get_type(this->libObjPtr()), type);
     }
 };
 
 using Value = CommonValue<bt_value>;
 using ConstValue = CommonValue<const bt_value>;
 
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>&
+CommonValueRawValueProxy<ValueObjT>::operator=(const bool rawVal) noexcept
+{
+    _mObj.asBool().value(rawVal);
+    return *this;
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>&
+CommonValueRawValueProxy<ValueObjT>::operator=(const std::int64_t rawVal) noexcept
+{
+    _mObj.asSignedInteger().value(rawVal);
+    return *this;
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>&
+CommonValueRawValueProxy<ValueObjT>::operator=(const std::uint64_t rawVal) noexcept
+{
+    _mObj.asUnsignedInteger().value(rawVal);
+    return *this;
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>&
+CommonValueRawValueProxy<ValueObjT>::operator=(const double rawVal) noexcept
+{
+    _mObj.asReal().value(rawVal);
+    return *this;
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>&
+CommonValueRawValueProxy<ValueObjT>::operator=(const char * const rawVal)
+{
+    _mObj.asString().value(rawVal);
+    return *this;
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>&
+CommonValueRawValueProxy<ValueObjT>::operator=(const bt2c::CStringView rawVal)
+{
+    _mObj.asString().value(rawVal);
+    return *this;
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>::operator bool() const noexcept
+{
+    return _mObj.asBool().value();
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>::operator std::int64_t() const noexcept
+{
+    return _mObj.asSignedInteger().value();
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>::operator std::uint64_t() const noexcept
+{
+    return _mObj.asUnsignedInteger().value();
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>::operator double() const noexcept
+{
+    return _mObj.asReal().value();
+}
+
+template <typename ValueObjT>
+CommonValueRawValueProxy<ValueObjT>::operator bt2c::CStringView() const noexcept
+{
+    return _mObj.asString().value();
+}
+
+namespace internal {
+
+struct ValueTypeDescr
+{
+    using Const = ConstValue;
+    using NonConst = Value;
+};
+
+template <>
+struct TypeDescr<Value> : public ValueTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstValue> : public ValueTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonNullValue final : public CommonValue<LibObjT>
 {
@@ -237,51 +400,76 @@ private:
     using typename CommonValue<LibObjT>::_ThisCommonValue;
 
 public:
-    using Shared = internal::SharedValue<CommonNullValue<LibObjT>, LibObjT>;
+    using Shared = SharedValue<CommonNullValue<LibObjT>, LibObjT>;
 
     CommonNullValue() noexcept : _ThisCommonValue {bt_value_null}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonNullValue(const CommonNullValue<OtherLibObjT>& val) noexcept : _ThisCommonValue {val}
+    CommonNullValue(const CommonNullValue<OtherLibObjT> val) noexcept : _ThisCommonValue {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonNullValue<LibObjT>& operator=(const CommonNullValue<OtherLibObjT>& val) noexcept
+    CommonNullValue<LibObjT> operator=(const CommonNullValue<OtherLibObjT> val) noexcept
     {
         _ThisCommonValue::operator=(val);
         return *this;
     }
 
+    CommonNullValue<const bt_value> asConst() const noexcept
+    {
+        return CommonNullValue<const bt_value> {*this};
+    }
+
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using NullValue = CommonNullValue<bt_value>;
 using ConstNullValue = CommonNullValue<const bt_value>;
 
+namespace internal {
+
+struct NullValueTypeDescr
+{
+    using Const = ConstNullValue;
+    using NonConst = NullValue;
+};
+
+template <>
+struct TypeDescr<NullValue> : public NullValueTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstNullValue> : public NullValueTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonBoolValue final : public CommonValue<LibObjT>
 {
 private:
-    using typename CommonValue<LibObjT>::_LibObjPtr;
     using typename CommonValue<LibObjT>::_ThisCommonValue;
 
 public:
-    using Shared = internal::SharedValue<CommonBoolValue<LibObjT>, LibObjT>;
+    using typename CommonValue<LibObjT>::LibObjPtr;
+    using Shared = SharedValue<CommonBoolValue<LibObjT>, LibObjT>;
     using Value = bool;
 
-    explicit CommonBoolValue(const _LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
+    explicit CommonBoolValue(const LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
     {
         BT_ASSERT_DBG(this->isBool());
     }
 
     template <typename OtherLibObjT>
-    CommonBoolValue(const CommonBoolValue<OtherLibObjT>& val) noexcept : _ThisCommonValue {val}
+    CommonBoolValue(const CommonBoolValue<OtherLibObjT> val) noexcept : _ThisCommonValue {val}
     {
     }
 
@@ -290,55 +478,80 @@ public:
         const auto libObjPtr = bt_value_bool_create_init(static_cast<bt_bool>(rawVal));
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Shared {CommonBoolValue<LibObjT> {libObjPtr}};
+        return CommonBoolValue::Shared::createWithoutRef(libObjPtr);
     }
 
     template <typename OtherLibObjT>
-    CommonBoolValue<LibObjT>& operator=(const CommonBoolValue<OtherLibObjT>& val) noexcept
+    CommonBoolValue<LibObjT> operator=(const CommonBoolValue<OtherLibObjT> val) noexcept
     {
         _ThisCommonValue::operator=(val);
         return *this;
     }
 
-    CommonBoolValue<LibObjT>& operator=(const Value rawVal) noexcept
+    CommonBoolValue<const bt_value> asConst() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return CommonBoolValue<const bt_value> {*this};
+    }
 
-        bt_value_bool_set(this->_libObjPtr(), static_cast<bt_bool>(rawVal));
-        return *this;
+    RawValueProxy<CommonBoolValue> operator*() const noexcept
+    {
+        return RawValueProxy<CommonBoolValue> {*this};
     }
 
     Value value() const noexcept
     {
-        return static_cast<Value>(bt_value_bool_get(this->_libObjPtr()));
+        return static_cast<Value>(bt_value_bool_get(this->libObjPtr()));
     }
 
-    operator Value() const noexcept
+    CommonBoolValue value(const Value val) const noexcept
     {
-        return this->value();
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstBoolValue`.");
+
+        bt_value_bool_set(this->libObjPtr(), static_cast<bt_bool>(val));
+        return *this;
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using BoolValue = CommonBoolValue<bt_value>;
 using ConstBoolValue = CommonBoolValue<const bt_value>;
 
+namespace internal {
+
+struct BoolValueTypeDescr
+{
+    using Const = ConstBoolValue;
+    using NonConst = BoolValue;
+};
+
+template <>
+struct TypeDescr<BoolValue> : public BoolValueTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstBoolValue> : public BoolValueTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonUnsignedIntegerValue final : public CommonValue<LibObjT>
 {
 private:
-    using typename CommonValue<LibObjT>::_LibObjPtr;
     using typename CommonValue<LibObjT>::_ThisCommonValue;
 
 public:
-    using Shared = internal::SharedValue<CommonUnsignedIntegerValue<LibObjT>, LibObjT>;
+    using typename CommonValue<LibObjT>::LibObjPtr;
+    using Shared = SharedValue<CommonUnsignedIntegerValue<LibObjT>, LibObjT>;
     using Value = std::uint64_t;
 
-    explicit CommonUnsignedIntegerValue(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonUnsignedIntegerValue(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonValue {libObjPtr}
     {
         BT_ASSERT_DBG(this->isUnsignedInteger());
@@ -349,62 +562,88 @@ public:
         const auto libObjPtr = bt_value_integer_unsigned_create_init(rawVal);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Shared {CommonUnsignedIntegerValue<LibObjT> {libObjPtr}};
+        return CommonUnsignedIntegerValue::Shared::createWithoutRef(libObjPtr);
     }
 
     template <typename OtherLibObjT>
-    CommonUnsignedIntegerValue(const CommonUnsignedIntegerValue<OtherLibObjT>& val) noexcept :
+    CommonUnsignedIntegerValue(const CommonUnsignedIntegerValue<OtherLibObjT> val) noexcept :
         _ThisCommonValue {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonUnsignedIntegerValue<LibObjT>&
-    operator=(const CommonUnsignedIntegerValue<OtherLibObjT>& val) noexcept
+    CommonUnsignedIntegerValue<LibObjT>
+    operator=(const CommonUnsignedIntegerValue<OtherLibObjT> val) noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
-
         _ThisCommonValue::operator=(val);
         return *this;
     }
 
-    CommonUnsignedIntegerValue<LibObjT>& operator=(const Value rawVal) noexcept
+    CommonUnsignedIntegerValue<const bt_value> asConst() const noexcept
     {
-        bt_value_integer_unsigned_set(this->_libObjPtr(), rawVal);
-        return *this;
+        return CommonUnsignedIntegerValue<const bt_value> {*this};
     }
 
-    Value value() const noexcept
+    RawValueProxy<CommonUnsignedIntegerValue> operator*() const noexcept
     {
-        return bt_value_integer_unsigned_get(this->_libObjPtr());
+        return RawValueProxy<CommonUnsignedIntegerValue> {*this};
+    }
+
+    CommonUnsignedIntegerValue value(const Value val) const noexcept
+    {
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstUnsignedIntegerValue`.");
+
+        bt_value_integer_unsigned_set(this->libObjPtr(), val);
+        return *this;
     }
 
-    operator Value() const noexcept
+    Value value() const noexcept
     {
-        return this->value();
+        return bt_value_integer_unsigned_get(this->libObjPtr());
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using UnsignedIntegerValue = CommonUnsignedIntegerValue<bt_value>;
 using ConstUnsignedIntegerValue = CommonUnsignedIntegerValue<const bt_value>;
 
+namespace internal {
+
+struct UnsignedIntegerValueTypeDescr
+{
+    using Const = ConstUnsignedIntegerValue;
+    using NonConst = UnsignedIntegerValue;
+};
+
+template <>
+struct TypeDescr<UnsignedIntegerValue> : public UnsignedIntegerValueTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstUnsignedIntegerValue> : public UnsignedIntegerValueTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonSignedIntegerValue final : public CommonValue<LibObjT>
 {
 private:
-    using typename CommonValue<LibObjT>::_LibObjPtr;
     using typename CommonValue<LibObjT>::_ThisCommonValue;
 
 public:
-    using Shared = internal::SharedValue<CommonSignedIntegerValue<LibObjT>, LibObjT>;
+    using typename CommonValue<LibObjT>::LibObjPtr;
+    using Shared = SharedValue<CommonSignedIntegerValue<LibObjT>, LibObjT>;
     using Value = std::int64_t;
 
-    explicit CommonSignedIntegerValue(const _LibObjPtr libObjPtr) noexcept :
+    explicit CommonSignedIntegerValue(const LibObjPtr libObjPtr) noexcept :
         _ThisCommonValue {libObjPtr}
     {
         BT_ASSERT_DBG(this->isSignedInteger());
@@ -415,62 +654,88 @@ public:
         const auto libObjPtr = bt_value_integer_signed_create_init(rawVal);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Shared {CommonSignedIntegerValue<LibObjT> {libObjPtr}};
+        return CommonSignedIntegerValue::Shared::createWithoutRef(libObjPtr);
     }
 
     template <typename OtherLibObjT>
-    CommonSignedIntegerValue(const CommonSignedIntegerValue<OtherLibObjT>& val) noexcept :
+    CommonSignedIntegerValue(const CommonSignedIntegerValue<OtherLibObjT> val) noexcept :
         _ThisCommonValue {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonSignedIntegerValue<LibObjT>&
-    operator=(const CommonSignedIntegerValue<OtherLibObjT>& val) noexcept
+    CommonSignedIntegerValue<LibObjT>
+    operator=(const CommonSignedIntegerValue<OtherLibObjT> val) noexcept
     {
         _ThisCommonValue::operator=(val);
         return *this;
     }
 
-    CommonSignedIntegerValue<LibObjT>& operator=(const Value rawVal) noexcept
+    CommonSignedIntegerValue<const bt_value> asConst() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return CommonSignedIntegerValue<const bt_value> {*this};
+    }
 
-        bt_value_integer_signed_set(this->_libObjPtr(), rawVal);
-        return *this;
+    RawValueProxy<CommonSignedIntegerValue> operator*() const noexcept
+    {
+        return RawValueProxy<CommonSignedIntegerValue> {*this};
     }
 
-    Value value() const noexcept
+    CommonSignedIntegerValue value(const Value val) const noexcept
     {
-        return bt_value_integer_signed_get(this->_libObjPtr());
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstSignedIntegerValue`.");
+
+        bt_value_integer_signed_set(this->libObjPtr(), val);
+        return *this;
     }
 
-    operator Value() const noexcept
+    Value value() const noexcept
     {
-        return this->value();
+        return bt_value_integer_signed_get(this->libObjPtr());
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using SignedIntegerValue = CommonSignedIntegerValue<bt_value>;
 using ConstSignedIntegerValue = CommonSignedIntegerValue<const bt_value>;
 
+namespace internal {
+
+struct SignedIntegerValueTypeDescr
+{
+    using Const = ConstSignedIntegerValue;
+    using NonConst = SignedIntegerValue;
+};
+
+template <>
+struct TypeDescr<SignedIntegerValue> : public SignedIntegerValueTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstSignedIntegerValue> : public SignedIntegerValueTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonRealValue final : public CommonValue<LibObjT>
 {
 private:
-    using typename CommonValue<LibObjT>::_LibObjPtr;
     using typename CommonValue<LibObjT>::_ThisCommonValue;
 
 public:
-    using Shared = internal::SharedValue<CommonRealValue<LibObjT>, LibObjT>;
+    using typename CommonValue<LibObjT>::LibObjPtr;
+    using Shared = SharedValue<CommonRealValue<LibObjT>, LibObjT>;
     using Value = double;
 
-    explicit CommonRealValue(const _LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
+    explicit CommonRealValue(const LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
     {
         BT_ASSERT_DBG(this->isReal());
     }
@@ -480,114 +745,141 @@ public:
         const auto libObjPtr = bt_value_real_create_init(rawVal);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Shared {CommonRealValue<LibObjT> {libObjPtr}};
+        return CommonRealValue::Shared::createWithoutRef(libObjPtr);
     }
 
     template <typename OtherLibObjT>
-    CommonRealValue(const CommonRealValue<OtherLibObjT>& val) noexcept : _ThisCommonValue {val}
+    CommonRealValue(const CommonRealValue<OtherLibObjT> val) noexcept : _ThisCommonValue {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonRealValue<LibObjT>& operator=(const CommonRealValue<OtherLibObjT>& val) noexcept
+    CommonRealValue<LibObjT> operator=(const CommonRealValue<OtherLibObjT> val) noexcept
     {
         _ThisCommonValue::operator=(val);
         return *this;
     }
 
-    CommonRealValue<LibObjT>& operator=(const Value rawVal) noexcept
+    CommonRealValue<const bt_value> asConst() const noexcept
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        return CommonRealValue<const bt_value> {*this};
+    }
 
-        bt_value_real_set(this->_libObjPtr(), rawVal);
-        return *this;
+    RawValueProxy<CommonRealValue> operator*() const noexcept
+    {
+        return RawValueProxy<CommonRealValue> {*this};
     }
 
-    Value value() const noexcept
+    CommonRealValue value(const Value val) const noexcept
     {
-        return bt_value_real_get(this->_libObjPtr());
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstRealValue`.");
+
+        bt_value_real_set(this->libObjPtr(), val);
+        return *this;
     }
 
-    operator Value() const noexcept
+    Value value() const noexcept
     {
-        return this->value();
+        return bt_value_real_get(this->libObjPtr());
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
 using RealValue = CommonRealValue<bt_value>;
 using ConstRealValue = CommonRealValue<const bt_value>;
 
+namespace internal {
+
+struct RealValueTypeDescr
+{
+    using Const = ConstRealValue;
+    using NonConst = RealValue;
+};
+
+template <>
+struct TypeDescr<RealValue> : public RealValueTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstRealValue> : public RealValueTypeDescr
+{
+};
+
+} /* namespace internal */
+
 template <typename LibObjT>
 class CommonStringValue final : public CommonValue<LibObjT>
 {
 private:
-    using typename CommonValue<LibObjT>::_LibObjPtr;
     using typename CommonValue<LibObjT>::_ThisCommonValue;
 
 public:
-    using Shared = internal::SharedValue<CommonStringValue<LibObjT>, LibObjT>;
+    using typename CommonValue<LibObjT>::LibObjPtr;
+    using Shared = SharedValue<CommonStringValue<LibObjT>, LibObjT>;
+    using Value = bt2c::CStringView;
 
-    explicit CommonStringValue(const _LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
+    explicit CommonStringValue(const LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
     {
         BT_ASSERT_DBG(this->isString());
     }
 
-    static Shared create(const char * const rawVal = "")
+    static Shared create(const bt2c::CStringView rawVal = "")
     {
         const auto libObjPtr = bt_value_string_create_init(rawVal);
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Shared {CommonStringValue<LibObjT> {libObjPtr}};
-    }
-
-    static Shared create(const std::string& rawVal)
-    {
-        return CommonStringValue<LibObjT>::create(rawVal.data());
+        return CommonStringValue::Shared::createWithoutRef(libObjPtr);
     }
 
     template <typename OtherLibObjT>
-    CommonStringValue(const CommonStringValue<OtherLibObjT>& val) noexcept : _ThisCommonValue {val}
+    CommonStringValue(const CommonStringValue<OtherLibObjT> val) noexcept : _ThisCommonValue {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonStringValue<LibObjT>& operator=(const CommonStringValue<OtherLibObjT>& val) noexcept
+    CommonStringValue<LibObjT> operator=(const CommonStringValue<OtherLibObjT> val) noexcept
     {
         _ThisCommonValue::operator=(val);
         return *this;
     }
 
-    CommonStringValue<LibObjT>& operator=(const char * const rawVal)
+    CommonStringValue<const bt_value> asConst() const noexcept
+    {
+        return CommonStringValue<const bt_value> {*this};
+    }
+
+    RawValueProxy<CommonStringValue> operator*() const noexcept
+    {
+        return RawValueProxy<CommonStringValue> {*this};
+    }
+
+    CommonStringValue value(const Value val) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value,
+                      "Not available with `bt2::ConstStringValue`.");
 
-        const auto status = bt_value_string_set(this->_libObjPtr(), rawVal);
+        const auto status = bt_value_string_set(this->libObjPtr(), *val);
 
         if (status == BT_VALUE_STRING_SET_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
 
         return *this;
     }
 
-    CommonStringValue<LibObjT>& operator=(const std::string& rawVal) noexcept
-    {
-        return *this = rawVal.data();
-    }
-
-    bpstd::string_view value() const noexcept
+    Value value() const noexcept
     {
-        return bt_value_string_get(this->_libObjPtr());
+        return bt_value_string_get(this->libObjPtr());
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 };
 
@@ -596,10 +888,26 @@ using ConstStringValue = CommonStringValue<const bt_value>;
 
 namespace internal {
 
+struct StringValueTypeDescr
+{
+    using Const = ConstStringValue;
+    using NonConst = StringValue;
+};
+
+template <>
+struct TypeDescr<StringValue> : public StringValueTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstStringValue> : public StringValueTypeDescr
+{
+};
+
 template <typename LibObjT>
 struct CommonArrayValueSpec;
 
-// Functions specific to mutable array values
+/* Functions specific to mutable array values */
 template <>
 struct CommonArrayValueSpec<bt_value> final
 {
@@ -609,7 +917,7 @@ struct CommonArrayValueSpec<bt_value> final
     }
 };
 
-// Functions specific to constant array values
+/* Functions specific to constant array values */
 template <>
 struct CommonArrayValueSpec<const bt_value> final
 {
@@ -620,19 +928,20 @@ struct CommonArrayValueSpec<const bt_value> final
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonArrayValue final : public CommonValue<LibObjT>
 {
 private:
-    using typename CommonValue<LibObjT>::_LibObjPtr;
     using typename CommonValue<LibObjT>::_ThisCommonValue;
 
 public:
-    using Shared = internal::SharedValue<CommonArrayValue<LibObjT>, LibObjT>;
+    using typename CommonValue<LibObjT>::LibObjPtr;
+    using Shared = SharedValue<CommonArrayValue<LibObjT>, LibObjT>;
+    using Iterator = BorrowedObjectIterator<CommonArrayValue<LibObjT>>;
 
-    explicit CommonArrayValue(const _LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
+    explicit CommonArrayValue(const LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
     {
         BT_ASSERT_DBG(this->isArray());
     }
@@ -642,153 +951,167 @@ public:
         const auto libObjPtr = bt_value_array_create();
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Shared {CommonArrayValue<LibObjT> {libObjPtr}};
+        return CommonArrayValue::Shared::createWithoutRef(libObjPtr);
     }
 
     template <typename OtherLibObjT>
-    CommonArrayValue(const CommonArrayValue<OtherLibObjT>& val) noexcept : _ThisCommonValue {val}
+    CommonArrayValue(const CommonArrayValue<OtherLibObjT> val) noexcept : _ThisCommonValue {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonArrayValue<LibObjT>& operator=(const CommonArrayValue<OtherLibObjT>& val) noexcept
+    CommonArrayValue<LibObjT> operator=(const CommonArrayValue<OtherLibObjT> val) noexcept
     {
         _ThisCommonValue::operator=(val);
         return *this;
     }
 
+    CommonArrayValue<const bt_value> asConst() const noexcept
+    {
+        return CommonArrayValue<const bt_value> {*this};
+    }
+
     std::uint64_t length() const noexcept
     {
-        return bt_value_array_get_length(this->_libObjPtr());
+        return bt_value_array_get_length(this->libObjPtr());
     }
 
-    bool isEmpty() const noexcept
+    Iterator begin() const noexcept
     {
-        return this->length() == 0;
+        return Iterator {*this, 0};
+    }
+
+    Iterator end() const noexcept
+    {
+        return Iterator {*this, this->length()};
     }
 
-    ConstValue operator[](const std::uint64_t index) const noexcept
+    bool isEmpty() const noexcept
     {
-        return ConstValue {internal::CommonArrayValueSpec<const bt_value>::elementByIndex(
-            this->_libObjPtr(), index)};
+        return this->length() == 0;
     }
 
-    CommonValue<LibObjT> operator[](const std::uint64_t index) noexcept
+    CommonValue<LibObjT> operator[](const std::uint64_t index) const noexcept
     {
         return CommonValue<LibObjT> {
-            internal::CommonArrayValueSpec<LibObjT>::elementByIndex(this->_libObjPtr(), index)};
+            internal::CommonArrayValueSpec<LibObjT>::elementByIndex(this->libObjPtr(), index)};
     }
 
-    void append(const Value& val)
+    CommonArrayValue append(const Value val) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstArrayValue`.");
 
-        const auto status = bt_value_array_append_element(this->_libObjPtr(), val._libObjPtr());
+        const auto status = bt_value_array_append_element(this->libObjPtr(), val.libObjPtr());
 
         this->_handleAppendLibStatus(status);
+        return *this;
     }
 
-    void append(const bool rawVal)
+    CommonArrayValue append(const bool rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstArrayValue`.");
 
         const auto status =
-            bt_value_array_append_bool_element(this->_libObjPtr(), static_cast<bt_bool>(rawVal));
+            bt_value_array_append_bool_element(this->libObjPtr(), static_cast<bt_bool>(rawVal));
 
         this->_handleAppendLibStatus(status);
+        return *this;
     }
 
-    void append(const std::uint64_t rawVal)
+    CommonArrayValue append(const std::uint64_t rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstArrayValue`.");
 
         const auto status =
-            bt_value_array_append_unsigned_integer_element(this->_libObjPtr(), rawVal);
+            bt_value_array_append_unsigned_integer_element(this->libObjPtr(), rawVal);
 
         this->_handleAppendLibStatus(status);
+        return *this;
     }
 
-    void append(const std::int64_t rawVal)
+    CommonArrayValue append(const std::int64_t rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstArrayValue`.");
 
-        const auto status =
-            bt_value_array_append_signed_integer_element(this->_libObjPtr(), rawVal);
+        const auto status = bt_value_array_append_signed_integer_element(this->libObjPtr(), rawVal);
 
         this->_handleAppendLibStatus(status);
+        return *this;
     }
 
-    void append(const double rawVal)
+    CommonArrayValue append(const double rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstArrayValue`.");
 
-        const auto status = bt_value_array_append_real_element(this->_libObjPtr(), rawVal);
+        const auto status = bt_value_array_append_real_element(this->libObjPtr(), rawVal);
 
         this->_handleAppendLibStatus(status);
+        return *this;
     }
 
-    void append(const char * const rawVal)
+    CommonArrayValue append(const char * const rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstArrayValue`.");
 
-        const auto status = bt_value_array_append_string_element(this->_libObjPtr(), rawVal);
+        const auto status = bt_value_array_append_string_element(this->libObjPtr(), rawVal);
 
         this->_handleAppendLibStatus(status);
+        return *this;
     }
 
-    void append(const std::string& rawVal)
+    CommonArrayValue append(const bt2c::CStringView rawVal) const
     {
-        this->append(rawVal.data());
+        return this->append(rawVal.data());
     }
 
-    CommonArrayValue<bt_value> appendEmptyArray();
-    CommonMapValue<bt_value> appendEmptyMap();
+    CommonArrayValue<bt_value> appendEmptyArray() const;
+    CommonMapValue<bt_value> appendEmptyMap() const;
 
-    void operator+=(const Value& val)
+    void operator+=(const Value val) const
     {
         this->append(val);
     }
 
-    void operator+=(const bool rawVal)
+    void operator+=(const bool rawVal) const
     {
         this->append(rawVal);
     }
 
-    void operator+=(const std::uint64_t rawVal)
+    void operator+=(const std::uint64_t rawVal) const
     {
         this->append(rawVal);
     }
 
-    void operator+=(const std::int64_t rawVal)
+    void operator+=(const std::int64_t rawVal) const
     {
         this->append(rawVal);
     }
 
-    void operator+=(const double rawVal)
+    void operator+=(const double rawVal) const
     {
         this->append(rawVal);
     }
 
-    void operator+=(const char * const rawVal)
+    void operator+=(const char * const rawVal) const
     {
         this->append(rawVal);
     }
 
-    void operator+=(const std::string& rawVal)
+    void operator+=(const bt2c::CStringView rawVal) const
     {
         this->append(rawVal);
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 
 private:
     void _handleAppendLibStatus(const bt_value_array_append_element_status status) const
     {
         if (status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
     }
 };
@@ -798,13 +1121,29 @@ using ConstArrayValue = CommonArrayValue<const bt_value>;
 
 namespace internal {
 
+struct ArrayValueTypeDescr
+{
+    using Const = ConstArrayValue;
+    using NonConst = ArrayValue;
+};
+
+template <>
+struct TypeDescr<ArrayValue> : public ArrayValueTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstArrayValue> : public ArrayValueTypeDescr
+{
+};
+
 /*
  * Type of a user function passed to `CommonMapValue<ObjT>::forEach()`.
  *
  * First argument is the entry's key, second is its value.
  */
 template <typename ObjT>
-using CommonMapValueForEachUserFunc = std::function<void(const bpstd::string_view&, ObjT)>;
+using CommonMapValueForEachUserFunc = std::function<void(bt2c::CStringView, ObjT)>;
 
 /*
  * Template of a function to be passed to bt_value_map_foreach_entry()
@@ -814,7 +1153,7 @@ using CommonMapValueForEachUserFunc = std::function<void(const bpstd::string_vie
  * `CommonMapValueForEachUserFunc<ObjT>` (the user function to call).
  *
  * This function catches any exception which the user function throws
- * and returns the `ErrorStatus` value. If there's no execption, this
+ * and returns the `ErrorStatus` value. If there's no exception, this
  * function returns the `OkStatus` value.
  */
 template <typename ObjT, typename LibObjT, typename LibStatusT, int OkStatus, int ErrorStatus>
@@ -835,7 +1174,7 @@ LibStatusT mapValueForEachLibFunc(const char * const key, LibObjT * const libObj
 template <typename LibObjT>
 struct CommonMapValueSpec;
 
-// Functions specific to mutable map values
+/* Functions specific to mutable map values */
 template <>
 struct CommonMapValueSpec<bt_value> final
 {
@@ -859,14 +1198,14 @@ struct CommonMapValueSpec<bt_value> final
             return;
         case BT_VALUE_MAP_FOREACH_ENTRY_STATUS_USER_ERROR:
         case BT_VALUE_MAP_FOREACH_ENTRY_STATUS_ERROR:
-            throw LibError {};
+            throw Error {};
         default:
             bt_common_abort();
         }
     }
 };
 
-// Functions specific to constant map values
+/* Functions specific to constant map values */
 template <>
 struct CommonMapValueSpec<const bt_value> final
 {
@@ -892,26 +1231,26 @@ struct CommonMapValueSpec<const bt_value> final
             return;
         case BT_VALUE_MAP_FOREACH_ENTRY_CONST_STATUS_USER_ERROR:
         case BT_VALUE_MAP_FOREACH_ENTRY_CONST_STATUS_ERROR:
-            throw LibError {};
+            throw Error {};
         default:
             bt_common_abort();
         }
     }
 };
 
-} // namespace internal
+} /* namespace internal */
 
 template <typename LibObjT>
 class CommonMapValue final : public CommonValue<LibObjT>
 {
 private:
-    using typename CommonValue<LibObjT>::_LibObjPtr;
     using typename CommonValue<LibObjT>::_ThisCommonValue;
 
 public:
-    using Shared = internal::SharedValue<CommonMapValue<LibObjT>, LibObjT>;
+    using typename CommonValue<LibObjT>::LibObjPtr;
+    using Shared = SharedValue<CommonMapValue<LibObjT>, LibObjT>;
 
-    explicit CommonMapValue(const _LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
+    explicit CommonMapValue(const LibObjPtr libObjPtr) noexcept : _ThisCommonValue {libObjPtr}
     {
         BT_ASSERT_DBG(this->isMap());
     }
@@ -921,197 +1260,135 @@ public:
         const auto libObjPtr = bt_value_map_create();
 
         internal::validateCreatedObjPtr(libObjPtr);
-        return Shared {CommonMapValue<LibObjT> {libObjPtr}};
+        return CommonMapValue::Shared::createWithoutRef(libObjPtr);
     }
 
     template <typename OtherLibObjT>
-    CommonMapValue(const CommonMapValue<OtherLibObjT>& val) noexcept : _ThisCommonValue {val}
+    CommonMapValue(const CommonMapValue<OtherLibObjT> val) noexcept : _ThisCommonValue {val}
     {
     }
 
     template <typename OtherLibObjT>
-    CommonMapValue<LibObjT>& operator=(const CommonMapValue<OtherLibObjT>& val) noexcept
+    CommonMapValue<LibObjT> operator=(const CommonMapValue<OtherLibObjT> val) noexcept
     {
         _ThisCommonValue::operator=(val);
         return *this;
     }
 
-    std::uint64_t size() const noexcept
+    CommonMapValue<const bt_value> asConst() const noexcept
     {
-        return bt_value_map_get_size(this->_libObjPtr());
+        return CommonMapValue<const bt_value> {*this};
     }
 
-    bool isEmpty() const noexcept
-    {
-        return this->size() == 0;
-    }
-
-    nonstd::optional<ConstValue> operator[](const char * const key) const noexcept
-    {
-        const auto libObjPtr =
-            internal::CommonMapValueSpec<const bt_value>::entryByKey(this->_libObjPtr(), key);
-
-        if (!libObjPtr) {
-            return nonstd::nullopt;
-        }
-
-        return ConstValue {libObjPtr};
-    }
-
-    nonstd::optional<ConstValue> operator[](const std::string& key) const noexcept
-    {
-        return (*this)[key.data()];
-    }
-
-    nonstd::optional<CommonValue<LibObjT>> operator[](const char * const key) noexcept
+    std::uint64_t length() const noexcept
     {
-        const auto libObjPtr =
-            internal::CommonMapValueSpec<LibObjT>::entryByKey(this->_libObjPtr(), key);
-
-        if (!libObjPtr) {
-            return nonstd::nullopt;
-        }
-
-        return CommonValue<LibObjT> {libObjPtr};
+        return bt_value_map_get_size(this->libObjPtr());
     }
 
-    nonstd::optional<CommonValue<LibObjT>> operator[](const std::string& key) noexcept
+    bool isEmpty() const noexcept
     {
-        return (*this)[key.data()];
+        return this->length() == 0;
     }
 
-    bool hasEntry(const char * const key) const noexcept
+    OptionalBorrowedObject<CommonValue<LibObjT>>
+    operator[](const bt2c::CStringView key) const noexcept
     {
-        return static_cast<bool>(bt_value_map_has_entry(this->_libObjPtr(), key));
+        return internal::CommonMapValueSpec<LibObjT>::entryByKey(this->libObjPtr(), key);
     }
 
-    bool hasEntry(const std::string& key) const noexcept
+    bool hasEntry(const bt2c::CStringView key) const noexcept
     {
-        return this->hasEntry(key.data());
+        return static_cast<bool>(bt_value_map_has_entry(this->libObjPtr(), key));
     }
 
-    void insert(const char * const key, const Value& val)
+    CommonMapValue insert(const bt2c::CStringView key, const Value val) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstMapValue`.");
 
-        const auto status = bt_value_map_insert_entry(this->_libObjPtr(), key, val._libObjPtr());
+        const auto status = bt_value_map_insert_entry(this->libObjPtr(), key, val.libObjPtr());
 
         this->_handleInsertLibStatus(status);
+        return *this;
     }
 
-    void insert(const std::string& key, const Value& val)
-    {
-        this->insert(key.data(), val);
-    }
-
-    void insert(const char * const key, const bool rawVal)
+    CommonMapValue insert(const bt2c::CStringView key, const bool rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstMapValue`.");
 
         const auto status =
-            bt_value_map_insert_bool_entry(this->_libObjPtr(), key, static_cast<bt_bool>(rawVal));
+            bt_value_map_insert_bool_entry(this->libObjPtr(), key, static_cast<bt_bool>(rawVal));
 
         this->_handleInsertLibStatus(status);
+        return *this;
     }
 
-    void insert(const std::string& key, const bool rawVal)
-    {
-        this->insert(key.data(), rawVal);
-    }
-
-    void insert(const char * const key, const std::uint64_t rawVal)
+    CommonMapValue insert(const bt2c::CStringView key, const std::uint64_t rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstMapValue`.");
 
         const auto status =
-            bt_value_map_insert_unsigned_integer_entry(this->_libObjPtr(), key, rawVal);
+            bt_value_map_insert_unsigned_integer_entry(this->libObjPtr(), key, rawVal);
 
         this->_handleInsertLibStatus(status);
+        return *this;
     }
 
-    void insert(const std::string& key, const std::uint64_t rawVal)
-    {
-        this->insert(key.data(), rawVal);
-    }
-
-    void insert(const char * const key, const std::int64_t rawVal)
+    CommonMapValue insert(const bt2c::CStringView key, const std::int64_t rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstMapValue`.");
 
         const auto status =
-            bt_value_map_insert_signed_integer_entry(this->_libObjPtr(), key, rawVal);
+            bt_value_map_insert_signed_integer_entry(this->libObjPtr(), key, rawVal);
 
         this->_handleInsertLibStatus(status);
+        return *this;
     }
 
-    void insert(const std::string& key, const std::int64_t rawVal)
-    {
-        this->insert(key.data(), rawVal);
-    }
-
-    void insert(const char * const key, const double rawVal)
+    CommonMapValue insert(const bt2c::CStringView key, const double rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstMapValue`.");
 
-        const auto status = bt_value_map_insert_real_entry(this->_libObjPtr(), key, rawVal);
+        const auto status = bt_value_map_insert_real_entry(this->libObjPtr(), key, rawVal);
 
         this->_handleInsertLibStatus(status);
+        return *this;
     }
 
-    void insert(const std::string& key, const double rawVal)
-    {
-        this->insert(key.data(), rawVal);
-    }
-
-    void insert(const char * const key, const char * const rawVal)
+    CommonMapValue insert(const bt2c::CStringView key, const char *rawVal) const
     {
-        static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+        static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstMapValue`.");
 
-        const auto status = bt_value_map_insert_string_entry(this->_libObjPtr(), key, rawVal);
+        const auto status = bt_value_map_insert_string_entry(this->libObjPtr(), key, rawVal);
 
         this->_handleInsertLibStatus(status);
+        return *this;
     }
 
-    void insert(const char * const key, const std::string& rawVal)
-    {
-        this->insert(key, rawVal.data());
-    }
-
-    void insert(const std::string& key, const char * const rawVal)
-    {
-        this->insert(key.data(), rawVal);
-    }
-
-    void insert(const std::string& key, const std::string& rawVal)
+    CommonMapValue insert(const bt2c::CStringView key, const bt2c::CStringView rawVal) const
     {
-        this->insert(key.data(), rawVal.data());
+        return this->insert(key, rawVal.data());
     }
 
-    CommonArrayValue<bt_value> insertEmptyArray(const char *key);
-    CommonArrayValue<bt_value> insertEmptyArray(const std::string& key);
-    CommonMapValue<bt_value> insertEmptyMap(const char *key);
-    CommonMapValue<bt_value> insertEmptyMap(const std::string& key);
-
-    void forEach(const internal::CommonMapValueForEachUserFunc<ConstValue>& func) const
-    {
-        internal::CommonMapValueSpec<const bt_value>::forEach(this->_libObjPtr(), func);
-    }
+    CommonArrayValue<bt_value> insertEmptyArray(bt2c::CStringView key) const;
+    CommonMapValue<bt_value> insertEmptyMap(bt2c::CStringView key) const;
 
-    void forEach(const internal::CommonMapValueForEachUserFunc<CommonValue<LibObjT>>& func)
+    CommonMapValue
+    forEach(const internal::CommonMapValueForEachUserFunc<CommonValue<LibObjT>>& func) const
     {
-        internal::CommonMapValueSpec<LibObjT>::forEach(this->_libObjPtr(), func);
+        internal::CommonMapValueSpec<LibObjT>::forEach(this->libObjPtr(), func);
+        return *this;
     }
 
     Shared shared() const noexcept
     {
-        return Shared {*this};
+        return Shared::createWithRef(*this);
     }
 
 private:
     void _handleInsertLibStatus(const bt_value_map_insert_entry_status status) const
     {
         if (status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_MEMORY_ERROR) {
-            throw LibMemoryError {};
+            throw MemoryError {};
         }
     }
 };
@@ -1119,123 +1396,147 @@ private:
 using MapValue = CommonMapValue<bt_value>;
 using ConstMapValue = CommonMapValue<const bt_value>;
 
+namespace internal {
+
+struct MapValueTypeDescr
+{
+    using Const = ConstMapValue;
+    using NonConst = MapValue;
+};
+
+template <>
+struct TypeDescr<MapValue> : public MapValueTypeDescr
+{
+};
+
+template <>
+struct TypeDescr<ConstMapValue> : public MapValueTypeDescr
+{
+};
+
+} /* namespace internal */
+
+template <typename LibObjT>
+ArrayValue CommonValue<LibObjT>::appendEmptyArray() const
+{
+    return this->asArray().appendEmptyArray();
+}
+
+template <typename LibObjT>
+MapValue CommonValue<LibObjT>::appendEmptyMap() const
+{
+    return this->asArray().appendEmptyMap();
+}
+
+template <typename LibObjT>
+ArrayValue CommonValue<LibObjT>::insertEmptyArray(const bt2c::CStringView key) const
+{
+    return this->asMap().insertEmptyArray(key);
+}
+
+template <typename LibObjT>
+MapValue CommonValue<LibObjT>::insertEmptyMap(const bt2c::CStringView key) const
+{
+    return this->asMap().insertEmptyMap(key);
+}
+
 template <typename LibObjT>
 CommonNullValue<LibObjT> CommonValue<LibObjT>::asNull() const noexcept
 {
     BT_ASSERT_DBG(this->isNull());
-    return CommonNullValue<LibObjT> {this->_libObjPtr()};
+    return CommonNullValue<LibObjT> {};
 }
 
 template <typename LibObjT>
 CommonBoolValue<LibObjT> CommonValue<LibObjT>::asBool() const noexcept
 {
-    BT_ASSERT_DBG(this->isBool());
-    return CommonBoolValue<LibObjT> {this->_libObjPtr()};
+    return CommonBoolValue<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonSignedIntegerValue<LibObjT> CommonValue<LibObjT>::asSignedInteger() const noexcept
 {
-    BT_ASSERT_DBG(this->isSignedInteger());
-    return CommonSignedIntegerValue<LibObjT> {this->_libObjPtr()};
+    return CommonSignedIntegerValue<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonUnsignedIntegerValue<LibObjT> CommonValue<LibObjT>::asUnsignedInteger() const noexcept
 {
-    BT_ASSERT_DBG(this->isUnsignedInteger());
-    return CommonUnsignedIntegerValue<LibObjT> {this->_libObjPtr()};
+    return CommonUnsignedIntegerValue<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonRealValue<LibObjT> CommonValue<LibObjT>::asReal() const noexcept
 {
-    BT_ASSERT_DBG(this->isReal());
-    return CommonRealValue<LibObjT> {this->_libObjPtr()};
+    return CommonRealValue<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonStringValue<LibObjT> CommonValue<LibObjT>::asString() const noexcept
 {
-    BT_ASSERT_DBG(this->isString());
-    return CommonStringValue<LibObjT> {this->_libObjPtr()};
+    return CommonStringValue<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonArrayValue<LibObjT> CommonValue<LibObjT>::asArray() const noexcept
 {
-    BT_ASSERT_DBG(this->isArray());
-    return CommonArrayValue<LibObjT> {this->_libObjPtr()};
+    return CommonArrayValue<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
 CommonMapValue<LibObjT> CommonValue<LibObjT>::asMap() const noexcept
 {
-    BT_ASSERT_DBG(this->isMap());
-    return CommonMapValue<LibObjT> {this->_libObjPtr()};
+    return CommonMapValue<LibObjT> {this->libObjPtr()};
 }
 
 template <typename LibObjT>
-ArrayValue CommonArrayValue<LibObjT>::appendEmptyArray()
+ArrayValue CommonArrayValue<LibObjT>::appendEmptyArray() const
 {
-    static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+    static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstArrayValue`.");
 
     bt_value *libElemPtr;
-    const auto status = bt_value_array_append_empty_array_element(this->_libObjPtr(), &libElemPtr);
+    const auto status = bt_value_array_append_empty_array_element(this->libObjPtr(), &libElemPtr);
 
     this->_handleAppendLibStatus(status);
     return ArrayValue {libElemPtr};
 }
 
 template <typename LibObjT>
-MapValue CommonArrayValue<LibObjT>::appendEmptyMap()
+MapValue CommonArrayValue<LibObjT>::appendEmptyMap() const
 {
-    static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+    static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstArrayValue`.");
 
     bt_value *libElemPtr;
-    const auto status = bt_value_array_append_empty_map_element(this->_libObjPtr(), &libElemPtr);
+    const auto status = bt_value_array_append_empty_map_element(this->libObjPtr(), &libElemPtr);
 
     this->_handleAppendLibStatus(status);
     return MapValue {libElemPtr};
 }
 
 template <typename LibObjT>
-ArrayValue CommonMapValue<LibObjT>::insertEmptyArray(const char * const key)
+ArrayValue CommonMapValue<LibObjT>::insertEmptyArray(const bt2c::CStringView key) const
 {
-    static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+    static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstMapValue`.");
 
     bt_value *libEntryPtr;
-    const auto status =
-        bt_value_map_insert_empty_array_entry(this->_libObjPtr(), key, &libEntryPtr);
+    const auto status = bt_value_map_insert_empty_array_entry(this->libObjPtr(), key, &libEntryPtr);
 
     this->_handleInsertLibStatus(status);
     return ArrayValue {libEntryPtr};
 }
 
 template <typename LibObjT>
-ArrayValue CommonMapValue<LibObjT>::insertEmptyArray(const std::string& key)
+MapValue CommonMapValue<LibObjT>::insertEmptyMap(const bt2c::CStringView key) const
 {
-    return this->insertEmptyArray(key.data());
-}
-
-template <typename LibObjT>
-MapValue CommonMapValue<LibObjT>::insertEmptyMap(const char * const key)
-{
-    static_assert(!std::is_const<LibObjT>::value, "`LibObjT` must NOT be `const`.");
+    static_assert(!std::is_const<LibObjT>::value, "Not available with `bt2::ConstMapValue`.");
 
     bt_value *libEntryPtr;
-    const auto status = bt_value_map_insert_empty_map_entry(this->_libObjPtr(), key, &libEntryPtr);
+    const auto status = bt_value_map_insert_empty_map_entry(this->libObjPtr(), key, &libEntryPtr);
 
     this->_handleInsertLibStatus(status);
     return MapValue {libEntryPtr};
 }
 
-template <typename LibObjT>
-MapValue CommonMapValue<LibObjT>::insertEmptyMap(const std::string& key)
-{
-    return this->insertEmptyMap(key.data());
-}
-
 inline BoolValue::Shared createValue(const bool rawVal)
 {
     return BoolValue::create(rawVal);
@@ -1261,11 +1562,11 @@ inline StringValue::Shared createValue(const char * const rawVal)
     return StringValue::create(rawVal);
 }
 
-inline StringValue::Shared createValue(const std::string& rawVal)
+inline StringValue::Shared createValue(const bt2c::CStringView rawVal)
 {
     return StringValue::create(rawVal);
 }
 
-} // namespace bt2
+} /* namespace bt2 */
 
-#endif // BABELTRACE_CPP_COMMON_BT2_VALUE_HPP
+#endif /* BABELTRACE_CPP_COMMON_BT2_VALUE_HPP */
diff --git a/src/cpp-common/bt2/wrap.hpp b/src/cpp-common/bt2/wrap.hpp
new file mode 100644 (file)
index 0000000..c3b177c
--- /dev/null
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2_WRAP_HPP
+#define BABELTRACE_CPP_COMMON_BT2_WRAP_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "clock-class.hpp"
+#include "clock-snapshot.hpp"
+#include "component-port.hpp"
+#include "error.hpp"
+#include "field-class.hpp"
+#include "field.hpp"
+#include "graph.hpp"
+#include "integer-range-set.hpp"
+#include "integer-range.hpp"
+#include "message-iterator.hpp"
+#include "message.hpp"
+#include "optional-borrowed-object.hpp"
+#include "private-query-executor.hpp"
+#include "self-component-class.hpp"
+#include "self-component-port.hpp"
+#include "self-message-iterator-configuration.hpp"
+#include "self-message-iterator.hpp"
+#include "trace-ir.hpp"
+#include "value.hpp"
+
+namespace bt2 {
+
+inline ConstErrorCause wrap(const bt_error_cause * const libObjPtr) noexcept
+{
+    return ConstErrorCause {libObjPtr};
+}
+
+inline Graph wrap(bt_graph * const libObjPtr) noexcept
+{
+    return Graph {libObjPtr};
+}
+
+inline ClockClass wrap(bt_clock_class * const libObjPtr) noexcept
+{
+    return ClockClass {libObjPtr};
+}
+
+inline ConstClockClass wrap(const bt_clock_class * const libObjPtr) noexcept
+{
+    return ConstClockClass {libObjPtr};
+}
+
+inline ConstClockSnapshot wrap(const bt_clock_snapshot * const libObjPtr) noexcept
+{
+    return ConstClockSnapshot {libObjPtr};
+}
+
+inline ConstComponent wrap(const bt_component * const libObjPtr) noexcept
+{
+    return ConstComponent {libObjPtr};
+}
+
+inline ConstSourceComponent wrap(const bt_component_source * const libObjPtr) noexcept
+{
+    return ConstSourceComponent {libObjPtr};
+}
+
+inline ConstFilterComponent wrap(const bt_component_filter * const libObjPtr) noexcept
+{
+    return ConstFilterComponent {libObjPtr};
+}
+
+inline ConstSinkComponent wrap(const bt_component_sink * const libObjPtr) noexcept
+{
+    return ConstSinkComponent {libObjPtr};
+}
+
+inline ConstInputPort wrap(const bt_port_input * const libObjPtr) noexcept
+{
+    return ConstInputPort {libObjPtr};
+}
+
+inline ConstOutputPort wrap(const bt_port_output * const libObjPtr) noexcept
+{
+    return ConstOutputPort {libObjPtr};
+}
+
+inline ConstUnsignedEnumerationFieldClassMapping
+wrap(const bt_field_class_enumeration_unsigned_mapping * const libObjPtr) noexcept
+{
+    return ConstUnsignedEnumerationFieldClassMapping {libObjPtr};
+}
+
+inline ConstSignedEnumerationFieldClassMapping
+wrap(const bt_field_class_enumeration_signed_mapping * const libObjPtr) noexcept
+{
+    return ConstSignedEnumerationFieldClassMapping {libObjPtr};
+}
+
+inline VariantFieldClassOption wrap(bt_field_class_variant_option * const libObjPtr) noexcept
+{
+    return VariantFieldClassOption {libObjPtr};
+}
+
+inline ConstVariantFieldClassOption
+wrap(const bt_field_class_variant_option * const libObjPtr) noexcept
+{
+    return ConstVariantFieldClassOption {libObjPtr};
+}
+
+inline ConstVariantWithUnsignedIntegerSelectorFieldClassOption
+wrap(const bt_field_class_variant_with_selector_field_integer_unsigned_option
+         * const libObjPtr) noexcept
+{
+    return ConstVariantWithUnsignedIntegerSelectorFieldClassOption {libObjPtr};
+}
+
+inline ConstVariantWithSignedIntegerSelectorFieldClassOption
+wrap(const bt_field_class_variant_with_selector_field_integer_signed_option
+         * const libObjPtr) noexcept
+{
+    return ConstVariantWithSignedIntegerSelectorFieldClassOption {libObjPtr};
+}
+
+inline FieldClass wrap(bt_field_class * const libObjPtr) noexcept
+{
+    return FieldClass {libObjPtr};
+}
+
+inline ConstFieldClass wrap(const bt_field_class * const libObjPtr) noexcept
+{
+    return ConstFieldClass {libObjPtr};
+}
+
+inline ConstFieldPathItem wrap(const bt_field_path_item * const libObjPtr) noexcept
+{
+    return ConstFieldPathItem {libObjPtr};
+}
+
+inline ConstFieldPath wrap(const bt_field_path * const libObjPtr) noexcept
+{
+    return ConstFieldPath {libObjPtr};
+}
+
+inline Field wrap(bt_field * const libObjPtr) noexcept
+{
+    return Field {libObjPtr};
+}
+
+inline ConstField wrap(const bt_field * const libObjPtr) noexcept
+{
+    return ConstField {libObjPtr};
+}
+
+inline UnsignedIntegerRangeSet wrap(bt_integer_range_set_unsigned * const libObjPtr) noexcept
+{
+    return UnsignedIntegerRangeSet {libObjPtr};
+}
+
+inline ConstUnsignedIntegerRangeSet
+wrap(const bt_integer_range_set_unsigned * const libObjPtr) noexcept
+{
+    return ConstUnsignedIntegerRangeSet {libObjPtr};
+}
+
+inline SignedIntegerRangeSet wrap(bt_integer_range_set_signed * const libObjPtr) noexcept
+{
+    return SignedIntegerRangeSet {libObjPtr};
+}
+
+inline ConstSignedIntegerRangeSet wrap(const bt_integer_range_set_signed * const libObjPtr) noexcept
+{
+    return ConstSignedIntegerRangeSet {libObjPtr};
+}
+
+inline ConstUnsignedIntegerRange wrap(const bt_integer_range_unsigned * const libObjPtr) noexcept
+{
+    return ConstUnsignedIntegerRange {libObjPtr};
+}
+
+inline ConstSignedIntegerRange wrap(const bt_integer_range_signed * const libObjPtr) noexcept
+{
+    return ConstSignedIntegerRange {libObjPtr};
+}
+
+inline MessageIterator wrap(bt_message_iterator * const libObjPtr) noexcept
+{
+    return MessageIterator {libObjPtr};
+}
+
+inline Message wrap(bt_message * const libObjPtr) noexcept
+{
+    return Message {libObjPtr};
+}
+
+inline ConstMessage wrap(const bt_message * const libObjPtr) noexcept
+{
+    return ConstMessage {libObjPtr};
+}
+
+inline PrivateQueryExecutor wrap(bt_private_query_executor * const libObjPtr) noexcept
+{
+    return PrivateQueryExecutor {libObjPtr};
+}
+
+inline SelfComponentClass wrap(bt_self_component_class * const libObjPtr) noexcept
+{
+    return SelfComponentClass {libObjPtr};
+}
+
+inline SelfComponentClass wrap(bt_self_component_class_source * const libObjPtr) noexcept
+{
+    return SelfComponentClass {libObjPtr};
+}
+
+inline SelfComponentClass wrap(bt_self_component_class_filter * const libObjPtr) noexcept
+{
+    return SelfComponentClass {libObjPtr};
+}
+
+inline SelfComponentClass wrap(bt_self_component_class_sink * const libObjPtr) noexcept
+{
+    return SelfComponentClass {libObjPtr};
+}
+
+inline SelfComponent wrap(bt_self_component * const libObjPtr) noexcept
+{
+    return SelfComponent {libObjPtr};
+}
+
+inline SelfSourceComponent wrap(bt_self_component_source * const libObjPtr) noexcept
+{
+    return SelfSourceComponent {libObjPtr};
+}
+
+inline SelfFilterComponent wrap(bt_self_component_filter * const libObjPtr) noexcept
+{
+    return SelfFilterComponent {libObjPtr};
+}
+
+inline SelfSinkComponent wrap(bt_self_component_sink * const libObjPtr) noexcept
+{
+    return SelfSinkComponent {libObjPtr};
+}
+
+inline SelfComponentInputPort wrap(bt_self_component_port_input * const libObjPtr) noexcept
+{
+    return SelfComponentInputPort {libObjPtr};
+}
+
+inline SelfComponentOutputPort wrap(bt_self_component_port_output * const libObjPtr) noexcept
+{
+    return SelfComponentOutputPort {libObjPtr};
+}
+
+inline SelfMessageIterator wrap(bt_self_message_iterator * const libObjPtr) noexcept
+{
+    return SelfMessageIterator {libObjPtr};
+}
+
+inline SelfMessageIteratorConfiguration
+wrap(bt_self_message_iterator_configuration * const libObjPtr) noexcept
+{
+    return SelfMessageIteratorConfiguration {libObjPtr};
+}
+
+inline Event wrap(bt_event * const libObjPtr) noexcept
+{
+    return Event {libObjPtr};
+}
+
+inline ConstEvent wrap(const bt_event * const libObjPtr) noexcept
+{
+    return ConstEvent {libObjPtr};
+}
+
+inline Packet wrap(bt_packet * const libObjPtr) noexcept
+{
+    return Packet {libObjPtr};
+}
+
+inline ConstPacket wrap(const bt_packet * const libObjPtr) noexcept
+{
+    return ConstPacket {libObjPtr};
+}
+
+inline Stream wrap(bt_stream * const libObjPtr) noexcept
+{
+    return Stream {libObjPtr};
+}
+
+inline ConstStream wrap(const bt_stream * const libObjPtr) noexcept
+{
+    return ConstStream {libObjPtr};
+}
+
+inline Trace wrap(bt_trace * const libObjPtr) noexcept
+{
+    return Trace {libObjPtr};
+}
+
+inline ConstTrace wrap(const bt_trace * const libObjPtr) noexcept
+{
+    return ConstTrace {libObjPtr};
+}
+
+inline EventClass wrap(bt_event_class * const libObjPtr) noexcept
+{
+    return EventClass {libObjPtr};
+}
+
+inline ConstEventClass wrap(const bt_event_class * const libObjPtr) noexcept
+{
+    return ConstEventClass {libObjPtr};
+}
+
+inline StreamClass wrap(bt_stream_class * const libObjPtr) noexcept
+{
+    return StreamClass {libObjPtr};
+}
+
+inline ConstStreamClass wrap(const bt_stream_class * const libObjPtr) noexcept
+{
+    return ConstStreamClass {libObjPtr};
+}
+
+inline TraceClass wrap(bt_trace_class * const libObjPtr) noexcept
+{
+    return TraceClass {libObjPtr};
+}
+
+inline ConstTraceClass wrap(const bt_trace_class * const libObjPtr) noexcept
+{
+    return ConstTraceClass {libObjPtr};
+}
+
+inline Value wrap(bt_value * const libObjPtr) noexcept
+{
+    return Value {libObjPtr};
+}
+
+inline ConstValue wrap(const bt_value * const libObjPtr) noexcept
+{
+    return ConstValue {libObjPtr};
+}
+
+inline OptionalBorrowedObject<ClockClass> wrapOptional(bt_clock_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstClockClass>
+wrapOptional(const bt_clock_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstClockSnapshot>
+wrapOptional(const bt_clock_snapshot * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstComponent>
+wrapOptional(const bt_component * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstSourceComponent>
+wrapOptional(const bt_component_source * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstFilterComponent>
+wrapOptional(const bt_component_filter * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstSinkComponent>
+wrapOptional(const bt_component_sink * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstInputPort>
+wrapOptional(const bt_port_input * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstOutputPort>
+wrapOptional(const bt_port_output * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<FieldClass> wrapOptional(bt_field_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstFieldClass>
+wrapOptional(const bt_field_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstFieldPathItem>
+wrapOptional(const bt_field_path_item * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstFieldPath>
+wrapOptional(const bt_field_path * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<Field> wrapOptional(bt_field * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstField> wrapOptional(const bt_field * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<UnsignedIntegerRangeSet>
+wrapOptional(bt_integer_range_set_unsigned * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstUnsignedIntegerRangeSet>
+wrapOptional(const bt_integer_range_set_unsigned * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SignedIntegerRangeSet>
+wrapOptional(bt_integer_range_set_signed * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstSignedIntegerRangeSet>
+wrapOptional(const bt_integer_range_set_signed * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstUnsignedIntegerRange>
+wrapOptional(const bt_integer_range_unsigned * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstSignedIntegerRange>
+wrapOptional(const bt_integer_range_signed * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<MessageIterator>
+wrapOptional(bt_message_iterator * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<Message> wrapOptional(bt_message * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstMessage>
+wrapOptional(const bt_message * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<PrivateQueryExecutor>
+wrapOptional(bt_private_query_executor * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SelfComponentClass>
+wrapOptional(bt_self_component_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SelfComponentClass>
+wrapOptional(bt_self_component_class_source * const libObjPtr) noexcept
+{
+    return bt_self_component_class_source_as_self_component_class(libObjPtr);
+}
+
+inline OptionalBorrowedObject<SelfComponentClass>
+wrapOptional(bt_self_component_class_filter * const libObjPtr) noexcept
+{
+    return bt_self_component_class_filter_as_self_component_class(libObjPtr);
+}
+
+inline OptionalBorrowedObject<SelfComponentClass>
+wrapOptional(bt_self_component_class_sink * const libObjPtr) noexcept
+{
+    return bt_self_component_class_sink_as_self_component_class(libObjPtr);
+}
+
+inline OptionalBorrowedObject<SelfComponent>
+wrapOptional(bt_self_component * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SelfSourceComponent>
+wrapOptional(bt_self_component_source * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SelfFilterComponent>
+wrapOptional(bt_self_component_filter * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SelfSinkComponent>
+wrapOptional(bt_self_component_sink * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SelfComponentInputPort>
+wrapOptional(bt_self_component_port_input * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SelfComponentOutputPort>
+wrapOptional(bt_self_component_port_output * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SelfMessageIterator>
+wrapOptional(bt_self_message_iterator * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<SelfMessageIteratorConfiguration>
+wrapOptional(bt_self_message_iterator_configuration * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<Event> wrapOptional(bt_event * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstEvent> wrapOptional(const bt_event * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<Packet> wrapOptional(bt_packet * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstPacket> wrapOptional(const bt_packet * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<Stream> wrapOptional(bt_stream * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstStream> wrapOptional(const bt_stream * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<Trace> wrapOptional(bt_trace * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstTrace> wrapOptional(const bt_trace * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<EventClass> wrapOptional(bt_event_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstEventClass>
+wrapOptional(const bt_event_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<StreamClass> wrapOptional(bt_stream_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstStreamClass>
+wrapOptional(const bt_stream_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<TraceClass> wrapOptional(bt_trace_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstTraceClass>
+wrapOptional(const bt_trace_class * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<Value> wrapOptional(bt_value * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+inline OptionalBorrowedObject<ConstValue> wrapOptional(const bt_value * const libObjPtr) noexcept
+{
+    return libObjPtr;
+}
+
+} /* namespace bt2 */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2_WRAP_HPP */
diff --git a/src/cpp-common/bt2c/align.hpp b/src/cpp-common/bt2c/align.hpp
new file mode 100644 (file)
index 0000000..ef443aa
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_ALIGN_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_ALIGN_HPP
+
+#include <type_traits>
+
+#include "common/align.h"
+
+namespace bt2c {
+
+template <typename ValT, typename AlignT>
+ValT align(const ValT val, const AlignT align) noexcept
+{
+    static_assert(std::is_unsigned<ValT>::value, "`ValT` is unsigned.");
+    return BT_ALIGN(val, static_cast<ValT>(align));
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_ALIGN_HPP */
diff --git a/src/cpp-common/bt2c/c-string-view.hpp b/src/cpp-common/bt2c/c-string-view.hpp
new file mode 100644 (file)
index 0000000..771954c
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_C_STRING_VIEW_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_C_STRING_VIEW_HPP
+
+#include <cstddef>
+#include <cstring>
+#include <string>
+
+#include "common/assert.h"
+#include "cpp-common/bt2s/string-view.hpp"
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+
+#include "type-traits.hpp"
+
+namespace bt2c {
+
+/*
+ * A view on a constant null-terminated C string.
+ *
+ * Similar to `bt2s::string_view`, but len() and end() compute the
+ * length on demand.
+ */
+class CStringView final
+{
+public:
+    /*
+     * Builds an empty view (data() returns `nullptr`).
+     *
+     * Intentionally not explicit.
+     */
+    constexpr CStringView() noexcept = default;
+
+    /*
+     * Builds a view of the C string `str` (may be `nullptr`).
+     *
+     * Intentionally not explicit.
+     */
+    constexpr CStringView(const char * const str) noexcept : _mStr {str}
+    {
+    }
+
+    /*
+     * Builds a view of the string `str`.
+     *
+     * Intentionally not explicit.
+     */
+    CStringView(const std::string& str) noexcept : _mStr {str.c_str()}
+    {
+    }
+
+    /*
+     * Makes this view view the C string `str` (may be `nullptr`).
+     *
+     * Intentionally not explicit.
+     */
+    CStringView& operator=(const char * const str) noexcept
+    {
+        _mStr = str;
+        return *this;
+    }
+
+    /*
+     * Viewed null-terminated C string (may be `nullptr`).
+     */
+    const char *data() const noexcept
+    {
+        return _mStr;
+    }
+
+    /*
+     * Alias of data().
+     */
+    operator const char *() const noexcept
+    {
+        return this->data();
+    }
+
+    /*
+     * Alias of data().
+     */
+    const char *operator*() const noexcept
+    {
+        return this->data();
+    }
+
+    /*
+     * Alias of data().
+     *
+     * data() must not return `nullptr`.
+     */
+    const char *begin() const noexcept
+    {
+        BT_ASSERT_DBG(_mStr);
+        return this->data();
+    }
+
+    /*
+     * Pointer to the null character of the viewed C string.
+     *
+     * data() must not return `nullptr`.
+     */
+    const char *end() const noexcept
+    {
+        BT_ASSERT_DBG(_mStr);
+        return _mStr + this->len();
+    }
+
+    /*
+     * Length of the viewed C string, excluding the null character.
+     *
+     * data() must not return `nullptr`.
+     */
+    std::size_t len() const noexcept
+    {
+        BT_ASSERT_DBG(_mStr);
+        return std::strlen(_mStr);
+    }
+
+    /*
+     * Returns an `std::string` instance containing a copy of the viewed
+     * C string.
+     *
+     * data() must not return `nullptr`.
+     */
+    std::string str() const
+    {
+        BT_ASSERT_DBG(_mStr);
+        return std::string {_mStr};
+    }
+
+    /*
+     * Alias of str().
+     */
+    operator std::string() const
+    {
+        return this->str();
+    }
+
+    /*
+     * Returns a `bt2s::string_view` instance to view the contents,
+     * excluding the null character, of the viewed C string.
+     */
+    bt2s::string_view strView() const noexcept
+    {
+        if (_mStr) {
+            return bt2s::string_view {this->begin(), this->len()};
+        } else {
+            return {};
+        }
+    }
+
+    /*
+     * Alias of strView().
+     */
+    operator bt2s::string_view() const noexcept
+    {
+        return this->strView();
+    }
+
+    /*
+     * Returns the character at index `i`.
+     *
+     * `i` must be less than what len() returns.
+     *
+     * data() must not return `nullptr`.
+     */
+    char operator[](const std::size_t i) const noexcept
+    {
+        BT_ASSERT_DBG(_mStr);
+        BT_ASSERT_DBG(i < this->len());
+        return _mStr[i];
+    }
+
+    bool startsWith(const bt2c::CStringView prefix) const noexcept
+    {
+        BT_ASSERT_DBG(_mStr);
+        BT_ASSERT_DBG(prefix);
+        return std::strncmp(_mStr, prefix, prefix.len()) == 0;
+    }
+
+private:
+    const char *_mStr = nullptr;
+};
+
+inline const char *format_as(const CStringView& str)
+{
+    return str ? *str : "(null)";
+}
+
+namespace internal {
+
+template <typename StrT>
+const char *asConstCharPtr(StrT&& val) noexcept
+{
+    return val.data();
+}
+
+inline const char *asConstCharPtr(const char * const val) noexcept
+{
+    return val;
+}
+
+template <typename StrT>
+using ComparableWithCStringView =
+    IsOneOf<typename std::decay<StrT>::type, CStringView, std::string, const char *>;
+
+} /* namespace internal */
+
+/*
+ * Returns true if `lhs` is equal to `rhs`.
+ *
+ * `LhsT` and `RhsT` may be any of:
+ *
+ * • `const char *`
+ * • `std::string`
+ * • `CStringView`
+ *
+ * Both `lhs` and `rhs` must not have an underlying `nullptr` raw data.
+ */
+template <
+    typename LhsT, typename RhsT,
+    typename = typename std::enable_if<internal::ComparableWithCStringView<LhsT>::value>::type,
+    typename = typename std::enable_if<internal::ComparableWithCStringView<RhsT>::value>::type>
+bool operator==(LhsT&& lhs, RhsT&& rhs) noexcept
+{
+    const auto rawLhs = internal::asConstCharPtr(lhs);
+    const auto rawRhs = internal::asConstCharPtr(rhs);
+
+    BT_ASSERT_DBG(rawLhs);
+    BT_ASSERT_DBG(rawRhs);
+    return std::strcmp(rawLhs, rawRhs) == 0;
+}
+
+/*
+ * Returns true if `lhs` is not equal to `rhs`.
+ *
+ * `LhsT` and `RhsT` may be any of:
+ *
+ * • `const char *`
+ * • `std::string`
+ * • `CStringView`
+ *
+ * Both `lhs` and `rhs` must not have an underlying `nullptr` raw data.
+ */
+template <
+    typename LhsT, typename RhsT,
+    typename = typename std::enable_if<internal::ComparableWithCStringView<LhsT>::value>::type,
+    typename = typename std::enable_if<internal::ComparableWithCStringView<RhsT>::value>::type>
+bool operator!=(LhsT&& lhs, RhsT&& rhs) noexcept
+{
+    return !(std::forward<LhsT>(lhs) == std::forward<RhsT>(rhs));
+}
+
+} /* namespace bt2c */
+
+/*
+ * Appends `rhs` to `lhs`.
+ */
+inline void operator+=(std::string& lhs, bt2c::CStringView rhs)
+{
+    lhs += rhs.data();
+}
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_C_STRING_VIEW_HPP */
diff --git a/src/cpp-common/bt2c/call.hpp b/src/cpp-common/bt2c/call.hpp
new file mode 100644 (file)
index 0000000..3241f73
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_CALL_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_CALL_HPP
+
+#include <functional>
+#include <utility>
+
+namespace bt2c {
+
+/*
+ * Partial implementation of INVOKE.
+ *
+ * As found in
+ * <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0312r1.html>.
+ */
+template <typename FuncT, typename... ArgTs>
+auto call(FuncT func, ArgTs&&...args) -> decltype(std::ref(func)(std::forward<ArgTs>(args)...))
+{
+    return std::ref(func)(std::forward<ArgTs>(args)...);
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_CALL_HPP */
diff --git a/src/cpp-common/bt2c/contains.hpp b/src/cpp-common/bt2c/contains.hpp
new file mode 100644 (file)
index 0000000..913cf1a
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2024 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_CONTAINS_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_CONTAINS_HPP
+
+namespace bt2c {
+
+/*
+ * Returns whether or not the STL container `container` contains the
+ * value `val`.
+ */
+template <typename T, typename V>
+bool contains(const T& container, const V& val) noexcept
+{
+    return container.find(val) != container.end();
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_CONTAINS_HPP */
diff --git a/src/cpp-common/bt2c/data-len.hpp b/src/cpp-common/bt2c/data-len.hpp
new file mode 100644 (file)
index 0000000..55760f4
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_DATA_LEN_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_DATA_LEN_HPP
+
+#include "safe-ops.hpp"
+
+namespace bt2c {
+
+/*
+ * A data length is a quantity of binary data (bits).
+ *
+ * This class can make some code clearer and safer because its
+ * constructor is private so that you need to call DataLen::fromBits()
+ * or DataLen::fromBytes() to create an instance.
+ *
+ * With a `DataLen` instance `len`, use `*len` or `len.bits()` to get
+ * the quantity in bits and `len.bytes()` to get it in bytes (floored).
+ *
+ * You can add, subtract, and compare data lengths.
+ */
+class DataLen final
+{
+private:
+    constexpr explicit DataLen(const unsigned long long lenBits) noexcept : _mLenBits {lenBits}
+    {
+    }
+
+public:
+    /*
+     * Creates and returns a data length instance representing `lenBits`
+     * bits.
+     */
+    static constexpr DataLen fromBits(const unsigned long long lenBits) noexcept
+    {
+        return DataLen {lenBits};
+    }
+
+    /*
+     * Creates and returns a data length instance representing
+     * `lenBytes` bytes.
+     */
+    static DataLen fromBytes(const unsigned long long lenBytes) noexcept
+    {
+        return DataLen {safeMul(lenBytes, 8ULL)};
+    }
+
+    /*
+     * Number of bits of this data length.
+     */
+    constexpr unsigned long long operator*() const noexcept
+    {
+        return _mLenBits;
+    }
+
+    /*
+     * Number of bits of this data length.
+     */
+    constexpr unsigned long long bits() const noexcept
+    {
+        return _mLenBits;
+    }
+
+    /*
+     * Number of bytes (floor) of this data length.
+     */
+    constexpr unsigned long long bytes() const noexcept
+    {
+        return _mLenBits / 8;
+    }
+
+    /*
+     * Whether or not this data length represents a multiple of eight
+     * bits.
+     */
+    constexpr bool hasExtraBits() const noexcept
+    {
+        return this->extraBitCount() > 0;
+    }
+
+    /*
+     * Remainder of this data length, in bits, divided by eight.
+     */
+    constexpr unsigned int extraBitCount() const noexcept
+    {
+        return _mLenBits & 7;
+    }
+
+    /*
+     * Returns whether or not this data length is a power of two bits.
+     */
+    constexpr bool isPowOfTwo() const noexcept
+    {
+        return ((_mLenBits & (_mLenBits - 1)) == 0) && _mLenBits > 0;
+    }
+
+    constexpr bool operator==(const DataLen& other) const noexcept
+    {
+        return _mLenBits == other._mLenBits;
+    }
+
+    constexpr bool operator!=(const DataLen& other) const noexcept
+    {
+        return !(*this == other);
+    }
+
+    constexpr bool operator<(const DataLen& other) const noexcept
+    {
+        return _mLenBits < other._mLenBits;
+    }
+
+    constexpr bool operator<=(const DataLen& other) const noexcept
+    {
+        return (*this == other) || (*this < other);
+    }
+
+    constexpr bool operator>(const DataLen& other) const noexcept
+    {
+        return !(*this <= other);
+    }
+
+    constexpr bool operator>=(const DataLen& other) const noexcept
+    {
+        return (*this > other) || (*this == other);
+    }
+
+    DataLen& operator+=(const DataLen len) noexcept
+    {
+        _mLenBits = safeAdd(_mLenBits, len._mLenBits);
+        return *this;
+    }
+
+    DataLen& operator-=(const DataLen len) noexcept
+    {
+        _mLenBits = safeSub(_mLenBits, len._mLenBits);
+        return *this;
+    }
+
+    DataLen& operator*=(const unsigned long long mul) noexcept
+    {
+        _mLenBits = safeMul(_mLenBits, mul);
+        return *this;
+    }
+
+private:
+    unsigned long long _mLenBits = 0;
+};
+
+static inline DataLen operator+(const DataLen lenA, const DataLen lenB) noexcept
+{
+    return DataLen::fromBits(safeAdd(*lenA, *lenB));
+}
+
+static inline DataLen operator-(const DataLen lenA, const DataLen lenB) noexcept
+{
+    return DataLen::fromBits(safeSub(*lenA, *lenB));
+}
+
+static inline DataLen operator*(const DataLen len, const unsigned long long mul) noexcept
+{
+    return DataLen::fromBits(safeMul(*len, mul));
+}
+
+/*
+ * Use this namespace to access handy data length user literals, for
+ * example:
+ *
+ *     using namespace bt2c::literals::datalen;
+ *
+ *     const auto bufSize = 64_MiBytes + 8_KiBits;
+ */
+namespace literals {
+namespace datalen {
+
+static inline DataLen operator""_bits(const unsigned long long val) noexcept
+{
+    return DataLen::fromBits(val);
+}
+
+static inline DataLen operator""_KiBits(const unsigned long long val) noexcept
+{
+    return DataLen::fromBits(safeMul(val, 1024ULL));
+}
+
+static inline DataLen operator""_MiBits(const unsigned long long val) noexcept
+{
+    return DataLen::fromBits(safeMul(val, 1024ULL * 1024));
+}
+
+static inline DataLen operator""_GiBits(const unsigned long long val) noexcept
+{
+    return DataLen::fromBits(safeMul(val, 1024ULL * 1024 * 1024));
+}
+
+static inline DataLen operator""_bytes(const unsigned long long val) noexcept
+{
+    return DataLen::fromBytes(val);
+}
+
+static inline DataLen operator""_KiBytes(const unsigned long long val) noexcept
+{
+    return DataLen::fromBytes(safeMul(val, 1024ULL));
+}
+
+static inline DataLen operator""_MiBytes(const unsigned long long val) noexcept
+{
+    return DataLen::fromBytes(safeMul(val, 1024ULL * 1024));
+}
+
+static inline DataLen operator""_GiBytes(const unsigned long long val) noexcept
+{
+    return DataLen::fromBytes(safeMul(val, 1024ULL * 1024 * 1024));
+}
+
+} /* namespace datalen */
+} /* namespace literals */
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_DATA_LEN_HPP */
diff --git a/src/cpp-common/bt2c/dummy.cpp b/src/cpp-common/bt2c/dummy.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/cpp-common/bt2c/endian.hpp b/src/cpp-common/bt2c/endian.hpp
new file mode 100644 (file)
index 0000000..908fc58
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_ENDIAN_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_ENDIAN_HPP
+
+#include <cstdint>
+
+#include "compat/endian.h" /* IWYU pragma: keep  */
+
+namespace bt2c {
+
+inline std::uint8_t littleEndianToNative(const std::uint8_t val) noexcept
+{
+    return val;
+}
+
+inline std::uint8_t bigEndianToNative(const std::uint8_t val) noexcept
+{
+    return val;
+}
+
+inline std::int8_t littleEndianToNative(const std::int8_t val) noexcept
+{
+    return val;
+}
+
+inline std::int8_t bigEndianToNative(const std::int8_t val) noexcept
+{
+    return val;
+}
+
+inline std::uint16_t littleEndianToNative(const std::uint16_t val) noexcept
+{
+    return static_cast<std::uint16_t>(le16toh(val));
+}
+
+inline std::uint16_t bigEndianToNative(const std::uint16_t val) noexcept
+{
+    return static_cast<std::uint16_t>(be16toh(val));
+}
+
+inline std::int16_t littleEndianToNative(const std::int16_t val) noexcept
+{
+    return static_cast<std::int16_t>(littleEndianToNative(static_cast<std::uint16_t>(val)));
+}
+
+inline std::int16_t bigEndianToNative(const std::int16_t val) noexcept
+{
+    return static_cast<std::int16_t>(bigEndianToNative(static_cast<std::uint16_t>(val)));
+}
+
+inline std::uint32_t littleEndianToNative(const std::uint32_t val) noexcept
+{
+    return static_cast<std::uint32_t>(le32toh(val));
+}
+
+inline std::uint32_t bigEndianToNative(const std::uint32_t val) noexcept
+{
+    return static_cast<std::uint32_t>(be32toh(val));
+}
+
+inline std::int32_t littleEndianToNative(const std::int32_t val) noexcept
+{
+    return static_cast<std::int32_t>(littleEndianToNative(static_cast<std::uint32_t>(val)));
+}
+
+inline std::int32_t bigEndianToNative(const std::int32_t val) noexcept
+{
+    return static_cast<std::int32_t>(bigEndianToNative(static_cast<std::uint32_t>(val)));
+}
+
+inline std::uint64_t littleEndianToNative(const std::uint64_t val) noexcept
+{
+    return static_cast<std::uint64_t>(le64toh(val));
+}
+
+inline std::uint64_t bigEndianToNative(const std::uint64_t val) noexcept
+{
+    return static_cast<std::uint64_t>(be64toh(val));
+}
+
+inline std::int64_t littleEndianToNative(const std::int64_t val) noexcept
+{
+    return static_cast<std::int64_t>(littleEndianToNative(static_cast<std::uint64_t>(val)));
+}
+
+inline std::int64_t bigEndianToNative(const std::int64_t val) noexcept
+{
+    return static_cast<std::int64_t>(bigEndianToNative(static_cast<std::uint64_t>(val)));
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_ENDIAN_HPP */
diff --git a/src/cpp-common/bt2c/exc.hpp b/src/cpp-common/bt2c/exc.hpp
new file mode 100644 (file)
index 0000000..3400dfa
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_EXC_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_EXC_HPP
+
+#include <exception>
+#include <new>
+#include <stdexcept>
+#include <string>
+
+namespace bt2c {
+
+/*
+ * End of iteration.
+ */
+class End : public std::exception
+{
+public:
+    explicit End() noexcept : std::exception {}
+    {
+    }
+};
+
+/*
+ * General error.
+ */
+class Error : public std::runtime_error
+{
+public:
+    explicit Error(std::string msg = "Error") : std::runtime_error {std::move(msg)}
+    {
+    }
+};
+
+/*
+ * Overflow error.
+ */
+class OverflowError : public Error
+{
+public:
+    explicit OverflowError() noexcept : Error {"Overflow error"}
+    {
+    }
+};
+
+/*
+ * Memory error.
+ */
+class MemoryError : public std::bad_alloc
+{
+public:
+    explicit MemoryError() noexcept : std::bad_alloc {}
+    {
+    }
+};
+
+/*
+ * Not available right now: try again later.
+ */
+class TryAgain : public std::exception
+{
+public:
+    explicit TryAgain() noexcept : std::exception {}
+    {
+    }
+};
+
+/*
+ * No such file or directory.
+ */
+class NoSuchFileOrDirectoryError : public Error
+{
+public:
+    explicit NoSuchFileOrDirectoryError() noexcept : Error {"No such file or directory"}
+    {
+    }
+};
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_EXC_HPP */
diff --git a/src/cpp-common/bt2c/file-utils.cpp b/src/cpp-common/bt2c/file-utils.cpp
new file mode 100644 (file)
index 0000000..9352018
--- /dev/null
@@ -0,0 +1,38 @@
+
+/*
+ * Copyright (c) 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#include <fstream>
+
+#include "exc.hpp"
+#include "file-utils.hpp"
+
+namespace bt2c {
+
+std::vector<std::uint8_t> dataFromFile(const char * const filePath)
+{
+    /*
+     * Open a file stream and seek to the end of the stream to compute the size
+     * of the buffer required.
+    */
+    std::ifstream file {filePath, std::ios::binary | std::ios::ate};
+
+    if (!file) {
+        throw NoSuchFileOrDirectoryError {};
+    }
+
+    const auto size = file.tellg();
+    std::vector<uint8_t> buffer(static_cast<std::size_t>(size));
+
+    /*
+     * Seek the reading head back at the beginning of the stream to actually
+     * read the content.
+     */
+    file.seekg(0, std::ios::beg);
+    file.read(reinterpret_cast<char *>(buffer.data()), size);
+    return buffer;
+}
+
+} /* namespace bt2c */
diff --git a/src/cpp-common/bt2c/file-utils.hpp b/src/cpp-common/bt2c/file-utils.hpp
new file mode 100644 (file)
index 0000000..9890f0a
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_FILE_UTILS_HPP
+#define BABELTRACE_CPP_COMMON_FILE_UTILS_HPP
+
+#include <cstdint>
+#include <vector>
+
+namespace bt2c {
+
+/*
+ * Returns a vector of all the bytes contained in `path`.
+ */
+std::vector<std::uint8_t> dataFromFile(const char *path);
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_FILE_UTILS_HPP */
diff --git a/src/cpp-common/bt2c/fmt.hpp b/src/cpp-common/bt2c/fmt.hpp
new file mode 100644 (file)
index 0000000..a78d7a6
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2024 EfficiOS, inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+#include "cpp-common/vendor/wise-enum/wise_enum.h"
+
+#include "uuid.hpp"
+
+namespace internal {
+
+template <typename T>
+using EnableIfIsWiseEnum =
+    typename std::enable_if<wise_enum::is_wise_enum<T>::value, const char *>::type;
+
+} /* namespace internal */
+
+namespace bt2 {
+
+template <typename T>
+::internal::EnableIfIsWiseEnum<T> format_as(const T val) noexcept
+{
+    return wise_enum::to_string<T>(val);
+}
+
+} /* namespace bt2 */
+
+namespace bt2c {
+
+template <typename T>
+::internal::EnableIfIsWiseEnum<T> format_as(const T val) noexcept
+{
+    return wise_enum::to_string<T>(val);
+}
+
+inline std::string format_as(const UuidView uuid)
+{
+    return uuid.str();
+}
+
+} /* namespace bt2c */
diff --git a/src/cpp-common/bt2c/glib-up.hpp b/src/cpp-common/bt2c/glib-up.hpp
new file mode 100644 (file)
index 0000000..bfe2ead
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2022 EfficiOS, inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_GLIB_UP_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_GLIB_UP_HPP
+
+#include <memory>
+
+#include <glib.h>
+
+namespace bt2c {
+namespace internal {
+
+struct GCharDeleter final
+{
+    void operator()(gchar * const p) noexcept
+    {
+        g_free(p);
+    }
+};
+
+} /* namespace internal */
+
+using GCharUP = std::unique_ptr<gchar, internal::GCharDeleter>;
+
+namespace internal {
+
+struct GStringDeleter final
+{
+    void operator()(GString * const str)
+    {
+        g_string_free(str, TRUE);
+    }
+};
+
+} /* namespace internal */
+
+using GStringUP = std::unique_ptr<GString, internal::GStringDeleter>;
+
+namespace internal {
+
+struct GDirDeleter final
+{
+    void operator()(GDir * const dir)
+    {
+        g_dir_close(dir);
+    }
+};
+
+} /* namespace internal */
+
+using GDirUP = std::unique_ptr<GDir, internal::GDirDeleter>;
+
+namespace internal {
+
+struct GMappedFileDeleter final
+{
+    void operator()(GMappedFile * const f)
+    {
+        g_mapped_file_unref(f);
+    }
+};
+
+} /* namespace internal */
+
+using GMappedFileUP = std::unique_ptr<GMappedFile, internal::GMappedFileDeleter>;
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_GLIB_UP_HPP */
diff --git a/src/cpp-common/bt2c/libc-up.hpp b/src/cpp-common/bt2c/libc-up.hpp
new file mode 100644 (file)
index 0000000..fa7468f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2022 EfficiOS, inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_LIBC_UP_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_LIBC_UP_HPP
+
+#include <cstdio>
+#include <memory>
+
+namespace bt2c {
+namespace internal {
+
+struct FileCloserDeleter
+{
+    void operator()(std::FILE * const f) noexcept
+    {
+        std::fclose(f);
+    }
+};
+
+} /* namespace internal */
+
+using FileUP = std::unique_ptr<std::FILE, internal::FileCloserDeleter>;
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_LIBC_UP_HPP */
diff --git a/src/cpp-common/bt2c/logging.hpp b/src/cpp-common/bt2c/logging.hpp
new file mode 100644 (file)
index 0000000..df5cb49
--- /dev/null
@@ -0,0 +1,1032 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Philippe Proulx <pproulx@efficios.com>
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_LOGGING_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_LOGGING_HPP
+
+#include <cstring>
+#include <iterator>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <glib.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "cpp-common/bt2/private-query-executor.hpp"
+#include "cpp-common/bt2/self-component-class.hpp"
+#include "cpp-common/bt2/self-component-port.hpp"
+#include "cpp-common/bt2/self-message-iterator.hpp"
+#include "cpp-common/bt2s/optional.hpp"
+#include "cpp-common/vendor/fmt/core.h"
+#include "cpp-common/vendor/wise-enum/wise_enum.h"
+#include "logging/log-api.h"
+
+namespace bt2c {
+
+/*
+ * A logger contains an actor (self component class, self component,
+ * self message iterator, or simple module name), a current logging
+ * level, and a logging tag.
+ *
+ * It offers the log(), logMem(), logErrno(), logErrorAndThrow(),
+ * logErrorAndRethrow(), logErrorErrnoAndThrow(), and
+ * logErrorErrnoAndRethrow() method templates to log using a given
+ * level, optionally append a cause to the error of the current thread
+ * using the correct actor, and optionally throw or rethrow.
+ *
+ * The methods above expect a format string and zero or more arguments
+ * to be formatted with fmt::format().
+ */
+class Logger final
+{
+public:
+    /* clang-format off */
+
+    /* Available log levels */
+    WISE_ENUM_CLASS_MEMBER(Level,
+        (Trace, BT_LOG_TRACE),
+        (Debug, BT_LOG_DEBUG),
+        (Info, BT_LOG_INFO),
+        (Warning, BT_LOG_WARNING),
+        (Error, BT_LOG_ERROR),
+        (Fatal, BT_LOG_FATAL),
+        (None, BT_LOG_NONE));
+
+    /* clang-format on */
+
+    /*
+     * Builds a logger from the self component class `selfCompCls` using
+     * the tag `tag` and the logging level of `privQueryExec`.
+     */
+    explicit Logger(const bt2::SelfComponentClass selfCompCls,
+                    const bt2::PrivateQueryExecutor privQueryExec, std::string tag) noexcept :
+        _mSelfCompCls {selfCompCls},
+        _mLevel {static_cast<Level>(privQueryExec.loggingLevel())}, _mTag {std::move(tag)}
+    {
+    }
+
+    /*
+     * Builds a logger from the self component `selfComp` using the tag
+     * `tag`.
+     */
+    explicit Logger(const bt2::SelfComponent selfComp, std::string tag) noexcept :
+        _mSelfComp {selfComp}, _mLevel {static_cast<Level>(selfComp.loggingLevel())}, _mTag {
+                                                                                          std::move(
+                                                                                              tag)}
+    {
+    }
+
+    /*
+     * Builds a logger from the self source component `selfComp` using
+     * the tag `tag`.
+     */
+    explicit Logger(const bt2::SelfSourceComponent selfComp, std::string tag) noexcept :
+        Logger {
+            bt2::SelfComponent {bt_self_component_source_as_self_component(selfComp.libObjPtr())},
+            std::move(tag)}
+    {
+    }
+
+    /*
+     * Builds a logger from the self filter component `selfComp` using
+     * the tag `tag`.
+     */
+    explicit Logger(const bt2::SelfFilterComponent selfComp, std::string tag) noexcept :
+        Logger {
+            bt2::SelfComponent {bt_self_component_filter_as_self_component(selfComp.libObjPtr())},
+            std::move(tag)}
+    {
+    }
+
+    /*
+     * Builds a logger from the self sink component `selfComp` using the
+     * tag `tag`.
+     */
+    explicit Logger(const bt2::SelfSinkComponent selfComp, std::string tag) noexcept :
+        Logger {bt2::SelfComponent {bt_self_component_sink_as_self_component(selfComp.libObjPtr())},
+                std::move(tag)}
+    {
+    }
+
+    /*
+     * Builds a logger from the self message iterator `selfMsgIter`
+     * using the tag `tag`.
+     */
+    explicit Logger(const bt2::SelfMessageIterator selfMsgIter, std::string tag) noexcept :
+        _mSelfMsgIter {selfMsgIter},
+        _mLevel {static_cast<Level>(selfMsgIter.component().loggingLevel())}, _mTag {std::move(tag)}
+    {
+    }
+
+    /*
+     * Builds a logger from the module named `moduleName` using the tag
+     * `tag` and logging level `logLevel`.
+     */
+    explicit Logger(std::string moduleName, std::string tag, const Level logLevel) noexcept :
+        _mModuleName {std::move(moduleName)}, _mLevel {logLevel}, _mTag {std::move(tag)}
+    {
+    }
+
+    /*
+     * Builds a logger from another logger `other` using the new tag
+     * `newTag`.
+     */
+    explicit Logger(const Logger& other, std::string newTag) :
+        _mSelfCompCls {other._mSelfCompCls}, _mSelfComp {other._mSelfComp},
+        _mSelfMsgIter {other._mSelfMsgIter},
+        _mModuleName {other._mModuleName}, _mLevel {other._mLevel}, _mTag {std::move(newTag)}
+    {
+    }
+
+    /*
+     * Current logging level.
+     */
+    Level level() const noexcept
+    {
+        return _mLevel;
+    }
+
+    /*
+     * Current logging level converted to a `bt_log_level` value.
+     *
+     * For legacy code.
+     */
+    bt_log_level cLevel() const noexcept
+    {
+        return static_cast<bt_log_level>(_mLevel);
+    }
+
+    /*
+     * Whether or not this logger would log at the level `level`.
+     */
+    bool wouldLog(const Level level) const noexcept
+    {
+        return BT_LOG_ON_CUR_LVL(static_cast<int>(level), static_cast<int>(_mLevel));
+    }
+
+    /*
+     * Whether or not this logger would log at the trace level.
+     */
+    bool wouldLogT() const noexcept
+    {
+        return this->wouldLog(Level::Trace);
+    }
+
+    /*
+     * Whether or not this logger would log at the debug level.
+     */
+    bool wouldLogD() const noexcept
+    {
+        return this->wouldLog(Level::Debug);
+    }
+
+    /*
+     * Whether or not this logger would log at the info level.
+     */
+    bool wouldLogI() const noexcept
+    {
+        return this->wouldLog(Level::Info);
+    }
+
+    /*
+     * Whether or not this logger would log at the warning level.
+     */
+    bool wouldLogW() const noexcept
+    {
+        return this->wouldLog(Level::Warning);
+    }
+
+    /*
+     * Whether or not this logger would log at the error level.
+     */
+    bool wouldLogE() const noexcept
+    {
+        return this->wouldLog(Level::Error);
+    }
+
+    /*
+     * Whether or not this logger would log at the fatal level.
+     */
+    bool wouldLogF() const noexcept
+    {
+        return this->wouldLog(Level::Fatal);
+    }
+
+    /*
+     * Logging tag.
+     */
+    const std::string& tag() const noexcept
+    {
+        return _mTag;
+    }
+
+    /*
+     * Self component class actor, or `bt2s::nullopt` if none.
+     */
+    const bt2s::optional<bt2::SelfComponentClass>& selfCompCls() const noexcept
+    {
+        return _mSelfCompCls;
+    }
+
+    /*
+     * Self component actor, or `bt2s::nullopt` if none.
+     */
+    const bt2s::optional<bt2::SelfComponent>& selfComp() const noexcept
+    {
+        return _mSelfComp;
+    }
+
+    /*
+     * Self message iterator actor, or `bt2s::nullopt` if none.
+     */
+    const bt2s::optional<bt2::SelfMessageIterator>& selfMsgIter() const noexcept
+    {
+        return _mSelfMsgIter;
+    }
+
+    /*
+     * Name of module actor, or `bt2s::nullopt` if none.
+     */
+    const bt2s::optional<std::string>& moduleName() const noexcept
+    {
+        return _mModuleName;
+    }
+
+private:
+    struct _StdLogWriter final
+    {
+        static void write(const char * const fileName, const char * const funcName,
+                          const unsigned lineNo, const Level level, const char * const tag,
+                          const void *, unsigned int, const char * const initMsg,
+                          const char * const msg) noexcept
+        {
+            BT_ASSERT_DBG(initMsg && std::strcmp(initMsg, "") == 0);
+            bt_log_write(fileName, funcName, lineNo, static_cast<bt_log_level>(level), tag, msg);
+        }
+    };
+
+public:
+    /*
+     * Logs using the level `LevelV`.
+     *
+     * This method forwards `fmt` and `args` to fmt::format() to create
+     * the log message.
+     *
+     * If `AppendCauseV` is true, this method also appends a cause to
+     * the error of the current thread using the same message.
+     */
+    template <Level LevelV, bool AppendCauseV, typename... ArgTs>
+    void log(const char * const fileName, const char * const funcName, const unsigned int lineNo,
+             const char * const fmt, ArgTs&&...args) const
+    {
+        this->_log<_StdLogWriter, LevelV, AppendCauseV>(fileName, funcName, lineNo, nullptr, 0, "",
+                                                        fmt, std::forward<ArgTs>(args)...);
+    }
+
+    /*
+     * Logs `msg` using the level `LevelV`.
+     *
+     * If `AppendCauseV` is true, this method also appends a cause to
+     * the error of the current thread using the same message.
+     */
+    template <Level LevelV, bool AppendCauseV>
+    void logStr(const char * const fileName, const char * const funcName, const unsigned int lineNo,
+                const char * const msg) const
+    {
+        this->_logStr<_StdLogWriter, LevelV, AppendCauseV>(fileName, funcName, lineNo, nullptr, 0,
+                                                           "", msg);
+    }
+
+    /*
+     * Like log() with the `Level::Error` level, but also throws a
+     * default-constructed instance of `ExcT`.
+     */
+    template <bool AppendCauseV, typename ExcT, typename... ArgTs>
+    [[noreturn]] void logErrorAndThrow(const char * const fileName, const char * const funcName,
+                                       const unsigned int lineNo, const char * const fmt,
+                                       ArgTs&&...args) const
+    {
+        this->log<Level::Error, AppendCauseV>(fileName, funcName, lineNo, fmt,
+                                              std::forward<ArgTs>(args)...);
+        throw ExcT {};
+    }
+
+    /*
+     * Like logStr() with the `Level::Error` level, but also throws a
+     * default-constructed instance of `ExcT`.
+     */
+    template <bool AppendCauseV, typename ExcT>
+    [[noreturn]] void logErrorStrAndThrow(const char * const fileName, const char * const funcName,
+                                          const unsigned int lineNo, const char * const msg) const
+    {
+        this->logStr<Level::Error, AppendCauseV>(fileName, funcName, lineNo, msg);
+        throw ExcT {};
+    }
+
+    /*
+     * Like log() with the `Level::Error` level, but also rethrows.
+     */
+    template <bool AppendCauseV, typename... ArgTs>
+    [[noreturn]] void logErrorAndRethrow(const char * const fileName, const char * const funcName,
+                                         const unsigned int lineNo, const char * const fmt,
+                                         ArgTs&&...args) const
+    {
+        this->log<Level::Error, AppendCauseV>(fileName, funcName, lineNo, fmt,
+                                              std::forward<ArgTs>(args)...);
+        throw;
+    }
+
+    /*
+     * Like logStr() with the `Level::Error` level, but also rethrows.
+     */
+    template <bool AppendCauseV>
+    [[noreturn]] void logErrorStrAndRethrow(const char * const fileName,
+                                            const char * const funcName, const unsigned int lineNo,
+                                            const char * const msg) const
+    {
+        this->logStr<Level::Error, AppendCauseV>(fileName, funcName, lineNo, msg);
+        throw;
+    }
+
+private:
+    struct _InitMsgLogWriter final
+    {
+        static void write(const char * const fileName, const char * const funcName,
+                          const unsigned lineNo, const Level level, const char * const tag,
+                          const void *, unsigned int, const char * const initMsg,
+                          const char * const msg) noexcept
+        {
+            bt_log_write_printf(funcName, fileName, lineNo, static_cast<bt_log_level>(level), tag,
+                                "%s%s", initMsg, msg);
+        }
+    };
+
+public:
+    /*
+     * Logs the message of `errno` using the level `LevelV`.
+     *
+     * The log message starts with `initMsg`, is followed with the
+     * message for `errno`, and then with what fmt::format() creates
+     * given `fmt` and `args`.
+     *
+     * If `AppendCauseV` is true, this method also appends a cause to
+     * the error of the current thread using the same message.
+     */
+    template <Level LevelV, bool AppendCauseV, typename... ArgTs>
+    void logErrno(const char * const fileName, const char * const funcName,
+                  const unsigned int lineNo, const char * const initMsg, const char * const fmt,
+                  ArgTs&&...args) const
+    {
+        this->_log<_InitMsgLogWriter, LevelV, AppendCauseV>(fileName, funcName, lineNo, nullptr, 0,
+                                                            this->_errnoIntroStr(initMsg).c_str(),
+                                                            fmt, std::forward<ArgTs>(args)...);
+    }
+
+    /*
+     * Logs the message of `errno` using the level `LevelV`.
+     *
+     * The log message starts with `initMsg`, is followed with the
+     * message for `errno`, and then with `msg`.
+     *
+     * If `AppendCauseV` is true, this method also appends a cause to
+     * the error of the current thread using the same message.
+     */
+    template <Level LevelV, bool AppendCauseV>
+    void logErrnoStr(const char * const fileName, const char * const funcName,
+                     const unsigned int lineNo, const char * const initMsg,
+                     const char * const msg) const
+    {
+        this->_logStr<_InitMsgLogWriter, LevelV, AppendCauseV>(
+            fileName, funcName, lineNo, nullptr, 0, this->_errnoIntroStr(initMsg).c_str(), msg);
+    }
+
+    /*
+     * Like logErrno() with the `Level::Error` level, but also throws a
+     * default-constructed instance of `ExcT`.
+     */
+    template <bool AppendCauseV, typename ExcT, typename... ArgTs>
+    [[noreturn]] void logErrorErrnoAndThrow(const char * const fileName,
+                                            const char * const funcName, const unsigned int lineNo,
+                                            const char * const initMsg, const char * const fmt,
+                                            ArgTs&&...args) const
+    {
+        this->logErrno<Level::Error, AppendCauseV>(fileName, funcName, lineNo, initMsg, fmt,
+                                                   std::forward<ArgTs>(args)...);
+        throw ExcT {};
+    }
+
+    /*
+     * Like logErrnoStr() with the `Level::Error` level, but also throws
+     * a default-constructed instance of `ExcT`.
+     */
+    template <bool AppendCauseV, typename ExcT>
+    [[noreturn]] void
+    logErrorErrnoStrAndThrow(const char * const fileName, const char * const funcName,
+                             const unsigned int lineNo, const char * const initMsg,
+                             const char * const msg) const
+    {
+        this->logErrnoStr<Level::Error, AppendCauseV>(fileName, funcName, lineNo, initMsg, msg);
+        throw ExcT {};
+    }
+
+    /*
+     * Like logErrno() with the `Level::Error` level, but also rethrows.
+     */
+    template <bool AppendCauseV, typename... ArgTs>
+    [[noreturn]] void logErrorErrnoAndRethrow(const char * const fileName,
+                                              const char * const funcName,
+                                              const unsigned int lineNo, const char * const initMsg,
+                                              const char * const fmt, ArgTs&&...args) const
+    {
+        this->logErrno<Level::Error, AppendCauseV>(fileName, funcName, lineNo, initMsg, fmt,
+                                                   std::forward<ArgTs>(args)...);
+        throw;
+    }
+
+    /*
+     * Like logErrnoStr() with the `Level::Error` level, but also
+     * rethrows.
+     */
+    template <bool AppendCauseV>
+    [[noreturn]] void
+    logErrorErrnoStrAndRethrow(const char * const fileName, const char * const funcName,
+                               const unsigned int lineNo, const char * const initMsg,
+                               const char * const msg) const
+    {
+        this->logErrnoStr<Level::Error, AppendCauseV>(fileName, funcName, lineNo, initMsg, msg);
+        throw;
+    }
+
+private:
+    struct _MemLogWriter final
+    {
+        static void write(const char * const fileName, const char * const funcName,
+                          const unsigned lineNo, const Level level, const char * const tag,
+                          const void * const memData, const unsigned int memLen, const char *,
+                          const char * const msg) noexcept
+        {
+            bt_log_write_mem(funcName, fileName, lineNo, static_cast<bt_log_level>(level), tag,
+                             memData, memLen, msg);
+        }
+    };
+
+public:
+    /*
+     * Logs memory data using the level `LevelV`.
+     *
+     * This method forwards `fmt` and `args` to fmt::format() to create
+     * the log message.
+     */
+    template <Level LevelV, typename... ArgTs>
+    void logMem(const char * const fileName, const char * const funcName, const unsigned int lineNo,
+                const void * const memData, const unsigned int memLen, const char * const fmt,
+                ArgTs&&...args) const
+    {
+        this->_log<_MemLogWriter, LevelV, false>(fileName, funcName, lineNo, memData, memLen, "",
+                                                 fmt, std::forward<ArgTs>(args)...);
+    }
+
+    /*
+     * Logs memory data using the level `LevelV`, starting with the
+     * message `msg`.
+     */
+    template <Level LevelV>
+    void logMemStr(const char * const fileName, const char * const funcName,
+                   const unsigned int lineNo, const void * const memData, const unsigned int memLen,
+                   const char * const msg) const
+    {
+        this->_logStr<_MemLogWriter, LevelV, false>(fileName, funcName, lineNo, memData, memLen, "",
+                                                    msg);
+    }
+
+private:
+    /*
+     * Formats a log message with fmt::format() given `fmt` and `args`,
+     * and then forwards everything to _logStr().
+     */
+    template <typename LogWriterT, Level LevelV, bool AppendCauseV, typename... ArgTs>
+    void _log(const char * const fileName, const char * const funcName, const unsigned int lineNo,
+              const void * const memData, const std::size_t memLen, const char * const initMsg,
+              const char * const fmt, ArgTs&&...args) const
+    {
+        /* Only format arguments if logging or appending an error cause */
+        if (G_UNLIKELY(this->wouldLog(LevelV) || AppendCauseV)) {
+            /*
+             * Format arguments to our buffer (fmt::format_to() doesn't
+             * append a null character).
+             */
+            _mBuf.clear();
+            fmt::format_to(std::back_inserter(_mBuf), fmt, std::forward<ArgTs>(args)...);
+            _mBuf.push_back('\0');
+        }
+
+        this->_logStr<LogWriterT, LevelV, AppendCauseV>(fileName, funcName, lineNo, memData, memLen,
+                                                        initMsg, _mBuf.data());
+    }
+
+    /*
+     * Calls LogWriterT::write() with its arguments to log using the
+     * level `LevelV`.
+     *
+     * If `AppendCauseV` is true, this method also appends a cause to
+     * the error of the current thread using the concatenation of
+     * `initMsg` and `msg` as the message.
+     */
+    template <typename LogWriterT, Level LevelV, bool AppendCauseV>
+    void _logStr(const char * const fileName, const char * const funcName,
+                 const unsigned int lineNo, const void * const memData, const std::size_t memLen,
+                 const char * const initMsg, const char * const msg) const
+    {
+        /* Initial message and main message are required */
+        BT_ASSERT(initMsg);
+        BT_ASSERT(msg);
+
+        /* Log if needed */
+        if (this->wouldLog(LevelV)) {
+            LogWriterT::write(fileName, funcName, lineNo, LevelV, _mTag.data(), memData, memLen,
+                              initMsg, msg);
+        }
+
+        /* Append an error cause if needed */
+        if (AppendCauseV) {
+            if (_mSelfMsgIter) {
+                bt_current_thread_error_append_cause_from_message_iterator(
+                    _mSelfMsgIter->libObjPtr(), fileName, lineNo, "%s%s", initMsg, msg);
+            } else if (_mSelfComp) {
+                bt_current_thread_error_append_cause_from_component(
+                    _mSelfComp->libObjPtr(), fileName, lineNo, "%s%s", initMsg, msg);
+            } else if (_mSelfCompCls) {
+                bt_current_thread_error_append_cause_from_component_class(
+                    _mSelfCompCls->libObjPtr(), fileName, lineNo, "%s%s", initMsg, msg);
+            } else {
+                BT_ASSERT(_mModuleName);
+                bt_current_thread_error_append_cause_from_unknown(_mModuleName->data(), fileName,
+                                                                  lineNo, "%s%s", initMsg, msg);
+            }
+        }
+    }
+
+    static std::string _errnoIntroStr(const char * const initMsg)
+    {
+        BT_ASSERT(errno != 0);
+        return fmt::format("{}: {}", initMsg, g_strerror(errno));
+    }
+
+    /* Exactly one of the following four members has a value */
+    bt2s::optional<bt2::SelfComponentClass> _mSelfCompCls;
+    bt2s::optional<bt2::SelfComponent> _mSelfComp;
+    bt2s::optional<bt2::SelfMessageIterator> _mSelfMsgIter;
+    bt2s::optional<std::string> _mModuleName;
+
+    /* Current logging level */
+    Level _mLevel;
+
+    /* Logging tag */
+    std::string _mTag;
+
+    /* Formatting buffer */
+    mutable std::vector<char> _mBuf;
+};
+
+/*
+ * Returns `s` if it's not `nullptr`, or the `(null)` string otherwise.
+ */
+inline const char *maybeNull(const char * const s) noexcept
+{
+    return s ? s : "(null)";
+}
+
+} /* namespace bt2c */
+
+/* Internal: default logger name */
+#define _BT_CPPLOG_DEF_LOGGER _mLogger
+
+/*
+ * Calls log() on `_logger` to log using the level `_lvl`.
+ */
+#define BT_CPPLOG_EX(_lvl, _logger, _fmt, ...)                                                     \
+    do {                                                                                           \
+        if (G_UNLIKELY((_logger).wouldLog(_lvl))) {                                                \
+            (_logger).log<(_lvl), false>(__FILE__, __func__, __LINE__, (_fmt), ##__VA_ARGS__);     \
+        }                                                                                          \
+    } while (0)
+
+/*
+ * BT_CPPLOG_EX() with specific logging levels.
+ */
+#define BT_CPPLOGT_SPEC(_logger, _fmt, ...)                                                        \
+    BT_CPPLOG_EX(bt2c::Logger::Level::Trace, (_logger), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGD_SPEC(_logger, _fmt, ...)                                                        \
+    BT_CPPLOG_EX(bt2c::Logger::Level::Debug, (_logger), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGI_SPEC(_logger, _fmt, ...)                                                        \
+    BT_CPPLOG_EX(bt2c::Logger::Level::Info, (_logger), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGW_SPEC(_logger, _fmt, ...)                                                        \
+    BT_CPPLOG_EX(bt2c::Logger::Level::Warning, (_logger), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGE_SPEC(_logger, _fmt, ...)                                                        \
+    BT_CPPLOG_EX(bt2c::Logger::Level::Error, (_logger), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGF_SPEC(_logger, _fmt, ...)                                                        \
+    BT_CPPLOG_EX(bt2c::Logger::Level::Fatal, (_logger), (_fmt), ##__VA_ARGS__)
+
+/*
+ * BT_CPPLOG_EX() with specific logging levels and using the default
+ * logger.
+ */
+#define BT_CPPLOGT(_fmt, ...) BT_CPPLOGT_SPEC(_BT_CPPLOG_DEF_LOGGER, (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGD(_fmt, ...) BT_CPPLOGD_SPEC(_BT_CPPLOG_DEF_LOGGER, (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGI(_fmt, ...) BT_CPPLOGI_SPEC(_BT_CPPLOG_DEF_LOGGER, (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGW(_fmt, ...) BT_CPPLOGW_SPEC(_BT_CPPLOG_DEF_LOGGER, (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGE(_fmt, ...) BT_CPPLOGE_SPEC(_BT_CPPLOG_DEF_LOGGER, (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGF(_fmt, ...) BT_CPPLOGF_SPEC(_BT_CPPLOG_DEF_LOGGER, (_fmt), ##__VA_ARGS__)
+
+/*
+ * Calls logStr() on `_logger` to log using the level `_lvl`.
+ */
+#define BT_CPPLOG_STR_EX(_lvl, _logger, _msg)                                                      \
+    (_logger).logStr<(_lvl), false>(__FILE__, __func__, __LINE__, (_msg))
+
+/*
+ * BT_CPPLOG_STR_EX() with specific logging levels.
+ */
+#define BT_CPPLOGT_STR_SPEC(_logger, _msg)                                                         \
+    BT_CPPLOG_STR_EX(bt2c::Logger::Level::Trace, (_logger), (_msg))
+#define BT_CPPLOGD_STR_SPEC(_logger, _msg)                                                         \
+    BT_CPPLOG_STR_EX(bt2c::Logger::Level::Debug, (_logger), (_msg))
+#define BT_CPPLOGI_STR_SPEC(_logger, _msg)                                                         \
+    BT_CPPLOG_STR_EX(bt2c::Logger::Level::Info, (_logger), (_msg))
+#define BT_CPPLOGW_STR_SPEC(_logger, _msg)                                                         \
+    BT_CPPLOG_STR_EX(bt2c::Logger::Level::Warning, (_logger), (_msg))
+#define BT_CPPLOGE_STR_SPEC(_logger, _msg)                                                         \
+    BT_CPPLOG_STR_EX(bt2c::Logger::Level::Error, (_logger), (_msg))
+#define BT_CPPLOGF_STR_SPEC(_logger, _msg)                                                         \
+    BT_CPPLOG_STR_EX(bt2c::Logger::Level::Fatal, (_logger), (_msg))
+
+/*
+ * BT_CPPLOG_STR_EX() with specific logging levels and using the default
+ * logger.
+ */
+#define BT_CPPLOGT_STR(_msg) BT_CPPLOGT_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_msg))
+#define BT_CPPLOGD_STR(_msg) BT_CPPLOGD_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_msg))
+#define BT_CPPLOGI_STR(_msg) BT_CPPLOGI_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_msg))
+#define BT_CPPLOGW_STR(_msg) BT_CPPLOGW_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_msg))
+#define BT_CPPLOGE_STR(_msg) BT_CPPLOGE_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_msg))
+#define BT_CPPLOGF_STR(_msg) BT_CPPLOGF_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_msg))
+
+/*
+ * Calls logMem() on `_logger` to log using the level `_lvl`.
+ */
+#define BT_CPPLOG_MEM_EX(_lvl, _logger, _mem_data, _mem_len, _fmt, ...)                            \
+    do {                                                                                           \
+        if (G_UNLIKELY((_logger).wouldLog(_lvl))) {                                                \
+            (_logger).logMem<(_lvl)>(__FILE__, __func__, __LINE__, (_mem_data), (_mem_len),        \
+                                     (_fmt), ##__VA_ARGS__);                                       \
+        }                                                                                          \
+    } while (0)
+
+/*
+ * BT_CPPLOG_MEM_EX() with specific logging levels.
+ */
+#define BT_CPPLOGT_MEM_SPEC(_logger, _mem_data, _mem_len, _fmt, ...)                               \
+    BT_CPPLOG_MEM_EX(bt2c::Logger::Level::Trace, (_logger), (_mem_data), (_mem_len), (_fmt),       \
+                     ##__VA_ARGS__)
+#define BT_CPPLOGD_MEM_SPEC(_logger, _mem_data, _mem_len, _fmt, ...)                               \
+    BT_CPPLOG_MEM_EX(bt2c::Logger::Level::Debug, (_logger), (_mem_data), (_mem_len), (_fmt),       \
+                     ##__VA_ARGS__)
+#define BT_CPPLOGI_MEM_SPEC(_logger, _mem_data, _mem_len, _fmt, ...)                               \
+    BT_CPPLOG_MEM_EX(bt2c::Logger::Level::Info, (_logger), (_mem_data), (_mem_len), (_fmt),        \
+                     ##__VA_ARGS__)
+#define BT_CPPLOGW_MEM_SPEC(_logger, _mem_data, _mem_len, _fmt, ...)                               \
+    BT_CPPLOG_MEM_EX(bt2c::Logger::Level::Warning, (_logger), (_mem_data), (_mem_len), (_fmt),     \
+                     ##__VA_ARGS__)
+#define BT_CPPLOGE_MEM_SPEC(_logger, _mem_data, _mem_len, _fmt, ...)                               \
+    BT_CPPLOG_MEM_EX(bt2c::Logger::Level::Error, (_logger), (_mem_data), (_mem_len), (_fmt),       \
+                     ##__VA_ARGS__)
+#define BT_CPPLOGF_MEM_SPEC(_logger, _mem_data, _mem_len, _fmt, ...)                               \
+    BT_CPPLOG_MEM_EX(bt2c::Logger::Level::Fatal, (_logger), (_mem_data), (_mem_len), (_fmt),       \
+                     ##__VA_ARGS__)
+
+/*
+ * BT_CPPLOG_MEM_EX() with specific logging levels and using the default
+ * logger.
+ */
+#define BT_CPPLOGT_MEM(_mem_data, _mem_len, _fmt, ...)                                             \
+    BT_CPPLOGT_MEM_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGD_MEM(_mem_data, _mem_len, _fmt, ...)                                             \
+    BT_CPPLOGD_MEM_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGI_MEM(_mem_data, _mem_len, _fmt, ...)                                             \
+    BT_CPPLOGI_MEM_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGW_MEM(_mem_data, _mem_len, _fmt, ...)                                             \
+    BT_CPPLOGW_MEM_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGE_MEM(_mem_data, _mem_len, _fmt, ...)                                             \
+    BT_CPPLOGE_MEM_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGF_MEM(_mem_data, _mem_len, _fmt, ...)                                             \
+    BT_CPPLOGF_MEM_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+
+/*
+ * Calls logMemStr() on `_logger` to log using the level `_lvl`.
+ */
+#define BT_CPPLOG_MEM_STR_EX(_lvl, _logger, _mem_data, _mem_len, _msg)                             \
+    (_logger).logMemStr<(_lvl)>(__FILE__, __func__, __LINE__, (_mem_data), (_mem_len), (_msg))
+
+/*
+ * BT_CPPLOG_MEM_STR_EX() with specific logging levels.
+ */
+#define BT_CPPLOGT_MEM_STR_SPEC(_logger, _mem_data, _mem_len, _msg)                                \
+    BT_CPPLOG_MEM_STR_EX(bt2c::Logger::Level::TRACE, (_logger), (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGD_MEM_STR_SPEC(_logger, _mem_data, _mem_len, _msg)                                \
+    BT_CPPLOG_MEM_STR_EX(bt2c::Logger::Level::DEBUG, (_logger), (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGI_MEM_STR_SPEC(_logger, _mem_data, _mem_len, _msg)                                \
+    BT_CPPLOG_MEM_STR_EX(bt2c::Logger::Level::INFO, (_logger), (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGW_MEM_STR_SPEC(_logger, _mem_data, _mem_len, _msg)                                \
+    BT_CPPLOG_MEM_STR_EX(bt2c::Logger::Level::WARNING, (_logger), (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGE_MEM_STR_SPEC(_logger, _mem_data, _mem_len, _msg)                                \
+    BT_CPPLOG_MEM_STR_EX(bt2c::Logger::Level::Error, (_logger), (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGF_MEM_STR_SPEC(_logger, _mem_data, _mem_len, _msg)                                \
+    BT_CPPLOG_MEM_STR_EX(bt2c::Logger::Level::FATAL, (_logger), (_mem_data), (_mem_len), (_msg))
+
+/*
+ * BT_CPPLOG_MEM_STR_EX() with specific logging levels and using the
+ * default logger.
+ */
+#define BT_CPPLOGT_MEM_STR(_mem_data, _mem_len, _msg)                                              \
+    BT_CPPLOGT_MEM_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGD_MEM_STR(_mem_data, _mem_len, _msg)                                              \
+    BT_CPPLOGD_MEM_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGI_MEM_STR(_mem_data, _mem_len, _msg)                                              \
+    BT_CPPLOGI_MEM_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGW_MEM_STR(_mem_data, _mem_len, _msg)                                              \
+    BT_CPPLOGW_MEM_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGE_MEM_STR(_mem_data, _mem_len, _msg)                                              \
+    BT_CPPLOGE_MEM_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_msg))
+#define BT_CPPLOGF_MEM_STR(_mem_data, _mem_len, _msg)                                              \
+    BT_CPPLOGF_MEM_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_mem_data), (_mem_len), (_msg))
+
+/*
+ * Calls logErrno() on `_logger` to log using the level `_lvl` and
+ * initial message `_init_msg`.
+ */
+#define BT_CPPLOG_ERRNO_EX(_lvl, _logger, _init_msg, _fmt, ...)                                    \
+    do {                                                                                           \
+        if (G_UNLIKELY((_logger).wouldLog(_lvl))) {                                                \
+            (_logger).logErrno<(_lvl), false>(__FILE__, __func__, __LINE__, (_init_msg), (_fmt),   \
+                                              ##__VA_ARGS__);                                      \
+        }                                                                                          \
+    } while (0)
+
+/*
+ * BT_CPPLOG_ERRNO_EX() with specific logging levels.
+ */
+#define BT_CPPLOGT_ERRNO_SPEC(_logger, _init_msg, _fmt, ...)                                       \
+    BT_CPPLOG_ERRNO_EX(bt2c::Logger::Level::Trace, (_logger), (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGD_ERRNO_SPEC(_logger, _init_msg, _fmt, ...)                                       \
+    BT_CPPLOG_ERRNO_EX(bt2c::Logger::Level::Debug, (_logger), (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGI_ERRNO_SPEC(_logger, _init_msg, _fmt, ...)                                       \
+    BT_CPPLOG_ERRNO_EX(bt2c::Logger::Level::Info, (_logger), (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGW_ERRNO_SPEC(_logger, _init_msg, _fmt, ...)                                       \
+    BT_CPPLOG_ERRNO_EX(bt2c::Logger::Level::Warning, (_logger), (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGE_ERRNO_SPEC(_logger, _init_msg, _fmt, ...)                                       \
+    BT_CPPLOG_ERRNO_EX(bt2c::Logger::Level::Error, (_logger), (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGF_ERRNO_SPEC(_logger, _init_msg, _fmt, ...)                                       \
+    BT_CPPLOG_ERRNO_EX(bt2c::Logger::Level::Fatal, (_logger), (_init_msg), (_fmt), ##__VA_ARGS__)
+
+/*
+ * BT_CPPLOG_ERRNO_EX() with specific logging levels and using the
+ * default logger.
+ */
+#define BT_CPPLOGT_ERRNO(_init_msg, _fmt, ...)                                                     \
+    BT_CPPLOGT_ERRNO_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGD_ERRNO(_init_msg, _fmt, ...)                                                     \
+    BT_CPPLOGD_ERRNO_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGI_ERRNO(_init_msg, _fmt, ...)                                                     \
+    BT_CPPLOGI_ERRNO_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGW_ERRNO(_init_msg, _fmt, ...)                                                     \
+    BT_CPPLOGW_ERRNO_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGE_ERRNO(_init_msg, _fmt, ...)                                                     \
+    BT_CPPLOGE_ERRNO_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_fmt), ##__VA_ARGS__)
+#define BT_CPPLOGF_ERRNO(_init_msg, _fmt, ...)                                                     \
+    BT_CPPLOGF_ERRNO_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_fmt), ##__VA_ARGS__)
+
+/*
+ * Calls logErrnoStr() on `_logger` to log using the level `_lvl` and
+ * initial message `_init_msg`.
+ */
+#define BT_CPPLOG_ERRNO_STR_EX(_lvl, _logger, _init_msg, _msg)                                     \
+    (_logger).logErrnoStr<(_lvl), false>(__FILE__, __func__, __LINE__, (_init_msg), (_msg))
+
+/*
+ * BT_CPPLOG_ERRNO_STR_EX() with specific logging levels.
+ */
+#define BT_CPPLOGT_ERRNO_STR_SPEC(_logger, _init_msg, _msg)                                        \
+    BT_CPPLOG_ERRNO_STR_EX(bt2c::Logger::Level::Trace, (_logger), (_init_msg), (_msg))
+#define BT_CPPLOGD_ERRNO_STR_SPEC(_logger, _init_msg, _msg)                                        \
+    BT_CPPLOG_ERRNO_STR_EX(bt2c::Logger::Level::Debug, (_logger), (_init_msg), (_msg))
+#define BT_CPPLOGI_ERRNO_STR_SPEC(_logger, _init_msg, _msg)                                        \
+    BT_CPPLOG_ERRNO_STR_EX(bt2c::Logger::Level::Info, (_logger), (_init_msg), (_msg))
+#define BT_CPPLOGW_ERRNO_STR_SPEC(_logger, _init_msg, _msg)                                        \
+    BT_CPPLOG_ERRNO_STR_EX(bt2c::Logger::Level::Warning, (_logger), (_init_msg), (_msg))
+#define BT_CPPLOGE_ERRNO_STR_SPEC(_logger, _init_msg, _msg)                                        \
+    BT_CPPLOG_ERRNO_STR_EX(bt2c::Logger::Level::Error, (_logger), (_init_msg), (_msg))
+#define BT_CPPLOGF_ERRNO_STR_SPEC(_logger, _init_msg, _msg)                                        \
+    BT_CPPLOG_ERRNO_STR_EX(bt2c::Logger::Level::Fatal, (_logger), (_init_msg), (_msg))
+
+/*
+ * BT_CPPLOG_ERRNO_STR_EX() with specific logging levels and using the
+ * default logger.
+ */
+#define BT_CPPLOGT_ERRNO_STR(_init_msg, _msg)                                                      \
+    BT_CPPLOGT_ERRNO_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_msg))
+#define BT_CPPLOGD_ERRNO_STR(_init_msg, _msg)                                                      \
+    BT_CPPLOGD_ERRNO_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_msg))
+#define BT_CPPLOGI_ERRNO_STR(_init_msg, _msg)                                                      \
+    BT_CPPLOGI_ERRNO_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_msg))
+#define BT_CPPLOGW_ERRNO_STR(_init_msg, _msg)                                                      \
+    BT_CPPLOGW_ERRNO_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_msg))
+#define BT_CPPLOGE_ERRNO_STR(_init_msg, _msg)                                                      \
+    BT_CPPLOGE_ERRNO_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_msg))
+#define BT_CPPLOGF_ERRNO_STR(_init_msg, _msg)                                                      \
+    BT_CPPLOGF_ERRNO_STR_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_msg))
+
+/*
+ * Calls log() on `_logger` with the `Error` level to log an error and
+ * append a cause to the error of the current thread.
+ */
+#define BT_CPPLOGE_APPEND_CAUSE_SPEC(_logger, _fmt, ...)                                           \
+    (_logger).log<bt2c::Logger::Level::Error, true>(__FILE__, __func__, __LINE__, (_fmt),          \
+                                                    ##__VA_ARGS__)
+
+/*
+ * BT_CPPLOGE_APPEND_CAUSE_SPEC() using the default logger.
+ */
+#define BT_CPPLOGE_APPEND_CAUSE(_fmt, ...)                                                         \
+    BT_CPPLOGE_APPEND_CAUSE_SPEC(_BT_CPPLOG_DEF_LOGGER, (_fmt), ##__VA_ARGS__)
+
+/*
+ * Calls logStr() on `_logger` with the `Error` level to log an error and
+ * append a cause to the error of the current thread.
+ */
+#define BT_CPPLOGE_STR_APPEND_CAUSE_SPEC(_logger, _msg)                                            \
+    (_logger).logStr<bt2c::Logger::Level::Error, true>(__FILE__, __func__, __LINE__, (_msg))
+
+/*
+ * BT_CPPLOGE_STR_APPEND_CAUSE_SPEC() using the default logger.
+ */
+#define BT_CPPLOGE_STR_APPEND_CAUSE(_msg)                                                          \
+    BT_CPPLOGE_STR_APPEND_CAUSE_SPEC(_BT_CPPLOG_DEF_LOGGER, (_msg))
+
+/*
+ * Calls logErrorAndThrow() on `_logger` to log an error, append a cause
+ * to the error of the current thread, and throw an instance of
+ * `_exc_cls`.
+ */
+#define BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(_logger, _exc_cls, _fmt, ...)                       \
+    (_logger).logErrorAndThrow<true, _exc_cls>(__FILE__, __func__, __LINE__, (_fmt), ##__VA_ARGS__)
+
+/*
+ * BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC() using the default logger.
+ */
+#define BT_CPPLOGE_APPEND_CAUSE_AND_THROW(_exc_cls, _fmt, ...)                                     \
+    BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(_BT_CPPLOG_DEF_LOGGER, _exc_cls, (_fmt), ##__VA_ARGS__)
+
+/*
+ * Calls logErrorStrAndThrow() on `_logger` to log an error, append a
+ * cause to the error of the current thread, and throw an instance of
+ * `_exc_cls`.
+ */
+#define BT_CPPLOGE_STR_APPEND_CAUSE_AND_THROW_SPEC(_logger, _exc_cls, _msg)                        \
+    (_logger).logErrorStrAndThrow<true, _exc_cls>(__FILE__, __func__, __LINE__, (_msg))
+
+/*
+ * BT_CPPLOGE_STR_APPEND_CAUSE_AND_THROW_SPEC() using the default
+ * logger.
+ */
+#define BT_CPPLOGE_STR_APPEND_CAUSE_AND_THROW(_exc_cls, _msg)                                      \
+    BT_CPPLOGE_STR_APPEND_CAUSE_AND_THROW_SPEC(_BT_CPPLOG_DEF_LOGGER, _exc_cls, (_msg))
+
+/*
+ * Calls logErrorAndRethrow() on `_logger` to log an error, append a
+ * cause to the error of the current thread, and throw an instance of
+ * `_exc_cls`.
+ */
+#define BT_CPPLOGE_APPEND_CAUSE_AND_RETHROW_SPEC(_logger, _fmt, ...)                               \
+    (_logger).logErrorAndRethrow<true>(__FILE__, __func__, __LINE__, (_fmt), ##__VA_ARGS__)
+
+/*
+ * BT_CPPLOGE_APPEND_CAUSE_AND_RETHROW_SPEC() using the default logger.
+ */
+#define BT_CPPLOGE_APPEND_CAUSE_AND_RETHROW(_fmt, ...)                                             \
+    BT_CPPLOGE_APPEND_CAUSE_AND_RETHROW_SPEC(_BT_CPPLOG_DEF_LOGGER, (_fmt), ##__VA_ARGS__)
+
+/*
+ * Calls logErrorStrAndRethrow() on `_logger` to log an error, append a
+ * cause to the error of the current thread, and throw an instance of
+ * `_exc_cls`.
+ */
+#define BT_CPPLOGE_STR_APPEND_CAUSE_AND_RETHROW_SPEC(_logger, _msg)                                \
+    (_logger).logErrorStrAndRethrow<true>(__FILE__, __func__, __LINE__, (_msg))
+
+/*
+ * BT_CPPLOGE_STR_APPEND_CAUSE_AND_RETHROW_SPEC() using the default
+ * logger.
+ */
+#define BT_CPPLOGE_STR_APPEND_CAUSE_AND_RETHROW(_msg)                                              \
+    BT_CPPLOGE_STR_APPEND_CAUSE_AND_RETHROW_SPEC(_BT_CPPLOG_DEF_LOGGER, (_msg))
+
+/*
+ * Calls logErrno() on `_logger` with the `Level::Error` level to log an
+ * error and append a cause to the error of the current thread.
+ */
+#define BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(_logger, _init_msg, _fmt, ...)                          \
+    (_logger).logErrno<bt2c::Logger::Level::Error, true>(__FILE__, __func__, __LINE__,             \
+                                                         (_init_msg), (_fmt), ##__VA_ARGS__)
+
+/*
+ * BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC() using the default logger.
+ */
+#define BT_CPPLOGE_ERRNO_APPEND_CAUSE(_init_msg, _fmt, ...)                                        \
+    BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_fmt), ##__VA_ARGS__)
+
+/*
+ * Calls logErrnoStr() on `_logger` with the `Level::Error` level to log
+ * an error and append a cause to the error of the current thread.
+ */
+#define BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_SPEC(_logger, _init_msg, _msg)                           \
+    (_logger).logErrnoStr<bt2c::Logger::Level::Error, true>(__FILE__, __func__, __LINE__,          \
+                                                            (_init_msg), (_msg))
+
+/*
+ * BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_SPEC() using the default logger.
+ */
+#define BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE(_init_msg, _msg)                                         \
+    BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_msg))
+
+/*
+ * Calls logErrorErrnoAndThrow() on `_logger` to log an error, append a
+ * cause to the error of the current thread, and throw an instance of
+ * `_exc_cls`.
+ */
+#define BT_CPPLOGE_ERRNO_APPEND_CAUSE_AND_THROW_SPEC(_logger, _exc_cls, _init_msg, _fmt, ...)      \
+    (_logger).logErrorErrnoAndThrow<true, _exc_cls>(__FILE__, __func__, __LINE__, (_init_msg),     \
+                                                    (_fmt), ##__VA_ARGS__)
+
+/*
+ * BT_CPPLOGE_ERRNO_APPEND_CAUSE_AND_THROW_SPEC() using the default
+ * logger.
+ */
+#define BT_CPPLOGE_ERRNO_APPEND_CAUSE_AND_THROW(_exc_cls, _init_msg, _fmt, ...)                    \
+    BT_CPPLOGE_ERRNO_APPEND_CAUSE_AND_THROW_SPEC(_BT_CPPLOG_DEF_LOGGER, _exc_cls, (_init_msg),     \
+                                                 (_fmt), ##__VA_ARGS__)
+
+/*
+ * Calls logErrorErrnoStrAndThrow() on `_logger` to log an error, append
+ * a cause to the error of the current thread, and throw an instance of
+ * `_exc_cls`.
+ */
+#define BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_AND_THROW_SPEC(_logger, _exc_cls, _init_msg, _msg)       \
+    (_logger).logErrorErrnoStrAndThrow<true, _exc_cls>(__FILE__, __func__, __LINE__, (_init_msg),  \
+                                                       (_msg))
+
+/*
+ * BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_AND_THROW_SPEC() using the default
+ * logger.
+ */
+#define BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_AND_THROW(_exc_cls, _init_msg, _msg)                     \
+    BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_AND_THROW_SPEC(_BT_CPPLOG_DEF_LOGGER, _exc_cls, (_init_msg), \
+                                                     (_msg))
+
+/*
+ * Calls logErrorErrnoAndRethrow() on `_logger` to log an error, append
+ * a cause to the error of the current thread, and throw an instance of
+ * `_exc_cls`.
+ */
+#define BT_CPPLOGE_ERRNO_APPEND_CAUSE_AND_RETHROW_SPEC(_logger, _init_msg, _fmt, ...)              \
+    (_logger).logErrorErrnoAndRethrow<true>(__FILE__, __func__, __LINE__, (_init_msg), (_fmt),     \
+                                            ##__VA_ARGS__)
+
+/*
+ * BT_CPPLOGE_ERRNO_APPEND_CAUSE_AND_RETHROW_SPEC() using the default
+ * logger.
+ */
+#define BT_CPPLOGE_ERRNO_APPEND_CAUSE_AND_RETHROW(_init_msg, _fmt, ...)                            \
+    BT_CPPLOGE_ERRNO_APPEND_CAUSE_AND_RETHROW_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_fmt),     \
+                                                   ##__VA_ARGS__)
+
+/*
+ * Calls logErrorErrnoStrAndRethrow() on `_logger` to log an error,
+ * append a cause to the error of the current thread, and throw an
+ * instance of `_exc_cls`.
+ */
+#define BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_AND_RETHROW_SPEC(_logger, _init_msg, _msg)               \
+    (_logger).logErrorErrnoStrAndRethrow<true>(__FILE__, __func__, __LINE__, (_init_msg), (_msg))
+
+/*
+ * BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_AND_RETHROW_SPEC() using the
+ * default logger.
+ */
+#define BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_AND_RETHROW(_init_msg, _msg)                             \
+    BT_CPPLOGE_ERRNO_STR_APPEND_CAUSE_AND_RETHROW_SPEC(_BT_CPPLOG_DEF_LOGGER, (_init_msg), (_msg))
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_LOGGING_HPP */
diff --git a/src/cpp-common/bt2c/prio-heap.hpp b/src/cpp-common/bt2c/prio-heap.hpp
new file mode 100644 (file)
index 0000000..34ff0fc
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_PRIO_HEAP_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_PRIO_HEAP_HPP
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "common/assert.h"
+
+namespace bt2c {
+
+/*
+ * A templated C++ version of what used to be the `bt_heap_` C API,
+ * written by Mathieu Desnoyers, which implements an efficient heap data
+ * structure.
+ *
+ * This implements a static-sized priority heap based on CLRS,
+ * chapter 6.
+ *
+ * This version copies instances of `T` during its operations, so it's
+ * best to use with small objects such as pointers, integers, and small
+ * PODs.
+ *
+ * `T` must be default-constructible, copy-constructible, and
+ * copy-assignable.
+ *
+ * `CompT` is the type of the callable comparator. It must be possible
+ * to call an instance `comp` of `CompT` as such:
+ *
+ *     comp(a, b)
+ *
+ * `comp` accepts two different `const T&` values and returns a value
+ * contextually convertible to `bool` which must be true if `a` appears
+ * _after_ `b`.
+ *
+ * The benefit of this version over `std::priority_queue` is the
+ * replaceTop() method which you can call to remove the top (greatest)
+ * element and then insert a new one immediately afterwards with a
+ * single heap rebalance.
+ */
+template <typename T, typename CompT = std::greater<T>>
+class PrioHeap final
+{
+    static_assert(std::is_default_constructible<T>::value, "`T` is default-constructible.");
+    static_assert(std::is_copy_constructible<T>::value, "`T` is copy-constructible.");
+    static_assert(std::is_copy_assignable<T>::value, "`T` is copy-assignable.");
+
+public:
+    /*
+     * Builds a priority heap using the comparator `comp` and with an
+     * initial capacity of `cap` elements.
+     */
+    explicit PrioHeap(CompT comp, const std::size_t cap) : _mComp {std::move(comp)}
+    {
+        _mElems.reserve(cap);
+    }
+
+    /*
+     * Builds a priority heap using the comparator `comp` and with an
+     * initial capacity of zero.
+     */
+    explicit PrioHeap(CompT comp) : PrioHeap {std::move(comp), 0}
+    {
+    }
+
+    /*
+     * Builds a priority heap using a default comparator and with an
+     * initial capacity of zero.
+     */
+    explicit PrioHeap() : PrioHeap {CompT {}, 0}
+    {
+    }
+
+    /*
+     * Number of contained elements.
+     */
+    std::size_t len() const noexcept
+    {
+        return _mElems.size();
+    }
+
+    /*
+     * Whether or not this heap is empty.
+     */
+    bool isEmpty() const noexcept
+    {
+        return _mElems.empty();
+    }
+
+    /*
+     * Removes all the elements.
+     */
+    void clear()
+    {
+        _mElems.clear();
+    }
+
+    /*
+     * Current top (greatest) element (`const` version).
+     */
+    const T& top() const noexcept
+    {
+        BT_ASSERT_DBG(!this->isEmpty());
+        this->_validate();
+        return _mElems.front();
+    }
+
+    /*
+     * Current top (greatest) element.
+     */
+    T& top() noexcept
+    {
+        BT_ASSERT_DBG(!this->isEmpty());
+        this->_validate();
+        return _mElems.front();
+    }
+
+    /*
+     * Inserts a copy of the element `elem`.
+     */
+    void insert(const T& elem)
+    {
+        /* Default-construct the new one */
+        _mElems.resize(_mElems.size() + 1);
+
+        auto pos = this->len() - 1;
+
+        while (pos > 0 && this->_gt(elem, this->_parentElem(pos))) {
+            /* Move parent down until we find the right spot */
+            _mElems[pos] = this->_parentElem(pos);
+            pos = this->_parentPos(pos);
+        }
+
+        _mElems[pos] = elem;
+        this->_validate();
+    }
+
+    /*
+     * Removes the top (greatest) element.
+     *
+     * This heap must not be empty.
+     */
+    void removeTop()
+    {
+        BT_ASSERT_DBG(!this->isEmpty());
+
+        if (_mElems.size() == 1) {
+            /* Fast path for a single element */
+            _mElems.clear();
+            return;
+        }
+
+        /*
+         * Shrink, replace the current top by the previous last element,
+         * and heapify.
+         */
+        const auto lastElem = _mElems.back();
+
+        _mElems.resize(_mElems.size() - 1);
+        return this->replaceTop(lastElem);
+    }
+
+    /*
+     * Removes the top (greatest) element, and inserts a copy of `elem`.
+     *
+     * Equivalent to using removeTop() and then insert(), but more
+     * efficient (single rebalance).
+     *
+     * This heap must not be empty.
+     */
+    void replaceTop(const T& elem)
+    {
+        BT_ASSERT_DBG(!this->isEmpty());
+
+        /* Replace the current top and heapify */
+        _mElems[0] = elem;
+        this->_heapify(0);
+    }
+
+private:
+    static std::size_t _parentPos(const std::size_t pos) noexcept
+    {
+        return (pos - 1) >> 1;
+    }
+
+    void _heapify(std::size_t pos)
+    {
+        while (true) {
+            std::size_t largestPos;
+            const auto leftPos = (pos << 1) + 1;
+
+            if (leftPos < this->len() && this->_gt(_mElems[leftPos], _mElems[pos])) {
+                largestPos = leftPos;
+            } else {
+                largestPos = pos;
+            }
+
+            const auto rightPos = (pos << 1) + 2;
+
+            if (rightPos < this->len() && this->_gt(_mElems[rightPos], _mElems[largestPos])) {
+                largestPos = rightPos;
+            }
+
+            if (G_UNLIKELY(largestPos == pos)) {
+                break;
+            }
+
+            const auto tmpElem = _mElems[pos];
+
+            _mElems[pos] = _mElems[largestPos];
+            _mElems[largestPos] = tmpElem;
+            pos = largestPos;
+        }
+
+        this->_validate();
+    }
+
+    T& _parentElem(const std::size_t pos) noexcept
+    {
+        return _mElems[this->_parentPos(pos)];
+    }
+
+    bool _gt(const T& a, const T& b) const
+    {
+        /* Forward to user comparator */
+        return _mComp(a, b);
+    }
+
+    void _validate() const noexcept
+    {
+#ifdef BT_DEBUG_MODE
+        if (_mElems.empty()) {
+            return;
+        }
+
+        for (std::size_t i = 1; i < _mElems.size(); ++i) {
+            BT_ASSERT_DBG(!this->_gt(_mElems[i], _mElems.front()));
+        }
+#endif /* BT_DEBUG_MODE */
+    }
+
+    CompT _mComp;
+    std::vector<T> _mElems;
+};
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_PRIO_HEAP_HPP */
diff --git a/src/cpp-common/bt2c/read-fixed-len-int.hpp b/src/cpp-common/bt2c/read-fixed-len-int.hpp
new file mode 100644 (file)
index 0000000..93d8503
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_READ_FIXED_LEN_INT_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_READ_FIXED_LEN_INT_HPP
+
+#include <cstdint>
+#include <cstring>
+#include <type_traits>
+
+#include "endian.hpp"
+
+namespace bt2c {
+
+/*
+ * Reads a fixed-length integer of unknown byte order into a value of integral
+ * type `IntT` from the buffer `buf` and returns it.
+ */
+template <typename IntT>
+IntT readFixedLenInt(const std::uint8_t * const buf)
+{
+    static_assert(std::is_integral<IntT>::value, "`IntT` is an integral type.");
+
+    IntT val;
+
+    std::memcpy(&val, buf, sizeof(val));
+    return val;
+}
+
+/*
+ * Reads a fixed-length little-endian integer into a value of integral
+ * type `IntT` from the buffer `buf` and returns it.
+ */
+template <typename IntT>
+IntT readFixedLenIntLe(const std::uint8_t * const buf)
+{
+    return bt2c::littleEndianToNative(readFixedLenInt<IntT>(buf));
+}
+
+/*
+ * Reads a fixed-length big-endian integer into a value of integral
+ * type `IntT` from the buffer `buf` and returns it.
+ */
+template <typename IntT>
+IntT readFixedLenIntBe(const std::uint8_t * const buf)
+{
+    return bt2c::bigEndianToNative(readFixedLenInt<IntT>(buf));
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_READ_FIXED_LEN_INT_HPP */
diff --git a/src/cpp-common/bt2c/safe-ops.hpp b/src/cpp-common/bt2c/safe-ops.hpp
new file mode 100644 (file)
index 0000000..e1d6601
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_SAFE_OPS_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_SAFE_OPS_HPP
+
+#include <limits>
+#include <type_traits>
+
+#include "common/assert.h"
+
+namespace bt2c {
+
+template <typename T>
+constexpr bool safeToMul(const T a, const T b)
+{
+    static_assert(std::is_unsigned<T>::value, "`T` is an unsigned type.");
+
+    return a == 0 || b == 0 || a < std::numeric_limits<T>::max() / b;
+}
+
+template <typename T>
+T safeMul(const T a, const T b) noexcept
+{
+    static_assert(std::is_unsigned<T>::value, "`T` is an unsigned type.");
+
+    BT_ASSERT_DBG(safeToMul(a, b));
+    return a * b;
+}
+
+template <typename T>
+constexpr bool safeToAdd(const T a, const T b)
+{
+    static_assert(std::is_unsigned<T>::value, "`T` is an unsigned type.");
+
+    return a <= std::numeric_limits<T>::max() - b;
+}
+
+template <typename T>
+T safeAdd(const T a, const T b) noexcept
+{
+    static_assert(std::is_unsigned<T>::value, "`T` is an unsigned type.");
+
+    BT_ASSERT_DBG(safeToAdd(a, b));
+    return a + b;
+}
+
+template <typename T>
+constexpr bool safeToSub(const T a, const T b)
+{
+    static_assert(std::is_unsigned<T>::value, "`T` is an unsigned type.");
+
+    return a >= b;
+}
+
+template <typename T>
+T safeSub(const T a, const T b) noexcept
+{
+    static_assert(std::is_unsigned<T>::value, "`T` is an unsigned type.");
+
+    BT_ASSERT_DBG(safeToSub(a, b));
+    return a - b;
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_SAFE_OPS_HPP */
diff --git a/src/cpp-common/bt2c/span.hpp b/src/cpp-common/bt2c/span.hpp
new file mode 100644 (file)
index 0000000..05ab3d7
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2024 EfficiOS Inc.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_SPAN_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_SPAN_HPP
+
+#include "cpp-common/bt2s/span.hpp"
+
+namespace bt2c {
+
+template <class T>
+inline constexpr bt2s::span<T> makeSpan(T * const ptr, const size_t count) noexcept
+{
+    return nonstd::make_span(ptr, count);
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_SPAN_HPP */
diff --git a/src/cpp-common/bt2c/std-int.hpp b/src/cpp-common/bt2c/std-int.hpp
new file mode 100644 (file)
index 0000000..2fe3535
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_STD_INT_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_STD_INT_HPP
+
+#include <cstdint>
+
+namespace bt2c {
+namespace internal {
+
+template <std::size_t LenBitsV, bool IsSignedV>
+struct StdIntTBase;
+
+template <>
+struct StdIntTBase<8, true>
+{
+    using Type = std::int8_t;
+};
+
+template <>
+struct StdIntTBase<8, false>
+{
+    using Type = std::uint8_t;
+};
+
+template <>
+struct StdIntTBase<16, true>
+{
+    using Type = std::int16_t;
+};
+
+template <>
+struct StdIntTBase<16, false>
+{
+    using Type = std::uint16_t;
+};
+
+template <>
+struct StdIntTBase<32, true>
+{
+    using Type = std::int32_t;
+};
+
+template <>
+struct StdIntTBase<32, false>
+{
+    using Type = std::uint32_t;
+};
+
+template <>
+struct StdIntTBase<64, true>
+{
+    using Type = std::int64_t;
+};
+
+template <>
+struct StdIntTBase<64, false>
+{
+    using Type = std::uint64_t;
+};
+
+} /* namespace internal */
+
+/*
+ * Standard fixed-length integer type `Type` of length `LenBitsV` bits
+ * and signedness `IsSignedV`.
+ *
+ * `LenBitsV` must be one of 8, 16, 32, or 64.
+ *
+ * For example, `StdIntT<32, true>` is `std::int32_t`.
+ */
+template <std::size_t LenBitsV, bool IsSignedV>
+using StdIntT = typename internal::StdIntTBase<LenBitsV, IsSignedV>::Type;
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_STD_INT_HPP */
diff --git a/src/cpp-common/bt2c/type-traits.hpp b/src/cpp-common/bt2c/type-traits.hpp
new file mode 100644 (file)
index 0000000..da8e0e0
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_TYPE_TRAITS_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_TYPE_TRAITS_HPP
+
+#include <type_traits>
+
+namespace bt2c {
+
+/*
+ * Provides the member constant `value` equal to:
+ *
+ * `T` is in the list of types `Ts`:
+ *     `true`
+ *
+ * Otherwise:
+ *     `false`
+ */
+template <typename T, typename... Ts>
+struct IsOneOf : std::false_type
+{
+};
+
+template <typename T, typename... Ts>
+struct IsOneOf<T, T, Ts...> : std::true_type
+{
+};
+
+template <typename T, typename U, typename... Ts>
+struct IsOneOf<T, U, Ts...> : IsOneOf<T, Ts...>
+{
+};
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_TYPE_TRAITS_HPP */
diff --git a/src/cpp-common/bt2c/uuid.hpp b/src/cpp-common/bt2c/uuid.hpp
new file mode 100644 (file)
index 0000000..30f3014
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * SPDX-FileCopyrightText: 2020-2023 Philippe Proulx <pproulx@efficios.com>
+ * SPDX-FileCopyrightText: 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_UUID_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_UUID_HPP
+
+#include <algorithm>
+#include <array>
+#include <cstdint>
+#include <string>
+
+#include "common/assert.h"
+#include "common/uuid.h"
+#include "cpp-common/bt2c/c-string-view.hpp"
+
+namespace bt2c {
+
+class Uuid;
+
+/*
+ * A view on existing UUID data.
+ *
+ * A `UuidView` object doesn't contain its UUID data: see `Uuid` for a
+ * UUID data container.
+ */
+class UuidView final
+{
+public:
+    using Val = std::uint8_t;
+    using ConstIter = const Val *;
+
+public:
+    explicit UuidView(const Val * const uuid) noexcept : _mUuid {uuid}
+    {
+        BT_ASSERT_DBG(uuid);
+    }
+
+    explicit UuidView(const Uuid& uuid) noexcept;
+    UuidView(const UuidView&) noexcept = default;
+    UuidView& operator=(const UuidView&) noexcept = default;
+
+    UuidView& operator=(const Val * const uuid) noexcept
+    {
+        _mUuid = uuid;
+        return *this;
+    }
+
+    operator Uuid() const noexcept;
+
+    std::string str() const
+    {
+        std::string s;
+
+        s.resize(BT_UUID_STR_LEN);
+        bt_uuid_to_str(_mUuid, &s[0]);
+
+        return s;
+    }
+
+    bool operator==(const UuidView& other) const noexcept
+    {
+        return bt_uuid_compare(_mUuid, other._mUuid) == 0;
+    }
+
+    bool operator!=(const UuidView& other) const noexcept
+    {
+        return !(*this == other);
+    }
+
+    bool operator<(const UuidView& other) const noexcept
+    {
+        return bt_uuid_compare(_mUuid, other._mUuid) < 0;
+    }
+
+    static constexpr std::size_t size() noexcept
+    {
+        return BT_UUID_LEN;
+    }
+
+    const Val *data() const noexcept
+    {
+        return _mUuid;
+    }
+
+    Val operator[](const std::size_t index) const noexcept
+    {
+        return _mUuid[index];
+    }
+
+    ConstIter begin() const noexcept
+    {
+        return _mUuid;
+    }
+
+    ConstIter end() const noexcept
+    {
+        return _mUuid + this->size();
+    }
+
+    bool isNil() const noexcept
+    {
+        return std::all_of(this->begin(), this->end(), [](const std::uint8_t byte) {
+            return byte == 0;
+        });
+    }
+
+private:
+    const Val *_mUuid;
+};
+
+/*
+ * A universally unique identifier.
+ *
+ * A `Uuid` object contains its UUID data: see `UuidView` to have a
+ * UUID view on existing UUID data.
+ */
+class Uuid final
+{
+public:
+    using Val = UuidView::Val;
+    using ConstIter = UuidView::ConstIter;
+
+public:
+    /*
+     * Builds a nil UUID.
+     */
+    explicit Uuid() noexcept = default;
+
+    explicit Uuid(const Val * const uuid) noexcept
+    {
+        this->_setFromPtr(uuid);
+    }
+
+    explicit Uuid(const bt2c::CStringView str) noexcept
+    {
+        const auto ret = bt_uuid_from_str(str.data(), _mUuid.data());
+        BT_ASSERT(ret == 0);
+    }
+
+    explicit Uuid(const UuidView& view) noexcept : Uuid {view.data()}
+    {
+    }
+
+    Uuid(const Uuid&) noexcept = default;
+    Uuid& operator=(const Uuid&) noexcept = default;
+
+    Uuid& operator=(const Val * const uuid) noexcept
+    {
+        this->_setFromPtr(uuid);
+        return *this;
+    }
+
+    static Uuid generate() noexcept
+    {
+        bt_uuid_t uuidGen;
+
+        bt_uuid_generate(uuidGen);
+        return Uuid {uuidGen};
+    }
+
+    std::string str() const
+    {
+        return this->_view().str();
+    }
+
+    bool operator==(const Uuid& other) const noexcept
+    {
+        return this->_view() == other._view();
+    }
+
+    bool operator!=(const Uuid& other) const noexcept
+    {
+        return this->_view() != other._view();
+    }
+
+    bool operator<(const Uuid& other) const noexcept
+    {
+        return this->_view() < other._view();
+    }
+
+    /*
+     * The returned UUID view must not outlive the UUID object.
+     */
+    operator UuidView() const noexcept
+    {
+        return this->_view();
+    }
+
+    static constexpr std::size_t size() noexcept
+    {
+        return UuidView::size();
+    }
+
+    const Val *data() const noexcept
+    {
+        return _mUuid.data();
+    }
+
+    Val operator[](const std::size_t index) const noexcept
+    {
+        return this->_view()[index];
+    }
+
+    ConstIter begin() const noexcept
+    {
+        return this->_view().begin();
+    }
+
+    ConstIter end() const noexcept
+    {
+        return this->_view().end();
+    }
+
+    bool isNil() const noexcept
+    {
+        return this->_view().isNil();
+    }
+
+private:
+    /*
+     * std::copy_n() won't throw when simply copying bytes below,
+     * therefore this method won't throw.
+     */
+    void _setFromPtr(const Val * const uuid) noexcept
+    {
+        BT_ASSERT(uuid);
+        std::copy_n(uuid, BT_UUID_LEN, std::begin(_mUuid));
+    }
+
+    UuidView _view() const noexcept
+    {
+        return UuidView {_mUuid.data()};
+    }
+
+    std::array<Val, UuidView::size()> _mUuid = {};
+};
+
+inline UuidView::UuidView(const Uuid& uuid) noexcept : _mUuid {uuid.data()}
+{
+}
+
+inline UuidView::operator Uuid() const noexcept
+{
+    return Uuid {*this};
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_UUID_HPP */
diff --git a/src/cpp-common/bt2c/vector.hpp b/src/cpp-common/bt2c/vector.hpp
new file mode 100644 (file)
index 0000000..516a826
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef SRC_CPP_COMMON_VECTOR_HPP
+#define SRC_CPP_COMMON_VECTOR_HPP
+
+#include <vector>
+
+#include "common/assert.h"
+
+namespace bt2c {
+
+/*
+ * Moves the last entry of `vec` to the index `idx`, then removes the last entry.
+ *
+ * Meant to be a direct replacement for g_ptr_array_remove_index_fast(), but for
+ * `std::vector`.
+ */
+template <typename T, typename AllocatorT>
+void vectorFastRemove(std::vector<T, AllocatorT>& vec,
+                      const typename std::vector<T, AllocatorT>::size_type idx)
+{
+    BT_ASSERT_DBG(idx < vec.size());
+
+    if (idx < vec.size() - 1) {
+        vec[idx] = std::move(vec.back());
+    }
+
+    vec.pop_back();
+}
+
+} /* namespace bt2c */
+
+#endif /* SRC_CPP_COMMON_VECTOR_HPP */
diff --git a/src/cpp-common/bt2s/make-unique.hpp b/src/cpp-common/bt2s/make-unique.hpp
new file mode 100644 (file)
index 0000000..6c4d1bf
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2S_MAKE_UNIQUE_HPP
+#define BABELTRACE_CPP_COMMON_BT2S_MAKE_UNIQUE_HPP
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace bt2s {
+
+/*
+ * Our implementation of std::make_unique<>() for C++11.
+ */
+template <typename T, typename... ArgTs>
+std::unique_ptr<T> make_unique(ArgTs&&...args)
+{
+    static_assert(!std::is_array<T>::value, "`T` is not an array (unsupported).");
+
+    return std::unique_ptr<T>(new T {std::forward<ArgTs>(args)...});
+}
+
+} /* namespace bt2s */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2S_MAKE_UNIQUE_HPP */
diff --git a/src/cpp-common/bt2s/optional.hpp b/src/cpp-common/bt2s/optional.hpp
new file mode 100644 (file)
index 0000000..13f121c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2S_OPTIONAL_HPP
+#define BABELTRACE_CPP_COMMON_BT2S_OPTIONAL_HPP
+
+#include "cpp-common/vendor/optional-lite/optional.hpp"
+
+namespace bt2s {
+
+using nonstd::optional;
+using nonstd::nullopt_t;
+using nonstd::bad_optional_access;
+using nonstd::nullopt;
+using nonstd::make_optional;
+using nonstd::in_place_t;
+using nonstd::in_place;
+using nonstd::in_place_type;
+using nonstd::in_place_index;
+
+} /* namespace bt2s */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2S_OPTIONAL_HPP */
diff --git a/src/cpp-common/bt2s/span.hpp b/src/cpp-common/bt2s/span.hpp
new file mode 100644 (file)
index 0000000..4a7fc32
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2024 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2S_SPAN_HPP
+#define BABELTRACE_CPP_COMMON_BT2S_SPAN_HPP
+
+#define span_FEATURE_MAKE_SPAN 1
+
+#ifdef BT_DEBUG_MODE
+#    define span_CONFIG_CONTRACT_LEVEL_ON 1
+#else
+#    define span_CONFIG_CONTRACT_LEVEL_OFF 1
+#endif
+
+#include "cpp-common/vendor/span-lite/span.hpp" /* IWYU pragma: export */
+
+namespace bt2s {
+
+using nonstd::dynamic_extent;
+using nonstd::span;
+
+} /* namespace bt2s */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2S_SPAN_HPP */
diff --git a/src/cpp-common/bt2s/string-view.hpp b/src/cpp-common/bt2s/string-view.hpp
new file mode 100644 (file)
index 0000000..e2e61dd
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2S_STRING_VIEW_HPP
+#define BABELTRACE_CPP_COMMON_BT2S_STRING_VIEW_HPP
+
+#include "cpp-common/vendor/string-view-lite/string_view.hpp"
+
+namespace bt2s {
+
+using nonstd::basic_string_view;
+using nonstd::string_view;
+using nonstd::wstring_view;
+using nonstd::u16string_view;
+using nonstd::u32string_view;
+
+} /* namespace bt2s */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2S_STRING_VIEW_HPP */
diff --git a/src/cpp-common/optional.hpp b/src/cpp-common/optional.hpp
deleted file mode 100644 (file)
index 86e3b40..0000000
+++ /dev/null
@@ -1,1846 +0,0 @@
-//
-// Copyright (c) 2014-2021 Martin Moene
-//
-// https://github.com/martinmoene/optional-lite
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-#pragma once
-
-#ifndef NONSTD_OPTIONAL_LITE_HPP
-#define NONSTD_OPTIONAL_LITE_HPP
-
-#define optional_lite_MAJOR  3
-#define optional_lite_MINOR  5
-#define optional_lite_PATCH  0
-
-#define optional_lite_VERSION  optional_STRINGIFY(optional_lite_MAJOR) "." optional_STRINGIFY(optional_lite_MINOR) "." optional_STRINGIFY(optional_lite_PATCH)
-
-#define optional_STRINGIFY(  x )  optional_STRINGIFY_( x )
-#define optional_STRINGIFY_( x )  #x
-
-// optional-lite configuration:
-
-#define optional_OPTIONAL_DEFAULT  0
-#define optional_OPTIONAL_NONSTD   1
-#define optional_OPTIONAL_STD      2
-
-// tweak header support:
-
-#ifdef __has_include
-# if __has_include(<nonstd/optional.tweak.hpp>)
-#  include <nonstd/optional.tweak.hpp>
-# endif
-#define optional_HAVE_TWEAK_HEADER  1
-#else
-#define optional_HAVE_TWEAK_HEADER  0
-//# pragma message("optional.hpp: Note: Tweak header not supported.")
-#endif
-
-// optional selection and configuration:
-
-#if !defined( optional_CONFIG_SELECT_OPTIONAL )
-# define optional_CONFIG_SELECT_OPTIONAL  ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD )
-#endif
-
-// Control presence of extensions:
-
-#ifndef optional_CONFIG_NO_EXTENSIONS
-#define optional_CONFIG_NO_EXTENSIONS  0
-#endif
-
-// Control presence of exception handling (try and auto discover):
-
-#ifndef optional_CONFIG_NO_EXCEPTIONS
-# if defined(_MSC_VER)
-# include <cstddef>     // for _HAS_EXCEPTIONS
-# endif
-# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
-#  define optional_CONFIG_NO_EXCEPTIONS  0
-# else
-#  define optional_CONFIG_NO_EXCEPTIONS  1
-# endif
-#endif
-
-// C++ language version detection (C++20 is speculative):
-// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
-
-#ifndef   optional_CPLUSPLUS
-# if defined(_MSVC_LANG ) && !defined(__clang__)
-#  define optional_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
-# else
-#  define optional_CPLUSPLUS  __cplusplus
-# endif
-#endif
-
-#define optional_CPP98_OR_GREATER  ( optional_CPLUSPLUS >= 199711L )
-#define optional_CPP11_OR_GREATER  ( optional_CPLUSPLUS >= 201103L )
-#define optional_CPP11_OR_GREATER_ ( optional_CPLUSPLUS >= 201103L )
-#define optional_CPP14_OR_GREATER  ( optional_CPLUSPLUS >= 201402L )
-#define optional_CPP17_OR_GREATER  ( optional_CPLUSPLUS >= 201703L )
-#define optional_CPP20_OR_GREATER  ( optional_CPLUSPLUS >= 202000L )
-
-// C++ language version (represent 98 as 3):
-
-#define optional_CPLUSPLUS_V  ( optional_CPLUSPLUS / 100 - (optional_CPLUSPLUS > 200000 ? 2000 : 1994) )
-
-// Use C++17 std::optional if available and requested:
-
-#if optional_CPP17_OR_GREATER && defined(__has_include )
-# if __has_include( <optional> )
-#  define optional_HAVE_STD_OPTIONAL  1
-# else
-#  define optional_HAVE_STD_OPTIONAL  0
-# endif
-#else
-# define  optional_HAVE_STD_OPTIONAL  0
-#endif
-
-#define optional_USES_STD_OPTIONAL  ( (optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_STD) || ((optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_DEFAULT) && optional_HAVE_STD_OPTIONAL) )
-
-//
-// in_place: code duplicated in any-lite, expected-lite, optional-lite, value-ptr-lite, variant-lite:
-//
-
-#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES
-#define nonstd_lite_HAVE_IN_PLACE_TYPES  1
-
-// C++17 std::in_place in <utility>:
-
-#if optional_CPP17_OR_GREATER
-
-#include <utility>
-
-namespace nonstd {
-
-using std::in_place;
-using std::in_place_type;
-using std::in_place_index;
-using std::in_place_t;
-using std::in_place_type_t;
-using std::in_place_index_t;
-
-#define nonstd_lite_in_place_t(      T)  std::in_place_t
-#define nonstd_lite_in_place_type_t( T)  std::in_place_type_t<T>
-#define nonstd_lite_in_place_index_t(K)  std::in_place_index_t<K>
-
-#define nonstd_lite_in_place(      T)    std::in_place_t{}
-#define nonstd_lite_in_place_type( T)    std::in_place_type_t<T>{}
-#define nonstd_lite_in_place_index(K)    std::in_place_index_t<K>{}
-
-} // namespace nonstd
-
-#else // optional_CPP17_OR_GREATER
-
-#include <cstddef>
-
-namespace nonstd {
-namespace detail {
-
-template< class T >
-struct in_place_type_tag {};
-
-template< std::size_t K >
-struct in_place_index_tag {};
-
-} // namespace detail
-
-struct in_place_t {};
-
-template< class T >
-inline in_place_t in_place( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() )
-{
-    return in_place_t();
-}
-
-template< std::size_t K >
-inline in_place_t in_place( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() )
-{
-    return in_place_t();
-}
-
-template< class T >
-inline in_place_t in_place_type( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() )
-{
-    return in_place_t();
-}
-
-template< std::size_t K >
-inline in_place_t in_place_index( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() )
-{
-    return in_place_t();
-}
-
-// mimic templated typedef:
-
-#define nonstd_lite_in_place_t(      T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
-#define nonstd_lite_in_place_type_t( T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
-#define nonstd_lite_in_place_index_t(K)  nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag<K> )
-
-#define nonstd_lite_in_place(      T)    nonstd::in_place_type<T>
-#define nonstd_lite_in_place_type( T)    nonstd::in_place_type<T>
-#define nonstd_lite_in_place_index(K)    nonstd::in_place_index<K>
-
-} // namespace nonstd
-
-#endif // optional_CPP17_OR_GREATER
-#endif // nonstd_lite_HAVE_IN_PLACE_TYPES
-
-//
-// Using std::optional:
-//
-
-#if optional_USES_STD_OPTIONAL
-
-#include <optional>
-
-namespace nonstd {
-
-    using std::optional;
-    using std::bad_optional_access;
-    using std::hash;
-
-    using std::nullopt;
-    using std::nullopt_t;
-
-    using std::operator==;
-    using std::operator!=;
-    using std::operator<;
-    using std::operator<=;
-    using std::operator>;
-    using std::operator>=;
-    using std::make_optional;
-    using std::swap;
-}
-
-#else // optional_USES_STD_OPTIONAL
-
-#include <cassert>
-#include <utility>
-
-// optional-lite alignment configuration:
-
-#ifndef  optional_CONFIG_MAX_ALIGN_HACK
-# define optional_CONFIG_MAX_ALIGN_HACK  0
-#endif
-
-#ifndef  optional_CONFIG_ALIGN_AS
-// no default, used in #if defined()
-#endif
-
-#ifndef  optional_CONFIG_ALIGN_AS_FALLBACK
-# define optional_CONFIG_ALIGN_AS_FALLBACK  double
-#endif
-
-// Compiler warning suppression:
-
-#if defined(__clang__)
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wundef"
-#elif defined(__GNUC__)
-# pragma GCC   diagnostic push
-# pragma GCC   diagnostic ignored "-Wundef"
-#elif defined(_MSC_VER )
-# pragma warning( push )
-#endif
-
-// half-open range [lo..hi):
-#define optional_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
-
-// Compiler versions:
-//
-// MSVC++  6.0  _MSC_VER == 1200  optional_COMPILER_MSVC_VERSION ==  60  (Visual Studio 6.0)
-// MSVC++  7.0  _MSC_VER == 1300  optional_COMPILER_MSVC_VERSION ==  70  (Visual Studio .NET 2002)
-// MSVC++  7.1  _MSC_VER == 1310  optional_COMPILER_MSVC_VERSION ==  71  (Visual Studio .NET 2003)
-// MSVC++  8.0  _MSC_VER == 1400  optional_COMPILER_MSVC_VERSION ==  80  (Visual Studio 2005)
-// MSVC++  9.0  _MSC_VER == 1500  optional_COMPILER_MSVC_VERSION ==  90  (Visual Studio 2008)
-// MSVC++ 10.0  _MSC_VER == 1600  optional_COMPILER_MSVC_VERSION == 100  (Visual Studio 2010)
-// MSVC++ 11.0  _MSC_VER == 1700  optional_COMPILER_MSVC_VERSION == 110  (Visual Studio 2012)
-// MSVC++ 12.0  _MSC_VER == 1800  optional_COMPILER_MSVC_VERSION == 120  (Visual Studio 2013)
-// MSVC++ 14.0  _MSC_VER == 1900  optional_COMPILER_MSVC_VERSION == 140  (Visual Studio 2015)
-// MSVC++ 14.1  _MSC_VER >= 1910  optional_COMPILER_MSVC_VERSION == 141  (Visual Studio 2017)
-// MSVC++ 14.2  _MSC_VER >= 1920  optional_COMPILER_MSVC_VERSION == 142  (Visual Studio 2019)
-
-#if defined(_MSC_VER ) && !defined(__clang__)
-# define optional_COMPILER_MSVC_VER      (_MSC_VER )
-# define optional_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
-#else
-# define optional_COMPILER_MSVC_VER      0
-# define optional_COMPILER_MSVC_VERSION  0
-#endif
-
-#define optional_COMPILER_VERSION( major, minor, patch )  ( 10 * (10 * (major) + (minor) ) + (patch) )
-
-#if defined(__GNUC__) && !defined(__clang__)
-# define optional_COMPILER_GNUC_VERSION   optional_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
-#else
-# define optional_COMPILER_GNUC_VERSION   0
-#endif
-
-#if defined(__clang__)
-# define optional_COMPILER_CLANG_VERSION  optional_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
-#else
-# define optional_COMPILER_CLANG_VERSION  0
-#endif
-
-#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 140 )
-# pragma warning( disable: 4345 )   // initialization behavior changed
-#endif
-
-#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 150 )
-# pragma warning( disable: 4814 )   // in C++14 'constexpr' will not imply 'const'
-#endif
-
-// Presence of language and library features:
-
-#define optional_HAVE(FEATURE) ( optional_HAVE_##FEATURE )
-
-#ifdef _HAS_CPP0X
-# define optional_HAS_CPP0X  _HAS_CPP0X
-#else
-# define optional_HAS_CPP0X  0
-#endif
-
-// Unless defined otherwise below, consider VC14 as C++11 for optional-lite:
-
-#if optional_COMPILER_MSVC_VER >= 1900
-# undef  optional_CPP11_OR_GREATER
-# define optional_CPP11_OR_GREATER  1
-#endif
-
-#define optional_CPP11_90   (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1500)
-#define optional_CPP11_100  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1600)
-#define optional_CPP11_110  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1700)
-#define optional_CPP11_120  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1800)
-#define optional_CPP11_140  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1900)
-#define optional_CPP11_141  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1910)
-
-#define optional_CPP14_000  (optional_CPP14_OR_GREATER)
-#define optional_CPP17_000  (optional_CPP17_OR_GREATER)
-
-// clang >= 2.9, gcc >= 4.9, msvc >= vc14.0/1900 (vs15):
-#define optional_CPP11_140_C290_G490    ((optional_CPP11_OR_GREATER_ && (optional_COMPILER_CLANG_VERSION >= 290 || optional_COMPILER_GNUC_VERSION >= 490)) || (optional_COMPILER_MSVC_VER >= 1900))
-
-// clang >= 3.5, msvc >= vc11 (vs12):
-#define optional_CPP11_110_C350         ( optional_CPP11_110 && !optional_BETWEEN( optional_COMPILER_CLANG_VERSION, 1, 350 ) )
-
-// clang >= 3.5, gcc >= 5.0, msvc >= vc11 (vs12):
-#define optional_CPP11_110_C350_G500 \
-    (  optional_CPP11_110 && \
-    !( optional_BETWEEN( optional_COMPILER_CLANG_VERSION, 1, 350 ) \
-    || optional_BETWEEN( optional_COMPILER_GNUC_VERSION , 1, 500 ) ) )
-
-// Presence of C++11 language features:
-
-#define optional_HAVE_CONSTEXPR_11      optional_CPP11_140
-#define optional_HAVE_IS_DEFAULT        optional_CPP11_140
-#define optional_HAVE_NOEXCEPT          optional_CPP11_140
-#define optional_HAVE_NULLPTR           optional_CPP11_100
-#define optional_HAVE_REF_QUALIFIER     optional_CPP11_140_C290_G490
-#define optional_HAVE_STATIC_ASSERT     optional_CPP11_110
-#define optional_HAVE_INITIALIZER_LIST  optional_CPP11_140
-
-// Presence of C++14 language features:
-
-#define optional_HAVE_CONSTEXPR_14      optional_CPP14_000
-
-// Presence of C++17 language features:
-
-#define optional_HAVE_NODISCARD         optional_CPP17_000
-
-// Presence of C++ library features:
-
-#define optional_HAVE_CONDITIONAL       optional_CPP11_120
-#define optional_HAVE_REMOVE_CV         optional_CPP11_120
-#define optional_HAVE_TYPE_TRAITS       optional_CPP11_90
-
-#define optional_HAVE_TR1_TYPE_TRAITS   (!! optional_COMPILER_GNUC_VERSION )
-#define optional_HAVE_TR1_ADD_POINTER   (!! optional_COMPILER_GNUC_VERSION )
-
-#define optional_HAVE_IS_ASSIGNABLE                     optional_CPP11_110_C350
-#define optional_HAVE_IS_MOVE_CONSTRUCTIBLE             optional_CPP11_110_C350
-#define optional_HAVE_IS_NOTHROW_MOVE_ASSIGNABLE        optional_CPP11_110_C350
-#define optional_HAVE_IS_NOTHROW_MOVE_CONSTRUCTIBLE     optional_CPP11_110_C350
-#define optional_HAVE_IS_TRIVIALLY_COPY_CONSTRUCTIBLE   optional_CPP11_110_C350_G500
-#define optional_HAVE_IS_TRIVIALLY_MOVE_CONSTRUCTIBLE   optional_CPP11_110_C350_G500
-
-// C++ feature usage:
-
-#if optional_HAVE( CONSTEXPR_11 )
-# define optional_constexpr  constexpr
-#else
-# define optional_constexpr  /*constexpr*/
-#endif
-
-#if optional_HAVE( IS_DEFAULT )
-# define optional_is_default  = default;
-#else
-# define optional_is_default  {}
-#endif
-
-#if optional_HAVE( CONSTEXPR_14 )
-# define optional_constexpr14  constexpr
-#else
-# define optional_constexpr14  /*constexpr*/
-#endif
-
-#if optional_HAVE( NODISCARD )
-# define optional_nodiscard  [[nodiscard]]
-#else
-# define optional_nodiscard  /*[[nodiscard]]*/
-#endif
-
-#if optional_HAVE( NOEXCEPT )
-# define optional_noexcept  noexcept
-#else
-# define optional_noexcept  /*noexcept*/
-#endif
-
-#if optional_HAVE( NULLPTR )
-# define optional_nullptr  nullptr
-#else
-# define optional_nullptr  NULL
-#endif
-
-#if optional_HAVE( REF_QUALIFIER )
-// NOLINTNEXTLINE( bugprone-macro-parentheses )
-# define optional_ref_qual  &
-# define optional_refref_qual  &&
-#else
-# define optional_ref_qual  /*&*/
-# define optional_refref_qual  /*&&*/
-#endif
-
-#if optional_HAVE( STATIC_ASSERT )
-# define optional_static_assert(expr, text)    static_assert(expr, text);
-#else
-# define optional_static_assert(expr, text)  /*static_assert(expr, text);*/
-#endif
-
-// additional includes:
-
-#if optional_CONFIG_NO_EXCEPTIONS
-// already included: <cassert>
-#else
-# include <stdexcept>
-#endif
-
-#if optional_CPP11_OR_GREATER
-# include <functional>
-#endif
-
-#if optional_HAVE( INITIALIZER_LIST )
-# include <initializer_list>
-#endif
-
-#if optional_HAVE( TYPE_TRAITS )
-# include <type_traits>
-#elif optional_HAVE( TR1_TYPE_TRAITS )
-# include <tr1/type_traits>
-#endif
-
-// Method enabling
-
-#if optional_CPP11_OR_GREATER
-
-#define optional_REQUIRES_0(...) \
-    template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 >
-
-#define optional_REQUIRES_T(...) \
-    , typename std::enable_if< (__VA_ARGS__), int >::type = 0
-
-#define optional_REQUIRES_R(R, ...) \
-    typename std::enable_if< (__VA_ARGS__), R>::type
-
-#define optional_REQUIRES_A(...) \
-    , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr
-
-#endif
-
-//
-// optional:
-//
-
-namespace nonstd { namespace optional_lite {
-
-namespace std11 {
-
-template< class T, T v > struct integral_constant { enum { value = v }; };
-template< bool B       > struct bool_constant : integral_constant<bool, B>{};
-
-typedef bool_constant< true  > true_type;
-typedef bool_constant< false > false_type;
-
-#if optional_CPP11_OR_GREATER
-    using std::move;
-#else
-    template< typename T > T & move( T & t ) { return t; }
-#endif
-
-#if optional_HAVE( CONDITIONAL )
-    using std::conditional;
-#else
-    template< bool B, typename T, typename F > struct conditional              { typedef T type; };
-    template<         typename T, typename F > struct conditional<false, T, F> { typedef F type; };
-#endif // optional_HAVE_CONDITIONAL
-
-#if optional_HAVE( IS_ASSIGNABLE )
-    using std::is_assignable;
-#else
-    template< class T, class U > struct is_assignable : std11::true_type{};
-#endif
-
-#if optional_HAVE( IS_MOVE_CONSTRUCTIBLE )
-    using std::is_move_constructible;
-#else
-    template< class T > struct is_move_constructible : std11::true_type{};
-#endif
-
-#if optional_HAVE( IS_NOTHROW_MOVE_ASSIGNABLE )
-    using std::is_nothrow_move_assignable;
-#else
-    template< class T > struct is_nothrow_move_assignable : std11::true_type{};
-#endif
-
-#if optional_HAVE( IS_NOTHROW_MOVE_CONSTRUCTIBLE )
-    using std::is_nothrow_move_constructible;
-#else
-    template< class T > struct is_nothrow_move_constructible : std11::true_type{};
-#endif
-
-#if optional_HAVE( IS_TRIVIALLY_COPY_CONSTRUCTIBLE )
-    using std::is_trivially_copy_constructible;
-#else
-    template< class T > struct is_trivially_copy_constructible : std11::true_type{};
-#endif
-
-#if optional_HAVE( IS_TRIVIALLY_MOVE_CONSTRUCTIBLE )
-    using std::is_trivially_move_constructible;
-#else
-    template< class T > struct is_trivially_move_constructible : std11::true_type{};
-#endif
-
-} // namespace std11
-
-#if optional_CPP11_OR_GREATER
-
-/// type traits C++17:
-
-namespace std17 {
-
-#if optional_CPP17_OR_GREATER
-
-using std::is_swappable;
-using std::is_nothrow_swappable;
-
-#elif optional_CPP11_OR_GREATER
-
-namespace detail {
-
-using std::swap;
-
-struct is_swappable
-{
-    template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) >
-    static std11::true_type test( int /*unused*/ );
-
-    template< typename >
-    static std11::false_type test(...);
-};
-
-struct is_nothrow_swappable
-{
-    // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015):
-
-    template< typename T >
-    static constexpr bool satisfies()
-    {
-        return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) );
-    }
-
-    template< typename T >
-    static auto test( int /*unused*/ ) -> std11::integral_constant<bool, satisfies<T>()>{}
-
-    template< typename >
-    static auto test(...) -> std11::false_type;
-};
-
-} // namespace detail
-
-// is [nothow] swappable:
-
-template< typename T >
-struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
-
-template< typename T >
-struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){};
-
-#endif // optional_CPP17_OR_GREATER
-
-} // namespace std17
-
-/// type traits C++20:
-
-namespace std20 {
-
-template< typename T >
-struct remove_cvref
-{
-    typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type;
-};
-
-} // namespace std20
-
-#endif // optional_CPP11_OR_GREATER
-
-/// class optional
-
-template< typename T >
-class optional;
-
-namespace detail {
-
-// C++11 emulation:
-
-struct nulltype{};
-
-template< typename Head, typename Tail >
-struct typelist
-{
-    typedef Head head;
-    typedef Tail tail;
-};
-
-#if optional_CONFIG_MAX_ALIGN_HACK
-
-// Max align, use most restricted type for alignment:
-
-#define optional_UNIQUE(  name )       optional_UNIQUE2( name, __LINE__ )
-#define optional_UNIQUE2( name, line ) optional_UNIQUE3( name, line )
-#define optional_UNIQUE3( name, line ) name ## line
-
-#define optional_ALIGN_TYPE( type ) \
-    type optional_UNIQUE( _t ); struct_t< type > optional_UNIQUE( _st )
-
-template< typename T >
-struct struct_t { T _; };
-
-union max_align_t
-{
-    optional_ALIGN_TYPE( char );
-    optional_ALIGN_TYPE( short int );
-    optional_ALIGN_TYPE( int );
-    optional_ALIGN_TYPE( long int  );
-    optional_ALIGN_TYPE( float  );
-    optional_ALIGN_TYPE( double );
-    optional_ALIGN_TYPE( long double );
-    optional_ALIGN_TYPE( char * );
-    optional_ALIGN_TYPE( short int * );
-    optional_ALIGN_TYPE( int *  );
-    optional_ALIGN_TYPE( long int * );
-    optional_ALIGN_TYPE( float * );
-    optional_ALIGN_TYPE( double * );
-    optional_ALIGN_TYPE( long double * );
-    optional_ALIGN_TYPE( void * );
-
-#ifdef HAVE_LONG_LONG
-    optional_ALIGN_TYPE( long long );
-#endif
-
-    struct Unknown;
-
-    Unknown ( * optional_UNIQUE(_) )( Unknown );
-    Unknown * Unknown::* optional_UNIQUE(_);
-    Unknown ( Unknown::* optional_UNIQUE(_) )( Unknown );
-
-    struct_t< Unknown ( * )( Unknown)         > optional_UNIQUE(_);
-    struct_t< Unknown * Unknown::*            > optional_UNIQUE(_);
-    struct_t< Unknown ( Unknown::* )(Unknown) > optional_UNIQUE(_);
-};
-
-#undef optional_UNIQUE
-#undef optional_UNIQUE2
-#undef optional_UNIQUE3
-
-#undef optional_ALIGN_TYPE
-
-#elif defined( optional_CONFIG_ALIGN_AS ) // optional_CONFIG_MAX_ALIGN_HACK
-
-// Use user-specified type for alignment:
-
-#define optional_ALIGN_AS( unused ) \
-    optional_CONFIG_ALIGN_AS
-
-#else // optional_CONFIG_MAX_ALIGN_HACK
-
-// Determine POD type to use for alignment:
-
-#define optional_ALIGN_AS( to_align ) \
-    typename type_of_size< alignment_types, alignment_of< to_align >::value >::type
-
-template< typename T >
-struct alignment_of;
-
-template< typename T >
-struct alignment_of_hack
-{
-    char c;
-    T t;
-    alignment_of_hack();
-};
-
-template< size_t A, size_t S >
-struct alignment_logic
-{
-    enum { value = A < S ? A : S };
-};
-
-template< typename T >
-struct alignment_of
-{
-    enum { value = alignment_logic<
-        sizeof( alignment_of_hack<T> ) - sizeof(T), sizeof(T) >::value };
-};
-
-template< typename List, size_t N >
-struct type_of_size
-{
-    typedef typename std11::conditional<
-        N == sizeof( typename List::head ),
-            typename List::head,
-            typename type_of_size<typename List::tail, N >::type >::type type;
-};
-
-template< size_t N >
-struct type_of_size< nulltype, N >
-{
-    typedef optional_CONFIG_ALIGN_AS_FALLBACK type;
-};
-
-template< typename T>
-struct struct_t { T _; };
-
-#define optional_ALIGN_TYPE( type ) \
-    typelist< type , typelist< struct_t< type >
-
-struct Unknown;
-
-typedef
-    optional_ALIGN_TYPE( char ),
-    optional_ALIGN_TYPE( short ),
-    optional_ALIGN_TYPE( int ),
-    optional_ALIGN_TYPE( long ),
-    optional_ALIGN_TYPE( float ),
-    optional_ALIGN_TYPE( double ),
-    optional_ALIGN_TYPE( long double ),
-
-    optional_ALIGN_TYPE( char *),
-    optional_ALIGN_TYPE( short * ),
-    optional_ALIGN_TYPE( int * ),
-    optional_ALIGN_TYPE( long * ),
-    optional_ALIGN_TYPE( float * ),
-    optional_ALIGN_TYPE( double * ),
-    optional_ALIGN_TYPE( long double * ),
-
-    optional_ALIGN_TYPE( Unknown ( * )( Unknown ) ),
-    optional_ALIGN_TYPE( Unknown * Unknown::*     ),
-    optional_ALIGN_TYPE( Unknown ( Unknown::* )( Unknown ) ),
-
-    nulltype
-    > > > > > > >    > > > > > > >
-    > > > > > > >    > > > > > > >
-    > > > > > >
-    alignment_types;
-
-#undef optional_ALIGN_TYPE
-
-#endif // optional_CONFIG_MAX_ALIGN_HACK
-
-/// C++03 constructed union to hold value.
-
-template< typename T >
-union storage_t
-{
-//private:
-//    template< typename > friend class optional;
-
-    typedef T value_type;
-
-    storage_t() optional_is_default
-
-    explicit storage_t( value_type const & v )
-    {
-        construct_value( v );
-    }
-
-    void construct_value( value_type const & v )
-    {
-        ::new( value_ptr() ) value_type( v );
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    explicit storage_t( value_type && v )
-    {
-        construct_value( std::move( v ) );
-    }
-
-    void construct_value( value_type && v )
-    {
-        ::new( value_ptr() ) value_type( std::move( v ) );
-    }
-
-    template< class... Args >
-    storage_t( nonstd_lite_in_place_t(T), Args&&... args )
-    {
-        emplace( std::forward<Args>(args)... );
-    }
-
-    template< class... Args >
-    void emplace( Args&&... args )
-    {
-        ::new( value_ptr() ) value_type( std::forward<Args>(args)... );
-    }
-
-    template< class U, class... Args >
-    void emplace( std::initializer_list<U> il, Args&&... args )
-    {
-        ::new( value_ptr() ) value_type( il, std::forward<Args>(args)... );
-    }
-
-#endif
-
-    void destruct_value()
-    {
-        value_ptr()->~T();
-    }
-
-    optional_nodiscard value_type const * value_ptr() const
-    {
-        return as<value_type>();
-    }
-
-    value_type * value_ptr()
-    {
-        return as<value_type>();
-    }
-
-    optional_nodiscard value_type const & value() const optional_ref_qual
-    {
-        return * value_ptr();
-    }
-
-    value_type & value() optional_ref_qual
-    {
-        return * value_ptr();
-    }
-
-#if optional_HAVE( REF_QUALIFIER )
-
-    optional_nodiscard value_type const && value() const optional_refref_qual
-    {
-        return std::move( value() );
-    }
-
-    value_type && value() optional_refref_qual
-    {
-        return std::move( value() );
-    }
-
-#endif
-
-#if optional_CPP11_OR_GREATER
-
-    using aligned_storage_t = typename std::aligned_storage< sizeof(value_type), alignof(value_type) >::type;
-    aligned_storage_t data;
-
-#elif optional_CONFIG_MAX_ALIGN_HACK
-
-    typedef struct { unsigned char data[ sizeof(value_type) ]; } aligned_storage_t;
-
-    max_align_t hack;
-    aligned_storage_t data;
-
-#else
-    typedef optional_ALIGN_AS(value_type) align_as_type;
-
-    typedef struct { align_as_type data[ 1 + ( sizeof(value_type) - 1 ) / sizeof(align_as_type) ]; } aligned_storage_t;
-    aligned_storage_t data;
-
-#   undef optional_ALIGN_AS
-
-#endif // optional_CONFIG_MAX_ALIGN_HACK
-
-    optional_nodiscard void * ptr() optional_noexcept
-    {
-        return &data;
-    }
-
-    optional_nodiscard void const * ptr() const optional_noexcept
-    {
-        return &data;
-    }
-
-    template <typename U>
-    optional_nodiscard U * as()
-    {
-        return reinterpret_cast<U*>( ptr() );
-    }
-
-    template <typename U>
-    optional_nodiscard U const * as() const
-    {
-        return reinterpret_cast<U const *>( ptr() );
-    }
-};
-
-} // namespace detail
-
-/// disengaged state tag
-
-struct nullopt_t
-{
-    struct init{};
-    explicit optional_constexpr nullopt_t( init /*unused*/ ) optional_noexcept {}
-};
-
-#if optional_HAVE( CONSTEXPR_11 )
-constexpr nullopt_t nullopt{ nullopt_t::init{} };
-#else
-// extra parenthesis to prevent the most vexing parse:
-const nullopt_t nullopt(( nullopt_t::init() ));
-#endif
-
-/// optional access error
-
-#if ! optional_CONFIG_NO_EXCEPTIONS
-
-class bad_optional_access : public std::logic_error
-{
-public:
-  explicit bad_optional_access()
-  : logic_error( "bad optional access" ) {}
-};
-
-#endif //optional_CONFIG_NO_EXCEPTIONS
-
-/// optional
-
-template< typename T>
-class optional
-{
-    optional_static_assert(( !std::is_same<typename std::remove_cv<T>::type, nullopt_t>::value  ),
-        "T in optional<T> must not be of type 'nullopt_t'.")
-
-    optional_static_assert(( !std::is_same<typename std::remove_cv<T>::type, in_place_t>::value ),
-        "T in optional<T> must not be of type 'in_place_t'.")
-
-    optional_static_assert(( std::is_object<T>::value && std::is_destructible<T>::value && !std::is_array<T>::value ),
-        "T in optional<T> must meet the Cpp17Destructible requirements.")
-
-private:
-    template< typename > friend class optional;
-
-    typedef void (optional::*safe_bool)() const;
-
-public:
-    typedef T value_type;
-
-     // x.x.3.1, constructors
-
-    // 1a - default construct
-    optional_constexpr optional() optional_noexcept
-    : has_value_( false )
-    , contained()
-    {}
-
-    // 1b - construct explicitly empty
-    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
-    optional_constexpr optional( nullopt_t /*unused*/ ) optional_noexcept
-    : has_value_( false )
-    , contained()
-    {}
-
-    // 2 - copy-construct
-#if optional_CPP11_OR_GREATER
-    // template< typename U = T
-    //     optional_REQUIRES_T(
-    //         std::is_copy_constructible<U>::value
-    //         || std11::is_trivially_copy_constructible<U>::value
-    //     )
-    // >
-#endif
-    optional_constexpr14 optional( optional const & other )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( other.contained.value() );
-        }
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    // 3 (C++11) - move-construct from optional
-    template< typename U = T
-        optional_REQUIRES_T(
-            std11::is_move_constructible<U>::value
-            || std11::is_trivially_move_constructible<U>::value
-        )
-    >
-    optional_constexpr14 optional( optional && other )
-    // NOLINTNEXTLINE( performance-noexcept-move-constructor )
-        noexcept( std11::is_nothrow_move_constructible<T>::value )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( std::move( other.contained.value() ) );
-        }
-    }
-
-    // 4a (C++11) - explicit converting copy-construct from optional
-    template< typename U
-        optional_REQUIRES_T(
-            std::is_constructible<T, U const &>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            && !std::is_convertible<               U const & , T>::value /*=> explicit */
-        )
-    >
-    explicit optional( optional<U> const & other )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( T{ other.contained.value() } );
-        }
-    }
-#endif // optional_CPP11_OR_GREATER
-
-    // 4b (C++98 and later) - non-explicit converting copy-construct from optional
-    template< typename U
-#if optional_CPP11_OR_GREATER
-        optional_REQUIRES_T(
-            std::is_constructible<T, U const &>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            &&  std::is_convertible<               U const & , T>::value /*=> non-explicit */
-        )
-#endif // optional_CPP11_OR_GREATER
-    >
-    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
-    /*non-explicit*/ optional( optional<U> const & other )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( other.contained.value() );
-        }
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    // 5a (C++11) - explicit converting move-construct from optional
-    template< typename U
-        optional_REQUIRES_T(
-            std::is_constructible<T, U &&>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            && !std::is_convertible<                     U &&, T>::value /*=> explicit */
-        )
-    >
-    explicit optional( optional<U> && other
-    )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( T{ std::move( other.contained.value() ) } );
-        }
-    }
-
-    // 5a (C++11) - non-explicit converting move-construct from optional
-    template< typename U
-        optional_REQUIRES_T(
-            std::is_constructible<T, U &&>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            &&  std::is_convertible<                     U &&, T>::value /*=> non-explicit */
-        )
-    >
-    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
-    /*non-explicit*/ optional( optional<U> && other )
-    : has_value_( other.has_value() )
-    {
-        if ( other.has_value() )
-        {
-            contained.construct_value( std::move( other.contained.value() ) );
-        }
-    }
-
-    // 6 (C++11) - in-place construct
-    template< typename... Args
-        optional_REQUIRES_T(
-            std::is_constructible<T, Args&&...>::value
-        )
-    >
-    optional_constexpr explicit optional( nonstd_lite_in_place_t(T), Args&&... args )
-    : has_value_( true )
-    , contained( in_place, std::forward<Args>(args)... )
-    {}
-
-    // 7 (C++11) - in-place construct,  initializer-list
-    template< typename U, typename... Args
-        optional_REQUIRES_T(
-            std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value
-        )
-    >
-    optional_constexpr explicit optional( nonstd_lite_in_place_t(T), std::initializer_list<U> il, Args&&... args )
-    : has_value_( true )
-    , contained( T( il, std::forward<Args>(args)...) )
-    {}
-
-    // 8a (C++11) - explicit move construct from value
-    template< typename U = T
-        optional_REQUIRES_T(
-            std::is_constructible<T, U&&>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
-            && !std::is_convertible<U&&, T>::value /*=> explicit */
-        )
-    >
-    optional_constexpr explicit optional( U && value )
-    : has_value_( true )
-    , contained( nonstd_lite_in_place(T), std::forward<U>( value ) )
-    {}
-
-    // 8b (C++11) - non-explicit move construct from value
-    template< typename U = T
-        optional_REQUIRES_T(
-            std::is_constructible<T, U&&>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
-            && std::is_convertible<U&&, T>::value /*=> non-explicit */
-        )
-    >
-    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
-    optional_constexpr /*non-explicit*/ optional( U && value )
-    : has_value_( true )
-    , contained( nonstd_lite_in_place(T), std::forward<U>( value ) )
-    {}
-
-#else // optional_CPP11_OR_GREATER
-
-    // 8 (C++98)
-    optional( value_type const & value )
-    : has_value_( true )
-    , contained( value )
-    {}
-
-#endif // optional_CPP11_OR_GREATER
-
-    // x.x.3.2, destructor
-
-    ~optional()
-    {
-        if ( has_value() )
-        {
-            contained.destruct_value();
-        }
-    }
-
-    // x.x.3.3, assignment
-
-    // 1 (C++98and later) -  assign explicitly empty
-    optional & operator=( nullopt_t /*unused*/) optional_noexcept
-    {
-        reset();
-        return *this;
-    }
-
-    // 2 (C++98and later) - copy-assign from optional
-#if optional_CPP11_OR_GREATER
-    // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-    optional_REQUIRES_R(
-        optional &,
-        true
-//      std::is_copy_constructible<T>::value
-//      && std::is_copy_assignable<T>::value
-    )
-    operator=( optional const & other )
-        noexcept(
-            std11::is_nothrow_move_assignable<T>::value
-            && std11::is_nothrow_move_constructible<T>::value
-        )
-#else
-    optional & operator=( optional const & other )
-#endif
-    {
-        if      ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
-        else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( *other ); }
-        else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = *other; }
-        return *this;
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    // 3 (C++11) - move-assign from optional
-    // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-    optional_REQUIRES_R(
-        optional &,
-        true
-//      std11::is_move_constructible<T>::value
-//      && std::is_move_assignable<T>::value
-    )
-    operator=( optional && other ) noexcept
-    {
-        if      ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
-        else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std::move( *other ) ); }
-        else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = std::move( *other ); }
-        return *this;
-    }
-
-    // 4 (C++11) - move-assign from value
-    template< typename U = T >
-        // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-        optional_REQUIRES_R(
-            optional &,
-            std::is_constructible<T , U>::value
-            && std11::is_assignable<T&, U>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
-            && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
-            && !(std::is_scalar<T>::value && std::is_same<T, typename std::decay<U>::type>::value)
-        )
-    operator=( U && value )
-    {
-        if ( has_value() )
-        {
-            contained.value() = std::forward<U>( value );
-        }
-        else
-        {
-            initialize( T( std::forward<U>( value ) ) );
-        }
-        return *this;
-    }
-
-#else // optional_CPP11_OR_GREATER
-
-    // 4 (C++98) - copy-assign from value
-    template< typename U /*= T*/ >
-    optional & operator=( U const & value )
-    {
-        if ( has_value() ) contained.value() = value;
-        else               initialize( T( value ) );
-        return *this;
-    }
-
-#endif // optional_CPP11_OR_GREATER
-
-    // 5 (C++98 and later) - converting copy-assign from optional
-    template< typename U >
-#if optional_CPP11_OR_GREATER
-        // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-        optional_REQUIRES_R(
-            optional&,
-            std::is_constructible<  T , U const &>::value
-            &&  std11::is_assignable< T&, U const &>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            && !std11::is_assignable<  T&, optional<U> &          >::value
-            && !std11::is_assignable<  T&, optional<U> &&         >::value
-            && !std11::is_assignable<  T&, optional<U> const &    >::value
-            && !std11::is_assignable<  T&, optional<U> const &&   >::value
-        )
-#else
-    optional&
-#endif // optional_CPP11_OR_GREATER
-    operator=( optional<U> const & other )
-    {
-        return *this = optional( other );
-    }
-
-#if optional_CPP11_OR_GREATER
-
-    // 6 (C++11) -  converting move-assign from optional
-    template< typename U >
-        // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
-        optional_REQUIRES_R(
-            optional&,
-            std::is_constructible<  T , U>::value
-            &&  std11::is_assignable< T&, U>::value
-            && !std::is_constructible<T, optional<U> &          >::value
-            && !std::is_constructible<T, optional<U> &&         >::value
-            && !std::is_constructible<T, optional<U> const &    >::value
-            && !std::is_constructible<T, optional<U> const &&   >::value
-            && !std::is_convertible<     optional<U> &       , T>::value
-            && !std::is_convertible<     optional<U> &&      , T>::value
-            && !std::is_convertible<     optional<U> const & , T>::value
-            && !std::is_convertible<     optional<U> const &&, T>::value
-            && !std11::is_assignable<  T&, optional<U> &          >::value
-            && !std11::is_assignable<  T&, optional<U> &&         >::value
-            && !std11::is_assignable<  T&, optional<U> const &    >::value
-            && !std11::is_assignable<  T&, optional<U> const &&   >::value
-        )
-    operator=( optional<U> && other )
-    {
-        return *this = optional( std::move( other ) );
-    }
-
-    // 7 (C++11) - emplace
-    template< typename... Args
-        optional_REQUIRES_T(
-            std::is_constructible<T, Args&&...>::value
-        )
-    >
-    T& emplace( Args&&... args )
-    {
-        *this = nullopt;
-        contained.emplace( std::forward<Args>(args)...  );
-        has_value_ = true;
-        return contained.value();
-    }
-
-    // 8 (C++11) - emplace, initializer-list
-    template< typename U, typename... Args
-        optional_REQUIRES_T(
-            std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value
-        )
-    >
-    T& emplace( std::initializer_list<U> il, Args&&... args )
-    {
-        *this = nullopt;
-        contained.emplace( il, std::forward<Args>(args)...  );
-        has_value_ = true;
-        return contained.value();
-    }
-
-#endif // optional_CPP11_OR_GREATER
-
-    // x.x.3.4, swap
-
-    void swap( optional & other )
-#if optional_CPP11_OR_GREATER
-        noexcept(
-            std11::is_nothrow_move_constructible<T>::value
-            && std17::is_nothrow_swappable<T>::value
-        )
-#endif
-    {
-        using std::swap;
-        if      ( (has_value() == true ) && (other.has_value() == true ) ) { swap( **this, *other ); }
-        else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std11::move(*other) ); other.reset(); }
-        else if ( (has_value() == true ) && (other.has_value() == false) ) { other.initialize( std11::move(**this) ); reset(); }
-    }
-
-    // x.x.3.5, observers
-
-    optional_constexpr value_type const * operator ->() const
-    {
-        return assert( has_value() ),
-            contained.value_ptr();
-    }
-
-    optional_constexpr14 value_type * operator ->()
-    {
-        return assert( has_value() ),
-            contained.value_ptr();
-    }
-
-    optional_constexpr value_type const & operator *() const optional_ref_qual
-    {
-        return assert( has_value() ),
-            contained.value();
-    }
-
-    optional_constexpr14 value_type & operator *() optional_ref_qual
-    {
-        return assert( has_value() ),
-            contained.value();
-    }
-
-#if optional_HAVE( REF_QUALIFIER )
-
-    optional_constexpr value_type const && operator *() const optional_refref_qual
-    {
-        return std::move( **this );
-    }
-
-    optional_constexpr14 value_type && operator *() optional_refref_qual
-    {
-        return std::move( **this );
-    }
-
-#endif
-
-#if optional_CPP11_OR_GREATER
-    optional_constexpr explicit operator bool() const optional_noexcept
-    {
-        return has_value();
-    }
-#else
-    optional_constexpr operator safe_bool() const optional_noexcept
-    {
-        return has_value() ? &optional::this_type_does_not_support_comparisons : 0;
-    }
-#endif
-
-    // NOLINTNEXTLINE( modernize-use-nodiscard )
-    /*optional_nodiscard*/ optional_constexpr bool has_value() const optional_noexcept
-    {
-        return has_value_;
-    }
-
-    // NOLINTNEXTLINE( modernize-use-nodiscard )
-    /*optional_nodiscard*/ optional_constexpr14 value_type const & value() const optional_ref_qual
-    {
-#if optional_CONFIG_NO_EXCEPTIONS
-        assert( has_value() );
-#else
-        if ( ! has_value() )
-        {
-            throw bad_optional_access();
-        }
-#endif
-        return contained.value();
-    }
-
-    optional_constexpr14 value_type & value() optional_ref_qual
-    {
-#if optional_CONFIG_NO_EXCEPTIONS
-        assert( has_value() );
-#else
-        if ( ! has_value() )
-        {
-            throw bad_optional_access();
-        }
-#endif
-        return contained.value();
-    }
-
-#if optional_HAVE( REF_QUALIFIER )  &&  ( !optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490 )
-
-    // NOLINTNEXTLINE( modernize-use-nodiscard )
-    /*optional_nodiscard*/ optional_constexpr value_type const && value() const optional_refref_qual
-    {
-        return std::move( value() );
-    }
-
-    optional_constexpr14 value_type && value() optional_refref_qual
-    {
-        return std::move( value() );
-    }
-
-#endif
-
-#if optional_HAVE( REF_QUALIFIER )
-
-    template< typename U >
-    optional_constexpr value_type value_or( U && v ) const optional_ref_qual
-    {
-        return has_value() ? contained.value() : static_cast<T>(std::forward<U>( v ) );
-    }
-
-    template< typename U >
-    optional_constexpr14 value_type value_or( U && v ) optional_refref_qual
-    {
-#if optional_COMPILER_CLANG_VERSION
-        return has_value() ? /*std::move*/( contained.value() ) : static_cast<T>(std::forward<U>( v ) );
-#else
-        return has_value() ? std::move( contained.value() ) : static_cast<T>(std::forward<U>( v ) );
-#endif
-    }
-
-#else
-
-    template< typename U >
-    optional_constexpr value_type value_or( U const & v ) const
-    {
-        return has_value() ? contained.value() : static_cast<value_type>( v );
-    }
-
-#endif // optional_HAVE( REF_QUALIFIER )
-
-#if !optional_CONFIG_NO_EXTENSIONS
-#if  optional_HAVE( REF_QUALIFIER )
-
-    template< typename F >
-    optional_constexpr value_type value_or_eval( F f ) const &
-    {
-        return has_value() ? contained.value() : f();
-    }
-
-    template< typename F >
-    optional_constexpr14 value_type value_or_eval( F f ) &&
-    {
-        if ( has_value() )
-        {
-            return std::move( contained.value() );
-        }
-        else
-        {
-            return f();
-        }
-    }
-
-#else
-
-    template< typename F >
-    optional_constexpr value_type value_or_eval( F f ) const
-    {
-        return has_value() ? contained.value() : f();
-    }
-
-#endif //  optional_HAVE( REF_QUALIFIER )
-#endif // !optional_CONFIG_NO_EXTENSIONS
-
-    // x.x.3.6, modifiers
-
-    void reset() optional_noexcept
-    {
-        if ( has_value() )
-        {
-            contained.destruct_value();
-        }
-
-        has_value_ = false;
-    }
-
-private:
-    void this_type_does_not_support_comparisons() const {}
-
-    template< typename V >
-    void initialize( V const & value )
-    {
-        assert( ! has_value()  );
-        contained.construct_value( value );
-        has_value_ = true;
-    }
-
-#if optional_CPP11_OR_GREATER
-    template< typename V >
-    void initialize( V && value )
-    {
-        assert( ! has_value()  );
-        contained.construct_value( std::move( value ) );
-        has_value_ = true;
-    }
-
-#endif
-
-private:
-    bool has_value_;
-    detail::storage_t< value_type > contained;
-
-};
-
-// Relational operators
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator==( optional<T> const & x, optional<U> const & y )
-{
-    return bool(x) != bool(y) ? false : !bool( x ) ? true : *x == *y;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator!=( optional<T> const & x, optional<U> const & y )
-{
-    return !(x == y);
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator<( optional<T> const & x, optional<U> const & y )
-{
-    return (!y) ? false : (!x) ? true : *x < *y;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator>( optional<T> const & x, optional<U> const & y )
-{
-    return (y < x);
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator<=( optional<T> const & x, optional<U> const & y )
-{
-    return !(y < x);
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator>=( optional<T> const & x, optional<U> const & y )
-{
-    return !(x < y);
-}
-
-// Comparison with nullopt
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator==( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return (!x);
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator==( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
-{
-    return (!x);
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator!=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return bool(x);
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator!=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
-{
-    return bool(x);
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator<( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return false;
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator<( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
-{
-    return bool(x);
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator<=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return (!x);
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator<=( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
-{
-    return true;
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator>( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return bool(x);
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator>( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
-{
-    return false;
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator>=( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
-{
-    return true;
-}
-
-template< typename T >
-optional_nodiscard optional_constexpr bool operator>=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
-{
-    return (!x);
-}
-
-// Comparison with T
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator==( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x == v : false;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator==( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v == *x : false;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator!=( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x != v : true;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator!=( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v != *x : true;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator<( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x < v : true;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator<( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v < *x : false;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator<=( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x <= v : true;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator<=( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v <= *x : false;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator>( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x > v : false;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator>( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v > *x : true;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator>=( optional<T> const & x, U const & v )
-{
-    return bool(x) ? *x >= v : false;
-}
-
-template< typename T, typename U >
-optional_nodiscard optional_constexpr bool operator>=( U const & v, optional<T> const & x )
-{
-    return bool(x) ? v >= *x : true;
-}
-
-// Specialized algorithms
-
-template< typename T
-#if optional_CPP11_OR_GREATER
-    optional_REQUIRES_T(
-        std11::is_move_constructible<T>::value
-        && std17::is_swappable<T>::value )
-#endif
->
-void swap( optional<T> & x, optional<T> & y )
-#if optional_CPP11_OR_GREATER
-    noexcept( noexcept( x.swap(y) ) )
-#endif
-{
-    x.swap( y );
-}
-
-#if optional_CPP11_OR_GREATER
-
-template< typename T >
-optional_constexpr optional< typename std::decay<T>::type > make_optional( T && value )
-{
-    return optional< typename std::decay<T>::type >( std::forward<T>( value ) );
-}
-
-template< typename T, typename...Args >
-optional_constexpr optional<T> make_optional( Args&&... args )
-{
-    return optional<T>( nonstd_lite_in_place(T), std::forward<Args>(args)...);
-}
-
-template< typename T, typename U, typename... Args >
-optional_constexpr optional<T> make_optional( std::initializer_list<U> il, Args&&... args )
-{
-    return optional<T>( nonstd_lite_in_place(T), il, std::forward<Args>(args)...);
-}
-
-#else
-
-template< typename T >
-optional<T> make_optional( T const & value )
-{
-    return optional<T>( value );
-}
-
-#endif // optional_CPP11_OR_GREATER
-
-} // namespace optional_lite
-
-using optional_lite::optional;
-using optional_lite::nullopt_t;
-using optional_lite::nullopt;
-
-#if ! optional_CONFIG_NO_EXCEPTIONS
-using optional_lite::bad_optional_access;
-#endif
-
-using optional_lite::make_optional;
-
-} // namespace nonstd
-
-#if optional_CPP11_OR_GREATER
-
-// specialize the std::hash algorithm:
-
-namespace std {
-
-template< class T >
-struct hash< nonstd::optional<T> >
-{
-public:
-    std::size_t operator()( nonstd::optional<T> const & v ) const optional_noexcept
-    {
-        return bool( v ) ? std::hash<T>{}( *v ) : 0;
-    }
-};
-
-} //namespace std
-
-#endif // optional_CPP11_OR_GREATER
-
-#if defined(__clang__)
-# pragma clang diagnostic pop
-#elif defined(__GNUC__)
-# pragma GCC   diagnostic pop
-#elif defined(_MSC_VER )
-# pragma warning( pop )
-#endif
-
-#endif // optional_USES_STD_OPTIONAL
-
-#endif // NONSTD_OPTIONAL_LITE_HPP
diff --git a/src/cpp-common/string_view.hpp b/src/cpp-common/string_view.hpp
deleted file mode 100644 (file)
index 4c26e49..0000000
+++ /dev/null
@@ -1,1428 +0,0 @@
-/*
- * Copyright (c) 2016 Matthew Rodusek <http://rodusek.me>
- *
- * SPDX-License-Identifier: MIT
- */
-
-/**
- * \file string_view.hpp
- *
- * \brief This header contains the definition of the string_view type, as
- *        described by the C++17 standard.
- *
- * \author Matthew Rodusek (matthew.rodusek@gmail.com)
- * \copyright Matthew Rodusek
- */
-
-/*
- * The MIT License (MIT)
- *
- * Licensed under the MIT License <http://opensource.org/licenses/MIT>.
- * Copyright (c) 2016 Matthew Rodusek <http://rodusek.me>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-#ifndef BPSTD_STRING_VIEW_HPP
-#define BPSTD_STRING_VIEW_HPP
-
-#include <algorithm>  // std::
-#include <string>     // std::char_traits
-#include <ostream>    // std::basic_ostream
-#include <cstddef>    // std::size_t
-#include <memory>     // std::allocator
-#include <stdexcept>  // std::out_of_range
-#include <iterator>   // std::reverse_iterator
-namespace bpstd { // back-port std
-
-  ////////////////////////////////////////////////////////////////////////////
-  /// \brief A wrapper around non-owned strings.
-  ///
-  /// This is an implementation of the C++17 string_view proposal
-  ///
-  /// \ingroup core
-  ////////////////////////////////////////////////////////////////////////////
-  template<
-    typename CharT,
-    typename Traits = std::char_traits<CharT>
-  >
-  class basic_string_view final
-  {
-    //------------------------------------------------------------------------
-    // Public Member Types
-    //------------------------------------------------------------------------
-  public:
-
-    using char_type   = CharT;
-    using traits_type = Traits;
-    using size_type   = std::size_t;
-
-    using value_type      = CharT;
-    using reference       = value_type&;
-    using const_reference = const value_type&;
-    using pointer         = value_type*;
-    using const_pointer   = const value_type*;
-
-    using iterator       = const CharT*;
-    using const_iterator = const CharT*;
-    using reverse_iterator = std::reverse_iterator<iterator>;
-    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-
-    //------------------------------------------------------------------------
-    // Public Members
-    //------------------------------------------------------------------------
-  public:
-
-    static constexpr size_type npos = size_type(-1);
-
-    //------------------------------------------------------------------------
-    // Constructors
-    //------------------------------------------------------------------------
-  public:
-
-    /// \brief Default constructs a basic_string_view without any content
-    constexpr basic_string_view() noexcept;
-
-    /// \brief Constructs a basic_string_view by copying another one
-    ///
-    /// \param other the string view being copied
-    constexpr basic_string_view(const basic_string_view& other) noexcept = default;
-
-    /// \brief Constructs a basic_string_view by moving anothe rone
-    ///
-    /// \param other the string view being moved
-    constexpr basic_string_view(basic_string_view&& other) noexcept = default;
-
-    /// \brief Constructs a basic_string_view from a std::basic_string
-    ///
-    /// \param str the string to view
-    template<typename Allocator>
-    basic_string_view(const std::basic_string<CharT,Traits,Allocator>& str) noexcept;
-
-    /// \brief Constructs a basic_string_view from an ansi-string
-    ///
-    /// \param str the string to view
-    constexpr basic_string_view(const char_type* str) noexcept;
-
-    /// \brief Constructs a basic_string_view from an ansi string of a given size
-    ///
-    /// \param str the string to view
-    /// \param count the size of the string
-    constexpr basic_string_view(const char_type* str, size_type count) noexcept;
-
-    //------------------------------------------------------------------------
-    // Assignment
-    //------------------------------------------------------------------------
-  public:
-
-    /// \brief Assigns a basic_string_view from an ansi-string
-    ///
-    /// \param view the string to view
-    /// \return reference to \c (*this)
-    basic_string_view& operator=(const basic_string_view& view) = default;
-
-    //------------------------------------------------------------------------
-    // Capacity
-    //------------------------------------------------------------------------
-  public:
-
-    /// \brief Returns the length of the string, in terms of bytes
-    ///
-    /// \return the length of the string, in terms of bytes
-    constexpr size_type size() const noexcept;
-
-    /// \copydoc basic_string_view::size
-    constexpr size_type length() const noexcept;
-
-    /// \brief The largest possible number of char-like objects that can be
-    ///        referred to by a basic_string_view.
-    /// \return Maximum number of characters
-    constexpr size_type max_size() const noexcept;
-
-    /// \brief Returns whether the basic_string_view is empty
-    ///        (i.e. whether its length is 0).
-    ///
-    /// \return whether the basic_string_view is empty
-    constexpr bool empty() const noexcept;
-
-    //------------------------------------------------------------------------
-    // Element Access
-    //------------------------------------------------------------------------
-  public:
-
-    /// \brief Gets the ansi-string of the current basic_string_view
-    ///
-    /// \return the ansi-string pointer
-    constexpr const char_type* c_str() const noexcept;
-
-    /// \brief Gets the data of the current basic_string_view
-    ///
-    /// \note This is an alias of #c_str
-    ///
-    /// \return the data this basic_string_view contains
-    constexpr const char_type* data() const noexcept;
-
-    /// \brief Accesses the element at index \p pos
-    ///
-    /// \param pos the index to access
-    /// \return const reference to the character
-    constexpr const_reference operator[](size_type pos) const noexcept;
-
-    /// \brief Accesses the element at index \p pos
-    ///
-    /// \param pos the index to access
-    /// \return const reference to the character
-    constexpr const_reference at(size_type pos) const;
-
-    /// \brief Access the first character of the string
-    ///
-    /// \note Undefined behavior if basic_string_view is empty
-    ///
-    /// \return reference to the first character of the string
-    constexpr const_reference front() const noexcept;
-
-    /// \brief References the last character of the string
-    ///
-    /// \note Undefined behavior if basic_string_view is empty
-    ///
-    /// \return reference to the last character of the string
-    constexpr const_reference back() const noexcept;
-
-    //------------------------------------------------------------------------
-    // Modifiers
-    //------------------------------------------------------------------------
-  public:
-
-    /// \brief Moves the start of the view forward by n characters.
-    ///
-    /// The behavior is undefined if n > size().
-    ///
-    /// \param n number of characters to remove from the start of the view
-    void remove_prefix(size_type n) noexcept;
-
-    /// \brief Moves the end of the view back by n characters.
-    ///
-    /// The behavior is undefined if n > size().
-    ///
-    /// \param n number of characters to remove from the end of the view
-    void remove_suffix(size_type n) noexcept;
-
-    /// \brief Exchanges the view with that of v.
-    ///
-    /// \param v view to swap with
-    void swap(basic_string_view& v) noexcept;
-
-    //------------------------------------------------------------------------
-    // Conversions
-    //------------------------------------------------------------------------
-  public:
-
-    /// \brief Creates a basic_string with a copy of the content of the current view.
-    ///
-    /// \tparam Allocator type used to allocate internal storage
-    /// \param a Allocator instance to use for allocating the new string
-    ///
-    /// \return A basic_string containing a copy of the characters of the current view.
-    template<class Allocator = std::allocator<CharT>>
-    constexpr std::basic_string<CharT, Traits, Allocator>
-      to_string(const Allocator& a = Allocator()) const;
-
-    /// \copydoc basic_string_view::to_string
-    template<class Allocator>
-    explicit constexpr operator std::basic_string<CharT, Traits, Allocator>() const;
-
-    //------------------------------------------------------------------------
-    // Operations
-    //------------------------------------------------------------------------
-  public:
-
-    /// \brief Copies the substring [pos, pos + rcount) to the character string pointed
-    ///        to by dest, where rcount is the smaller of count and size() - pos.
-    ///
-    /// \param dest pointer to the destination character string
-    /// \param count requested substring length
-    /// \param pos position of the first character
-    size_type copy( char_type* dest,
-                    size_type count = npos,
-                    size_type pos = 0 ) const;
-
-    /// \brief Returns a substring of this viewed string
-    ///
-    /// \param pos the position of the first character in the substring
-    /// \param len the length of the substring
-    /// \return the created substring
-    basic_string_view substr(size_t pos = 0, size_t len = npos) const;
-
-    //------------------------------------------------------------------------
-
-    /// \brief Compares two character sequences
-    ///
-    /// \param v view to compare
-    /// \return negative value if this view is less than the other character
-    ///         sequence, zero if the both character sequences are equal, positive
-    ///         value if this view is greater than the other character sequence.
-    int compare(basic_string_view v) const noexcept;
-
-    /// \brief Compares two character sequences
-    ///
-    /// \param pos   position of the first character in this view to compare
-    /// \param count number of characters of this view to compare
-    /// \param v     view to compare
-    /// \return negative value if this view is less than the other character
-    ///         sequence, zero if the both character sequences are equal, positive
-    ///         value if this view is greater than the other character sequence.
-    int compare(size_type pos, size_type count, basic_string_view v) const;
-
-    /// \brief Compares two character sequences
-    ///
-    /// \param pos1   position of the first character in this view to compare
-    /// \param count1 number of characters of this view to compare
-    /// \param v      view to compare
-    /// \param pos2   position of the second character in this view to compare
-    /// \param count2 number of characters of the given view to compare
-    /// \return negative value if this view is less than the other character
-    ///         sequence, zero if the both character sequences are equal, positive
-    ///         value if this view is greater than the other character sequence.
-    int compare( size_type pos1, size_type count1, basic_string_view v,
-                 size_type pos2, size_type count2 ) const;
-
-    /// \brief Compares two character sequences
-    ///
-    /// \param s pointer to the character string to compare to
-    /// \return negative value if this view is less than the other character
-    ///         sequence, zero if the both character sequences are equal, positive
-    ///         value if this view is greater than the other character sequence.
-    int compare(const char_type* s) const;
-
-    /// \brief Compares two character sequences
-    ///
-    /// \param pos   position of the first character in this view to compare
-    /// \param count number of characters of this view to compare
-    /// \param s pointer to the character string to compare to
-    /// \return negative value if this view is less than the other character
-    ///         sequence, zero if the both character sequences are equal, positive
-    ///         value if this view is greater than the other character sequence.
-    int compare(size_type pos, size_type count, const char_type* s) const;
-
-    /// \brief Compares two character sequences
-    ///
-    /// \param pos   position of the first character in this view to compare
-    /// \param count1 number of characters of this view to compare
-    /// \param s pointer to the character string to compare to
-    /// \param count2 number of characters of the given view to compare
-    /// \return negative value if this view is less than the other character
-    ///         sequence, zero if the both character sequences are equal, positive
-    ///         value if this view is greater than the other character sequence.
-    int compare( size_type pos, size_type count1, const char_type* s,
-                 size_type count2 ) const;
-
-    //------------------------------------------------------------------------
-
-    size_type find(basic_string_view v, size_type pos = 0) const;
-
-    size_type find(char_type c, size_type pos = 0) const;
-
-    size_type find(const char_type* s, size_type pos, size_type count) const;
-
-    size_type find(const char_type* s, size_type pos = 0) const;
-
-    //------------------------------------------------------------------------
-
-    size_type rfind(basic_string_view v, size_type pos = npos) const;
-
-    size_type rfind(char_type c, size_type pos = npos) const;
-
-    size_type rfind(const char_type* s, size_type pos, size_type count) const;
-
-    size_type rfind(const char_type* s, size_type pos = npos) const;
-
-    //------------------------------------------------------------------------
-
-    size_type find_first_of(basic_string_view v, size_type pos = 0) const;
-
-    size_type find_first_of(char_type c, size_type pos = 0) const;
-
-    size_type find_first_of(const char_type* s, size_type pos, size_type count) const;
-
-    size_type find_first_of(const char_type* s, size_type pos = 0) const;
-
-    //------------------------------------------------------------------------
-
-    size_type find_last_of(basic_string_view v, size_type pos = npos) const;
-
-    size_type find_last_of(char_type c, size_type pos = npos) const;
-
-    size_type find_last_of(const char_type* s, size_type pos, size_type count) const;
-
-    size_type find_last_of(const char_type* s, size_type pos = npos) const;
-
-    //------------------------------------------------------------------------
-
-    size_type find_first_not_of(basic_string_view v, size_type pos = 0) const;
-
-    size_type find_first_not_of(char_type c, size_type pos = 0) const;
-
-    size_type find_first_not_of(const char_type* s, size_type pos, size_type count) const;
-
-    size_type find_first_not_of(const char_type* s, size_type pos = 0) const;
-
-    //------------------------------------------------------------------------
-
-    size_type find_last_not_of(basic_string_view v, size_type pos = npos) const;
-
-    size_type find_last_not_of(char_type c, size_type pos = npos) const;
-
-    size_type find_last_not_of(const char_type* s, size_type pos, size_type count) const;
-
-    size_type find_last_not_of(const char_type* s, size_type pos = npos) const;
-
-    //------------------------------------------------------------------------
-    // Iterators
-    //------------------------------------------------------------------------
-  public:
-
-    /// \{
-    /// \brief Retrieves the begin iterator for this basic_string_view
-    ///
-    /// \return the begin iterator
-    const_iterator begin() const noexcept;
-    const_iterator cbegin() const noexcept;
-    /// \}
-
-    /// \{
-    /// \brief Retrieves the end iterator for this basic_string_view
-    ///
-    /// \return the end iterator
-    const_iterator end() const noexcept;
-    const_iterator cend() const noexcept;
-    /// \}
-
-    /// \{
-    /// \brief Retrieves the reverse begin iterator for this basic_string_view
-    ///
-    /// \return the reverse begin iterator
-    const_reverse_iterator rbegin() const noexcept;
-    const_reverse_iterator rend() const noexcept;
-    /// \}
-
-    /// \{
-    /// \brief Retrieves the reverse end iterator for this basic_string_view
-    ///
-    /// \return the reverse end iterator
-    const_reverse_iterator crbegin() const noexcept;
-    const_reverse_iterator crend() const noexcept;
-    /// \}
-
-    //------------------------------------------------------------------------
-    // Private Member
-    //------------------------------------------------------------------------
-  private:
-
-    const char_type* m_str;  ///< The internal string type
-    size_type        m_size; ///< The size of this string
-
-    /// \brief Checks whether \p c is one of the characters in \p str
-    ///
-    /// \param c the character to check
-    /// \param str the characters to compare against
-    /// \return true if \p c is one of the characters in \p str
-    static bool is_one_of(CharT c, basic_string_view str);
-  };
-
-  template <typename CharT, typename Traits>
-  const typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::npos;
-
-  //--------------------------------------------------------------------------
-  // Public Functions
-  //--------------------------------------------------------------------------
-
-  /// \brief Overload for ostream output of basic_string_view
-  ///
-  /// \param o   The output stream to print to
-  /// \param str the string to print
-  /// \return reference to the output stream
-  template<typename CharT, typename Traits>
-  std::basic_ostream<CharT,Traits>& operator<<(std::basic_ostream<CharT,Traits>& o,
-                                               const basic_string_view<CharT,Traits>& str);
-
-  template<typename CharT, typename Traits>
-  void swap(basic_string_view<CharT,Traits>& lhs,
-            basic_string_view<CharT,Traits>& rhs) noexcept;
-
-  //--------------------------------------------------------------------------
-  // Comparison Functions
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  bool operator==(const basic_string_view<CharT,Traits>& lhs,
-                  const basic_string_view<CharT,Traits>& rhs) noexcept;
-  template<typename CharT, typename Traits>
-  bool operator!=(const basic_string_view<CharT,Traits>& lhs,
-                  const basic_string_view<CharT,Traits>& rhs) noexcept;
-  template<typename CharT, typename Traits>
-  bool operator<(const basic_string_view<CharT,Traits>& lhs,
-                 const basic_string_view<CharT,Traits>& rhs) noexcept;
-  template<typename CharT, typename Traits>
-  bool operator>(const basic_string_view<CharT,Traits>& lhs,
-                 const basic_string_view<CharT,Traits>& rhs) noexcept;
-  template<typename CharT, typename Traits>
-  bool operator<=(const basic_string_view<CharT,Traits>& lhs,
-                  const basic_string_view<CharT,Traits>& rhs) noexcept;
-  template<typename CharT, typename Traits>
-  bool operator>=(const basic_string_view<CharT,Traits>& lhs,
-                  const basic_string_view<CharT,Traits>& rhs) noexcept;
-
-  //--------------------------------------------------------------------------
-  // Type Aliases
-  //--------------------------------------------------------------------------
-
-  using string_view    = basic_string_view<char>;
-  using wstring_view   = basic_string_view<wchar_t>;
-  using u16string_view = basic_string_view<char16_t>;
-  using u32string_view = basic_string_view<char32_t>;
-
-} // namespace bpstd
-
-#ifndef BPSTD_DETAIL_STRING_VIEW_INL
-#define BPSTD_DETAIL_STRING_VIEW_INL
-
-namespace bpstd {
-
-  //--------------------------------------------------------------------------
-  // Constructor
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline constexpr basic_string_view<CharT,Traits>::basic_string_view()
-    noexcept
-    : m_str(nullptr),
-      m_size(0)
-  {
-
-  }
-
-  template<typename CharT, typename Traits>
-  template<typename Allocator>
-  inline basic_string_view<CharT,Traits>::basic_string_view(const std::basic_string<CharT,Traits,Allocator>& str)
-    noexcept
-    : m_str(str.c_str()),
-      m_size(str.size())
-  {
-
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr basic_string_view<CharT,Traits>::basic_string_view(const char_type* str)
-    noexcept
-    : m_str(str),
-      m_size(traits_type::length(str))
-  {
-
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr basic_string_view<CharT,Traits>::basic_string_view(const char_type* str, size_type count)
-    noexcept
-    : m_str(str),
-      m_size(count)
-  {
-
-  }
-
-  //--------------------------------------------------------------------------
-  // Capacity
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline constexpr typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::size()
-    const noexcept
-  {
-    return m_size;
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::length()
-    const noexcept
-  {
-    return size();
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::max_size()
-    const noexcept
-  {
-    return npos - 1;
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr bool basic_string_view<CharT,Traits>::empty()
-    const noexcept
-  {
-    return m_size == 0;
-  }
-
-  //--------------------------------------------------------------------------
-  // Element Access
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline constexpr const typename basic_string_view<CharT,Traits>::char_type*
-    basic_string_view<CharT,Traits>::c_str()
-    const noexcept
-  {
-    return m_str;
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr const typename basic_string_view<CharT,Traits>::char_type*
-    basic_string_view<CharT,Traits>::data()
-    const noexcept
-  {
-    return m_str;
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr typename basic_string_view<CharT,Traits>::const_reference
-    basic_string_view<CharT,Traits>::operator[](size_type pos)
-    const noexcept
-  {
-    return m_str[pos];
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr typename basic_string_view<CharT,Traits>::const_reference
-    basic_string_view<CharT,Traits>::at(size_type pos)
-    const
-  {
-    return pos < m_size ? m_str[pos] : throw std::out_of_range("Input out of range in basic_string_view::at"), m_str[pos];
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr typename basic_string_view<CharT,Traits>::const_reference
-    basic_string_view<CharT,Traits>::front( )
-    const noexcept
-  {
-    return *m_str;
-  }
-
-  template<typename CharT, typename Traits>
-  inline constexpr typename basic_string_view<CharT,Traits>::const_reference
-    basic_string_view<CharT,Traits>::back( )
-    const noexcept
-  {
-    return m_str[m_size-1];
-  }
-
-  //--------------------------------------------------------------------------
-  // Modifiers
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline void
-    basic_string_view<CharT,Traits>::remove_prefix(size_type n)
-    noexcept
-  {
-    m_str += n, m_size -= n;
-  }
-
-  template<typename CharT, typename Traits>
-  inline void
-    basic_string_view<CharT,Traits>::remove_suffix(size_type n)
-    noexcept
-  {
-    m_size -= n;
-  }
-
-  template<typename CharT, typename Traits>
-  inline void
-    basic_string_view<CharT,Traits>::swap(basic_string_view& v)
-    noexcept
-  {
-    using std::swap;
-    swap(m_size,v.m_size);
-    swap(m_str,v.m_str);
-  }
-
-  //--------------------------------------------------------------------------
-  // Conversions
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  template<class Allocator>
-  inline constexpr std::basic_string<CharT, Traits, Allocator>
-    basic_string_view<CharT,Traits>::to_string(const Allocator& a)
-    const
-  {
-    return std::basic_string<CharT,Traits,Allocator>(m_str, m_size, a);
-  }
-
-  template<typename CharT, typename Traits>
-  template<class Allocator>
-  inline constexpr basic_string_view<CharT,Traits>::operator
-    std::basic_string<CharT, Traits, Allocator>()
-    const
-  {
-    return std::basic_string<CharT,Traits,Allocator>(m_str, m_size);
-  }
-
-  //--------------------------------------------------------------------------
-  // String Operations
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::copy(char_type* dest,
-                                          size_type count,
-                                          size_type pos)
-    const
-  {
-    if(pos >= m_size) {
-      throw std::out_of_range("Index out of range in basic_string_view::copy");
-    }
-
-    const size_type rcount = std::min(m_size - pos,count+1);
-    std::copy( m_str + pos, m_str + pos + rcount, dest);
-    return rcount;
-  }
-
-  template<typename CharT, typename Traits>
-  inline basic_string_view<CharT,Traits>
-    basic_string_view<CharT,Traits>::substr(size_type pos,
-                                            size_type len)
-    const
-  {
-    const size_type max_length = pos > m_size ? 0 : m_size - pos;
-
-    if (pos > size()) {
-      throw std::out_of_range("Index out of range in basic_string_view::substr");
-    }
-
-    return basic_string_view(m_str + pos, std::min(len, max_length) );
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline int basic_string_view<CharT,Traits>::compare(basic_string_view v)
-    const noexcept
-  {
-    const size_type rlen = std::min(m_size,v.m_size);
-    const int compare = Traits::compare(m_str,v.m_str,rlen);
-
-    return (compare ? compare : (m_size < v.m_size ? -1 : (m_size > v.m_size ? 1 : 0)));
-  }
-
-  template<typename CharT, typename Traits>
-  inline int basic_string_view<CharT,Traits>::compare(size_type pos,
-                                                      size_type count,
-                                                      basic_string_view v)
-    const
-  {
-    return substr(pos,count).compare(v);
-  }
-
-  template<typename CharT, typename Traits>
-  inline int basic_string_view<CharT,Traits>::compare(size_type pos1,
-                                                      size_type count1,
-                                                      basic_string_view v,
-                                                      size_type pos2,
-                                                      size_type count2)
-    const
-  {
-    return substr(pos1,count1).compare(v.substr(pos2,count2));
-  }
-
-  template<typename CharT, typename Traits>
-  inline int basic_string_view<CharT,Traits>::compare(const char_type* s)
-    const
-  {
-    return compare(basic_string_view<CharT,Traits>(s));
-  }
-
-  template<typename CharT, typename Traits>
-  inline int basic_string_view<CharT,Traits>::compare(size_type pos,
-                                                      size_type count,
-                                                      const char_type* s)
-    const
-  {
-    return substr(pos, count).compare(basic_string_view<CharT,Traits>(s));
-  }
-
-  template<typename CharT, typename Traits>
-  inline int basic_string_view<CharT,Traits>::compare(size_type pos,
-                                                      size_type count1,
-                                                      const char_type* s,
-                                                      size_type count2)
-    const
-  {
-    return substr(pos, count1).compare(basic_string_view<CharT,Traits>(s, count2));
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find(basic_string_view v,
-                                          size_type pos)
-    const
-  {
-    // Can't find a substring if the substring is bigger than this
-    if (pos > size()) {
-      return npos;
-    }
-    if ((pos + v.size()) > size()) {
-      return npos;
-    }
-
-    const auto offset = pos;
-    const auto increments = size() - v.size();
-
-    for (auto i = 0u; i <= increments; ++i) {
-      const auto j = i + offset;
-      if (substr(j, v.size()) == v) {
-        return j;
-      }
-    }
-    return npos;
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find(char_type c,
-                                          size_type pos)
-    const
-  {
-    return find(basic_string_view<CharT,Traits>(&c, 1), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find(const char_type* s, size_type pos,
-                                          size_type count)
-    const
-  {
-    return find(basic_string_view<CharT,Traits>(s, count), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find(const char_type* s,
-                                          size_type pos)
-    const
-  {
-    return find(basic_string_view<CharT,Traits>(s), pos);
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::rfind(basic_string_view v,
-                                           size_type pos)
-    const
-  {
-    if (empty()) {
-      return v.empty() ? 0u : npos;
-    }
-    if (v.empty()) {
-      return std::min(size() - 1, pos);
-    }
-    if (v.size() > size()) {
-      return npos;
-    }
-
-    auto i = std::min(pos, (size() - v.size()));
-    while (i != npos) {
-      if (substr(i, v.size()) == v) {
-        return i;
-      }
-      --i;
-    }
-
-    return npos;
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::rfind(char_type c,
-                                           size_type pos)
-    const
-  {
-    return rfind(basic_string_view<CharT,Traits>(&c, 1), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::rfind(const char_type* s, size_type pos,
-                                           size_type count)
-    const
-  {
-    return rfind(basic_string_view<CharT,Traits>(s, count), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::rfind(const char_type* s,
-                                           size_type pos)
-    const
-  {
-    return rfind(basic_string_view<CharT,Traits>(s), pos);
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_first_of(basic_string_view v,
-                                                   size_type pos)
-    const
-  {
-    const auto max_index = size();
-    for (auto i = pos; i < max_index;  ++i) {
-      if (is_one_of(m_str[i],v)) {
-        return i;
-      }
-    }
-
-    return npos;
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_first_of(char_type c,
-                                                   size_type pos)
-    const
-  {
-    return find_first_of(basic_string_view<CharT,Traits>(&c, 1), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_first_of(const char_type* s, size_type pos,
-                                                   size_type count)
-    const
-  {
-    return find_first_of(basic_string_view<CharT,Traits>(s, count), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_first_of(const char_type* s,
-                                                   size_type pos)
-    const
-  {
-    return find_first_of(basic_string_view<CharT,Traits>(s), pos);
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_last_of(basic_string_view v,
-                                                  size_type pos)
-    const
-  {
-    if (empty()) {
-      return npos;
-    }
-    const auto max_index = std::min(size() - 1, pos);
-    for (auto i = 0u; i <= max_index;  ++i) {
-      const auto j = max_index - i;
-
-      if (is_one_of(m_str[j],v)) {
-        return j;
-      }
-    }
-
-    return npos;
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_last_of(char_type c,
-                                                  size_type pos)
-    const
-  {
-    return find_last_of(basic_string_view<CharT,Traits>(&c, 1), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_last_of(const char_type* s, size_type pos,
-                                                  size_type count)
-    const
-  {
-    return find_last_of(basic_string_view<CharT,Traits>(s, count), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_last_of(const char_type* s,
-                                                  size_type pos)
-    const
-  {
-    return find_last_of(basic_string_view<CharT,Traits>(s), pos);
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_first_not_of(basic_string_view v,
-                                                       size_type pos)
-    const
-  {
-    const auto max_index = size();
-    for (auto i = pos; i < max_index;  ++i) {
-      if (!is_one_of(m_str[i],v)) {
-        return i;
-      }
-    }
-
-    return npos;
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_first_not_of(char_type c,
-                                                       size_type pos)
-    const
-  {
-    return find_first_not_of(basic_string_view<CharT,Traits>(&c, 1), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_first_not_of(const char_type* s,
-                                                       size_type pos,
-                                                       size_type count)
-    const
-  {
-    return find_first_not_of(basic_string_view<CharT,Traits>(s, count), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_first_not_of(const char_type* s,
-                                                       size_type pos)
-    const
-  {
-    return find_first_not_of(basic_string_view<CharT,Traits>(s), pos);
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_last_not_of(basic_string_view v,
-                                                      size_type pos)
-    const
-  {
-    if (empty()) {
-      return npos;
-    }
-    const auto max_index = std::min(size() - 1, pos);
-    for (auto i = 0u; i <= max_index;  ++i) {
-      const auto j = max_index - i;
-
-      if (!is_one_of(m_str[j],v)) {
-        return j;
-      }
-    }
-
-    return npos;
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_last_not_of(char_type c,
-                                                      size_type pos)
-    const
-  {
-    return find_last_not_of(basic_string_view<CharT,Traits>(&c, 1), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_last_not_of(const char_type* s,
-                                                      size_type pos,
-                                                      size_type count)
-    const
-  {
-    return find_last_not_of(basic_string_view<CharT,Traits>(s, count), pos);
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::size_type
-    basic_string_view<CharT,Traits>::find_last_not_of(const char_type* s,
-                                                      size_type pos)
-    const
-  {
-    return find_last_not_of(basic_string_view<CharT,Traits>(s), pos);
-  }
-
-  //--------------------------------------------------------------------------
-  // Iterator
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::const_iterator
-    basic_string_view<CharT,Traits>::begin()
-    const noexcept
-  {
-    return m_str;
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::const_iterator
-    basic_string_view<CharT,Traits>::cbegin()
-    const noexcept
-  {
-    return begin();
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::const_iterator
-    basic_string_view<CharT,Traits>::end()
-    const noexcept
-  {
-    return m_str + m_size;
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::const_iterator
-    basic_string_view<CharT,Traits>::cend()
-    const noexcept
-  {
-    return cend();
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::const_reverse_iterator
-    basic_string_view<CharT,Traits>::rbegin()
-    const noexcept
-  {
-    return const_reverse_iterator{end()};
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::const_reverse_iterator
-    basic_string_view<CharT,Traits>::crbegin()
-    const noexcept
-  {
-    return rbegin();
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::const_reverse_iterator
-    basic_string_view<CharT,Traits>::rend()
-    const noexcept
-  {
-    return const_reverse_iterator{begin()};
-  }
-
-  template<typename CharT, typename Traits>
-  inline typename basic_string_view<CharT,Traits>::const_reverse_iterator
-    basic_string_view<CharT,Traits>::crend()
-    const noexcept
-  {
-    return crend();
-  }
-
-  template <typename CharT, typename Traits>
-  inline bool basic_string_view<CharT,Traits>::is_one_of(CharT c,
-                                                         basic_string_view str)
-  {
-    for (auto s : str) {
-      if (c == s) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  //--------------------------------------------------------------------------
-  // Public Functions
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  std::basic_ostream<CharT,Traits>& operator<<(std::basic_ostream<CharT,Traits>& o,
-                                               const basic_string_view<CharT,Traits>& str)
-  {
-    o.write(str.data(),str.size());
-    return o;
-  }
-
-  template<typename CharT, typename Traits>
-  inline void swap(basic_string_view<CharT,Traits>& lhs,
-                   basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    lhs.swap(rhs);
-  }
-
-  //--------------------------------------------------------------------------
-  // Comparison Functions
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline bool operator==(const basic_string_view<CharT,Traits>& lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return lhs.compare(rhs) == 0;
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator==(basic_string_view<CharT,Traits> lhs,
-                         const CharT* rhs)
-    noexcept
-  {
-    return lhs == basic_string_view<CharT,Traits>(rhs);
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator==(const CharT* lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return basic_string_view<CharT,Traits>(lhs) == rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator==(const std::basic_string<CharT,Traits,Allocator>& lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-  {
-    return basic_string_view<CharT,Traits>(lhs) == rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator==(const basic_string_view<CharT,Traits>& lhs,
-                         const std::basic_string<CharT,Traits,Allocator>& rhs)
-  {
-    return lhs == basic_string_view<CharT,Traits>(rhs);
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline bool operator!=(const basic_string_view<CharT,Traits>& lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return lhs.compare(rhs) != 0;
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator!=(const basic_string_view<CharT,Traits>& lhs,
-                         const CharT* rhs)
-    noexcept
-  {
-    return lhs != basic_string_view<CharT,Traits>(rhs);
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator!=(const CharT* lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return basic_string_view<CharT,Traits>(lhs) != rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator!=(const std::basic_string<CharT,Traits,Allocator>& lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-  {
-    return basic_string_view<CharT,Traits>(lhs) != rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator!=(const basic_string_view<CharT,Traits>& lhs,
-                         const std::basic_string<CharT,Traits,Allocator>& rhs)
-  {
-    return lhs != basic_string_view<CharT,Traits>(rhs);
-  }
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline bool operator<(const basic_string_view<CharT,Traits>& lhs,
-                        const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return lhs.compare(rhs) < 0;
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator<(const basic_string_view<CharT,Traits>& lhs,
-                        const CharT* rhs)
-    noexcept
-  {
-    return lhs < basic_string_view<CharT,Traits>(rhs);
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator<(const CharT* lhs,
-                        const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return basic_string_view<CharT,Traits>(lhs) < rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator<(const std::basic_string<CharT,Traits,Allocator>& lhs,
-                        const basic_string_view<CharT,Traits>& rhs)
-  {
-    return basic_string_view<CharT,Traits>(lhs) < rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator<(const basic_string_view<CharT,Traits>& lhs,
-                        const std::basic_string<CharT,Traits,Allocator>& rhs)
-  {
-    return lhs < basic_string_view<CharT,Traits>(rhs);
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline bool operator>(const basic_string_view<CharT,Traits>& lhs,
-                        const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return lhs.compare(rhs) > 0;
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator>(const basic_string_view<CharT,Traits>& lhs,
-                        const CharT* rhs)
-    noexcept
-  {
-    return lhs > basic_string_view<CharT,Traits>(rhs);
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator>(const CharT* lhs,
-                        const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return basic_string_view<CharT,Traits>(lhs) > rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator>(const std::basic_string<CharT,Traits,Allocator>& lhs,
-                        const basic_string_view<CharT,Traits>& rhs)
-  {
-    return basic_string_view<CharT,Traits>(lhs) > rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator>(const basic_string_view<CharT,Traits>& lhs,
-                        const std::basic_string<CharT,Traits,Allocator>& rhs)
-  {
-    return lhs > basic_string_view<CharT,Traits>(rhs);
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline bool operator<=(const basic_string_view<CharT,Traits>& lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return lhs.compare(rhs) <= 0;
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator<=(const basic_string_view<CharT,Traits>& lhs,
-                         const CharT* rhs)
-    noexcept
-  {
-    return lhs <= basic_string_view<CharT,Traits>(rhs);
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator<=(const CharT* lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return basic_string_view<CharT,Traits>(lhs) <= rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator<=(const std::basic_string<CharT,Traits,Allocator>& lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-  {
-    return basic_string_view<CharT,Traits>(lhs) <= rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator<=(const basic_string_view<CharT,Traits>& lhs,
-                         const std::basic_string<CharT,Traits,Allocator>& rhs)
-  {
-    return lhs <= basic_string_view<CharT,Traits>(rhs);
-  }
-
-  //--------------------------------------------------------------------------
-
-  template<typename CharT, typename Traits>
-  inline bool operator>=(const basic_string_view<CharT,Traits>& lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return lhs.compare(rhs) >= 0;
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator>=(const basic_string_view<CharT,Traits>& lhs,
-                         const CharT* rhs)
-    noexcept
-  {
-    return lhs >= basic_string_view<CharT,Traits>(rhs);
-  }
-
-  template<typename CharT, typename Traits>
-  inline bool operator>=(const CharT* lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-    noexcept
-  {
-    return basic_string_view<CharT,Traits>(lhs) >= rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator>=(const std::basic_string<CharT,Traits,Allocator>& lhs,
-                         const basic_string_view<CharT,Traits>& rhs)
-  {
-    return basic_string_view<CharT,Traits>(lhs) >= rhs;
-  }
-
-  template<typename CharT, typename Traits, typename Allocator>
-  inline bool operator>=(const basic_string_view<CharT,Traits>& lhs,
-                         const std::basic_string<CharT,Traits,Allocator>& rhs)
-  {
-    return lhs >= basic_string_view<CharT,Traits>(rhs);
-  }
-
-} // namespace bpstd
-
-#endif /* BPSTD_DETAIL_STRING_VIEW_INL */
-
-#endif /* BPSTD_STRING_VIEW_HPP */
diff --git a/src/cpp-common/uuid-view.hpp b/src/cpp-common/uuid-view.hpp
deleted file mode 100644 (file)
index 7811664..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: MIT
- */
-
-#ifndef BABELTRACE_CPP_COMMON_UUID_VIEW_HPP
-#define BABELTRACE_CPP_COMMON_UUID_VIEW_HPP
-
-#include <cstdint>
-
-#include "common/assert.h"
-#include "common/uuid.h"
-
-namespace bt2_common {
-
-class UuidView
-{
-public:
-    explicit UuidView(const std::uint8_t * const uuid) noexcept : _mUuid {uuid}
-    {
-        BT_ASSERT_DBG(uuid);
-    }
-
-    UuidView(const UuidView&) noexcept = default;
-    UuidView& operator=(const UuidView&) noexcept = default;
-
-    bool operator==(const UuidView& other) const noexcept
-    {
-        return bt_uuid_compare(_mUuid, other._mUuid) == 0;
-    }
-
-    bool operator!=(const UuidView& other) const noexcept
-    {
-        return !(*this == other);
-    }
-
-    std::string string() const
-    {
-        std::array<char, BT_UUID_STR_LEN> buf;
-
-        bt_uuid_to_str(_mUuid, buf.data());
-        return {buf.data(), buf.size()};
-    }
-
-    static std::size_t size() noexcept
-    {
-        return BT_UUID_LEN;
-    }
-
-    const std::uint8_t *data() const noexcept
-    {
-        return _mUuid;
-    }
-
-private:
-    const std::uint8_t *_mUuid;
-};
-
-} // namespace bt2_common
-
-#endif // BABELTRACE_CPP_COMMON_UUID_VIEW_HPP
diff --git a/src/cpp-common/vendor/fmt/args.h b/src/cpp-common/vendor/fmt/args.h
new file mode 100644 (file)
index 0000000..2d684e7
--- /dev/null
@@ -0,0 +1,234 @@
+// Formatting library for C++ - dynamic argument lists
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_ARGS_H_
+#define FMT_ARGS_H_
+
+#include <functional>  // std::reference_wrapper
+#include <memory>      // std::unique_ptr
+#include <vector>
+
+#include "core.h"
+
+FMT_BEGIN_NAMESPACE
+
+namespace detail {
+
+template <typename T> struct is_reference_wrapper : std::false_type {};
+template <typename T>
+struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
+
+template <typename T> const T& unwrap(const T& v) { return v; }
+template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
+  return static_cast<const T&>(v);
+}
+
+class dynamic_arg_list {
+  // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
+  // templates it doesn't complain about inability to deduce single translation
+  // unit for placing vtable. So storage_node_base is made a fake template.
+  template <typename = void> struct node {
+    virtual ~node() = default;
+    std::unique_ptr<node<>> next;
+  };
+
+  template <typename T> struct typed_node : node<> {
+    T value;
+
+    template <typename Arg>
+    FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
+
+    template <typename Char>
+    FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
+        : value(arg.data(), arg.size()) {}
+  };
+
+  std::unique_ptr<node<>> head_;
+
+ public:
+  template <typename T, typename Arg> const T& push(const Arg& arg) {
+    auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
+    auto& value = new_node->value;
+    new_node->next = std::move(head_);
+    head_ = std::move(new_node);
+    return value;
+  }
+};
+}  // namespace detail
+
+/**
+  \rst
+  A dynamic version of `fmt::format_arg_store`.
+  It's equipped with a storage to potentially temporary objects which lifetimes
+  could be shorter than the format arguments object.
+
+  It can be implicitly converted into `~fmt::basic_format_args` for passing
+  into type-erased formatting functions such as `~fmt::vformat`.
+  \endrst
+ */
+template <typename Context>
+class dynamic_format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+    // Workaround a GCC template argument substitution bug.
+    : public basic_format_args<Context>
+#endif
+{
+ private:
+  using char_type = typename Context::char_type;
+
+  template <typename T> struct need_copy {
+    static constexpr detail::type mapped_type =
+        detail::mapped_type_constant<T, Context>::value;
+
+    enum {
+      value = !(detail::is_reference_wrapper<T>::value ||
+                std::is_same<T, basic_string_view<char_type>>::value ||
+                std::is_same<T, detail::std_string_view<char_type>>::value ||
+                (mapped_type != detail::type::cstring_type &&
+                 mapped_type != detail::type::string_type &&
+                 mapped_type != detail::type::custom_type))
+    };
+  };
+
+  template <typename T>
+  using stored_type = conditional_t<
+      std::is_convertible<T, std::basic_string<char_type>>::value &&
+          !detail::is_reference_wrapper<T>::value,
+      std::basic_string<char_type>, T>;
+
+  // Storage of basic_format_arg must be contiguous.
+  std::vector<basic_format_arg<Context>> data_;
+  std::vector<detail::named_arg_info<char_type>> named_info_;
+
+  // Storage of arguments not fitting into basic_format_arg must grow
+  // without relocation because items in data_ refer to it.
+  detail::dynamic_arg_list dynamic_args_;
+
+  friend class basic_format_args<Context>;
+
+  unsigned long long get_types() const {
+    return detail::is_unpacked_bit | data_.size() |
+           (named_info_.empty()
+                ? 0ULL
+                : static_cast<unsigned long long>(detail::has_named_args_bit));
+  }
+
+  const basic_format_arg<Context>* data() const {
+    return named_info_.empty() ? data_.data() : data_.data() + 1;
+  }
+
+  template <typename T> void emplace_arg(const T& arg) {
+    data_.emplace_back(detail::make_arg<Context>(arg));
+  }
+
+  template <typename T>
+  void emplace_arg(const detail::named_arg<char_type, T>& arg) {
+    if (named_info_.empty()) {
+      constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
+      data_.insert(data_.begin(), {zero_ptr, 0});
+    }
+    data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
+    auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
+      data->pop_back();
+    };
+    std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
+        guard{&data_, pop_one};
+    named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
+    data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
+    guard.release();
+  }
+
+ public:
+  constexpr dynamic_format_arg_store() = default;
+
+  /**
+    \rst
+    Adds an argument into the dynamic store for later passing to a formatting
+    function.
+
+    Note that custom types and string types (but not string views) are copied
+    into the store dynamically allocating memory if necessary.
+
+    **Example**::
+
+      fmt::dynamic_format_arg_store<fmt::format_context> store;
+      store.push_back(42);
+      store.push_back("abc");
+      store.push_back(1.5f);
+      std::string result = fmt::vformat("{} and {} and {}", store);
+    \endrst
+  */
+  template <typename T> void push_back(const T& arg) {
+    if (detail::const_check(need_copy<T>::value))
+      emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
+    else
+      emplace_arg(detail::unwrap(arg));
+  }
+
+  /**
+    \rst
+    Adds a reference to the argument into the dynamic store for later passing to
+    a formatting function.
+
+    **Example**::
+
+      fmt::dynamic_format_arg_store<fmt::format_context> store;
+      char band[] = "Rolling Stones";
+      store.push_back(std::cref(band));
+      band[9] = 'c'; // Changing str affects the output.
+      std::string result = fmt::vformat("{}", store);
+      // result == "Rolling Scones"
+    \endrst
+  */
+  template <typename T> void push_back(std::reference_wrapper<T> arg) {
+    static_assert(
+        need_copy<T>::value,
+        "objects of built-in types and string views are always copied");
+    emplace_arg(arg.get());
+  }
+
+  /**
+    Adds named argument into the dynamic store for later passing to a formatting
+    function. ``std::reference_wrapper`` is supported to avoid copying of the
+    argument. The name is always copied into the store.
+  */
+  template <typename T>
+  void push_back(const detail::named_arg<char_type, T>& arg) {
+    const char_type* arg_name =
+        dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
+    if (detail::const_check(need_copy<T>::value)) {
+      emplace_arg(
+          fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
+    } else {
+      emplace_arg(fmt::arg(arg_name, arg.value));
+    }
+  }
+
+  /** Erase all elements from the store */
+  void clear() {
+    data_.clear();
+    named_info_.clear();
+    dynamic_args_ = detail::dynamic_arg_list();
+  }
+
+  /**
+    \rst
+    Reserves space to store at least *new_cap* arguments including
+    *new_cap_named* named arguments.
+    \endrst
+  */
+  void reserve(size_t new_cap, size_t new_cap_named) {
+    FMT_ASSERT(new_cap >= new_cap_named,
+               "Set of arguments includes set of named arguments");
+    data_.reserve(new_cap);
+    named_info_.reserve(new_cap_named);
+  }
+};
+
+FMT_END_NAMESPACE
+
+#endif  // FMT_ARGS_H_
diff --git a/src/cpp-common/vendor/fmt/chrono.h b/src/cpp-common/vendor/fmt/chrono.h
new file mode 100644 (file)
index 0000000..ff3e144
--- /dev/null
@@ -0,0 +1,2208 @@
+// Formatting library for C++ - chrono support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_CHRONO_H_
+#define FMT_CHRONO_H_
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>    // std::isfinite
+#include <cstring>  // std::memcpy
+#include <ctime>
+#include <iterator>
+#include <locale>
+#include <ostream>
+#include <type_traits>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+// Check if std::chrono::local_t is available.
+#ifndef FMT_USE_LOCAL_TIME
+#  ifdef __cpp_lib_chrono
+#    define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
+#  else
+#    define FMT_USE_LOCAL_TIME 0
+#  endif
+#endif
+
+// Check if std::chrono::utc_timestamp is available.
+#ifndef FMT_USE_UTC_TIME
+#  ifdef __cpp_lib_chrono
+#    define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
+#  else
+#    define FMT_USE_UTC_TIME 0
+#  endif
+#endif
+
+// Enable tzset.
+#ifndef FMT_USE_TZSET
+// UWP doesn't provide _tzset.
+#  if FMT_HAS_INCLUDE("winapifamily.h")
+#    include <winapifamily.h>
+#  endif
+#  if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
+                          (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
+#    define FMT_USE_TZSET 1
+#  else
+#    define FMT_USE_TZSET 0
+#  endif
+#endif
+
+// Enable safe chrono durations, unless explicitly disabled.
+#ifndef FMT_SAFE_DURATION_CAST
+#  define FMT_SAFE_DURATION_CAST 1
+#endif
+#if FMT_SAFE_DURATION_CAST
+
+// For conversion between std::chrono::durations without undefined
+// behaviour or erroneous results.
+// This is a stripped down version of duration_cast, for inclusion in fmt.
+// See https://github.com/pauldreik/safe_duration_cast
+//
+// Copyright Paul Dreik 2019
+namespace safe_duration_cast {
+
+template <typename To, typename From,
+          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
+                        std::numeric_limits<From>::is_signed ==
+                            std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+  ec = 0;
+  using F = std::numeric_limits<From>;
+  using T = std::numeric_limits<To>;
+  static_assert(F::is_integer, "From must be integral");
+  static_assert(T::is_integer, "To must be integral");
+
+  // A and B are both signed, or both unsigned.
+  if (detail::const_check(F::digits <= T::digits)) {
+    // From fits in To without any problem.
+  } else {
+    // From does not always fit in To, resort to a dynamic check.
+    if (from < (T::min)() || from > (T::max)()) {
+      // outside range.
+      ec = 1;
+      return {};
+    }
+  }
+  return static_cast<To>(from);
+}
+
+/**
+ * converts From to To, without loss. If the dynamic value of from
+ * can't be converted to To without loss, ec is set.
+ */
+template <typename To, typename From,
+          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
+                        std::numeric_limits<From>::is_signed !=
+                            std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+  ec = 0;
+  using F = std::numeric_limits<From>;
+  using T = std::numeric_limits<To>;
+  static_assert(F::is_integer, "From must be integral");
+  static_assert(T::is_integer, "To must be integral");
+
+  if (detail::const_check(F::is_signed && !T::is_signed)) {
+    // From may be negative, not allowed!
+    if (fmt::detail::is_negative(from)) {
+      ec = 1;
+      return {};
+    }
+    // From is positive. Can it always fit in To?
+    if (detail::const_check(F::digits > T::digits) &&
+        from > static_cast<From>(detail::max_value<To>())) {
+      ec = 1;
+      return {};
+    }
+  }
+
+  if (detail::const_check(!F::is_signed && T::is_signed &&
+                          F::digits >= T::digits) &&
+      from > static_cast<From>(detail::max_value<To>())) {
+    ec = 1;
+    return {};
+  }
+  return static_cast<To>(from);  // Lossless conversion.
+}
+
+template <typename To, typename From,
+          FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+  ec = 0;
+  return from;
+}  // function
+
+// clang-format off
+/**
+ * converts From to To if possible, otherwise ec is set.
+ *
+ * input                            |    output
+ * ---------------------------------|---------------
+ * NaN                              | NaN
+ * Inf                              | Inf
+ * normal, fits in output           | converted (possibly lossy)
+ * normal, does not fit in output   | ec is set
+ * subnormal                        | best effort
+ * -Inf                             | -Inf
+ */
+// clang-format on
+template <typename To, typename From,
+          FMT_ENABLE_IF(!std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+  ec = 0;
+  using T = std::numeric_limits<To>;
+  static_assert(std::is_floating_point<From>::value, "From must be floating");
+  static_assert(std::is_floating_point<To>::value, "To must be floating");
+
+  // catch the only happy case
+  if (std::isfinite(from)) {
+    if (from >= T::lowest() && from <= (T::max)()) {
+      return static_cast<To>(from);
+    }
+    // not within range.
+    ec = 1;
+    return {};
+  }
+
+  // nan and inf will be preserved
+  return static_cast<To>(from);
+}  // function
+
+template <typename To, typename From,
+          FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+  ec = 0;
+  static_assert(std::is_floating_point<From>::value, "From must be floating");
+  return from;
+}
+
+/**
+ * safe duration cast between integral durations
+ */
+template <typename To, typename FromRep, typename FromPeriod,
+          FMT_ENABLE_IF(std::is_integral<FromRep>::value),
+          FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+                      int& ec) {
+  using From = std::chrono::duration<FromRep, FromPeriod>;
+  ec = 0;
+  // the basic idea is that we need to convert from count() in the from type
+  // to count() in the To type, by multiplying it with this:
+  struct Factor
+      : std::ratio_divide<typename From::period, typename To::period> {};
+
+  static_assert(Factor::num > 0, "num must be positive");
+  static_assert(Factor::den > 0, "den must be positive");
+
+  // the conversion is like this: multiply from.count() with Factor::num
+  // /Factor::den and convert it to To::rep, all this without
+  // overflow/underflow. let's start by finding a suitable type that can hold
+  // both To, From and Factor::num
+  using IntermediateRep =
+      typename std::common_type<typename From::rep, typename To::rep,
+                                decltype(Factor::num)>::type;
+
+  // safe conversion to IntermediateRep
+  IntermediateRep count =
+      lossless_integral_conversion<IntermediateRep>(from.count(), ec);
+  if (ec) return {};
+  // multiply with Factor::num without overflow or underflow
+  if (detail::const_check(Factor::num != 1)) {
+    const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
+    if (count > max1) {
+      ec = 1;
+      return {};
+    }
+    const auto min1 =
+        (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
+    if (detail::const_check(!std::is_unsigned<IntermediateRep>::value) &&
+        count < min1) {
+      ec = 1;
+      return {};
+    }
+    count *= Factor::num;
+  }
+
+  if (detail::const_check(Factor::den != 1)) count /= Factor::den;
+  auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
+  return ec ? To() : To(tocount);
+}
+
+/**
+ * safe duration_cast between floating point durations
+ */
+template <typename To, typename FromRep, typename FromPeriod,
+          FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
+          FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+                      int& ec) {
+  using From = std::chrono::duration<FromRep, FromPeriod>;
+  ec = 0;
+  if (std::isnan(from.count())) {
+    // nan in, gives nan out. easy.
+    return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
+  }
+  // maybe we should also check if from is denormal, and decide what to do about
+  // it.
+
+  // +-inf should be preserved.
+  if (std::isinf(from.count())) {
+    return To{from.count()};
+  }
+
+  // the basic idea is that we need to convert from count() in the from type
+  // to count() in the To type, by multiplying it with this:
+  struct Factor
+      : std::ratio_divide<typename From::period, typename To::period> {};
+
+  static_assert(Factor::num > 0, "num must be positive");
+  static_assert(Factor::den > 0, "den must be positive");
+
+  // the conversion is like this: multiply from.count() with Factor::num
+  // /Factor::den and convert it to To::rep, all this without
+  // overflow/underflow. let's start by finding a suitable type that can hold
+  // both To, From and Factor::num
+  using IntermediateRep =
+      typename std::common_type<typename From::rep, typename To::rep,
+                                decltype(Factor::num)>::type;
+
+  // force conversion of From::rep -> IntermediateRep to be safe,
+  // even if it will never happen be narrowing in this context.
+  IntermediateRep count =
+      safe_float_conversion<IntermediateRep>(from.count(), ec);
+  if (ec) {
+    return {};
+  }
+
+  // multiply with Factor::num without overflow or underflow
+  if (detail::const_check(Factor::num != 1)) {
+    constexpr auto max1 = detail::max_value<IntermediateRep>() /
+                          static_cast<IntermediateRep>(Factor::num);
+    if (count > max1) {
+      ec = 1;
+      return {};
+    }
+    constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
+                          static_cast<IntermediateRep>(Factor::num);
+    if (count < min1) {
+      ec = 1;
+      return {};
+    }
+    count *= static_cast<IntermediateRep>(Factor::num);
+  }
+
+  // this can't go wrong, right? den>0 is checked earlier.
+  if (detail::const_check(Factor::den != 1)) {
+    using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
+    count /= static_cast<common_t>(Factor::den);
+  }
+
+  // convert to the to type, safely
+  using ToRep = typename To::rep;
+
+  const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
+  if (ec) {
+    return {};
+  }
+  return To{tocount};
+}
+}  // namespace safe_duration_cast
+#endif
+
+// Prevents expansion of a preceding token as a function-style macro.
+// Usage: f FMT_NOMACRO()
+#define FMT_NOMACRO
+
+namespace detail {
+template <typename T = void> struct null {};
+inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
+inline null<> localtime_s(...) { return null<>(); }
+inline null<> gmtime_r(...) { return null<>(); }
+inline null<> gmtime_s(...) { return null<>(); }
+
+inline const std::locale& get_classic_locale() {
+  static const auto& locale = std::locale::classic();
+  return locale;
+}
+
+template <typename CodeUnit> struct codecvt_result {
+  static constexpr const size_t max_size = 32;
+  CodeUnit buf[max_size];
+  CodeUnit* end;
+};
+template <typename CodeUnit>
+constexpr const size_t codecvt_result<CodeUnit>::max_size;
+
+template <typename CodeUnit>
+void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
+                   const std::locale& loc) {
+#if FMT_CLANG_VERSION
+#  pragma clang diagnostic push
+#  pragma clang diagnostic ignored "-Wdeprecated"
+  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
+#  pragma clang diagnostic pop
+#else
+  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
+#endif
+  auto mb = std::mbstate_t();
+  const char* from_next = nullptr;
+  auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next,
+                     std::begin(out.buf), std::end(out.buf), out.end);
+  if (result != std::codecvt_base::ok)
+    FMT_THROW(format_error("failed to format time"));
+}
+
+template <typename OutputIt>
+auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
+    -> OutputIt {
+  if (detail::is_utf8() && loc != get_classic_locale()) {
+    // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
+    // gcc-4.
+#if FMT_MSC_VERSION != 0 || \
+    (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
+    // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
+    // and newer.
+    using code_unit = wchar_t;
+#else
+    using code_unit = char32_t;
+#endif
+
+    using unit_t = codecvt_result<code_unit>;
+    unit_t unit;
+    write_codecvt(unit, in, loc);
+    // In UTF-8 is used one to four one-byte code units.
+    auto u =
+        to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>();
+    if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
+      FMT_THROW(format_error("failed to format time"));
+    return copy_str<char>(u.c_str(), u.c_str() + u.size(), out);
+  }
+  return copy_str<char>(in.data(), in.data() + in.size(), out);
+}
+
+template <typename Char, typename OutputIt,
+          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
+    -> OutputIt {
+  codecvt_result<Char> unit;
+  write_codecvt(unit, sv, loc);
+  return copy_str<Char>(unit.buf, unit.end, out);
+}
+
+template <typename Char, typename OutputIt,
+          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
+auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
+    -> OutputIt {
+  return write_encoded_tm_str(out, sv, loc);
+}
+
+template <typename Char>
+inline void do_write(buffer<Char>& buf, const std::tm& time,
+                     const std::locale& loc, char format, char modifier) {
+  auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
+  auto&& os = std::basic_ostream<Char>(&format_buf);
+  os.imbue(loc);
+  using iterator = std::ostreambuf_iterator<Char>;
+  const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
+  auto end = facet.put(os, os, Char(' '), &time, format, modifier);
+  if (end.failed()) FMT_THROW(format_error("failed to format time"));
+}
+
+template <typename Char, typename OutputIt,
+          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+auto write(OutputIt out, const std::tm& time, const std::locale& loc,
+           char format, char modifier = 0) -> OutputIt {
+  auto&& buf = get_buffer<Char>(out);
+  do_write<Char>(buf, time, loc, format, modifier);
+  return get_iterator(buf, out);
+}
+
+template <typename Char, typename OutputIt,
+          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
+auto write(OutputIt out, const std::tm& time, const std::locale& loc,
+           char format, char modifier = 0) -> OutputIt {
+  auto&& buf = basic_memory_buffer<Char>();
+  do_write<char>(buf, time, loc, format, modifier);
+  return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
+}
+
+}  // namespace detail
+
+FMT_BEGIN_EXPORT
+
+/**
+  Converts given time since epoch as ``std::time_t`` value into calendar time,
+  expressed in local time. Unlike ``std::localtime``, this function is
+  thread-safe on most platforms.
+ */
+inline std::tm localtime(std::time_t time) {
+  struct dispatcher {
+    std::time_t time_;
+    std::tm tm_;
+
+    dispatcher(std::time_t t) : time_(t) {}
+
+    bool run() {
+      using namespace fmt::detail;
+      return handle(localtime_r(&time_, &tm_));
+    }
+
+    bool handle(std::tm* tm) { return tm != nullptr; }
+
+    bool handle(detail::null<>) {
+      using namespace fmt::detail;
+      return fallback(localtime_s(&tm_, &time_));
+    }
+
+    bool fallback(int res) { return res == 0; }
+
+#if !FMT_MSC_VERSION
+    bool fallback(detail::null<>) {
+      using namespace fmt::detail;
+      std::tm* tm = std::localtime(&time_);
+      if (tm) tm_ = *tm;
+      return tm != nullptr;
+    }
+#endif
+  };
+  dispatcher lt(time);
+  // Too big time values may be unsupported.
+  if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
+  return lt.tm_;
+}
+
+#if FMT_USE_LOCAL_TIME
+template <typename Duration>
+inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
+  return localtime(std::chrono::system_clock::to_time_t(
+      std::chrono::current_zone()->to_sys(time)));
+}
+#endif
+
+/**
+  Converts given time since epoch as ``std::time_t`` value into calendar time,
+  expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
+  function is thread-safe on most platforms.
+ */
+inline std::tm gmtime(std::time_t time) {
+  struct dispatcher {
+    std::time_t time_;
+    std::tm tm_;
+
+    dispatcher(std::time_t t) : time_(t) {}
+
+    bool run() {
+      using namespace fmt::detail;
+      return handle(gmtime_r(&time_, &tm_));
+    }
+
+    bool handle(std::tm* tm) { return tm != nullptr; }
+
+    bool handle(detail::null<>) {
+      using namespace fmt::detail;
+      return fallback(gmtime_s(&tm_, &time_));
+    }
+
+    bool fallback(int res) { return res == 0; }
+
+#if !FMT_MSC_VERSION
+    bool fallback(detail::null<>) {
+      std::tm* tm = std::gmtime(&time_);
+      if (tm) tm_ = *tm;
+      return tm != nullptr;
+    }
+#endif
+  };
+  auto gt = dispatcher(time);
+  // Too big time values may be unsupported.
+  if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
+  return gt.tm_;
+}
+
+inline std::tm gmtime(
+    std::chrono::time_point<std::chrono::system_clock> time_point) {
+  return gmtime(std::chrono::system_clock::to_time_t(time_point));
+}
+
+namespace detail {
+
+// Writes two-digit numbers a, b and c separated by sep to buf.
+// The method by Pavel Novikov based on
+// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
+inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
+                                   unsigned c, char sep) {
+  unsigned long long digits =
+      a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
+  // Convert each value to BCD.
+  // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
+  // The difference is
+  //   y - x = a * 6
+  // a can be found from x:
+  //   a = floor(x / 10)
+  // then
+  //   y = x + a * 6 = x + floor(x / 10) * 6
+  // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
+  digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
+  // Put low nibbles to high bytes and high nibbles to low bytes.
+  digits = ((digits & 0x00f00000f00000f0) >> 4) |
+           ((digits & 0x000f00000f00000f) << 8);
+  auto usep = static_cast<unsigned long long>(sep);
+  // Add ASCII '0' to each digit byte and insert separators.
+  digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
+
+  constexpr const size_t len = 8;
+  if (const_check(is_big_endian())) {
+    char tmp[len];
+    std::memcpy(tmp, &digits, len);
+    std::reverse_copy(tmp, tmp + len, buf);
+  } else {
+    std::memcpy(buf, &digits, len);
+  }
+}
+
+template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
+  if (std::is_same<Period, std::atto>::value) return "as";
+  if (std::is_same<Period, std::femto>::value) return "fs";
+  if (std::is_same<Period, std::pico>::value) return "ps";
+  if (std::is_same<Period, std::nano>::value) return "ns";
+  if (std::is_same<Period, std::micro>::value) return "µs";
+  if (std::is_same<Period, std::milli>::value) return "ms";
+  if (std::is_same<Period, std::centi>::value) return "cs";
+  if (std::is_same<Period, std::deci>::value) return "ds";
+  if (std::is_same<Period, std::ratio<1>>::value) return "s";
+  if (std::is_same<Period, std::deca>::value) return "das";
+  if (std::is_same<Period, std::hecto>::value) return "hs";
+  if (std::is_same<Period, std::kilo>::value) return "ks";
+  if (std::is_same<Period, std::mega>::value) return "Ms";
+  if (std::is_same<Period, std::giga>::value) return "Gs";
+  if (std::is_same<Period, std::tera>::value) return "Ts";
+  if (std::is_same<Period, std::peta>::value) return "Ps";
+  if (std::is_same<Period, std::exa>::value) return "Es";
+  if (std::is_same<Period, std::ratio<60>>::value) return "m";
+  if (std::is_same<Period, std::ratio<3600>>::value) return "h";
+  return nullptr;
+}
+
+enum class numeric_system {
+  standard,
+  // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
+  alternative
+};
+
+// Glibc extensions for formatting numeric values.
+enum class pad_type {
+  unspecified,
+  // Do not pad a numeric result string.
+  none,
+  // Pad a numeric result string with zeros even if the conversion specifier
+  // character uses space-padding by default.
+  zero,
+  // Pad a numeric result string with spaces.
+  space,
+};
+
+template <typename OutputIt>
+auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt {
+  if (pad == pad_type::none) return out;
+  return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0');
+}
+
+template <typename OutputIt>
+auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
+  if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0';
+  return out;
+}
+
+// Parses a put_time-like format string and invokes handler actions.
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
+                                              const Char* end,
+                                              Handler&& handler) {
+  if (begin == end || *begin == '}') return begin;
+  if (*begin != '%') FMT_THROW(format_error("invalid format"));
+  auto ptr = begin;
+  pad_type pad = pad_type::unspecified;
+  while (ptr != end) {
+    auto c = *ptr;
+    if (c == '}') break;
+    if (c != '%') {
+      ++ptr;
+      continue;
+    }
+    if (begin != ptr) handler.on_text(begin, ptr);
+    ++ptr;  // consume '%'
+    if (ptr == end) FMT_THROW(format_error("invalid format"));
+    c = *ptr;
+    switch (c) {
+    case '_':
+      pad = pad_type::space;
+      ++ptr;
+      break;
+    case '-':
+      pad = pad_type::none;
+      ++ptr;
+      break;
+    case '0':
+      pad = pad_type::zero;
+      ++ptr;
+      break;
+    }
+    if (ptr == end) FMT_THROW(format_error("invalid format"));
+    c = *ptr++;
+    switch (c) {
+    case '%':
+      handler.on_text(ptr - 1, ptr);
+      break;
+    case 'n': {
+      const Char newline[] = {'\n'};
+      handler.on_text(newline, newline + 1);
+      break;
+    }
+    case 't': {
+      const Char tab[] = {'\t'};
+      handler.on_text(tab, tab + 1);
+      break;
+    }
+    // Year:
+    case 'Y':
+      handler.on_year(numeric_system::standard);
+      break;
+    case 'y':
+      handler.on_short_year(numeric_system::standard);
+      break;
+    case 'C':
+      handler.on_century(numeric_system::standard);
+      break;
+    case 'G':
+      handler.on_iso_week_based_year();
+      break;
+    case 'g':
+      handler.on_iso_week_based_short_year();
+      break;
+    // Day of the week:
+    case 'a':
+      handler.on_abbr_weekday();
+      break;
+    case 'A':
+      handler.on_full_weekday();
+      break;
+    case 'w':
+      handler.on_dec0_weekday(numeric_system::standard);
+      break;
+    case 'u':
+      handler.on_dec1_weekday(numeric_system::standard);
+      break;
+    // Month:
+    case 'b':
+    case 'h':
+      handler.on_abbr_month();
+      break;
+    case 'B':
+      handler.on_full_month();
+      break;
+    case 'm':
+      handler.on_dec_month(numeric_system::standard);
+      break;
+    // Day of the year/month:
+    case 'U':
+      handler.on_dec0_week_of_year(numeric_system::standard);
+      break;
+    case 'W':
+      handler.on_dec1_week_of_year(numeric_system::standard);
+      break;
+    case 'V':
+      handler.on_iso_week_of_year(numeric_system::standard);
+      break;
+    case 'j':
+      handler.on_day_of_year();
+      break;
+    case 'd':
+      handler.on_day_of_month(numeric_system::standard);
+      break;
+    case 'e':
+      handler.on_day_of_month_space(numeric_system::standard);
+      break;
+    // Hour, minute, second:
+    case 'H':
+      handler.on_24_hour(numeric_system::standard, pad);
+      break;
+    case 'I':
+      handler.on_12_hour(numeric_system::standard, pad);
+      break;
+    case 'M':
+      handler.on_minute(numeric_system::standard, pad);
+      break;
+    case 'S':
+      handler.on_second(numeric_system::standard, pad);
+      break;
+    // Other:
+    case 'c':
+      handler.on_datetime(numeric_system::standard);
+      break;
+    case 'x':
+      handler.on_loc_date(numeric_system::standard);
+      break;
+    case 'X':
+      handler.on_loc_time(numeric_system::standard);
+      break;
+    case 'D':
+      handler.on_us_date();
+      break;
+    case 'F':
+      handler.on_iso_date();
+      break;
+    case 'r':
+      handler.on_12_hour_time();
+      break;
+    case 'R':
+      handler.on_24_hour_time();
+      break;
+    case 'T':
+      handler.on_iso_time();
+      break;
+    case 'p':
+      handler.on_am_pm();
+      break;
+    case 'Q':
+      handler.on_duration_value();
+      break;
+    case 'q':
+      handler.on_duration_unit();
+      break;
+    case 'z':
+      handler.on_utc_offset(numeric_system::standard);
+      break;
+    case 'Z':
+      handler.on_tz_name();
+      break;
+    // Alternative representation:
+    case 'E': {
+      if (ptr == end) FMT_THROW(format_error("invalid format"));
+      c = *ptr++;
+      switch (c) {
+      case 'Y':
+        handler.on_year(numeric_system::alternative);
+        break;
+      case 'y':
+        handler.on_offset_year();
+        break;
+      case 'C':
+        handler.on_century(numeric_system::alternative);
+        break;
+      case 'c':
+        handler.on_datetime(numeric_system::alternative);
+        break;
+      case 'x':
+        handler.on_loc_date(numeric_system::alternative);
+        break;
+      case 'X':
+        handler.on_loc_time(numeric_system::alternative);
+        break;
+      case 'z':
+        handler.on_utc_offset(numeric_system::alternative);
+        break;
+      default:
+        FMT_THROW(format_error("invalid format"));
+      }
+      break;
+    }
+    case 'O':
+      if (ptr == end) FMT_THROW(format_error("invalid format"));
+      c = *ptr++;
+      switch (c) {
+      case 'y':
+        handler.on_short_year(numeric_system::alternative);
+        break;
+      case 'm':
+        handler.on_dec_month(numeric_system::alternative);
+        break;
+      case 'U':
+        handler.on_dec0_week_of_year(numeric_system::alternative);
+        break;
+      case 'W':
+        handler.on_dec1_week_of_year(numeric_system::alternative);
+        break;
+      case 'V':
+        handler.on_iso_week_of_year(numeric_system::alternative);
+        break;
+      case 'd':
+        handler.on_day_of_month(numeric_system::alternative);
+        break;
+      case 'e':
+        handler.on_day_of_month_space(numeric_system::alternative);
+        break;
+      case 'w':
+        handler.on_dec0_weekday(numeric_system::alternative);
+        break;
+      case 'u':
+        handler.on_dec1_weekday(numeric_system::alternative);
+        break;
+      case 'H':
+        handler.on_24_hour(numeric_system::alternative, pad);
+        break;
+      case 'I':
+        handler.on_12_hour(numeric_system::alternative, pad);
+        break;
+      case 'M':
+        handler.on_minute(numeric_system::alternative, pad);
+        break;
+      case 'S':
+        handler.on_second(numeric_system::alternative, pad);
+        break;
+      case 'z':
+        handler.on_utc_offset(numeric_system::alternative);
+        break;
+      default:
+        FMT_THROW(format_error("invalid format"));
+      }
+      break;
+    default:
+      FMT_THROW(format_error("invalid format"));
+    }
+    begin = ptr;
+  }
+  if (begin != ptr) handler.on_text(begin, ptr);
+  return ptr;
+}
+
+template <typename Derived> struct null_chrono_spec_handler {
+  FMT_CONSTEXPR void unsupported() {
+    static_cast<Derived*>(this)->unsupported();
+  }
+  FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_offset_year() { unsupported(); }
+  FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
+  FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
+  FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
+  FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
+  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
+  FMT_CONSTEXPR void on_full_month() { unsupported(); }
+  FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
+  FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_us_date() { unsupported(); }
+  FMT_CONSTEXPR void on_iso_date() { unsupported(); }
+  FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
+  FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
+  FMT_CONSTEXPR void on_iso_time() { unsupported(); }
+  FMT_CONSTEXPR void on_am_pm() { unsupported(); }
+  FMT_CONSTEXPR void on_duration_value() { unsupported(); }
+  FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
+  FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_tz_name() { unsupported(); }
+};
+
+struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
+  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
+
+  template <typename Char>
+  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+  FMT_CONSTEXPR void on_year(numeric_system) {}
+  FMT_CONSTEXPR void on_short_year(numeric_system) {}
+  FMT_CONSTEXPR void on_offset_year() {}
+  FMT_CONSTEXPR void on_century(numeric_system) {}
+  FMT_CONSTEXPR void on_iso_week_based_year() {}
+  FMT_CONSTEXPR void on_iso_week_based_short_year() {}
+  FMT_CONSTEXPR void on_abbr_weekday() {}
+  FMT_CONSTEXPR void on_full_weekday() {}
+  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
+  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
+  FMT_CONSTEXPR void on_abbr_month() {}
+  FMT_CONSTEXPR void on_full_month() {}
+  FMT_CONSTEXPR void on_dec_month(numeric_system) {}
+  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
+  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
+  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
+  FMT_CONSTEXPR void on_day_of_year() {}
+  FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
+  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
+  FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_second(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_datetime(numeric_system) {}
+  FMT_CONSTEXPR void on_loc_date(numeric_system) {}
+  FMT_CONSTEXPR void on_loc_time(numeric_system) {}
+  FMT_CONSTEXPR void on_us_date() {}
+  FMT_CONSTEXPR void on_iso_date() {}
+  FMT_CONSTEXPR void on_12_hour_time() {}
+  FMT_CONSTEXPR void on_24_hour_time() {}
+  FMT_CONSTEXPR void on_iso_time() {}
+  FMT_CONSTEXPR void on_am_pm() {}
+  FMT_CONSTEXPR void on_utc_offset(numeric_system) {}
+  FMT_CONSTEXPR void on_tz_name() {}
+};
+
+inline const char* tm_wday_full_name(int wday) {
+  static constexpr const char* full_name_list[] = {
+      "Sunday",   "Monday", "Tuesday", "Wednesday",
+      "Thursday", "Friday", "Saturday"};
+  return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
+}
+inline const char* tm_wday_short_name(int wday) {
+  static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
+                                                    "Thu", "Fri", "Sat"};
+  return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
+}
+
+inline const char* tm_mon_full_name(int mon) {
+  static constexpr const char* full_name_list[] = {
+      "January", "February", "March",     "April",   "May",      "June",
+      "July",    "August",   "September", "October", "November", "December"};
+  return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
+}
+inline const char* tm_mon_short_name(int mon) {
+  static constexpr const char* short_name_list[] = {
+      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+  };
+  return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
+}
+
+template <typename T, typename = void>
+struct has_member_data_tm_gmtoff : std::false_type {};
+template <typename T>
+struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
+    : std::true_type {};
+
+template <typename T, typename = void>
+struct has_member_data_tm_zone : std::false_type {};
+template <typename T>
+struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
+    : std::true_type {};
+
+#if FMT_USE_TZSET
+inline void tzset_once() {
+  static bool init = []() -> bool {
+    _tzset();
+    return true;
+  }();
+  ignore_unused(init);
+}
+#endif
+
+// Converts value to Int and checks that it's in the range [0, upper).
+template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline Int to_nonnegative_int(T value, Int upper) {
+  FMT_ASSERT(std::is_unsigned<Int>::value ||
+                 (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
+             "invalid value");
+  (void)upper;
+  return static_cast<Int>(value);
+}
+template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+inline Int to_nonnegative_int(T value, Int upper) {
+  if (value < 0 || value > static_cast<T>(upper))
+    FMT_THROW(format_error("invalid value"));
+  return static_cast<Int>(value);
+}
+
+constexpr long long pow10(std::uint32_t n) {
+  return n == 0 ? 1 : 10 * pow10(n - 1);
+}
+
+// Counts the number of fractional digits in the range [0, 18] according to the
+// C++20 spec. If more than 18 fractional digits are required then returns 6 for
+// microseconds precision.
+template <long long Num, long long Den, int N = 0,
+          bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
+struct count_fractional_digits {
+  static constexpr int value =
+      Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
+};
+
+// Base case that doesn't instantiate any more templates
+// in order to avoid overflow.
+template <long long Num, long long Den, int N>
+struct count_fractional_digits<Num, Den, N, false> {
+  static constexpr int value = (Num % Den == 0) ? N : 6;
+};
+
+// Format subseconds which are given as an integer type with an appropriate
+// number of digits.
+template <typename Char, typename OutputIt, typename Duration>
+void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
+  constexpr auto num_fractional_digits =
+      count_fractional_digits<Duration::period::num,
+                              Duration::period::den>::value;
+
+  using subsecond_precision = std::chrono::duration<
+      typename std::common_type<typename Duration::rep,
+                                std::chrono::seconds::rep>::type,
+      std::ratio<1, detail::pow10(num_fractional_digits)>>;
+
+  const auto fractional =
+      d - std::chrono::duration_cast<std::chrono::seconds>(d);
+  const auto subseconds =
+      std::chrono::treat_as_floating_point<
+          typename subsecond_precision::rep>::value
+          ? fractional.count()
+          : std::chrono::duration_cast<subsecond_precision>(fractional).count();
+  auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
+  const int num_digits = detail::count_digits(n);
+
+  int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits);
+  if (precision < 0) {
+    FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
+    if (std::ratio_less<typename subsecond_precision::period,
+                        std::chrono::seconds::period>::value) {
+      *out++ = '.';
+      out = std::fill_n(out, leading_zeroes, '0');
+      out = format_decimal<Char>(out, n, num_digits).end;
+    }
+  } else {
+    *out++ = '.';
+    leading_zeroes = (std::min)(leading_zeroes, precision);
+    out = std::fill_n(out, leading_zeroes, '0');
+    int remaining = precision - leading_zeroes;
+    if (remaining != 0 && remaining < num_digits) {
+      n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining)));
+      out = format_decimal<Char>(out, n, remaining).end;
+      return;
+    }
+    out = format_decimal<Char>(out, n, num_digits).end;
+    remaining -= num_digits;
+    out = std::fill_n(out, remaining, '0');
+  }
+}
+
+// Format subseconds which are given as a floating point type with an
+// appropriate number of digits. We cannot pass the Duration here, as we
+// explicitly need to pass the Rep value in the chrono_formatter.
+template <typename Duration>
+void write_floating_seconds(memory_buffer& buf, Duration duration,
+                            int num_fractional_digits = -1) {
+  using rep = typename Duration::rep;
+  FMT_ASSERT(std::is_floating_point<rep>::value, "");
+
+  auto val = duration.count();
+
+  if (num_fractional_digits < 0) {
+    // For `std::round` with fallback to `round`:
+    // On some toolchains `std::round` is not available (e.g. GCC 6).
+    using namespace std;
+    num_fractional_digits =
+        count_fractional_digits<Duration::period::num,
+                                Duration::period::den>::value;
+    if (num_fractional_digits < 6 && static_cast<rep>(round(val)) != val)
+      num_fractional_digits = 6;
+  }
+
+  format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
+            std::fmod(val * static_cast<rep>(Duration::period::num) /
+                          static_cast<rep>(Duration::period::den),
+                      static_cast<rep>(60)),
+            num_fractional_digits);
+}
+
+template <typename OutputIt, typename Char,
+          typename Duration = std::chrono::seconds>
+class tm_writer {
+ private:
+  static constexpr int days_per_week = 7;
+
+  const std::locale& loc_;
+  const bool is_classic_;
+  OutputIt out_;
+  const Duration* subsecs_;
+  const std::tm& tm_;
+
+  auto tm_sec() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
+    return tm_.tm_sec;
+  }
+  auto tm_min() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
+    return tm_.tm_min;
+  }
+  auto tm_hour() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
+    return tm_.tm_hour;
+  }
+  auto tm_mday() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
+    return tm_.tm_mday;
+  }
+  auto tm_mon() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
+    return tm_.tm_mon;
+  }
+  auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
+  auto tm_wday() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
+    return tm_.tm_wday;
+  }
+  auto tm_yday() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
+    return tm_.tm_yday;
+  }
+
+  auto tm_hour12() const noexcept -> int {
+    const auto h = tm_hour();
+    const auto z = h < 12 ? h : h - 12;
+    return z == 0 ? 12 : z;
+  }
+
+  // POSIX and the C Standard are unclear or inconsistent about what %C and %y
+  // do if the year is negative or exceeds 9999. Use the convention that %C
+  // concatenated with %y yields the same output as %Y, and that %Y contains at
+  // least 4 characters, with more only if necessary.
+  auto split_year_lower(long long year) const noexcept -> int {
+    auto l = year % 100;
+    if (l < 0) l = -l;  // l in [0, 99]
+    return static_cast<int>(l);
+  }
+
+  // Algorithm:
+  // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
+  auto iso_year_weeks(long long curr_year) const noexcept -> int {
+    const auto prev_year = curr_year - 1;
+    const auto curr_p =
+        (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
+        days_per_week;
+    const auto prev_p =
+        (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
+        days_per_week;
+    return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
+  }
+  auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
+    return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
+           days_per_week;
+  }
+  auto tm_iso_week_year() const noexcept -> long long {
+    const auto year = tm_year();
+    const auto w = iso_week_num(tm_yday(), tm_wday());
+    if (w < 1) return year - 1;
+    if (w > iso_year_weeks(year)) return year + 1;
+    return year;
+  }
+  auto tm_iso_week_of_year() const noexcept -> int {
+    const auto year = tm_year();
+    const auto w = iso_week_num(tm_yday(), tm_wday());
+    if (w < 1) return iso_year_weeks(year - 1);
+    if (w > iso_year_weeks(year)) return 1;
+    return w;
+  }
+
+  void write1(int value) {
+    *out_++ = static_cast<char>('0' + to_unsigned(value) % 10);
+  }
+  void write2(int value) {
+    const char* d = digits2(to_unsigned(value) % 100);
+    *out_++ = *d++;
+    *out_++ = *d;
+  }
+  void write2(int value, pad_type pad) {
+    unsigned int v = to_unsigned(value) % 100;
+    if (v >= 10) {
+      const char* d = digits2(v);
+      *out_++ = *d++;
+      *out_++ = *d;
+    } else {
+      out_ = detail::write_padding(out_, pad);
+      *out_++ = static_cast<char>('0' + v);
+    }
+  }
+
+  void write_year_extended(long long year) {
+    // At least 4 characters.
+    int width = 4;
+    if (year < 0) {
+      *out_++ = '-';
+      year = 0 - year;
+      --width;
+    }
+    uint32_or_64_or_128_t<long long> n = to_unsigned(year);
+    const int num_digits = count_digits(n);
+    if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0');
+    out_ = format_decimal<Char>(out_, n, num_digits).end;
+  }
+  void write_year(long long year) {
+    if (year >= 0 && year < 10000) {
+      write2(static_cast<int>(year / 100));
+      write2(static_cast<int>(year % 100));
+    } else {
+      write_year_extended(year);
+    }
+  }
+
+  void write_utc_offset(long offset, numeric_system ns) {
+    if (offset < 0) {
+      *out_++ = '-';
+      offset = -offset;
+    } else {
+      *out_++ = '+';
+    }
+    offset /= 60;
+    write2(static_cast<int>(offset / 60));
+    if (ns != numeric_system::standard) *out_++ = ':';
+    write2(static_cast<int>(offset % 60));
+  }
+  template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
+  void format_utc_offset_impl(const T& tm, numeric_system ns) {
+    write_utc_offset(tm.tm_gmtoff, ns);
+  }
+  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
+  void format_utc_offset_impl(const T& tm, numeric_system ns) {
+#if defined(_WIN32) && defined(_UCRT)
+#  if FMT_USE_TZSET
+    tzset_once();
+#  endif
+    long offset = 0;
+    _get_timezone(&offset);
+    if (tm.tm_isdst) {
+      long dstbias = 0;
+      _get_dstbias(&dstbias);
+      offset += dstbias;
+    }
+    write_utc_offset(-offset, ns);
+#else
+    if (ns == numeric_system::standard) return format_localized('z');
+
+    // Extract timezone offset from timezone conversion functions.
+    std::tm gtm = tm;
+    std::time_t gt = std::mktime(&gtm);
+    std::tm ltm = gmtime(gt);
+    std::time_t lt = std::mktime(&ltm);
+    long offset = gt - lt;
+    write_utc_offset(offset, ns);
+#endif
+  }
+
+  template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
+  void format_tz_name_impl(const T& tm) {
+    if (is_classic_)
+      out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
+    else
+      format_localized('Z');
+  }
+  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
+  void format_tz_name_impl(const T&) {
+    format_localized('Z');
+  }
+
+  void format_localized(char format, char modifier = 0) {
+    out_ = write<Char>(out_, tm_, loc_, format, modifier);
+  }
+
+ public:
+  tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm,
+            const Duration* subsecs = nullptr)
+      : loc_(loc),
+        is_classic_(loc_ == get_classic_locale()),
+        out_(out),
+        subsecs_(subsecs),
+        tm_(tm) {}
+
+  OutputIt out() const { return out_; }
+
+  FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
+    out_ = copy_str<Char>(begin, end, out_);
+  }
+
+  void on_abbr_weekday() {
+    if (is_classic_)
+      out_ = write(out_, tm_wday_short_name(tm_wday()));
+    else
+      format_localized('a');
+  }
+  void on_full_weekday() {
+    if (is_classic_)
+      out_ = write(out_, tm_wday_full_name(tm_wday()));
+    else
+      format_localized('A');
+  }
+  void on_dec0_weekday(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday());
+    format_localized('w', 'O');
+  }
+  void on_dec1_weekday(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      auto wday = tm_wday();
+      write1(wday == 0 ? days_per_week : wday);
+    } else {
+      format_localized('u', 'O');
+    }
+  }
+
+  void on_abbr_month() {
+    if (is_classic_)
+      out_ = write(out_, tm_mon_short_name(tm_mon()));
+    else
+      format_localized('b');
+  }
+  void on_full_month() {
+    if (is_classic_)
+      out_ = write(out_, tm_mon_full_name(tm_mon()));
+    else
+      format_localized('B');
+  }
+
+  void on_datetime(numeric_system ns) {
+    if (is_classic_) {
+      on_abbr_weekday();
+      *out_++ = ' ';
+      on_abbr_month();
+      *out_++ = ' ';
+      on_day_of_month_space(numeric_system::standard);
+      *out_++ = ' ';
+      on_iso_time();
+      *out_++ = ' ';
+      on_year(numeric_system::standard);
+    } else {
+      format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
+    }
+  }
+  void on_loc_date(numeric_system ns) {
+    if (is_classic_)
+      on_us_date();
+    else
+      format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
+  }
+  void on_loc_time(numeric_system ns) {
+    if (is_classic_)
+      on_iso_time();
+    else
+      format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
+  }
+  void on_us_date() {
+    char buf[8];
+    write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
+                           to_unsigned(tm_mday()),
+                           to_unsigned(split_year_lower(tm_year())), '/');
+    out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
+  }
+  void on_iso_date() {
+    auto year = tm_year();
+    char buf[10];
+    size_t offset = 0;
+    if (year >= 0 && year < 10000) {
+      copy2(buf, digits2(static_cast<size_t>(year / 100)));
+    } else {
+      offset = 4;
+      write_year_extended(year);
+      year = 0;
+    }
+    write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
+                           to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
+                           '-');
+    out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
+  }
+
+  void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
+  void on_tz_name() { format_tz_name_impl(tm_); }
+
+  void on_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write_year(tm_year());
+    format_localized('Y', 'E');
+  }
+  void on_short_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(split_year_lower(tm_year()));
+    format_localized('y', 'O');
+  }
+  void on_offset_year() {
+    if (is_classic_) return write2(split_year_lower(tm_year()));
+    format_localized('y', 'E');
+  }
+
+  void on_century(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      auto year = tm_year();
+      auto upper = year / 100;
+      if (year >= -99 && year < 0) {
+        // Zero upper on negative year.
+        *out_++ = '-';
+        *out_++ = '0';
+      } else if (upper >= 0 && upper < 100) {
+        write2(static_cast<int>(upper));
+      } else {
+        out_ = write<Char>(out_, upper);
+      }
+    } else {
+      format_localized('C', 'E');
+    }
+  }
+
+  void on_dec_month(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_mon() + 1);
+    format_localized('m', 'O');
+  }
+
+  void on_dec0_week_of_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
+    format_localized('U', 'O');
+  }
+  void on_dec1_week_of_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      auto wday = tm_wday();
+      write2((tm_yday() + days_per_week -
+              (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
+             days_per_week);
+    } else {
+      format_localized('W', 'O');
+    }
+  }
+  void on_iso_week_of_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_iso_week_of_year());
+    format_localized('V', 'O');
+  }
+
+  void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
+  void on_iso_week_based_short_year() {
+    write2(split_year_lower(tm_iso_week_year()));
+  }
+
+  void on_day_of_year() {
+    auto yday = tm_yday() + 1;
+    write1(yday / 100);
+    write2(yday % 100);
+  }
+  void on_day_of_month(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday());
+    format_localized('d', 'O');
+  }
+  void on_day_of_month_space(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      auto mday = to_unsigned(tm_mday()) % 100;
+      const char* d2 = digits2(mday);
+      *out_++ = mday < 10 ? ' ' : d2[0];
+      *out_++ = d2[1];
+    } else {
+      format_localized('e', 'O');
+    }
+  }
+
+  void on_24_hour(numeric_system ns, pad_type pad) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_hour(), pad);
+    format_localized('H', 'O');
+  }
+  void on_12_hour(numeric_system ns, pad_type pad) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_hour12(), pad);
+    format_localized('I', 'O');
+  }
+  void on_minute(numeric_system ns, pad_type pad) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_min(), pad);
+    format_localized('M', 'O');
+  }
+
+  void on_second(numeric_system ns, pad_type pad) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      write2(tm_sec(), pad);
+      if (subsecs_) {
+        if (std::is_floating_point<typename Duration::rep>::value) {
+          auto buf = memory_buffer();
+          write_floating_seconds(buf, *subsecs_);
+          if (buf.size() > 1) {
+            // Remove the leading "0", write something like ".123".
+            out_ = std::copy(buf.begin() + 1, buf.end(), out_);
+          }
+        } else {
+          write_fractional_seconds<Char>(out_, *subsecs_);
+        }
+      }
+    } else {
+      // Currently no formatting of subseconds when a locale is set.
+      format_localized('S', 'O');
+    }
+  }
+
+  void on_12_hour_time() {
+    if (is_classic_) {
+      char buf[8];
+      write_digit2_separated(buf, to_unsigned(tm_hour12()),
+                             to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
+      out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
+      *out_++ = ' ';
+      on_am_pm();
+    } else {
+      format_localized('r');
+    }
+  }
+  void on_24_hour_time() {
+    write2(tm_hour());
+    *out_++ = ':';
+    write2(tm_min());
+  }
+  void on_iso_time() {
+    on_24_hour_time();
+    *out_++ = ':';
+    on_second(numeric_system::standard, pad_type::unspecified);
+  }
+
+  void on_am_pm() {
+    if (is_classic_) {
+      *out_++ = tm_hour() < 12 ? 'A' : 'P';
+      *out_++ = 'M';
+    } else {
+      format_localized('p');
+    }
+  }
+
+  // These apply to chrono durations but not tm.
+  void on_duration_value() {}
+  void on_duration_unit() {}
+};
+
+struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
+  bool has_precision_integral = false;
+
+  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
+
+  template <typename Char>
+  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+  FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_second(numeric_system, pad_type) {}
+  FMT_CONSTEXPR void on_12_hour_time() {}
+  FMT_CONSTEXPR void on_24_hour_time() {}
+  FMT_CONSTEXPR void on_iso_time() {}
+  FMT_CONSTEXPR void on_am_pm() {}
+  FMT_CONSTEXPR void on_duration_value() const {
+    if (has_precision_integral) {
+      FMT_THROW(format_error("precision not allowed for this argument type"));
+    }
+  }
+  FMT_CONSTEXPR void on_duration_unit() {}
+};
+
+template <typename T,
+          FMT_ENABLE_IF(std::is_integral<T>::value&& has_isfinite<T>::value)>
+inline bool isfinite(T) {
+  return true;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline T mod(T x, int y) {
+  return x % static_cast<T>(y);
+}
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+inline T mod(T x, int y) {
+  return std::fmod(x, static_cast<T>(y));
+}
+
+// If T is an integral type, maps T to its unsigned counterpart, otherwise
+// leaves it unchanged (unlike std::make_unsigned).
+template <typename T, bool INTEGRAL = std::is_integral<T>::value>
+struct make_unsigned_or_unchanged {
+  using type = T;
+};
+
+template <typename T> struct make_unsigned_or_unchanged<T, true> {
+  using type = typename std::make_unsigned<T>::type;
+};
+
+#if FMT_SAFE_DURATION_CAST
+// throwing version of safe_duration_cast
+template <typename To, typename FromRep, typename FromPeriod>
+To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
+  int ec;
+  To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
+  if (ec) FMT_THROW(format_error("cannot format duration"));
+  return to;
+}
+#endif
+
+template <typename Rep, typename Period,
+          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
+inline std::chrono::duration<Rep, std::milli> get_milliseconds(
+    std::chrono::duration<Rep, Period> d) {
+  // this may overflow and/or the result may not fit in the
+  // target type.
+#if FMT_SAFE_DURATION_CAST
+  using CommonSecondsType =
+      typename std::common_type<decltype(d), std::chrono::seconds>::type;
+  const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
+  const auto d_as_whole_seconds =
+      fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
+  // this conversion should be nonproblematic
+  const auto diff = d_as_common - d_as_whole_seconds;
+  const auto ms =
+      fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
+  return ms;
+#else
+  auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
+  return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
+#endif
+}
+
+template <typename Char, typename Rep, typename OutputIt,
+          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
+OutputIt format_duration_value(OutputIt out, Rep val, int) {
+  return write<Char>(out, val);
+}
+
+template <typename Char, typename Rep, typename OutputIt,
+          FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
+OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
+  auto specs = format_specs<Char>();
+  specs.precision = precision;
+  specs.type = precision >= 0 ? presentation_type::fixed_lower
+                              : presentation_type::general_lower;
+  return write<Char>(out, val, specs);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt copy_unit(string_view unit, OutputIt out, Char) {
+  return std::copy(unit.begin(), unit.end(), out);
+}
+
+template <typename OutputIt>
+OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
+  // This works when wchar_t is UTF-32 because units only contain characters
+  // that have the same representation in UTF-16 and UTF-32.
+  utf8_to_utf16 u(unit);
+  return std::copy(u.c_str(), u.c_str() + u.size(), out);
+}
+
+template <typename Char, typename Period, typename OutputIt>
+OutputIt format_duration_unit(OutputIt out) {
+  if (const char* unit = get_units<Period>())
+    return copy_unit(string_view(unit), out, Char());
+  *out++ = '[';
+  out = write<Char>(out, Period::num);
+  if (const_check(Period::den != 1)) {
+    *out++ = '/';
+    out = write<Char>(out, Period::den);
+  }
+  *out++ = ']';
+  *out++ = 's';
+  return out;
+}
+
+class get_locale {
+ private:
+  union {
+    std::locale locale_;
+  };
+  bool has_locale_ = false;
+
+ public:
+  get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
+    if (localized)
+      ::new (&locale_) std::locale(loc.template get<std::locale>());
+  }
+  ~get_locale() {
+    if (has_locale_) locale_.~locale();
+  }
+  operator const std::locale&() const {
+    return has_locale_ ? locale_ : get_classic_locale();
+  }
+};
+
+template <typename FormatContext, typename OutputIt, typename Rep,
+          typename Period>
+struct chrono_formatter {
+  FormatContext& context;
+  OutputIt out;
+  int precision;
+  bool localized = false;
+  // rep is unsigned to avoid overflow.
+  using rep =
+      conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
+                    unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
+  rep val;
+  using seconds = std::chrono::duration<rep>;
+  seconds s;
+  using milliseconds = std::chrono::duration<rep, std::milli>;
+  bool negative;
+
+  using char_type = typename FormatContext::char_type;
+  using tm_writer_type = tm_writer<OutputIt, char_type>;
+
+  chrono_formatter(FormatContext& ctx, OutputIt o,
+                   std::chrono::duration<Rep, Period> d)
+      : context(ctx),
+        out(o),
+        val(static_cast<rep>(d.count())),
+        negative(false) {
+    if (d.count() < 0) {
+      val = 0 - val;
+      negative = true;
+    }
+
+    // this may overflow and/or the result may not fit in the
+    // target type.
+#if FMT_SAFE_DURATION_CAST
+    // might need checked conversion (rep!=Rep)
+    auto tmpval = std::chrono::duration<rep, Period>(val);
+    s = fmt_safe_duration_cast<seconds>(tmpval);
+#else
+    s = std::chrono::duration_cast<seconds>(
+        std::chrono::duration<rep, Period>(val));
+#endif
+  }
+
+  // returns true if nan or inf, writes to out.
+  bool handle_nan_inf() {
+    if (isfinite(val)) {
+      return false;
+    }
+    if (isnan(val)) {
+      write_nan();
+      return true;
+    }
+    // must be +-inf
+    if (val > 0) {
+      write_pinf();
+    } else {
+      write_ninf();
+    }
+    return true;
+  }
+
+  Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
+
+  Rep hour12() const {
+    Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
+    return hour <= 0 ? 12 : hour;
+  }
+
+  Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
+  Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
+
+  std::tm time() const {
+    auto time = std::tm();
+    time.tm_hour = to_nonnegative_int(hour(), 24);
+    time.tm_min = to_nonnegative_int(minute(), 60);
+    time.tm_sec = to_nonnegative_int(second(), 60);
+    return time;
+  }
+
+  void write_sign() {
+    if (negative) {
+      *out++ = '-';
+      negative = false;
+    }
+  }
+
+  void write(Rep value, int width, pad_type pad = pad_type::unspecified) {
+    write_sign();
+    if (isnan(value)) return write_nan();
+    uint32_or_64_or_128_t<int> n =
+        to_unsigned(to_nonnegative_int(value, max_value<int>()));
+    int num_digits = detail::count_digits(n);
+    if (width > num_digits) {
+      out = detail::write_padding(out, pad, width - num_digits);
+    }
+    out = format_decimal<char_type>(out, n, num_digits).end;
+  }
+
+  void write_nan() { std::copy_n("nan", 3, out); }
+  void write_pinf() { std::copy_n("inf", 3, out); }
+  void write_ninf() { std::copy_n("-inf", 4, out); }
+
+  template <typename Callback, typename... Args>
+  void format_tm(const tm& time, Callback cb, Args... args) {
+    if (isnan(val)) return write_nan();
+    get_locale loc(localized, context.locale());
+    auto w = tm_writer_type(loc, out, time);
+    (w.*cb)(args...);
+    out = w.out();
+  }
+
+  void on_text(const char_type* begin, const char_type* end) {
+    std::copy(begin, end, out);
+  }
+
+  // These are not implemented because durations don't have date information.
+  void on_abbr_weekday() {}
+  void on_full_weekday() {}
+  void on_dec0_weekday(numeric_system) {}
+  void on_dec1_weekday(numeric_system) {}
+  void on_abbr_month() {}
+  void on_full_month() {}
+  void on_datetime(numeric_system) {}
+  void on_loc_date(numeric_system) {}
+  void on_loc_time(numeric_system) {}
+  void on_us_date() {}
+  void on_iso_date() {}
+  void on_utc_offset(numeric_system) {}
+  void on_tz_name() {}
+  void on_year(numeric_system) {}
+  void on_short_year(numeric_system) {}
+  void on_offset_year() {}
+  void on_century(numeric_system) {}
+  void on_iso_week_based_year() {}
+  void on_iso_week_based_short_year() {}
+  void on_dec_month(numeric_system) {}
+  void on_dec0_week_of_year(numeric_system) {}
+  void on_dec1_week_of_year(numeric_system) {}
+  void on_iso_week_of_year(numeric_system) {}
+  void on_day_of_year() {}
+  void on_day_of_month(numeric_system) {}
+  void on_day_of_month_space(numeric_system) {}
+
+  void on_24_hour(numeric_system ns, pad_type pad) {
+    if (handle_nan_inf()) return;
+
+    if (ns == numeric_system::standard) return write(hour(), 2, pad);
+    auto time = tm();
+    time.tm_hour = to_nonnegative_int(hour(), 24);
+    format_tm(time, &tm_writer_type::on_24_hour, ns, pad);
+  }
+
+  void on_12_hour(numeric_system ns, pad_type pad) {
+    if (handle_nan_inf()) return;
+
+    if (ns == numeric_system::standard) return write(hour12(), 2, pad);
+    auto time = tm();
+    time.tm_hour = to_nonnegative_int(hour12(), 12);
+    format_tm(time, &tm_writer_type::on_12_hour, ns, pad);
+  }
+
+  void on_minute(numeric_system ns, pad_type pad) {
+    if (handle_nan_inf()) return;
+
+    if (ns == numeric_system::standard) return write(minute(), 2, pad);
+    auto time = tm();
+    time.tm_min = to_nonnegative_int(minute(), 60);
+    format_tm(time, &tm_writer_type::on_minute, ns, pad);
+  }
+
+  void on_second(numeric_system ns, pad_type pad) {
+    if (handle_nan_inf()) return;
+
+    if (ns == numeric_system::standard) {
+      if (std::is_floating_point<rep>::value) {
+        auto buf = memory_buffer();
+        write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
+                               precision);
+        if (negative) *out++ = '-';
+        if (buf.size() < 2 || buf[1] == '.') {
+          out = detail::write_padding(out, pad);
+        }
+        out = std::copy(buf.begin(), buf.end(), out);
+      } else {
+        write(second(), 2, pad);
+        write_fractional_seconds<char_type>(
+            out, std::chrono::duration<rep, Period>(val), precision);
+      }
+      return;
+    }
+    auto time = tm();
+    time.tm_sec = to_nonnegative_int(second(), 60);
+    format_tm(time, &tm_writer_type::on_second, ns, pad);
+  }
+
+  void on_12_hour_time() {
+    if (handle_nan_inf()) return;
+    format_tm(time(), &tm_writer_type::on_12_hour_time);
+  }
+
+  void on_24_hour_time() {
+    if (handle_nan_inf()) {
+      *out++ = ':';
+      handle_nan_inf();
+      return;
+    }
+
+    write(hour(), 2);
+    *out++ = ':';
+    write(minute(), 2);
+  }
+
+  void on_iso_time() {
+    on_24_hour_time();
+    *out++ = ':';
+    if (handle_nan_inf()) return;
+    on_second(numeric_system::standard, pad_type::unspecified);
+  }
+
+  void on_am_pm() {
+    if (handle_nan_inf()) return;
+    format_tm(time(), &tm_writer_type::on_am_pm);
+  }
+
+  void on_duration_value() {
+    if (handle_nan_inf()) return;
+    write_sign();
+    out = format_duration_value<char_type>(out, val, precision);
+  }
+
+  void on_duration_unit() {
+    out = format_duration_unit<char_type, Period>(out);
+  }
+};
+
+}  // namespace detail
+
+#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
+using weekday = std::chrono::weekday;
+#else
+// A fallback version of weekday.
+class weekday {
+ private:
+  unsigned char value;
+
+ public:
+  weekday() = default;
+  explicit constexpr weekday(unsigned wd) noexcept
+      : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
+  constexpr unsigned c_encoding() const noexcept { return value; }
+};
+
+class year_month_day {};
+#endif
+
+// A rudimentary weekday formatter.
+template <typename Char> struct formatter<weekday, Char> {
+ private:
+  bool localized = false;
+
+ public:
+  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
+      -> decltype(ctx.begin()) {
+    auto begin = ctx.begin(), end = ctx.end();
+    if (begin != end && *begin == 'L') {
+      ++begin;
+      localized = true;
+    }
+    return begin;
+  }
+
+  template <typename FormatContext>
+  auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
+    auto time = std::tm();
+    time.tm_wday = static_cast<int>(wd.c_encoding());
+    detail::get_locale loc(localized, ctx.locale());
+    auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
+    w.on_abbr_weekday();
+    return w.out();
+  }
+};
+
+template <typename Rep, typename Period, typename Char>
+struct formatter<std::chrono::duration<Rep, Period>, Char> {
+ private:
+  format_specs<Char> specs_;
+  detail::arg_ref<Char> width_ref_;
+  detail::arg_ref<Char> precision_ref_;
+  bool localized_ = false;
+  basic_string_view<Char> format_str_;
+
+ public:
+  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
+      -> decltype(ctx.begin()) {
+    auto it = ctx.begin(), end = ctx.end();
+    if (it == end || *it == '}') return it;
+
+    it = detail::parse_align(it, end, specs_);
+    if (it == end) return it;
+
+    it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
+    if (it == end) return it;
+
+    auto checker = detail::chrono_format_checker();
+    if (*it == '.') {
+      checker.has_precision_integral = !std::is_floating_point<Rep>::value;
+      it = detail::parse_precision(it, end, specs_.precision, precision_ref_,
+                                   ctx);
+    }
+    if (it != end && *it == 'L') {
+      localized_ = true;
+      ++it;
+    }
+    end = detail::parse_chrono_format(it, end, checker);
+    format_str_ = {it, detail::to_unsigned(end - it)};
+    return end;
+  }
+
+  template <typename FormatContext>
+  auto format(std::chrono::duration<Rep, Period> d, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    auto specs = specs_;
+    auto precision = specs.precision;
+    specs.precision = -1;
+    auto begin = format_str_.begin(), end = format_str_.end();
+    // As a possible future optimization, we could avoid extra copying if width
+    // is not specified.
+    auto buf = basic_memory_buffer<Char>();
+    auto out = std::back_inserter(buf);
+    detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
+                                                       ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(precision,
+                                                           precision_ref_, ctx);
+    if (begin == end || *begin == '}') {
+      out = detail::format_duration_value<Char>(out, d.count(), precision);
+      detail::format_duration_unit<Char, Period>(out);
+    } else {
+      using chrono_formatter =
+          detail::chrono_formatter<FormatContext, decltype(out), Rep, Period>;
+      auto f = chrono_formatter(ctx, out, d);
+      f.precision = precision;
+      f.localized = localized_;
+      detail::parse_chrono_format(begin, end, f);
+    }
+    return detail::write(
+        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
+  }
+};
+
+template <typename Char, typename Duration>
+struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
+                 Char> : formatter<std::tm, Char> {
+  FMT_CONSTEXPR formatter() {
+    this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
+  }
+
+  template <typename FormatContext>
+  auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
+              FormatContext& ctx) const -> decltype(ctx.out()) {
+    using period = typename Duration::period;
+    if (detail::const_check(
+            period::num != 1 || period::den != 1 ||
+            std::is_floating_point<typename Duration::rep>::value)) {
+      const auto epoch = val.time_since_epoch();
+      auto subsecs = std::chrono::duration_cast<Duration>(
+          epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
+
+      if (subsecs.count() < 0) {
+        auto second =
+            std::chrono::duration_cast<Duration>(std::chrono::seconds(1));
+        if (epoch.count() < ((Duration::min)() + second).count())
+          FMT_THROW(format_error("duration is too small"));
+        subsecs += second;
+        val -= second;
+      }
+
+      return formatter<std::tm, Char>::do_format(
+          gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
+          &subsecs);
+    }
+
+    return formatter<std::tm, Char>::format(
+        gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
+  }
+};
+
+#if FMT_USE_LOCAL_TIME
+template <typename Char, typename Duration>
+struct formatter<std::chrono::local_time<Duration>, Char>
+    : formatter<std::tm, Char> {
+  FMT_CONSTEXPR formatter() {
+    this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
+  }
+
+  template <typename FormatContext>
+  auto format(std::chrono::local_time<Duration> val, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    using period = typename Duration::period;
+    if (period::num != 1 || period::den != 1 ||
+        std::is_floating_point<typename Duration::rep>::value) {
+      const auto epoch = val.time_since_epoch();
+      const auto subsecs = std::chrono::duration_cast<Duration>(
+          epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
+
+      return formatter<std::tm, Char>::do_format(
+          localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
+          ctx, &subsecs);
+    }
+
+    return formatter<std::tm, Char>::format(
+        localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
+        ctx);
+  }
+};
+#endif
+
+#if FMT_USE_UTC_TIME
+template <typename Char, typename Duration>
+struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
+                 Char>
+    : formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
+                Char> {
+  template <typename FormatContext>
+  auto format(std::chrono::time_point<std::chrono::utc_clock, Duration> val,
+              FormatContext& ctx) const -> decltype(ctx.out()) {
+    return formatter<
+        std::chrono::time_point<std::chrono::system_clock, Duration>,
+        Char>::format(std::chrono::utc_clock::to_sys(val), ctx);
+  }
+};
+#endif
+
+template <typename Char> struct formatter<std::tm, Char> {
+ private:
+  format_specs<Char> specs_;
+  detail::arg_ref<Char> width_ref_;
+
+ protected:
+  basic_string_view<Char> format_str_;
+
+  template <typename FormatContext, typename Duration>
+  auto do_format(const std::tm& tm, FormatContext& ctx,
+                 const Duration* subsecs) const -> decltype(ctx.out()) {
+    auto specs = specs_;
+    auto buf = basic_memory_buffer<Char>();
+    auto out = std::back_inserter(buf);
+    detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
+                                                       ctx);
+
+    auto loc_ref = ctx.locale();
+    detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
+    auto w =
+        detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
+    detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w);
+    return detail::write(
+        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
+  }
+
+ public:
+  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
+      -> decltype(ctx.begin()) {
+    auto it = ctx.begin(), end = ctx.end();
+    if (it == end || *it == '}') return it;
+
+    it = detail::parse_align(it, end, specs_);
+    if (it == end) return it;
+
+    it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
+    if (it == end) return it;
+
+    end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
+    // Replace the default format_str only if the new spec is not empty.
+    if (end != it) format_str_ = {it, detail::to_unsigned(end - it)};
+    return end;
+  }
+
+  template <typename FormatContext>
+  auto format(const std::tm& tm, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return do_format<FormatContext, std::chrono::seconds>(tm, ctx, nullptr);
+  }
+};
+
+FMT_END_EXPORT
+FMT_END_NAMESPACE
+
+#endif  // FMT_CHRONO_H_
diff --git a/src/cpp-common/vendor/fmt/color.h b/src/cpp-common/vendor/fmt/color.h
new file mode 100644 (file)
index 0000000..8697e1c
--- /dev/null
@@ -0,0 +1,632 @@
+// Formatting library for C++ - color support
+//
+// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_COLOR_H_
+#define FMT_COLOR_H_
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+FMT_BEGIN_EXPORT
+
+enum class color : uint32_t {
+  alice_blue = 0xF0F8FF,               // rgb(240,248,255)
+  antique_white = 0xFAEBD7,            // rgb(250,235,215)
+  aqua = 0x00FFFF,                     // rgb(0,255,255)
+  aquamarine = 0x7FFFD4,               // rgb(127,255,212)
+  azure = 0xF0FFFF,                    // rgb(240,255,255)
+  beige = 0xF5F5DC,                    // rgb(245,245,220)
+  bisque = 0xFFE4C4,                   // rgb(255,228,196)
+  black = 0x000000,                    // rgb(0,0,0)
+  blanched_almond = 0xFFEBCD,          // rgb(255,235,205)
+  blue = 0x0000FF,                     // rgb(0,0,255)
+  blue_violet = 0x8A2BE2,              // rgb(138,43,226)
+  brown = 0xA52A2A,                    // rgb(165,42,42)
+  burly_wood = 0xDEB887,               // rgb(222,184,135)
+  cadet_blue = 0x5F9EA0,               // rgb(95,158,160)
+  chartreuse = 0x7FFF00,               // rgb(127,255,0)
+  chocolate = 0xD2691E,                // rgb(210,105,30)
+  coral = 0xFF7F50,                    // rgb(255,127,80)
+  cornflower_blue = 0x6495ED,          // rgb(100,149,237)
+  cornsilk = 0xFFF8DC,                 // rgb(255,248,220)
+  crimson = 0xDC143C,                  // rgb(220,20,60)
+  cyan = 0x00FFFF,                     // rgb(0,255,255)
+  dark_blue = 0x00008B,                // rgb(0,0,139)
+  dark_cyan = 0x008B8B,                // rgb(0,139,139)
+  dark_golden_rod = 0xB8860B,          // rgb(184,134,11)
+  dark_gray = 0xA9A9A9,                // rgb(169,169,169)
+  dark_green = 0x006400,               // rgb(0,100,0)
+  dark_khaki = 0xBDB76B,               // rgb(189,183,107)
+  dark_magenta = 0x8B008B,             // rgb(139,0,139)
+  dark_olive_green = 0x556B2F,         // rgb(85,107,47)
+  dark_orange = 0xFF8C00,              // rgb(255,140,0)
+  dark_orchid = 0x9932CC,              // rgb(153,50,204)
+  dark_red = 0x8B0000,                 // rgb(139,0,0)
+  dark_salmon = 0xE9967A,              // rgb(233,150,122)
+  dark_sea_green = 0x8FBC8F,           // rgb(143,188,143)
+  dark_slate_blue = 0x483D8B,          // rgb(72,61,139)
+  dark_slate_gray = 0x2F4F4F,          // rgb(47,79,79)
+  dark_turquoise = 0x00CED1,           // rgb(0,206,209)
+  dark_violet = 0x9400D3,              // rgb(148,0,211)
+  deep_pink = 0xFF1493,                // rgb(255,20,147)
+  deep_sky_blue = 0x00BFFF,            // rgb(0,191,255)
+  dim_gray = 0x696969,                 // rgb(105,105,105)
+  dodger_blue = 0x1E90FF,              // rgb(30,144,255)
+  fire_brick = 0xB22222,               // rgb(178,34,34)
+  floral_white = 0xFFFAF0,             // rgb(255,250,240)
+  forest_green = 0x228B22,             // rgb(34,139,34)
+  fuchsia = 0xFF00FF,                  // rgb(255,0,255)
+  gainsboro = 0xDCDCDC,                // rgb(220,220,220)
+  ghost_white = 0xF8F8FF,              // rgb(248,248,255)
+  gold = 0xFFD700,                     // rgb(255,215,0)
+  golden_rod = 0xDAA520,               // rgb(218,165,32)
+  gray = 0x808080,                     // rgb(128,128,128)
+  green = 0x008000,                    // rgb(0,128,0)
+  green_yellow = 0xADFF2F,             // rgb(173,255,47)
+  honey_dew = 0xF0FFF0,                // rgb(240,255,240)
+  hot_pink = 0xFF69B4,                 // rgb(255,105,180)
+  indian_red = 0xCD5C5C,               // rgb(205,92,92)
+  indigo = 0x4B0082,                   // rgb(75,0,130)
+  ivory = 0xFFFFF0,                    // rgb(255,255,240)
+  khaki = 0xF0E68C,                    // rgb(240,230,140)
+  lavender = 0xE6E6FA,                 // rgb(230,230,250)
+  lavender_blush = 0xFFF0F5,           // rgb(255,240,245)
+  lawn_green = 0x7CFC00,               // rgb(124,252,0)
+  lemon_chiffon = 0xFFFACD,            // rgb(255,250,205)
+  light_blue = 0xADD8E6,               // rgb(173,216,230)
+  light_coral = 0xF08080,              // rgb(240,128,128)
+  light_cyan = 0xE0FFFF,               // rgb(224,255,255)
+  light_golden_rod_yellow = 0xFAFAD2,  // rgb(250,250,210)
+  light_gray = 0xD3D3D3,               // rgb(211,211,211)
+  light_green = 0x90EE90,              // rgb(144,238,144)
+  light_pink = 0xFFB6C1,               // rgb(255,182,193)
+  light_salmon = 0xFFA07A,             // rgb(255,160,122)
+  light_sea_green = 0x20B2AA,          // rgb(32,178,170)
+  light_sky_blue = 0x87CEFA,           // rgb(135,206,250)
+  light_slate_gray = 0x778899,         // rgb(119,136,153)
+  light_steel_blue = 0xB0C4DE,         // rgb(176,196,222)
+  light_yellow = 0xFFFFE0,             // rgb(255,255,224)
+  lime = 0x00FF00,                     // rgb(0,255,0)
+  lime_green = 0x32CD32,               // rgb(50,205,50)
+  linen = 0xFAF0E6,                    // rgb(250,240,230)
+  magenta = 0xFF00FF,                  // rgb(255,0,255)
+  maroon = 0x800000,                   // rgb(128,0,0)
+  medium_aquamarine = 0x66CDAA,        // rgb(102,205,170)
+  medium_blue = 0x0000CD,              // rgb(0,0,205)
+  medium_orchid = 0xBA55D3,            // rgb(186,85,211)
+  medium_purple = 0x9370DB,            // rgb(147,112,219)
+  medium_sea_green = 0x3CB371,         // rgb(60,179,113)
+  medium_slate_blue = 0x7B68EE,        // rgb(123,104,238)
+  medium_spring_green = 0x00FA9A,      // rgb(0,250,154)
+  medium_turquoise = 0x48D1CC,         // rgb(72,209,204)
+  medium_violet_red = 0xC71585,        // rgb(199,21,133)
+  midnight_blue = 0x191970,            // rgb(25,25,112)
+  mint_cream = 0xF5FFFA,               // rgb(245,255,250)
+  misty_rose = 0xFFE4E1,               // rgb(255,228,225)
+  moccasin = 0xFFE4B5,                 // rgb(255,228,181)
+  navajo_white = 0xFFDEAD,             // rgb(255,222,173)
+  navy = 0x000080,                     // rgb(0,0,128)
+  old_lace = 0xFDF5E6,                 // rgb(253,245,230)
+  olive = 0x808000,                    // rgb(128,128,0)
+  olive_drab = 0x6B8E23,               // rgb(107,142,35)
+  orange = 0xFFA500,                   // rgb(255,165,0)
+  orange_red = 0xFF4500,               // rgb(255,69,0)
+  orchid = 0xDA70D6,                   // rgb(218,112,214)
+  pale_golden_rod = 0xEEE8AA,          // rgb(238,232,170)
+  pale_green = 0x98FB98,               // rgb(152,251,152)
+  pale_turquoise = 0xAFEEEE,           // rgb(175,238,238)
+  pale_violet_red = 0xDB7093,          // rgb(219,112,147)
+  papaya_whip = 0xFFEFD5,              // rgb(255,239,213)
+  peach_puff = 0xFFDAB9,               // rgb(255,218,185)
+  peru = 0xCD853F,                     // rgb(205,133,63)
+  pink = 0xFFC0CB,                     // rgb(255,192,203)
+  plum = 0xDDA0DD,                     // rgb(221,160,221)
+  powder_blue = 0xB0E0E6,              // rgb(176,224,230)
+  purple = 0x800080,                   // rgb(128,0,128)
+  rebecca_purple = 0x663399,           // rgb(102,51,153)
+  red = 0xFF0000,                      // rgb(255,0,0)
+  rosy_brown = 0xBC8F8F,               // rgb(188,143,143)
+  royal_blue = 0x4169E1,               // rgb(65,105,225)
+  saddle_brown = 0x8B4513,             // rgb(139,69,19)
+  salmon = 0xFA8072,                   // rgb(250,128,114)
+  sandy_brown = 0xF4A460,              // rgb(244,164,96)
+  sea_green = 0x2E8B57,                // rgb(46,139,87)
+  sea_shell = 0xFFF5EE,                // rgb(255,245,238)
+  sienna = 0xA0522D,                   // rgb(160,82,45)
+  silver = 0xC0C0C0,                   // rgb(192,192,192)
+  sky_blue = 0x87CEEB,                 // rgb(135,206,235)
+  slate_blue = 0x6A5ACD,               // rgb(106,90,205)
+  slate_gray = 0x708090,               // rgb(112,128,144)
+  snow = 0xFFFAFA,                     // rgb(255,250,250)
+  spring_green = 0x00FF7F,             // rgb(0,255,127)
+  steel_blue = 0x4682B4,               // rgb(70,130,180)
+  tan = 0xD2B48C,                      // rgb(210,180,140)
+  teal = 0x008080,                     // rgb(0,128,128)
+  thistle = 0xD8BFD8,                  // rgb(216,191,216)
+  tomato = 0xFF6347,                   // rgb(255,99,71)
+  turquoise = 0x40E0D0,                // rgb(64,224,208)
+  violet = 0xEE82EE,                   // rgb(238,130,238)
+  wheat = 0xF5DEB3,                    // rgb(245,222,179)
+  white = 0xFFFFFF,                    // rgb(255,255,255)
+  white_smoke = 0xF5F5F5,              // rgb(245,245,245)
+  yellow = 0xFFFF00,                   // rgb(255,255,0)
+  yellow_green = 0x9ACD32              // rgb(154,205,50)
+};                                     // enum class color
+
+enum class terminal_color : uint8_t {
+  black = 30,
+  red,
+  green,
+  yellow,
+  blue,
+  magenta,
+  cyan,
+  white,
+  bright_black = 90,
+  bright_red,
+  bright_green,
+  bright_yellow,
+  bright_blue,
+  bright_magenta,
+  bright_cyan,
+  bright_white
+};
+
+enum class emphasis : uint8_t {
+  bold = 1,
+  faint = 1 << 1,
+  italic = 1 << 2,
+  underline = 1 << 3,
+  blink = 1 << 4,
+  reverse = 1 << 5,
+  conceal = 1 << 6,
+  strikethrough = 1 << 7,
+};
+
+// rgb is a struct for red, green and blue colors.
+// Using the name "rgb" makes some editors show the color in a tooltip.
+struct rgb {
+  FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
+  FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
+  FMT_CONSTEXPR rgb(uint32_t hex)
+      : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
+  FMT_CONSTEXPR rgb(color hex)
+      : r((uint32_t(hex) >> 16) & 0xFF),
+        g((uint32_t(hex) >> 8) & 0xFF),
+        b(uint32_t(hex) & 0xFF) {}
+  uint8_t r;
+  uint8_t g;
+  uint8_t b;
+};
+
+namespace detail {
+
+// color is a struct of either a rgb color or a terminal color.
+struct color_type {
+  FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
+  FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
+    value.rgb_color = static_cast<uint32_t>(rgb_color);
+  }
+  FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
+    value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
+                      (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
+  }
+  FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
+      : is_rgb(), value{} {
+    value.term_color = static_cast<uint8_t>(term_color);
+  }
+  bool is_rgb;
+  union color_union {
+    uint8_t term_color;
+    uint32_t rgb_color;
+  } value;
+};
+}  // namespace detail
+
+/** A text style consisting of foreground and background colors and emphasis. */
+class text_style {
+ public:
+  FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
+      : set_foreground_color(), set_background_color(), ems(em) {}
+
+  FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
+    if (!set_foreground_color) {
+      set_foreground_color = rhs.set_foreground_color;
+      foreground_color = rhs.foreground_color;
+    } else if (rhs.set_foreground_color) {
+      if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
+        FMT_THROW(format_error("can't OR a terminal color"));
+      foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
+    }
+
+    if (!set_background_color) {
+      set_background_color = rhs.set_background_color;
+      background_color = rhs.background_color;
+    } else if (rhs.set_background_color) {
+      if (!background_color.is_rgb || !rhs.background_color.is_rgb)
+        FMT_THROW(format_error("can't OR a terminal color"));
+      background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
+    }
+
+    ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
+                                static_cast<uint8_t>(rhs.ems));
+    return *this;
+  }
+
+  friend FMT_CONSTEXPR text_style operator|(text_style lhs,
+                                            const text_style& rhs) {
+    return lhs |= rhs;
+  }
+
+  FMT_CONSTEXPR bool has_foreground() const noexcept {
+    return set_foreground_color;
+  }
+  FMT_CONSTEXPR bool has_background() const noexcept {
+    return set_background_color;
+  }
+  FMT_CONSTEXPR bool has_emphasis() const noexcept {
+    return static_cast<uint8_t>(ems) != 0;
+  }
+  FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
+    FMT_ASSERT(has_foreground(), "no foreground specified for this style");
+    return foreground_color;
+  }
+  FMT_CONSTEXPR detail::color_type get_background() const noexcept {
+    FMT_ASSERT(has_background(), "no background specified for this style");
+    return background_color;
+  }
+  FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
+    FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
+    return ems;
+  }
+
+ private:
+  FMT_CONSTEXPR text_style(bool is_foreground,
+                           detail::color_type text_color) noexcept
+      : set_foreground_color(), set_background_color(), ems() {
+    if (is_foreground) {
+      foreground_color = text_color;
+      set_foreground_color = true;
+    } else {
+      background_color = text_color;
+      set_background_color = true;
+    }
+  }
+
+  friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept;
+
+  friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
+
+  detail::color_type foreground_color;
+  detail::color_type background_color;
+  bool set_foreground_color;
+  bool set_background_color;
+  emphasis ems;
+};
+
+/** Creates a text style from the foreground (text) color. */
+FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
+  return text_style(true, foreground);
+}
+
+/** Creates a text style from the background color. */
+FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
+  return text_style(false, background);
+}
+
+FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
+  return text_style(lhs) | rhs;
+}
+
+namespace detail {
+
+template <typename Char> struct ansi_color_escape {
+  FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
+                                  const char* esc) noexcept {
+    // If we have a terminal color, we need to output another escape code
+    // sequence.
+    if (!text_color.is_rgb) {
+      bool is_background = esc == string_view("\x1b[48;2;");
+      uint32_t value = text_color.value.term_color;
+      // Background ASCII codes are the same as the foreground ones but with
+      // 10 more.
+      if (is_background) value += 10u;
+
+      size_t index = 0;
+      buffer[index++] = static_cast<Char>('\x1b');
+      buffer[index++] = static_cast<Char>('[');
+
+      if (value >= 100u) {
+        buffer[index++] = static_cast<Char>('1');
+        value %= 100u;
+      }
+      buffer[index++] = static_cast<Char>('0' + value / 10u);
+      buffer[index++] = static_cast<Char>('0' + value % 10u);
+
+      buffer[index++] = static_cast<Char>('m');
+      buffer[index++] = static_cast<Char>('\0');
+      return;
+    }
+
+    for (int i = 0; i < 7; i++) {
+      buffer[i] = static_cast<Char>(esc[i]);
+    }
+    rgb color(text_color.value.rgb_color);
+    to_esc(color.r, buffer + 7, ';');
+    to_esc(color.g, buffer + 11, ';');
+    to_esc(color.b, buffer + 15, 'm');
+    buffer[19] = static_cast<Char>(0);
+  }
+  FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
+    uint8_t em_codes[num_emphases] = {};
+    if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
+    if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
+    if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
+    if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
+    if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
+    if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
+    if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
+    if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
+
+    size_t index = 0;
+    for (size_t i = 0; i < num_emphases; ++i) {
+      if (!em_codes[i]) continue;
+      buffer[index++] = static_cast<Char>('\x1b');
+      buffer[index++] = static_cast<Char>('[');
+      buffer[index++] = static_cast<Char>('0' + em_codes[i]);
+      buffer[index++] = static_cast<Char>('m');
+    }
+    buffer[index++] = static_cast<Char>(0);
+  }
+  FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
+
+  FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
+  FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
+    return buffer + std::char_traits<Char>::length(buffer);
+  }
+
+ private:
+  static constexpr size_t num_emphases = 8;
+  Char buffer[7u + 3u * num_emphases + 1u];
+
+  static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
+                                   char delimiter) noexcept {
+    out[0] = static_cast<Char>('0' + c / 100);
+    out[1] = static_cast<Char>('0' + c / 10 % 10);
+    out[2] = static_cast<Char>('0' + c % 10);
+    out[3] = static_cast<Char>(delimiter);
+  }
+  static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
+    return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
+  }
+};
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
+    detail::color_type foreground) noexcept {
+  return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
+}
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
+    detail::color_type background) noexcept {
+  return ansi_color_escape<Char>(background, "\x1b[48;2;");
+}
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
+  return ansi_color_escape<Char>(em);
+}
+
+template <typename Char> inline void reset_color(buffer<Char>& buffer) {
+  auto reset_color = string_view("\x1b[0m");
+  buffer.append(reset_color.begin(), reset_color.end());
+}
+
+template <typename T> struct styled_arg {
+  const T& value;
+  text_style style;
+};
+
+template <typename Char>
+void vformat_to(buffer<Char>& buf, const text_style& ts,
+                basic_string_view<Char> format_str,
+                basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  bool has_style = false;
+  if (ts.has_emphasis()) {
+    has_style = true;
+    auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
+    buf.append(emphasis.begin(), emphasis.end());
+  }
+  if (ts.has_foreground()) {
+    has_style = true;
+    auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
+    buf.append(foreground.begin(), foreground.end());
+  }
+  if (ts.has_background()) {
+    has_style = true;
+    auto background = detail::make_background_color<Char>(ts.get_background());
+    buf.append(background.begin(), background.end());
+  }
+  detail::vformat_to(buf, format_str, args, {});
+  if (has_style) detail::reset_color<Char>(buf);
+}
+
+}  // namespace detail
+
+inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
+                   format_args args) {
+  // Legacy wide streams are not supported.
+  auto buf = memory_buffer();
+  detail::vformat_to(buf, ts, fmt, args);
+  if (detail::is_utf8()) {
+    detail::print(f, string_view(buf.begin(), buf.size()));
+    return;
+  }
+  buf.push_back('\0');
+  int result = std::fputs(buf.data(), f);
+  if (result < 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+}
+
+/**
+  \rst
+  Formats a string and prints it to the specified file stream using ANSI
+  escape sequences to specify text formatting.
+
+  **Example**::
+
+    fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
+               "Elapsed time: {0:.2f} seconds", 1.23);
+  \endrst
+ */
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_string<S>::value)>
+void print(std::FILE* f, const text_style& ts, const S& format_str,
+           const Args&... args) {
+  vprint(f, ts, format_str,
+         fmt::make_format_args<buffer_context<char_t<S>>>(args...));
+}
+
+/**
+  \rst
+  Formats a string and prints it to stdout using ANSI escape sequences to
+  specify text formatting.
+
+  **Example**::
+
+    fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
+               "Elapsed time: {0:.2f} seconds", 1.23);
+  \endrst
+ */
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_string<S>::value)>
+void print(const text_style& ts, const S& format_str, const Args&... args) {
+  return print(stdout, ts, format_str, args...);
+}
+
+template <typename S, typename Char = char_t<S>>
+inline std::basic_string<Char> vformat(
+    const text_style& ts, const S& format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  basic_memory_buffer<Char> buf;
+  detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
+  return fmt::to_string(buf);
+}
+
+/**
+  \rst
+  Formats arguments and returns the result as a string using ANSI
+  escape sequences to specify text formatting.
+
+  **Example**::
+
+    #include <fmt/color.h>
+    std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
+                                      "The answer is {}", 42);
+  \endrst
+*/
+template <typename S, typename... Args, typename Char = char_t<S>>
+inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
+                                      const Args&... args) {
+  return fmt::vformat(ts, detail::to_string_view(format_str),
+                      fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+/**
+  Formats a string with the given text_style and writes the output to ``out``.
+ */
+template <typename OutputIt, typename Char,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
+OutputIt vformat_to(
+    OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  auto&& buf = detail::get_buffer<Char>(out);
+  detail::vformat_to(buf, ts, format_str, args);
+  return detail::get_iterator(buf, out);
+}
+
+/**
+  \rst
+  Formats arguments with the given text_style, writes the result to the output
+  iterator ``out`` and returns the iterator past the end of the output range.
+
+  **Example**::
+
+    std::vector<char> out;
+    fmt::format_to(std::back_inserter(out),
+                   fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
+  \endrst
+*/
+template <typename OutputIt, typename S, typename... Args,
+          bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
+              detail::is_string<S>::value>
+inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
+                      Args&&... args) ->
+    typename std::enable_if<enable, OutputIt>::type {
+  return vformat_to(out, ts, detail::to_string_view(format_str),
+                    fmt::make_format_args<buffer_context<char_t<S>>>(args...));
+}
+
+template <typename T, typename Char>
+struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
+  template <typename FormatContext>
+  auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    const auto& ts = arg.style;
+    const auto& value = arg.value;
+    auto out = ctx.out();
+
+    bool has_style = false;
+    if (ts.has_emphasis()) {
+      has_style = true;
+      auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
+      out = std::copy(emphasis.begin(), emphasis.end(), out);
+    }
+    if (ts.has_foreground()) {
+      has_style = true;
+      auto foreground =
+          detail::make_foreground_color<Char>(ts.get_foreground());
+      out = std::copy(foreground.begin(), foreground.end(), out);
+    }
+    if (ts.has_background()) {
+      has_style = true;
+      auto background =
+          detail::make_background_color<Char>(ts.get_background());
+      out = std::copy(background.begin(), background.end(), out);
+    }
+    out = formatter<T, Char>::format(value, ctx);
+    if (has_style) {
+      auto reset_color = string_view("\x1b[0m");
+      out = std::copy(reset_color.begin(), reset_color.end(), out);
+    }
+    return out;
+  }
+};
+
+/**
+  \rst
+  Returns an argument that will be formatted using ANSI escape sequences,
+  to be used in a formatting function.
+
+  **Example**::
+
+    fmt::print("Elapsed time: {0:.2f} seconds",
+               fmt::styled(1.23, fmt::fg(fmt::color::green) |
+                                 fmt::bg(fmt::color::blue)));
+  \endrst
+ */
+template <typename T>
+FMT_CONSTEXPR auto styled(const T& value, text_style ts)
+    -> detail::styled_arg<remove_cvref_t<T>> {
+  return detail::styled_arg<remove_cvref_t<T>>{value, ts};
+}
+
+FMT_END_EXPORT
+FMT_END_NAMESPACE
+
+#endif  // FMT_COLOR_H_
diff --git a/src/cpp-common/vendor/fmt/compile.h b/src/cpp-common/vendor/fmt/compile.h
new file mode 100644 (file)
index 0000000..a4c7e49
--- /dev/null
@@ -0,0 +1,534 @@
+// Formatting library for C++ - experimental format string compilation
+//
+// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_COMPILE_H_
+#define FMT_COMPILE_H_
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+template <typename Char, typename InputIt>
+FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
+                                                counting_iterator it) {
+  return it + (end - begin);
+}
+
+// A compile-time string which is compiled into fast formatting code.
+class compiled_string {};
+
+template <typename S>
+struct is_compiled_string : std::is_base_of<compiled_string, S> {};
+
+/**
+  \rst
+  Converts a string literal *s* into a format string that will be parsed at
+  compile time and converted into efficient formatting code. Requires C++17
+  ``constexpr if`` compiler support.
+
+  **Example**::
+
+    // Converts 42 into std::string using the most efficient method and no
+    // runtime format string processing.
+    std::string s = fmt::format(FMT_COMPILE("{}"), 42);
+  \endrst
+ */
+#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
+#  define FMT_COMPILE(s) \
+    FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
+#else
+#  define FMT_COMPILE(s) FMT_STRING(s)
+#endif
+
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct udl_compiled_string : compiled_string {
+  using char_type = Char;
+  explicit constexpr operator basic_string_view<char_type>() const {
+    return {Str.data, N - 1};
+  }
+};
+#endif
+
+template <typename T, typename... Tail>
+const T& first(const T& value, const Tail&...) {
+  return value;
+}
+
+#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
+template <typename... Args> struct type_list {};
+
+// Returns a reference to the argument at index N from [first, rest...].
+template <int N, typename T, typename... Args>
+constexpr const auto& get([[maybe_unused]] const T& first,
+                          [[maybe_unused]] const Args&... rest) {
+  static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
+  if constexpr (N == 0)
+    return first;
+  else
+    return detail::get<N - 1>(rest...);
+}
+
+template <typename Char, typename... Args>
+constexpr int get_arg_index_by_name(basic_string_view<Char> name,
+                                    type_list<Args...>) {
+  return get_arg_index_by_name<Args...>(name);
+}
+
+template <int N, typename> struct get_type_impl;
+
+template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
+  using type =
+      remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
+};
+
+template <int N, typename T>
+using get_type = typename get_type_impl<N, T>::type;
+
+template <typename T> struct is_compiled_format : std::false_type {};
+
+template <typename Char> struct text {
+  basic_string_view<Char> data;
+  using char_type = Char;
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&...) const {
+    return write<Char>(out, data);
+  }
+};
+
+template <typename Char>
+struct is_compiled_format<text<Char>> : std::true_type {};
+
+template <typename Char>
+constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
+                               size_t size) {
+  return {{&s[pos], size}};
+}
+
+template <typename Char> struct code_unit {
+  Char value;
+  using char_type = Char;
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&...) const {
+    *out++ = value;
+    return out;
+  }
+};
+
+// This ensures that the argument type is convertible to `const T&`.
+template <typename T, int N, typename... Args>
+constexpr const T& get_arg_checked(const Args&... args) {
+  const auto& arg = detail::get<N>(args...);
+  if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
+    return arg.value;
+  } else {
+    return arg;
+  }
+}
+
+template <typename Char>
+struct is_compiled_format<code_unit<Char>> : std::true_type {};
+
+// A replacement field that refers to argument N.
+template <typename Char, typename T, int N> struct field {
+  using char_type = Char;
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&... args) const {
+    const T& arg = get_arg_checked<T, N>(args...);
+    if constexpr (std::is_convertible_v<T, basic_string_view<Char>>) {
+      auto s = basic_string_view<Char>(arg);
+      return copy_str<Char>(s.begin(), s.end(), out);
+    }
+    return write<Char>(out, arg);
+  }
+};
+
+template <typename Char, typename T, int N>
+struct is_compiled_format<field<Char, T, N>> : std::true_type {};
+
+// A replacement field that refers to argument with name.
+template <typename Char> struct runtime_named_field {
+  using char_type = Char;
+  basic_string_view<Char> name;
+
+  template <typename OutputIt, typename T>
+  constexpr static bool try_format_argument(
+      OutputIt& out,
+      // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
+      [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
+    if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
+      if (arg_name == arg.name) {
+        out = write<Char>(out, arg.value);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&... args) const {
+    bool found = (try_format_argument(out, name, args) || ...);
+    if (!found) {
+      FMT_THROW(format_error("argument with specified name is not found"));
+    }
+    return out;
+  }
+};
+
+template <typename Char>
+struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
+
+// A replacement field that refers to argument N and has format specifiers.
+template <typename Char, typename T, int N> struct spec_field {
+  using char_type = Char;
+  formatter<T, Char> fmt;
+
+  template <typename OutputIt, typename... Args>
+  constexpr FMT_INLINE OutputIt format(OutputIt out,
+                                       const Args&... args) const {
+    const auto& vargs =
+        fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
+    basic_format_context<OutputIt, Char> ctx(out, vargs);
+    return fmt.format(get_arg_checked<T, N>(args...), ctx);
+  }
+};
+
+template <typename Char, typename T, int N>
+struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
+
+template <typename L, typename R> struct concat {
+  L lhs;
+  R rhs;
+  using char_type = typename L::char_type;
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&... args) const {
+    out = lhs.format(out, args...);
+    return rhs.format(out, args...);
+  }
+};
+
+template <typename L, typename R>
+struct is_compiled_format<concat<L, R>> : std::true_type {};
+
+template <typename L, typename R>
+constexpr concat<L, R> make_concat(L lhs, R rhs) {
+  return {lhs, rhs};
+}
+
+struct unknown_format {};
+
+template <typename Char>
+constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
+  for (size_t size = str.size(); pos != size; ++pos) {
+    if (str[pos] == '{' || str[pos] == '}') break;
+  }
+  return pos;
+}
+
+template <typename Args, size_t POS, int ID, typename S>
+constexpr auto compile_format_string(S format_str);
+
+template <typename Args, size_t POS, int ID, typename T, typename S>
+constexpr auto parse_tail(T head, S format_str) {
+  if constexpr (POS !=
+                basic_string_view<typename S::char_type>(format_str).size()) {
+    constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
+    if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
+                               unknown_format>())
+      return tail;
+    else
+      return make_concat(head, tail);
+  } else {
+    return head;
+  }
+}
+
+template <typename T, typename Char> struct parse_specs_result {
+  formatter<T, Char> fmt;
+  size_t end;
+  int next_arg_id;
+};
+
+enum { manual_indexing_id = -1 };
+
+template <typename T, typename Char>
+constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
+                                                  size_t pos, int next_arg_id) {
+  str.remove_prefix(pos);
+  auto ctx =
+      compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
+  auto f = formatter<T, Char>();
+  auto end = f.parse(ctx);
+  return {f, pos + fmt::detail::to_unsigned(end - str.data()),
+          next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
+}
+
+template <typename Char> struct arg_id_handler {
+  arg_ref<Char> arg_id;
+
+  constexpr int on_auto() {
+    FMT_ASSERT(false, "handler cannot be used with automatic indexing");
+    return 0;
+  }
+  constexpr int on_index(int id) {
+    arg_id = arg_ref<Char>(id);
+    return 0;
+  }
+  constexpr int on_name(basic_string_view<Char> id) {
+    arg_id = arg_ref<Char>(id);
+    return 0;
+  }
+};
+
+template <typename Char> struct parse_arg_id_result {
+  arg_ref<Char> arg_id;
+  const Char* arg_id_end;
+};
+
+template <int ID, typename Char>
+constexpr auto parse_arg_id(const Char* begin, const Char* end) {
+  auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
+  auto arg_id_end = parse_arg_id(begin, end, handler);
+  return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
+}
+
+template <typename T, typename Enable = void> struct field_type {
+  using type = remove_cvref_t<T>;
+};
+
+template <typename T>
+struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
+  using type = remove_cvref_t<decltype(T::value)>;
+};
+
+template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
+          typename S>
+constexpr auto parse_replacement_field_then_tail(S format_str) {
+  using char_type = typename S::char_type;
+  constexpr auto str = basic_string_view<char_type>(format_str);
+  constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
+  if constexpr (c == '}') {
+    return parse_tail<Args, END_POS + 1, NEXT_ID>(
+        field<char_type, typename field_type<T>::type, ARG_INDEX>(),
+        format_str);
+  } else if constexpr (c != ':') {
+    FMT_THROW(format_error("expected ':'"));
+  } else {
+    constexpr auto result = parse_specs<typename field_type<T>::type>(
+        str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
+    if constexpr (result.end >= str.size() || str[result.end] != '}') {
+      FMT_THROW(format_error("expected '}'"));
+      return 0;
+    } else {
+      return parse_tail<Args, result.end + 1, result.next_arg_id>(
+          spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
+              result.fmt},
+          format_str);
+    }
+  }
+}
+
+// Compiles a non-empty format string and returns the compiled representation
+// or unknown_format() on unrecognized input.
+template <typename Args, size_t POS, int ID, typename S>
+constexpr auto compile_format_string(S format_str) {
+  using char_type = typename S::char_type;
+  constexpr auto str = basic_string_view<char_type>(format_str);
+  if constexpr (str[POS] == '{') {
+    if constexpr (POS + 1 == str.size())
+      FMT_THROW(format_error("unmatched '{' in format string"));
+    if constexpr (str[POS + 1] == '{') {
+      return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+    } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
+      static_assert(ID != manual_indexing_id,
+                    "cannot switch from manual to automatic argument indexing");
+      constexpr auto next_id =
+          ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
+      return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
+                                               POS + 1, ID, next_id>(
+          format_str);
+    } else {
+      constexpr auto arg_id_result =
+          parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
+      constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
+      constexpr char_type c =
+          arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
+      static_assert(c == '}' || c == ':', "missing '}' in format string");
+      if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
+        static_assert(
+            ID == manual_indexing_id || ID == 0,
+            "cannot switch from automatic to manual argument indexing");
+        constexpr auto arg_index = arg_id_result.arg_id.val.index;
+        return parse_replacement_field_then_tail<get_type<arg_index, Args>,
+                                                 Args, arg_id_end_pos,
+                                                 arg_index, manual_indexing_id>(
+            format_str);
+      } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
+        constexpr auto arg_index =
+            get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
+        if constexpr (arg_index >= 0) {
+          constexpr auto next_id =
+              ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
+          return parse_replacement_field_then_tail<
+              decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
+              arg_index, next_id>(format_str);
+        } else if constexpr (c == '}') {
+          return parse_tail<Args, arg_id_end_pos + 1, ID>(
+              runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
+              format_str);
+        } else if constexpr (c == ':') {
+          return unknown_format();  // no type info for specs parsing
+        }
+      }
+    }
+  } else if constexpr (str[POS] == '}') {
+    if constexpr (POS + 1 == str.size())
+      FMT_THROW(format_error("unmatched '}' in format string"));
+    return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+  } else {
+    constexpr auto end = parse_text(str, POS + 1);
+    if constexpr (end - POS > 1) {
+      return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
+                                       format_str);
+    } else {
+      return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
+                                       format_str);
+    }
+  }
+}
+
+template <typename... Args, typename S,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+constexpr auto compile(S format_str) {
+  constexpr auto str = basic_string_view<typename S::char_type>(format_str);
+  if constexpr (str.size() == 0) {
+    return detail::make_text(str, 0, 0);
+  } else {
+    constexpr auto result =
+        detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
+            format_str);
+    return result;
+  }
+}
+#endif  // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
+}  // namespace detail
+
+FMT_BEGIN_EXPORT
+
+#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
+
+template <typename CompiledFormat, typename... Args,
+          typename Char = typename CompiledFormat::char_type,
+          FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
+FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
+                                          const Args&... args) {
+  auto s = std::basic_string<Char>();
+  cf.format(std::back_inserter(s), args...);
+  return s;
+}
+
+template <typename OutputIt, typename CompiledFormat, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
+constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
+                                        const Args&... args) {
+  return cf.format(out, args...);
+}
+
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
+                                                           Args&&... args) {
+  if constexpr (std::is_same<typename S::char_type, char>::value) {
+    constexpr auto str = basic_string_view<typename S::char_type>(S());
+    if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
+      const auto& first = detail::first(args...);
+      if constexpr (detail::is_named_arg<
+                        remove_cvref_t<decltype(first)>>::value) {
+        return fmt::to_string(first.value);
+      } else {
+        return fmt::to_string(first);
+      }
+    }
+  }
+  constexpr auto compiled = detail::compile<Args...>(S());
+  if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
+                             detail::unknown_format>()) {
+    return fmt::format(
+        static_cast<basic_string_view<typename S::char_type>>(S()),
+        std::forward<Args>(args)...);
+  } else {
+    return fmt::format(compiled, std::forward<Args>(args)...);
+  }
+}
+
+template <typename OutputIt, typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
+  constexpr auto compiled = detail::compile<Args...>(S());
+  if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
+                             detail::unknown_format>()) {
+    return fmt::format_to(
+        out, static_cast<basic_string_view<typename S::char_type>>(S()),
+        std::forward<Args>(args)...);
+  } else {
+    return fmt::format_to(out, compiled, std::forward<Args>(args)...);
+  }
+}
+#endif
+
+template <typename OutputIt, typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
+                                         const S& format_str, Args&&... args) {
+  using traits = detail::fixed_buffer_traits;
+  auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
+  format_to(std::back_inserter(buf), format_str, std::forward<Args>(args)...);
+  return {buf.out(), buf.count()};
+}
+
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
+                                      const Args&... args) {
+  return fmt::format_to(detail::counting_iterator(), format_str, args...)
+      .count();
+}
+
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+void print(std::FILE* f, const S& format_str, const Args&... args) {
+  memory_buffer buffer;
+  fmt::format_to(std::back_inserter(buffer), format_str, args...);
+  detail::print(f, {buffer.data(), buffer.size()});
+}
+
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+void print(const S& format_str, const Args&... args) {
+  print(stdout, format_str, args...);
+}
+
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+inline namespace literals {
+template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
+  using char_t = remove_cvref_t<decltype(Str.data[0])>;
+  return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
+                                     Str>();
+}
+}  // namespace literals
+#endif
+
+FMT_END_EXPORT
+FMT_END_NAMESPACE
+
+#endif  // FMT_COMPILE_H_
diff --git a/src/cpp-common/vendor/fmt/core.h b/src/cpp-common/vendor/fmt/core.h
new file mode 100644 (file)
index 0000000..1fe1388
--- /dev/null
@@ -0,0 +1,2922 @@
+// Formatting library for C++ - the core API for char/UTF-8
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_CORE_H_
+#define FMT_CORE_H_
+
+#include <cstddef>  // std::byte
+#include <cstdio>   // std::FILE
+#include <cstring>  // std::strlen
+#include <iterator>
+#include <limits>
+#include <memory>  // std::addressof
+#include <string>
+#include <type_traits>
+
+// The fmt library version in the form major * 10000 + minor * 100 + patch.
+#define FMT_VERSION 100100
+
+#if defined(__clang__) && !defined(__ibmxl__)
+#  define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
+#else
+#  define FMT_CLANG_VERSION 0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \
+    !defined(__NVCOMPILER)
+#  define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#else
+#  define FMT_GCC_VERSION 0
+#endif
+
+#ifndef FMT_GCC_PRAGMA
+// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
+#  if FMT_GCC_VERSION >= 504
+#    define FMT_GCC_PRAGMA(arg) _Pragma(arg)
+#  else
+#    define FMT_GCC_PRAGMA(arg)
+#  endif
+#endif
+
+#ifdef __ICL
+#  define FMT_ICC_VERSION __ICL
+#elif defined(__INTEL_COMPILER)
+#  define FMT_ICC_VERSION __INTEL_COMPILER
+#else
+#  define FMT_ICC_VERSION 0
+#endif
+
+#ifdef _MSC_VER
+#  define FMT_MSC_VERSION _MSC_VER
+#  define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
+#else
+#  define FMT_MSC_VERSION 0
+#  define FMT_MSC_WARNING(...)
+#endif
+
+#ifdef _MSVC_LANG
+#  define FMT_CPLUSPLUS _MSVC_LANG
+#else
+#  define FMT_CPLUSPLUS __cplusplus
+#endif
+
+#ifdef __has_feature
+#  define FMT_HAS_FEATURE(x) __has_feature(x)
+#else
+#  define FMT_HAS_FEATURE(x) 0
+#endif
+
+#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900
+#  define FMT_HAS_INCLUDE(x) __has_include(x)
+#else
+#  define FMT_HAS_INCLUDE(x) 0
+#endif
+
+#ifdef __has_cpp_attribute
+#  define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+#  define FMT_HAS_CPP_ATTRIBUTE(x) 0
+#endif
+
+#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
+  (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
+  (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+// Check if relaxed C++14 constexpr is supported.
+// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
+#ifndef FMT_USE_CONSTEXPR
+#  if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
+       (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) &&             \
+      !FMT_ICC_VERSION && (!defined(__NVCC__) || FMT_CPLUSPLUS >= 202002L)
+#    define FMT_USE_CONSTEXPR 1
+#  else
+#    define FMT_USE_CONSTEXPR 0
+#  endif
+#endif
+#if FMT_USE_CONSTEXPR
+#  define FMT_CONSTEXPR constexpr
+#else
+#  define FMT_CONSTEXPR
+#endif
+
+#if ((FMT_CPLUSPLUS >= 202002L) &&                            \
+     (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
+    (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
+#  define FMT_CONSTEXPR20 constexpr
+#else
+#  define FMT_CONSTEXPR20
+#endif
+
+// Check if constexpr std::char_traits<>::{compare,length} are supported.
+#if defined(__GLIBCXX__)
+#  if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \
+      _GLIBCXX_RELEASE >= 7  // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
+#    define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#  endif
+#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \
+    _LIBCPP_VERSION >= 4000
+#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L
+#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#endif
+#ifndef FMT_CONSTEXPR_CHAR_TRAITS
+#  define FMT_CONSTEXPR_CHAR_TRAITS
+#endif
+
+// Check if exceptions are disabled.
+#ifndef FMT_EXCEPTIONS
+#  if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
+      (FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
+#    define FMT_EXCEPTIONS 0
+#  else
+#    define FMT_EXCEPTIONS 1
+#  endif
+#endif
+
+// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings.
+#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \
+    !defined(__NVCC__)
+#  define FMT_NORETURN [[noreturn]]
+#else
+#  define FMT_NORETURN
+#endif
+
+#ifndef FMT_NODISCARD
+#  if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
+#    define FMT_NODISCARD [[nodiscard]]
+#  else
+#    define FMT_NODISCARD
+#  endif
+#endif
+
+#ifndef FMT_INLINE
+#  if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#    define FMT_INLINE inline __attribute__((always_inline))
+#  else
+#    define FMT_INLINE inline
+#  endif
+#endif
+
+#ifdef _MSC_VER
+#  define FMT_UNCHECKED_ITERATOR(It) \
+    using _Unchecked_type = It  // Mark iterator as checked.
+#else
+#  define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
+#endif
+
+#ifndef FMT_BEGIN_NAMESPACE
+#  define FMT_BEGIN_NAMESPACE \
+    namespace fmt {           \
+    inline namespace v10 {
+#  define FMT_END_NAMESPACE \
+    }                       \
+    }
+#endif
+
+#ifndef FMT_EXPORT
+#  define FMT_EXPORT
+#  define FMT_BEGIN_EXPORT
+#  define FMT_END_EXPORT
+#endif
+
+#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
+#  ifdef FMT_LIB_EXPORT
+#    define FMT_API __declspec(dllexport)
+#  elif defined(FMT_SHARED)
+#    define FMT_API __declspec(dllimport)
+#  endif
+#else
+#  if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
+#    if defined(__GNUC__) || defined(__clang__)
+#      define FMT_API __attribute__((visibility("default")))
+#    endif
+#  endif
+#endif
+#ifndef FMT_API
+#  define FMT_API
+#endif
+
+// libc++ supports string_view in pre-c++17.
+#if FMT_HAS_INCLUDE(<string_view>) && \
+    (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
+#  include <string_view>
+#  define FMT_USE_STRING_VIEW
+#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L
+#  include <experimental/string_view>
+#  define FMT_USE_EXPERIMENTAL_STRING_VIEW
+#endif
+
+#ifndef FMT_UNICODE
+#  define FMT_UNICODE !FMT_MSC_VERSION
+#endif
+
+#ifndef FMT_CONSTEVAL
+#  if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
+       (!defined(__apple_build_version__) ||                     \
+        __apple_build_version__ >= 14000029L) &&                 \
+       FMT_CPLUSPLUS >= 202002L) ||                              \
+      (defined(__cpp_consteval) &&                               \
+       (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
+// consteval is broken in MSVC before VS2022 and Apple clang before 14.
+#    define FMT_CONSTEVAL consteval
+#    define FMT_HAS_CONSTEVAL
+#  else
+#    define FMT_CONSTEVAL
+#  endif
+#endif
+
+#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
+#  if defined(__cpp_nontype_template_args) &&                  \
+      ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
+       __cpp_nontype_template_args >= 201911L) &&              \
+      !defined(__NVCOMPILER) && !defined(__LCC__)
+#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
+#  else
+#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
+#  endif
+#endif
+
+// Enable minimal optimizations for more compact code in debug mode.
+FMT_GCC_PRAGMA("GCC push_options")
+#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \
+    !defined(__CUDACC__)
+FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
+#endif
+
+FMT_BEGIN_NAMESPACE
+
+// Implementations of enable_if_t and other metafunctions for older systems.
+template <bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+template <bool B, typename T, typename F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+template <bool B> using bool_constant = std::integral_constant<bool, B>;
+template <typename T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <typename T>
+using remove_const_t = typename std::remove_const<T>::type;
+template <typename T>
+using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
+template <typename T> struct type_identity { using type = T; };
+template <typename T> using type_identity_t = typename type_identity<T>::type;
+template <typename T>
+using underlying_t = typename std::underlying_type<T>::type;
+
+// Checks whether T is a container with contiguous storage.
+template <typename T> struct is_contiguous : std::false_type {};
+template <typename Char>
+struct is_contiguous<std::basic_string<Char>> : std::true_type {};
+
+struct monostate {
+  constexpr monostate() {}
+};
+
+// An enable_if helper to be used in template parameters which results in much
+// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
+// to workaround a bug in MSVC 2019 (see #1140 and #1186).
+#ifdef FMT_DOC
+#  define FMT_ENABLE_IF(...)
+#else
+#  define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
+#endif
+
+// This is defined in core.h instead of format.h to avoid injecting in std.
+// It is a template to avoid undesirable implicit conversions to std::byte.
+#ifdef __cpp_lib_byte
+template <typename T, FMT_ENABLE_IF(std::is_same<T, std::byte>::value)>
+inline auto format_as(T b) -> unsigned char {
+  return static_cast<unsigned char>(b);
+}
+#endif
+
+namespace detail {
+// Suppresses "unused variable" warnings with the method described in
+// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
+// (void)var does not work on many Intel compilers.
+template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
+
+constexpr FMT_INLINE auto is_constant_evaluated(
+    bool default_value = false) noexcept -> bool {
+// Workaround for incompatibility between libstdc++ consteval-based
+// std::is_constant_evaluated() implementation and clang-14.
+// https://github.com/fmtlib/fmt/issues/3247
+#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \
+    _GLIBCXX_RELEASE >= 12 &&                                \
+    (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
+  ignore_unused(default_value);
+  return __builtin_is_constant_evaluated();
+#elif defined(__cpp_lib_is_constant_evaluated)
+  ignore_unused(default_value);
+  return std::is_constant_evaluated();
+#else
+  return default_value;
+#endif
+}
+
+// Suppresses "conditional expression is constant" warnings.
+template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
+  return value;
+}
+
+FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
+                                      const char* message);
+
+#ifndef FMT_ASSERT
+#  ifdef NDEBUG
+// FMT_ASSERT is not empty to avoid -Wempty-body.
+#    define FMT_ASSERT(condition, message) \
+      fmt::detail::ignore_unused((condition), (message))
+#  else
+#    define FMT_ASSERT(condition, message)                                    \
+      ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
+           ? (void)0                                                          \
+           : fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
+#  endif
+#endif
+
+#if defined(FMT_USE_STRING_VIEW)
+template <typename Char> using std_string_view = std::basic_string_view<Char>;
+#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
+template <typename Char>
+using std_string_view = std::experimental::basic_string_view<Char>;
+#else
+template <typename T> struct std_string_view {};
+#endif
+
+#ifdef FMT_USE_INT128
+// Do nothing.
+#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
+    !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
+#  define FMT_USE_INT128 1
+using int128_opt = __int128_t;  // An optional native 128-bit integer.
+using uint128_opt = __uint128_t;
+template <typename T> inline auto convert_for_visit(T value) -> T {
+  return value;
+}
+#else
+#  define FMT_USE_INT128 0
+#endif
+#if !FMT_USE_INT128
+enum class int128_opt {};
+enum class uint128_opt {};
+// Reduce template instantiations.
+template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
+#endif
+
+// Casts a nonnegative integer to unsigned.
+template <typename Int>
+FMT_CONSTEXPR auto to_unsigned(Int value) ->
+    typename std::make_unsigned<Int>::type {
+  FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
+  return static_cast<typename std::make_unsigned<Int>::type>(value);
+}
+
+FMT_CONSTEXPR inline auto is_utf8() -> bool {
+  FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7";
+
+  // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
+  using uchar = unsigned char;
+  return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 &&
+                         uchar(section[1]) == 0xA7);
+}
+}  // namespace detail
+
+/**
+  An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
+  subset of the API. ``fmt::basic_string_view`` is used for format strings even
+  if ``std::string_view`` is available to prevent issues when a library is
+  compiled with a different ``-std`` option than the client code (which is not
+  recommended).
+ */
+FMT_EXPORT
+template <typename Char> class basic_string_view {
+ private:
+  const Char* data_;
+  size_t size_;
+
+ public:
+  using value_type = Char;
+  using iterator = const Char*;
+
+  constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
+
+  /** Constructs a string reference object from a C string and a size. */
+  constexpr basic_string_view(const Char* s, size_t count) noexcept
+      : data_(s), size_(count) {}
+
+  /**
+    \rst
+    Constructs a string reference object from a C string computing
+    the size with ``std::char_traits<Char>::length``.
+    \endrst
+   */
+  FMT_CONSTEXPR_CHAR_TRAITS
+  FMT_INLINE
+  basic_string_view(const Char* s)
+      : data_(s),
+        size_(detail::const_check(std::is_same<Char, char>::value &&
+                                  !detail::is_constant_evaluated(true))
+                  ? std::strlen(reinterpret_cast<const char*>(s))
+                  : std::char_traits<Char>::length(s)) {}
+
+  /** Constructs a string reference from a ``std::basic_string`` object. */
+  template <typename Traits, typename Alloc>
+  FMT_CONSTEXPR basic_string_view(
+      const std::basic_string<Char, Traits, Alloc>& s) noexcept
+      : data_(s.data()), size_(s.size()) {}
+
+  template <typename S, FMT_ENABLE_IF(std::is_same<
+                                      S, detail::std_string_view<Char>>::value)>
+  FMT_CONSTEXPR basic_string_view(S s) noexcept
+      : data_(s.data()), size_(s.size()) {}
+
+  /** Returns a pointer to the string data. */
+  constexpr auto data() const noexcept -> const Char* { return data_; }
+
+  /** Returns the string size. */
+  constexpr auto size() const noexcept -> size_t { return size_; }
+
+  constexpr auto begin() const noexcept -> iterator { return data_; }
+  constexpr auto end() const noexcept -> iterator { return data_ + size_; }
+
+  constexpr auto operator[](size_t pos) const noexcept -> const Char& {
+    return data_[pos];
+  }
+
+  FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
+    data_ += n;
+    size_ -= n;
+  }
+
+  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(
+      basic_string_view<Char> sv) const noexcept {
+    return size_ >= sv.size_ &&
+           std::char_traits<Char>::compare(data_, sv.data_, sv.size_) == 0;
+  }
+  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept {
+    return size_ >= 1 && std::char_traits<Char>::eq(*data_, c);
+  }
+  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const {
+    return starts_with(basic_string_view<Char>(s));
+  }
+
+  // Lexicographically compare this string reference to other.
+  FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
+    size_t str_size = size_ < other.size_ ? size_ : other.size_;
+    int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
+    if (result == 0)
+      result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
+    return result;
+  }
+
+  FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs,
+                                                   basic_string_view rhs)
+      -> bool {
+    return lhs.compare(rhs) == 0;
+  }
+  friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) != 0;
+  }
+  friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) < 0;
+  }
+  friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) <= 0;
+  }
+  friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) > 0;
+  }
+  friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) >= 0;
+  }
+};
+
+FMT_EXPORT
+using string_view = basic_string_view<char>;
+
+/** Specifies if ``T`` is a character type. Can be specialized by users. */
+FMT_EXPORT
+template <typename T> struct is_char : std::false_type {};
+template <> struct is_char<char> : std::true_type {};
+
+namespace detail {
+
+// A base class for compile-time strings.
+struct compile_string {};
+
+template <typename S>
+struct is_compile_string : std::is_base_of<compile_string, S> {};
+
+template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
+FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
+  return s;
+}
+template <typename Char, typename Traits, typename Alloc>
+inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
+    -> basic_string_view<Char> {
+  return s;
+}
+template <typename Char>
+constexpr auto to_string_view(basic_string_view<Char> s)
+    -> basic_string_view<Char> {
+  return s;
+}
+template <typename Char,
+          FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
+inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> {
+  return s;
+}
+template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
+constexpr auto to_string_view(const S& s)
+    -> basic_string_view<typename S::char_type> {
+  return basic_string_view<typename S::char_type>(s);
+}
+void to_string_view(...);
+
+// Specifies whether S is a string type convertible to fmt::basic_string_view.
+// It should be a constexpr function but MSVC 2017 fails to compile it in
+// enable_if and MSVC 2015 fails to compile it as an alias template.
+// ADL is intentionally disabled as to_string_view is not an extension point.
+template <typename S>
+struct is_string
+    : std::is_class<decltype(detail::to_string_view(std::declval<S>()))> {};
+
+template <typename S, typename = void> struct char_t_impl {};
+template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
+  using result = decltype(to_string_view(std::declval<S>()));
+  using type = typename result::value_type;
+};
+
+enum class type {
+  none_type,
+  // Integer types should go first,
+  int_type,
+  uint_type,
+  long_long_type,
+  ulong_long_type,
+  int128_type,
+  uint128_type,
+  bool_type,
+  char_type,
+  last_integer_type = char_type,
+  // followed by floating-point types.
+  float_type,
+  double_type,
+  long_double_type,
+  last_numeric_type = long_double_type,
+  cstring_type,
+  string_type,
+  pointer_type,
+  custom_type
+};
+
+// Maps core type T to the corresponding type enum constant.
+template <typename T, typename Char>
+struct type_constant : std::integral_constant<type, type::custom_type> {};
+
+#define FMT_TYPE_CONSTANT(Type, constant) \
+  template <typename Char>                \
+  struct type_constant<Type, Char>        \
+      : std::integral_constant<type, type::constant> {}
+
+FMT_TYPE_CONSTANT(int, int_type);
+FMT_TYPE_CONSTANT(unsigned, uint_type);
+FMT_TYPE_CONSTANT(long long, long_long_type);
+FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
+FMT_TYPE_CONSTANT(int128_opt, int128_type);
+FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
+FMT_TYPE_CONSTANT(bool, bool_type);
+FMT_TYPE_CONSTANT(Char, char_type);
+FMT_TYPE_CONSTANT(float, float_type);
+FMT_TYPE_CONSTANT(double, double_type);
+FMT_TYPE_CONSTANT(long double, long_double_type);
+FMT_TYPE_CONSTANT(const Char*, cstring_type);
+FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
+FMT_TYPE_CONSTANT(const void*, pointer_type);
+
+constexpr bool is_integral_type(type t) {
+  return t > type::none_type && t <= type::last_integer_type;
+}
+constexpr bool is_arithmetic_type(type t) {
+  return t > type::none_type && t <= type::last_numeric_type;
+}
+
+constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
+constexpr auto in(type t, int set) -> bool {
+  return ((set >> static_cast<int>(t)) & 1) != 0;
+}
+
+// Bitsets of types.
+enum {
+  sint_set =
+      set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
+  uint_set = set(type::uint_type) | set(type::ulong_long_type) |
+             set(type::uint128_type),
+  bool_set = set(type::bool_type),
+  char_set = set(type::char_type),
+  float_set = set(type::float_type) | set(type::double_type) |
+              set(type::long_double_type),
+  string_set = set(type::string_type),
+  cstring_set = set(type::cstring_type),
+  pointer_set = set(type::pointer_type)
+};
+
+FMT_NORETURN FMT_API void throw_format_error(const char* message);
+
+struct error_handler {
+  constexpr error_handler() = default;
+
+  // This function is intentionally not constexpr to give a compile-time error.
+  FMT_NORETURN void on_error(const char* message) {
+    throw_format_error(message);
+  }
+};
+}  // namespace detail
+
+/** Throws ``format_error`` with a given message. */
+using detail::throw_format_error;
+
+/** String's character type. */
+template <typename S> using char_t = typename detail::char_t_impl<S>::type;
+
+/**
+  \rst
+  Parsing context consisting of a format string range being parsed and an
+  argument counter for automatic indexing.
+  You can use the ``format_parse_context`` type alias for ``char`` instead.
+  \endrst
+ */
+FMT_EXPORT
+template <typename Char> class basic_format_parse_context {
+ private:
+  basic_string_view<Char> format_str_;
+  int next_arg_id_;
+
+  FMT_CONSTEXPR void do_check_arg_id(int id);
+
+ public:
+  using char_type = Char;
+  using iterator = const Char*;
+
+  explicit constexpr basic_format_parse_context(
+      basic_string_view<Char> format_str, int next_arg_id = 0)
+      : format_str_(format_str), next_arg_id_(next_arg_id) {}
+
+  /**
+    Returns an iterator to the beginning of the format string range being
+    parsed.
+   */
+  constexpr auto begin() const noexcept -> iterator {
+    return format_str_.begin();
+  }
+
+  /**
+    Returns an iterator past the end of the format string range being parsed.
+   */
+  constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
+
+  /** Advances the begin iterator to ``it``. */
+  FMT_CONSTEXPR void advance_to(iterator it) {
+    format_str_.remove_prefix(detail::to_unsigned(it - begin()));
+  }
+
+  /**
+    Reports an error if using the manual argument indexing; otherwise returns
+    the next argument index and switches to the automatic indexing.
+   */
+  FMT_CONSTEXPR auto next_arg_id() -> int {
+    if (next_arg_id_ < 0) {
+      detail::throw_format_error(
+          "cannot switch from manual to automatic argument indexing");
+      return 0;
+    }
+    int id = next_arg_id_++;
+    do_check_arg_id(id);
+    return id;
+  }
+
+  /**
+    Reports an error if using the automatic argument indexing; otherwise
+    switches to the manual indexing.
+   */
+  FMT_CONSTEXPR void check_arg_id(int id) {
+    if (next_arg_id_ > 0) {
+      detail::throw_format_error(
+          "cannot switch from automatic to manual argument indexing");
+      return;
+    }
+    next_arg_id_ = -1;
+    do_check_arg_id(id);
+  }
+  FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
+  FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
+};
+
+FMT_EXPORT
+using format_parse_context = basic_format_parse_context<char>;
+
+namespace detail {
+// A parse context with extra data used only in compile-time checks.
+template <typename Char>
+class compile_parse_context : public basic_format_parse_context<Char> {
+ private:
+  int num_args_;
+  const type* types_;
+  using base = basic_format_parse_context<Char>;
+
+ public:
+  explicit FMT_CONSTEXPR compile_parse_context(
+      basic_string_view<Char> format_str, int num_args, const type* types,
+      int next_arg_id = 0)
+      : base(format_str, next_arg_id), num_args_(num_args), types_(types) {}
+
+  constexpr auto num_args() const -> int { return num_args_; }
+  constexpr auto arg_type(int id) const -> type { return types_[id]; }
+
+  FMT_CONSTEXPR auto next_arg_id() -> int {
+    int id = base::next_arg_id();
+    if (id >= num_args_) throw_format_error("argument not found");
+    return id;
+  }
+
+  FMT_CONSTEXPR void check_arg_id(int id) {
+    base::check_arg_id(id);
+    if (id >= num_args_) throw_format_error("argument not found");
+  }
+  using base::check_arg_id;
+
+  FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
+    detail::ignore_unused(arg_id);
+#if !defined(__LCC__)
+    if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
+      throw_format_error("width/precision is not integer");
+#endif
+  }
+};
+
+// Extracts a reference to the container from back_insert_iterator.
+template <typename Container>
+inline auto get_container(std::back_insert_iterator<Container> it)
+    -> Container& {
+  using base = std::back_insert_iterator<Container>;
+  struct accessor : base {
+    accessor(base b) : base(b) {}
+    using base::container;
+  };
+  return *accessor(it).container;
+}
+
+template <typename Char, typename InputIt, typename OutputIt>
+FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out)
+    -> OutputIt {
+  while (begin != end) *out++ = static_cast<Char>(*begin++);
+  return out;
+}
+
+template <typename Char, typename T, typename U,
+          FMT_ENABLE_IF(
+              std::is_same<remove_const_t<T>, U>::value&& is_char<U>::value)>
+FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
+  if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
+  auto size = to_unsigned(end - begin);
+  if (size > 0) memcpy(out, begin, size * sizeof(U));
+  return out + size;
+}
+
+/**
+  \rst
+  A contiguous memory buffer with an optional growing ability. It is an internal
+  class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
+  \endrst
+ */
+template <typename T> class buffer {
+ private:
+  T* ptr_;
+  size_t size_;
+  size_t capacity_;
+
+ protected:
+  // Don't initialize ptr_ since it is not accessed to save a few cycles.
+  FMT_MSC_WARNING(suppress : 26495)
+  buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
+
+  FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
+      : ptr_(p), size_(sz), capacity_(cap) {}
+
+  FMT_CONSTEXPR20 ~buffer() = default;
+  buffer(buffer&&) = default;
+
+  /** Sets the buffer data and capacity. */
+  FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
+    ptr_ = buf_data;
+    capacity_ = buf_capacity;
+  }
+
+  /** Increases the buffer capacity to hold at least *capacity* elements. */
+  virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
+
+ public:
+  using value_type = T;
+  using const_reference = const T&;
+
+  buffer(const buffer&) = delete;
+  void operator=(const buffer&) = delete;
+
+  FMT_INLINE auto begin() noexcept -> T* { return ptr_; }
+  FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; }
+
+  FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; }
+  FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; }
+
+  /** Returns the size of this buffer. */
+  constexpr auto size() const noexcept -> size_t { return size_; }
+
+  /** Returns the capacity of this buffer. */
+  constexpr auto capacity() const noexcept -> size_t { return capacity_; }
+
+  /** Returns a pointer to the buffer data (not null-terminated). */
+  FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
+  FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
+
+  /** Clears this buffer. */
+  void clear() { size_ = 0; }
+
+  // Tries resizing the buffer to contain *count* elements. If T is a POD type
+  // the new elements may not be initialized.
+  FMT_CONSTEXPR20 void try_resize(size_t count) {
+    try_reserve(count);
+    size_ = count <= capacity_ ? count : capacity_;
+  }
+
+  // Tries increasing the buffer capacity to *new_capacity*. It can increase the
+  // capacity by a smaller amount than requested but guarantees there is space
+  // for at least one additional element either by increasing the capacity or by
+  // flushing the buffer if it is full.
+  FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
+    if (new_capacity > capacity_) grow(new_capacity);
+  }
+
+  FMT_CONSTEXPR20 void push_back(const T& value) {
+    try_reserve(size_ + 1);
+    ptr_[size_++] = value;
+  }
+
+  /** Appends data to the end of the buffer. */
+  template <typename U> void append(const U* begin, const U* end);
+
+  template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
+    return ptr_[index];
+  }
+  template <typename Idx>
+  FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
+    return ptr_[index];
+  }
+};
+
+struct buffer_traits {
+  explicit buffer_traits(size_t) {}
+  auto count() const -> size_t { return 0; }
+  auto limit(size_t size) -> size_t { return size; }
+};
+
+class fixed_buffer_traits {
+ private:
+  size_t count_ = 0;
+  size_t limit_;
+
+ public:
+  explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
+  auto count() const -> size_t { return count_; }
+  auto limit(size_t size) -> size_t {
+    size_t n = limit_ > count_ ? limit_ - count_ : 0;
+    count_ += size;
+    return size < n ? size : n;
+  }
+};
+
+// A buffer that writes to an output iterator when flushed.
+template <typename OutputIt, typename T, typename Traits = buffer_traits>
+class iterator_buffer final : public Traits, public buffer<T> {
+ private:
+  OutputIt out_;
+  enum { buffer_size = 256 };
+  T data_[buffer_size];
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t) override {
+    if (this->size() == buffer_size) flush();
+  }
+
+  void flush() {
+    auto size = this->size();
+    this->clear();
+    out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
+  }
+
+ public:
+  explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
+      : Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {}
+  iterator_buffer(iterator_buffer&& other)
+      : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
+  ~iterator_buffer() { flush(); }
+
+  auto out() -> OutputIt {
+    flush();
+    return out_;
+  }
+  auto count() const -> size_t { return Traits::count() + this->size(); }
+};
+
+template <typename T>
+class iterator_buffer<T*, T, fixed_buffer_traits> final
+    : public fixed_buffer_traits,
+      public buffer<T> {
+ private:
+  T* out_;
+  enum { buffer_size = 256 };
+  T data_[buffer_size];
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t) override {
+    if (this->size() == this->capacity()) flush();
+  }
+
+  void flush() {
+    size_t n = this->limit(this->size());
+    if (this->data() == out_) {
+      out_ += n;
+      this->set(data_, buffer_size);
+    }
+    this->clear();
+  }
+
+ public:
+  explicit iterator_buffer(T* out, size_t n = buffer_size)
+      : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {}
+  iterator_buffer(iterator_buffer&& other)
+      : fixed_buffer_traits(other),
+        buffer<T>(std::move(other)),
+        out_(other.out_) {
+    if (this->data() != out_) {
+      this->set(data_, buffer_size);
+      this->clear();
+    }
+  }
+  ~iterator_buffer() { flush(); }
+
+  auto out() -> T* {
+    flush();
+    return out_;
+  }
+  auto count() const -> size_t {
+    return fixed_buffer_traits::count() + this->size();
+  }
+};
+
+template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t) override {}
+
+ public:
+  explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
+
+  auto out() -> T* { return &*this->end(); }
+};
+
+// A buffer that writes to a container with the contiguous storage.
+template <typename Container>
+class iterator_buffer<std::back_insert_iterator<Container>,
+                      enable_if_t<is_contiguous<Container>::value,
+                                  typename Container::value_type>>
+    final : public buffer<typename Container::value_type> {
+ private:
+  Container& container_;
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t capacity) override {
+    container_.resize(capacity);
+    this->set(&container_[0], capacity);
+  }
+
+ public:
+  explicit iterator_buffer(Container& c)
+      : buffer<typename Container::value_type>(c.size()), container_(c) {}
+  explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
+      : iterator_buffer(get_container(out)) {}
+
+  auto out() -> std::back_insert_iterator<Container> {
+    return std::back_inserter(container_);
+  }
+};
+
+// A buffer that counts the number of code units written discarding the output.
+template <typename T = char> class counting_buffer final : public buffer<T> {
+ private:
+  enum { buffer_size = 256 };
+  T data_[buffer_size];
+  size_t count_ = 0;
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t) override {
+    if (this->size() != buffer_size) return;
+    count_ += this->size();
+    this->clear();
+  }
+
+ public:
+  counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
+
+  auto count() -> size_t { return count_ + this->size(); }
+};
+}  // namespace detail
+
+template <typename Char>
+FMT_CONSTEXPR void basic_format_parse_context<Char>::do_check_arg_id(int id) {
+  // Argument id is only checked at compile-time during parsing because
+  // formatting has its own validation.
+  if (detail::is_constant_evaluated() &&
+      (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
+    using context = detail::compile_parse_context<Char>;
+    if (id >= static_cast<context*>(this)->num_args())
+      detail::throw_format_error("argument not found");
+  }
+}
+
+template <typename Char>
+FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
+    int arg_id) {
+  if (detail::is_constant_evaluated() &&
+      (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
+    using context = detail::compile_parse_context<Char>;
+    static_cast<context*>(this)->check_dynamic_spec(arg_id);
+  }
+}
+
+FMT_EXPORT template <typename Context> class basic_format_arg;
+FMT_EXPORT template <typename Context> class basic_format_args;
+FMT_EXPORT template <typename Context> class dynamic_format_arg_store;
+
+// A formatter for objects of type T.
+FMT_EXPORT
+template <typename T, typename Char = char, typename Enable = void>
+struct formatter {
+  // A deleted default constructor indicates a disabled formatter.
+  formatter() = delete;
+};
+
+// Specifies if T has an enabled formatter specialization. A type can be
+// formattable even if it doesn't have a formatter e.g. via a conversion.
+template <typename T, typename Context>
+using has_formatter =
+    std::is_constructible<typename Context::template formatter_type<T>>;
+
+// An output iterator that appends to a buffer.
+// It is used to reduce symbol sizes for the common case.
+class appender : public std::back_insert_iterator<detail::buffer<char>> {
+  using base = std::back_insert_iterator<detail::buffer<char>>;
+
+ public:
+  using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
+  appender(base it) noexcept : base(it) {}
+  FMT_UNCHECKED_ITERATOR(appender);
+
+  auto operator++() noexcept -> appender& { return *this; }
+  auto operator++(int) noexcept -> appender { return *this; }
+};
+
+namespace detail {
+
+template <typename Context, typename T>
+constexpr auto has_const_formatter_impl(T*)
+    -> decltype(typename Context::template formatter_type<T>().format(
+                    std::declval<const T&>(), std::declval<Context&>()),
+                true) {
+  return true;
+}
+template <typename Context>
+constexpr auto has_const_formatter_impl(...) -> bool {
+  return false;
+}
+template <typename T, typename Context>
+constexpr auto has_const_formatter() -> bool {
+  return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
+}
+
+template <typename T>
+using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
+                                      std::back_insert_iterator<buffer<T>>>;
+
+// Maps an output iterator to a buffer.
+template <typename T, typename OutputIt>
+auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
+  return iterator_buffer<OutputIt, T>(out);
+}
+template <typename T, typename Buf,
+          FMT_ENABLE_IF(std::is_base_of<buffer<char>, Buf>::value)>
+auto get_buffer(std::back_insert_iterator<Buf> out) -> buffer<char>& {
+  return get_container(out);
+}
+
+template <typename Buf, typename OutputIt>
+FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
+  return buf.out();
+}
+template <typename T, typename OutputIt>
+auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
+  return out;
+}
+
+struct view {};
+
+template <typename Char, typename T> struct named_arg : view {
+  const Char* name;
+  const T& value;
+  named_arg(const Char* n, const T& v) : name(n), value(v) {}
+};
+
+template <typename Char> struct named_arg_info {
+  const Char* name;
+  int id;
+};
+
+template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+struct arg_data {
+  // args_[0].named_args points to named_args_ to avoid bloating format_args.
+  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
+  T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
+  named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
+
+  template <typename... U>
+  arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
+  arg_data(const arg_data& other) = delete;
+  auto args() const -> const T* { return args_ + 1; }
+  auto named_args() -> named_arg_info<Char>* { return named_args_; }
+};
+
+template <typename T, typename Char, size_t NUM_ARGS>
+struct arg_data<T, Char, NUM_ARGS, 0> {
+  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
+  T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
+
+  template <typename... U>
+  FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
+  FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; }
+  FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t {
+    return nullptr;
+  }
+};
+
+template <typename Char>
+inline void init_named_args(named_arg_info<Char>*, int, int) {}
+
+template <typename T> struct is_named_arg : std::false_type {};
+template <typename T> struct is_statically_named_arg : std::false_type {};
+
+template <typename T, typename Char>
+struct is_named_arg<named_arg<Char, T>> : std::true_type {};
+
+template <typename Char, typename T, typename... Tail,
+          FMT_ENABLE_IF(!is_named_arg<T>::value)>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+                     int named_arg_count, const T&, const Tail&... args) {
+  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename Char, typename T, typename... Tail,
+          FMT_ENABLE_IF(is_named_arg<T>::value)>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+                     int named_arg_count, const T& arg, const Tail&... args) {
+  named_args[named_arg_count++] = {arg.name, arg_count};
+  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename... Args>
+FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int,
+                                              const Args&...) {}
+
+template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
+template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
+  return (B1 ? 1 : 0) + count<B2, Tail...>();
+}
+
+template <typename... Args> constexpr auto count_named_args() -> size_t {
+  return count<is_named_arg<Args>::value...>();
+}
+
+template <typename... Args>
+constexpr auto count_statically_named_args() -> size_t {
+  return count<is_statically_named_arg<Args>::value...>();
+}
+
+struct unformattable {};
+struct unformattable_char : unformattable {};
+struct unformattable_pointer : unformattable {};
+
+template <typename Char> struct string_value {
+  const Char* data;
+  size_t size;
+};
+
+template <typename Char> struct named_arg_value {
+  const named_arg_info<Char>* data;
+  size_t size;
+};
+
+template <typename Context> struct custom_value {
+  using parse_context = typename Context::parse_context_type;
+  void* value;
+  void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
+};
+
+// A formatting argument value.
+template <typename Context> class value {
+ public:
+  using char_type = typename Context::char_type;
+
+  union {
+    monostate no_value;
+    int int_value;
+    unsigned uint_value;
+    long long long_long_value;
+    unsigned long long ulong_long_value;
+    int128_opt int128_value;
+    uint128_opt uint128_value;
+    bool bool_value;
+    char_type char_value;
+    float float_value;
+    double double_value;
+    long double long_double_value;
+    const void* pointer;
+    string_value<char_type> string;
+    custom_value<Context> custom;
+    named_arg_value<char_type> named_args;
+  };
+
+  constexpr FMT_INLINE value() : no_value() {}
+  constexpr FMT_INLINE value(int val) : int_value(val) {}
+  constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
+  constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
+  constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
+  FMT_INLINE value(int128_opt val) : int128_value(val) {}
+  FMT_INLINE value(uint128_opt val) : uint128_value(val) {}
+  constexpr FMT_INLINE value(float val) : float_value(val) {}
+  constexpr FMT_INLINE value(double val) : double_value(val) {}
+  FMT_INLINE value(long double val) : long_double_value(val) {}
+  constexpr FMT_INLINE value(bool val) : bool_value(val) {}
+  constexpr FMT_INLINE value(char_type val) : char_value(val) {}
+  FMT_CONSTEXPR FMT_INLINE value(const char_type* val) {
+    string.data = val;
+    if (is_constant_evaluated()) string.size = {};
+  }
+  FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val) {
+    string.data = val.data();
+    string.size = val.size();
+  }
+  FMT_INLINE value(const void* val) : pointer(val) {}
+  FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
+      : named_args{args, size} {}
+
+  template <typename T> FMT_CONSTEXPR20 FMT_INLINE value(T& val) {
+    using value_type = remove_const_t<T>;
+    custom.value = const_cast<value_type*>(std::addressof(val));
+    // Get the formatter type through the context to allow different contexts
+    // have different extension points, e.g. `formatter<T>` for `format` and
+    // `printf_formatter<T>` for `printf`.
+    custom.format = format_custom_arg<
+        value_type, typename Context::template formatter_type<value_type>>;
+  }
+  value(unformattable);
+  value(unformattable_char);
+  value(unformattable_pointer);
+
+ private:
+  // Formats an argument of a custom type, such as a user-defined class.
+  template <typename T, typename Formatter>
+  static void format_custom_arg(void* arg,
+                                typename Context::parse_context_type& parse_ctx,
+                                Context& ctx) {
+    auto f = Formatter();
+    parse_ctx.advance_to(f.parse(parse_ctx));
+    using qualified_type =
+        conditional_t<has_const_formatter<T, Context>(), const T, T>;
+    ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
+  }
+};
+
+// To minimize the number of types we need to deal with, long is translated
+// either to int or to long long depending on its size.
+enum { long_short = sizeof(long) == sizeof(int) };
+using long_type = conditional_t<long_short, int, long long>;
+using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
+
+template <typename T> struct format_as_result {
+  template <typename U,
+            FMT_ENABLE_IF(std::is_enum<U>::value || std::is_class<U>::value)>
+  static auto map(U*) -> decltype(format_as(std::declval<U>()));
+  static auto map(...) -> void;
+
+  using type = decltype(map(static_cast<T*>(nullptr)));
+};
+template <typename T> using format_as_t = typename format_as_result<T>::type;
+
+template <typename T>
+struct has_format_as
+    : bool_constant<!std::is_same<format_as_t<T>, void>::value> {};
+
+// Maps formatting arguments to core types.
+// arg_mapper reports errors by returning unformattable instead of using
+// static_assert because it's used in the is_formattable trait.
+template <typename Context> struct arg_mapper {
+  using char_type = typename Context::char_type;
+
+  FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val)
+      -> unsigned long long {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
+
+  template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
+                                      std::is_same<T, char_type>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
+    return val;
+  }
+  template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value ||
+#ifdef __cpp_char8_t
+                                     std::is_same<T, char8_t>::value ||
+#endif
+                                     std::is_same<T, char16_t>::value ||
+                                     std::is_same<T, char32_t>::value) &&
+                                        !std::is_same<T, char_type>::value,
+                                    int> = 0>
+  FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char {
+    return {};
+  }
+
+  FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double {
+    return val;
+  }
+
+  FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
+    return val;
+  }
+  template <typename T,
+            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
+                          std::is_same<char_type, char_t<T>>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+      -> basic_string_view<char_type> {
+    return to_string_view(val);
+  }
+  template <typename T,
+            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
+                          !std::is_same<char_type, char_t<T>>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
+    return {};
+  }
+
+  FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* {
+    return val;
+  }
+
+  // Use SFINAE instead of a const T* parameter to avoid a conflict with the
+  // array overload.
+  template <
+      typename T,
+      FMT_ENABLE_IF(
+          std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
+          std::is_function<typename std::remove_pointer<T>::type>::value ||
+          (std::is_array<T>::value &&
+           !std::is_convertible<T, const char_type*>::value))>
+  FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
+    return {};
+  }
+
+  template <typename T, std::size_t N,
+            FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
+    return values;
+  }
+
+  // Only map owning types because mapping views can be unsafe.
+  template <typename T, typename U = format_as_t<T>,
+            FMT_ENABLE_IF(std::is_arithmetic<U>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) {
+    return map(format_as(val));
+  }
+
+  template <typename T, typename U = remove_const_t<T>>
+  struct formattable : bool_constant<has_const_formatter<U, Context>() ||
+                                     (has_formatter<U, Context>::value &&
+                                      !std::is_const<T>::value)> {};
+
+  template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto do_map(T& val) -> T& {
+    return val;
+  }
+  template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto do_map(T&) -> unformattable {
+    return {};
+  }
+
+  template <typename T, typename U = remove_const_t<T>,
+            FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value ||
+                           std::is_union<U>::value) &&
+                          !is_string<U>::value && !is_char<U>::value &&
+                          !is_named_arg<U>::value &&
+                          !std::is_arithmetic<format_as_t<U>>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) {
+    return do_map(val);
+  }
+
+  template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
+      -> decltype(this->map(named_arg.value)) {
+    return map(named_arg.value);
+  }
+
+  auto map(...) -> unformattable { return {}; }
+};
+
+// A type constant after applying arg_mapper<Context>.
+template <typename T, typename Context>
+using mapped_type_constant =
+    type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
+                  typename Context::char_type>;
+
+enum { packed_arg_bits = 4 };
+// Maximum number of arguments with packed types.
+enum { max_packed_args = 62 / packed_arg_bits };
+enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
+enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
+
+template <typename Char, typename InputIt>
+auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
+  get_container(out).append(begin, end);
+  return out;
+}
+template <typename Char, typename InputIt>
+auto copy_str(InputIt begin, InputIt end,
+              std::back_insert_iterator<std::string> out)
+    -> std::back_insert_iterator<std::string> {
+  get_container(out).append(begin, end);
+  return out;
+}
+
+template <typename Char, typename R, typename OutputIt>
+FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
+  return detail::copy_str<Char>(rng.begin(), rng.end(), out);
+}
+
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
+// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
+template <typename...> struct void_t_impl { using type = void; };
+template <typename... T> using void_t = typename void_t_impl<T...>::type;
+#else
+template <typename...> using void_t = void;
+#endif
+
+template <typename It, typename T, typename Enable = void>
+struct is_output_iterator : std::false_type {};
+
+template <typename It, typename T>
+struct is_output_iterator<
+    It, T,
+    void_t<typename std::iterator_traits<It>::iterator_category,
+           decltype(*std::declval<It>() = std::declval<T>())>>
+    : std::true_type {};
+
+template <typename It> struct is_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_back_insert_iterator<std::back_insert_iterator<Container>>
+    : std::true_type {};
+
+// A type-erased reference to an std::locale to avoid a heavy <locale> include.
+class locale_ref {
+ private:
+  const void* locale_;  // A type-erased pointer to std::locale.
+
+ public:
+  constexpr FMT_INLINE locale_ref() : locale_(nullptr) {}
+  template <typename Locale> explicit locale_ref(const Locale& loc);
+
+  explicit operator bool() const noexcept { return locale_ != nullptr; }
+
+  template <typename Locale> auto get() const -> Locale;
+};
+
+template <typename> constexpr auto encode_types() -> unsigned long long {
+  return 0;
+}
+
+template <typename Context, typename Arg, typename... Args>
+constexpr auto encode_types() -> unsigned long long {
+  return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
+         (encode_types<Context, Args...>() << packed_arg_bits);
+}
+
+#if defined(__cpp_if_constexpr)
+// This type is intentionally undefined, only used for errors
+template <typename T, typename Char> struct type_is_unformattable_for;
+#endif
+
+template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(PACKED)>
+FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value<Context> {
+  using arg_type = remove_cvref_t<decltype(arg_mapper<Context>().map(val))>;
+
+  constexpr bool formattable_char =
+      !std::is_same<arg_type, unformattable_char>::value;
+  static_assert(formattable_char, "Mixing character types is disallowed.");
+
+  // Formatting of arbitrary pointers is disallowed. If you want to format a
+  // pointer cast it to `void*` or `const void*`. In particular, this forbids
+  // formatting of `[const] volatile char*` printed as bool by iostreams.
+  constexpr bool formattable_pointer =
+      !std::is_same<arg_type, unformattable_pointer>::value;
+  static_assert(formattable_pointer,
+                "Formatting of non-void pointers is disallowed.");
+
+  constexpr bool formattable = !std::is_same<arg_type, unformattable>::value;
+#if defined(__cpp_if_constexpr)
+  if constexpr (!formattable) {
+    type_is_unformattable_for<T, typename Context::char_type> _;
+  }
+#endif
+  static_assert(
+      formattable,
+      "Cannot format an argument. To make type T formattable provide a "
+      "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
+  return {arg_mapper<Context>().map(val)};
+}
+
+template <typename Context, typename T>
+FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg<Context> {
+  auto arg = basic_format_arg<Context>();
+  arg.type_ = mapped_type_constant<T, Context>::value;
+  arg.value_ = make_arg<true, Context>(val);
+  return arg;
+}
+
+template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(!PACKED)>
+FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg<Context> {
+  return make_arg<Context>(val);
+}
+}  // namespace detail
+FMT_BEGIN_EXPORT
+
+// A formatting argument. It is a trivially copyable/constructible type to
+// allow storage in basic_memory_buffer.
+template <typename Context> class basic_format_arg {
+ private:
+  detail::value<Context> value_;
+  detail::type type_;
+
+  template <typename ContextType, typename T>
+  friend FMT_CONSTEXPR auto detail::make_arg(T& value)
+      -> basic_format_arg<ContextType>;
+
+  template <typename Visitor, typename Ctx>
+  friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
+                                             const basic_format_arg<Ctx>& arg)
+      -> decltype(vis(0));
+
+  friend class basic_format_args<Context>;
+  friend class dynamic_format_arg_store<Context>;
+
+  using char_type = typename Context::char_type;
+
+  template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+  friend struct detail::arg_data;
+
+  basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
+      : value_(args, size) {}
+
+ public:
+  class handle {
+   public:
+    explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
+
+    void format(typename Context::parse_context_type& parse_ctx,
+                Context& ctx) const {
+      custom_.format(custom_.value, parse_ctx, ctx);
+    }
+
+   private:
+    detail::custom_value<Context> custom_;
+  };
+
+  constexpr basic_format_arg() : type_(detail::type::none_type) {}
+
+  constexpr explicit operator bool() const noexcept {
+    return type_ != detail::type::none_type;
+  }
+
+  auto type() const -> detail::type { return type_; }
+
+  auto is_integral() const -> bool { return detail::is_integral_type(type_); }
+  auto is_arithmetic() const -> bool {
+    return detail::is_arithmetic_type(type_);
+  }
+};
+
+/**
+  \rst
+  Visits an argument dispatching to the appropriate visit method based on
+  the argument type. For example, if the argument type is ``double`` then
+  ``vis(value)`` will be called with the value of type ``double``.
+  \endrst
+ */
+// DEPRECATED!
+template <typename Visitor, typename Context>
+FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
+    Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
+  switch (arg.type_) {
+  case detail::type::none_type:
+    break;
+  case detail::type::int_type:
+    return vis(arg.value_.int_value);
+  case detail::type::uint_type:
+    return vis(arg.value_.uint_value);
+  case detail::type::long_long_type:
+    return vis(arg.value_.long_long_value);
+  case detail::type::ulong_long_type:
+    return vis(arg.value_.ulong_long_value);
+  case detail::type::int128_type:
+    return vis(detail::convert_for_visit(arg.value_.int128_value));
+  case detail::type::uint128_type:
+    return vis(detail::convert_for_visit(arg.value_.uint128_value));
+  case detail::type::bool_type:
+    return vis(arg.value_.bool_value);
+  case detail::type::char_type:
+    return vis(arg.value_.char_value);
+  case detail::type::float_type:
+    return vis(arg.value_.float_value);
+  case detail::type::double_type:
+    return vis(arg.value_.double_value);
+  case detail::type::long_double_type:
+    return vis(arg.value_.long_double_value);
+  case detail::type::cstring_type:
+    return vis(arg.value_.string.data);
+  case detail::type::string_type:
+    using sv = basic_string_view<typename Context::char_type>;
+    return vis(sv(arg.value_.string.data, arg.value_.string.size));
+  case detail::type::pointer_type:
+    return vis(arg.value_.pointer);
+  case detail::type::custom_type:
+    return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
+  }
+  return vis(monostate());
+}
+
+// Formatting context.
+template <typename OutputIt, typename Char> class basic_format_context {
+ private:
+  OutputIt out_;
+  basic_format_args<basic_format_context> args_;
+  detail::locale_ref loc_;
+
+ public:
+  using iterator = OutputIt;
+  using format_arg = basic_format_arg<basic_format_context>;
+  using format_args = basic_format_args<basic_format_context>;
+  using parse_context_type = basic_format_parse_context<Char>;
+  template <typename T> using formatter_type = formatter<T, Char>;
+
+  /** The character type for the output. */
+  using char_type = Char;
+
+  basic_format_context(basic_format_context&&) = default;
+  basic_format_context(const basic_format_context&) = delete;
+  void operator=(const basic_format_context&) = delete;
+  /**
+    Constructs a ``basic_format_context`` object. References to the arguments
+    are stored in the object so make sure they have appropriate lifetimes.
+   */
+  constexpr basic_format_context(OutputIt out, format_args ctx_args,
+                                 detail::locale_ref loc = {})
+      : out_(out), args_(ctx_args), loc_(loc) {}
+
+  constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
+  FMT_CONSTEXPR auto arg(basic_string_view<Char> name) -> format_arg {
+    return args_.get(name);
+  }
+  FMT_CONSTEXPR auto arg_id(basic_string_view<Char> name) -> int {
+    return args_.get_id(name);
+  }
+  auto args() const -> const format_args& { return args_; }
+
+  FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
+  void on_error(const char* message) { error_handler().on_error(message); }
+
+  // Returns an iterator to the beginning of the output range.
+  FMT_CONSTEXPR auto out() -> iterator { return out_; }
+
+  // Advances the begin iterator to ``it``.
+  void advance_to(iterator it) {
+    if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
+  }
+
+  FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
+};
+
+template <typename Char>
+using buffer_context =
+    basic_format_context<detail::buffer_appender<Char>, Char>;
+using format_context = buffer_context<char>;
+
+template <typename T, typename Char = char>
+using is_formattable = bool_constant<!std::is_base_of<
+    detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>()
+                                        .map(std::declval<T&>()))>::value>;
+
+/**
+  \rst
+  An array of references to arguments. It can be implicitly converted into
+  `~fmt::basic_format_args` for passing into type-erased formatting functions
+  such as `~fmt::vformat`.
+  \endrst
+ */
+template <typename Context, typename... Args>
+class format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+    // Workaround a GCC template argument substitution bug.
+    : public basic_format_args<Context>
+#endif
+{
+ private:
+  static const size_t num_args = sizeof...(Args);
+  static constexpr size_t num_named_args = detail::count_named_args<Args...>();
+  static const bool is_packed = num_args <= detail::max_packed_args;
+
+  using value_type = conditional_t<is_packed, detail::value<Context>,
+                                   basic_format_arg<Context>>;
+
+  detail::arg_data<value_type, typename Context::char_type, num_args,
+                   num_named_args>
+      data_;
+
+  friend class basic_format_args<Context>;
+
+  static constexpr unsigned long long desc =
+      (is_packed ? detail::encode_types<Context, Args...>()
+                 : detail::is_unpacked_bit | num_args) |
+      (num_named_args != 0
+           ? static_cast<unsigned long long>(detail::has_named_args_bit)
+           : 0);
+
+ public:
+  template <typename... T>
+  FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args)
+      :
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+        basic_format_args<Context>(*this),
+#endif
+        data_{detail::make_arg<is_packed, Context>(args)...} {
+    if (detail::const_check(num_named_args != 0))
+      detail::init_named_args(data_.named_args(), 0, 0, args...);
+  }
+};
+
+/**
+  \rst
+  Constructs a `~fmt::format_arg_store` object that contains references to
+  arguments and can be implicitly converted to `~fmt::format_args`. `Context`
+  can be omitted in which case it defaults to `~fmt::format_context`.
+  See `~fmt::arg` for lifetime considerations.
+  \endrst
+ */
+// Arguments are taken by lvalue references to avoid some lifetime issues.
+template <typename Context = format_context, typename... T>
+constexpr auto make_format_args(T&... args)
+    -> format_arg_store<Context, remove_cvref_t<T>...> {
+  return {args...};
+}
+
+/**
+  \rst
+  Returns a named argument to be used in a formatting function.
+  It should only be used in a call to a formatting function or
+  `dynamic_format_arg_store::push_back`.
+
+  **Example**::
+
+    fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
+  \endrst
+ */
+template <typename Char, typename T>
+inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
+  static_assert(!detail::is_named_arg<T>(), "nested named arguments");
+  return {name, arg};
+}
+FMT_END_EXPORT
+
+/**
+  \rst
+  A view of a collection of formatting arguments. To avoid lifetime issues it
+  should only be used as a parameter type in type-erased functions such as
+  ``vformat``::
+
+    void vlog(string_view format_str, format_args args);  // OK
+    format_args args = make_format_args();  // Error: dangling reference
+  \endrst
+ */
+template <typename Context> class basic_format_args {
+ public:
+  using size_type = int;
+  using format_arg = basic_format_arg<Context>;
+
+ private:
+  // A descriptor that contains information about formatting arguments.
+  // If the number of arguments is less or equal to max_packed_args then
+  // argument types are passed in the descriptor. This reduces binary code size
+  // per formatting function call.
+  unsigned long long desc_;
+  union {
+    // If is_packed() returns true then argument values are stored in values_;
+    // otherwise they are stored in args_. This is done to improve cache
+    // locality and reduce compiled code size since storing larger objects
+    // may require more code (at least on x86-64) even if the same amount of
+    // data is actually copied to stack. It saves ~10% on the bloat test.
+    const detail::value<Context>* values_;
+    const format_arg* args_;
+  };
+
+  constexpr auto is_packed() const -> bool {
+    return (desc_ & detail::is_unpacked_bit) == 0;
+  }
+  auto has_named_args() const -> bool {
+    return (desc_ & detail::has_named_args_bit) != 0;
+  }
+
+  FMT_CONSTEXPR auto type(int index) const -> detail::type {
+    int shift = index * detail::packed_arg_bits;
+    unsigned int mask = (1 << detail::packed_arg_bits) - 1;
+    return static_cast<detail::type>((desc_ >> shift) & mask);
+  }
+
+  constexpr FMT_INLINE basic_format_args(unsigned long long desc,
+                                         const detail::value<Context>* values)
+      : desc_(desc), values_(values) {}
+  constexpr basic_format_args(unsigned long long desc, const format_arg* args)
+      : desc_(desc), args_(args) {}
+
+ public:
+  constexpr basic_format_args() : desc_(0), args_(nullptr) {}
+
+  /**
+   \rst
+   Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
+   \endrst
+   */
+  template <typename... Args>
+  constexpr FMT_INLINE basic_format_args(
+      const format_arg_store<Context, Args...>& store)
+      : basic_format_args(format_arg_store<Context, Args...>::desc,
+                          store.data_.args()) {}
+
+  /**
+   \rst
+   Constructs a `basic_format_args` object from
+   `~fmt::dynamic_format_arg_store`.
+   \endrst
+   */
+  constexpr FMT_INLINE basic_format_args(
+      const dynamic_format_arg_store<Context>& store)
+      : basic_format_args(store.get_types(), store.data()) {}
+
+  /**
+   \rst
+   Constructs a `basic_format_args` object from a dynamic set of arguments.
+   \endrst
+   */
+  constexpr basic_format_args(const format_arg* args, int count)
+      : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
+                          args) {}
+
+  /** Returns the argument with the specified id. */
+  FMT_CONSTEXPR auto get(int id) const -> format_arg {
+    format_arg arg;
+    if (!is_packed()) {
+      if (id < max_size()) arg = args_[id];
+      return arg;
+    }
+    if (id >= detail::max_packed_args) return arg;
+    arg.type_ = type(id);
+    if (arg.type_ == detail::type::none_type) return arg;
+    arg.value_ = values_[id];
+    return arg;
+  }
+
+  template <typename Char>
+  auto get(basic_string_view<Char> name) const -> format_arg {
+    int id = get_id(name);
+    return id >= 0 ? get(id) : format_arg();
+  }
+
+  template <typename Char>
+  auto get_id(basic_string_view<Char> name) const -> int {
+    if (!has_named_args()) return -1;
+    const auto& named_args =
+        (is_packed() ? values_[-1] : args_[-1].value_).named_args;
+    for (size_t i = 0; i < named_args.size; ++i) {
+      if (named_args.data[i].name == name) return named_args.data[i].id;
+    }
+    return -1;
+  }
+
+  auto max_size() const -> int {
+    unsigned long long max_packed = detail::max_packed_args;
+    return static_cast<int>(is_packed() ? max_packed
+                                        : desc_ & ~detail::is_unpacked_bit);
+  }
+};
+
+/** An alias to ``basic_format_args<format_context>``. */
+// A separate type would result in shorter symbols but break ABI compatibility
+// between clang and gcc on ARM (#1919).
+FMT_EXPORT using format_args = basic_format_args<format_context>;
+
+// We cannot use enum classes as bit fields because of a gcc bug, so we put them
+// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
+// Additionally, if an underlying type is specified, older gcc incorrectly warns
+// that the type is too small. Both bugs are fixed in gcc 9.3.
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
+#  define FMT_ENUM_UNDERLYING_TYPE(type)
+#else
+#  define FMT_ENUM_UNDERLYING_TYPE(type) : type
+#endif
+namespace align {
+enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center,
+                                                  numeric};
+}
+using align_t = align::type;
+namespace sign {
+enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
+}
+using sign_t = sign::type;
+
+namespace detail {
+
+// Workaround an array initialization issue in gcc 4.8.
+template <typename Char> struct fill_t {
+ private:
+  enum { max_size = 4 };
+  Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
+  unsigned char size_ = 1;
+
+ public:
+  FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
+    auto size = s.size();
+    FMT_ASSERT(size <= max_size, "invalid fill");
+    for (size_t i = 0; i < size; ++i) data_[i] = s[i];
+    size_ = static_cast<unsigned char>(size);
+  }
+
+  constexpr auto size() const -> size_t { return size_; }
+  constexpr auto data() const -> const Char* { return data_; }
+
+  FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; }
+  FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& {
+    return data_[index];
+  }
+};
+}  // namespace detail
+
+enum class presentation_type : unsigned char {
+  none,
+  dec,             // 'd'
+  oct,             // 'o'
+  hex_lower,       // 'x'
+  hex_upper,       // 'X'
+  bin_lower,       // 'b'
+  bin_upper,       // 'B'
+  hexfloat_lower,  // 'a'
+  hexfloat_upper,  // 'A'
+  exp_lower,       // 'e'
+  exp_upper,       // 'E'
+  fixed_lower,     // 'f'
+  fixed_upper,     // 'F'
+  general_lower,   // 'g'
+  general_upper,   // 'G'
+  chr,             // 'c'
+  string,          // 's'
+  pointer,         // 'p'
+  debug            // '?'
+};
+
+// Format specifiers for built-in and string types.
+template <typename Char = char> struct format_specs {
+  int width;
+  int precision;
+  presentation_type type;
+  align_t align : 4;
+  sign_t sign : 3;
+  bool alt : 1;  // Alternate form ('#').
+  bool localized : 1;
+  detail::fill_t<Char> fill;
+
+  constexpr format_specs()
+      : width(0),
+        precision(-1),
+        type(presentation_type::none),
+        align(align::none),
+        sign(sign::none),
+        alt(false),
+        localized(false) {}
+};
+
+namespace detail {
+
+enum class arg_id_kind { none, index, name };
+
+// An argument reference.
+template <typename Char> struct arg_ref {
+  FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
+
+  FMT_CONSTEXPR explicit arg_ref(int index)
+      : kind(arg_id_kind::index), val(index) {}
+  FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
+      : kind(arg_id_kind::name), val(name) {}
+
+  FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
+    kind = arg_id_kind::index;
+    val.index = idx;
+    return *this;
+  }
+
+  arg_id_kind kind;
+  union value {
+    FMT_CONSTEXPR value(int idx = 0) : index(idx) {}
+    FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
+
+    int index;
+    basic_string_view<Char> name;
+  } val;
+};
+
+// Format specifiers with width and precision resolved at formatting rather
+// than parsing time to allow reusing the same parsed specifiers with
+// different sets of arguments (precompilation of format strings).
+template <typename Char = char>
+struct dynamic_format_specs : format_specs<Char> {
+  arg_ref<Char> width_ref;
+  arg_ref<Char> precision_ref;
+};
+
+// Converts a character to ASCII. Returns '\0' on conversion failure.
+template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
+constexpr auto to_ascii(Char c) -> char {
+  return c <= 0xff ? static_cast<char>(c) : '\0';
+}
+template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
+constexpr auto to_ascii(Char c) -> char {
+  return c <= 0xff ? static_cast<char>(c) : '\0';
+}
+
+// Returns the number of code units in a code point or 1 on error.
+template <typename Char>
+FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
+  if (const_check(sizeof(Char) != 1)) return 1;
+  auto c = static_cast<unsigned char>(*begin);
+  return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1;
+}
+
+// Return the result via the out param to workaround gcc bug 77539.
+template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
+FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
+  for (out = first; out != last; ++out) {
+    if (*out == value) return true;
+  }
+  return false;
+}
+
+template <>
+inline auto find<false, char>(const char* first, const char* last, char value,
+                              const char*& out) -> bool {
+  out = static_cast<const char*>(
+      std::memchr(first, value, to_unsigned(last - first)));
+  return out != nullptr;
+}
+
+// Parses the range [begin, end) as an unsigned integer. This function assumes
+// that the range is non-empty and the first character is a digit.
+template <typename Char>
+FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
+                                         int error_value) noexcept -> int {
+  FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
+  unsigned value = 0, prev = 0;
+  auto p = begin;
+  do {
+    prev = value;
+    value = value * 10 + unsigned(*p - '0');
+    ++p;
+  } while (p != end && '0' <= *p && *p <= '9');
+  auto num_digits = p - begin;
+  begin = p;
+  if (num_digits <= std::numeric_limits<int>::digits10)
+    return static_cast<int>(value);
+  // Check for overflow.
+  const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
+  return num_digits == std::numeric_limits<int>::digits10 + 1 &&
+                 prev * 10ull + unsigned(p[-1] - '0') <= max
+             ? static_cast<int>(value)
+             : error_value;
+}
+
+FMT_CONSTEXPR inline auto parse_align(char c) -> align_t {
+  switch (c) {
+  case '<':
+    return align::left;
+  case '>':
+    return align::right;
+  case '^':
+    return align::center;
+  }
+  return align::none;
+}
+
+template <typename Char> constexpr auto is_name_start(Char c) -> bool {
+  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
+                                   Handler&& handler) -> const Char* {
+  Char c = *begin;
+  if (c >= '0' && c <= '9') {
+    int index = 0;
+    constexpr int max = (std::numeric_limits<int>::max)();
+    if (c != '0')
+      index = parse_nonnegative_int(begin, end, max);
+    else
+      ++begin;
+    if (begin == end || (*begin != '}' && *begin != ':'))
+      throw_format_error("invalid format string");
+    else
+      handler.on_index(index);
+    return begin;
+  }
+  if (!is_name_start(c)) {
+    throw_format_error("invalid format string");
+    return begin;
+  }
+  auto it = begin;
+  do {
+    ++it;
+  } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
+  handler.on_name({begin, to_unsigned(it - begin)});
+  return it;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
+                                           Handler&& handler) -> const Char* {
+  FMT_ASSERT(begin != end, "");
+  Char c = *begin;
+  if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
+  handler.on_auto();
+  return begin;
+}
+
+template <typename Char> struct dynamic_spec_id_handler {
+  basic_format_parse_context<Char>& ctx;
+  arg_ref<Char>& ref;
+
+  FMT_CONSTEXPR void on_auto() {
+    int id = ctx.next_arg_id();
+    ref = arg_ref<Char>(id);
+    ctx.check_dynamic_spec(id);
+  }
+  FMT_CONSTEXPR void on_index(int id) {
+    ref = arg_ref<Char>(id);
+    ctx.check_arg_id(id);
+    ctx.check_dynamic_spec(id);
+  }
+  FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
+    ref = arg_ref<Char>(id);
+    ctx.check_arg_id(id);
+  }
+};
+
+// Parses [integer | "{" [arg_id] "}"].
+template <typename Char>
+FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
+                                      int& value, arg_ref<Char>& ref,
+                                      basic_format_parse_context<Char>& ctx)
+    -> const Char* {
+  FMT_ASSERT(begin != end, "");
+  if ('0' <= *begin && *begin <= '9') {
+    int val = parse_nonnegative_int(begin, end, -1);
+    if (val != -1)
+      value = val;
+    else
+      throw_format_error("number is too big");
+  } else if (*begin == '{') {
+    ++begin;
+    auto handler = dynamic_spec_id_handler<Char>{ctx, ref};
+    if (begin != end) begin = parse_arg_id(begin, end, handler);
+    if (begin != end && *begin == '}') return ++begin;
+    throw_format_error("invalid format string");
+  }
+  return begin;
+}
+
+template <typename Char>
+FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
+                                   int& value, arg_ref<Char>& ref,
+                                   basic_format_parse_context<Char>& ctx)
+    -> const Char* {
+  ++begin;
+  if (begin == end || *begin == '}') {
+    throw_format_error("invalid precision");
+    return begin;
+  }
+  return parse_dynamic_spec(begin, end, value, ref, ctx);
+}
+
+enum class state { start, align, sign, hash, zero, width, precision, locale };
+
+// Parses standard format specifiers.
+template <typename Char>
+FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
+    const Char* begin, const Char* end, dynamic_format_specs<Char>& specs,
+    basic_format_parse_context<Char>& ctx, type arg_type) -> const Char* {
+  auto c = '\0';
+  if (end - begin > 1) {
+    auto next = to_ascii(begin[1]);
+    c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
+  } else {
+    if (begin == end) return begin;
+    c = to_ascii(*begin);
+  }
+
+  struct {
+    state current_state = state::start;
+    FMT_CONSTEXPR void operator()(state s, bool valid = true) {
+      if (current_state >= s || !valid)
+        throw_format_error("invalid format specifier");
+      current_state = s;
+    }
+  } enter_state;
+
+  using pres = presentation_type;
+  constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
+  struct {
+    const Char*& begin;
+    dynamic_format_specs<Char>& specs;
+    type arg_type;
+
+    FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* {
+      if (!in(arg_type, set)) throw_format_error("invalid format specifier");
+      specs.type = type;
+      return begin + 1;
+    }
+  } parse_presentation_type{begin, specs, arg_type};
+
+  for (;;) {
+    switch (c) {
+    case '<':
+    case '>':
+    case '^':
+      enter_state(state::align);
+      specs.align = parse_align(c);
+      ++begin;
+      break;
+    case '+':
+    case '-':
+    case ' ':
+      enter_state(state::sign, in(arg_type, sint_set | float_set));
+      switch (c) {
+      case '+':
+        specs.sign = sign::plus;
+        break;
+      case '-':
+        specs.sign = sign::minus;
+        break;
+      case ' ':
+        specs.sign = sign::space;
+        break;
+      }
+      ++begin;
+      break;
+    case '#':
+      enter_state(state::hash, is_arithmetic_type(arg_type));
+      specs.alt = true;
+      ++begin;
+      break;
+    case '0':
+      enter_state(state::zero);
+      if (!is_arithmetic_type(arg_type))
+        throw_format_error("format specifier requires numeric argument");
+      if (specs.align == align::none) {
+        // Ignore 0 if align is specified for compatibility with std::format.
+        specs.align = align::numeric;
+        specs.fill[0] = Char('0');
+      }
+      ++begin;
+      break;
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+    case '{':
+      enter_state(state::width);
+      begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
+      break;
+    case '.':
+      enter_state(state::precision,
+                  in(arg_type, float_set | string_set | cstring_set));
+      begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
+                              ctx);
+      break;
+    case 'L':
+      enter_state(state::locale, is_arithmetic_type(arg_type));
+      specs.localized = true;
+      ++begin;
+      break;
+    case 'd':
+      return parse_presentation_type(pres::dec, integral_set);
+    case 'o':
+      return parse_presentation_type(pres::oct, integral_set);
+    case 'x':
+      return parse_presentation_type(pres::hex_lower, integral_set);
+    case 'X':
+      return parse_presentation_type(pres::hex_upper, integral_set);
+    case 'b':
+      return parse_presentation_type(pres::bin_lower, integral_set);
+    case 'B':
+      return parse_presentation_type(pres::bin_upper, integral_set);
+    case 'a':
+      return parse_presentation_type(pres::hexfloat_lower, float_set);
+    case 'A':
+      return parse_presentation_type(pres::hexfloat_upper, float_set);
+    case 'e':
+      return parse_presentation_type(pres::exp_lower, float_set);
+    case 'E':
+      return parse_presentation_type(pres::exp_upper, float_set);
+    case 'f':
+      return parse_presentation_type(pres::fixed_lower, float_set);
+    case 'F':
+      return parse_presentation_type(pres::fixed_upper, float_set);
+    case 'g':
+      return parse_presentation_type(pres::general_lower, float_set);
+    case 'G':
+      return parse_presentation_type(pres::general_upper, float_set);
+    case 'c':
+      return parse_presentation_type(pres::chr, integral_set);
+    case 's':
+      return parse_presentation_type(pres::string,
+                                     bool_set | string_set | cstring_set);
+    case 'p':
+      return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
+    case '?':
+      return parse_presentation_type(pres::debug,
+                                     char_set | string_set | cstring_set);
+    case '}':
+      return begin;
+    default: {
+      if (*begin == '}') return begin;
+      // Parse fill and alignment.
+      auto fill_end = begin + code_point_length(begin);
+      if (end - fill_end <= 0) {
+        throw_format_error("invalid format specifier");
+        return begin;
+      }
+      if (*begin == '{') {
+        throw_format_error("invalid fill character '{'");
+        return begin;
+      }
+      auto align = parse_align(to_ascii(*fill_end));
+      enter_state(state::align, align != align::none);
+      specs.fill = {begin, to_unsigned(fill_end - begin)};
+      specs.align = align;
+      begin = fill_end + 1;
+    }
+    }
+    if (begin == end) return begin;
+    c = to_ascii(*begin);
+  }
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
+                                           Handler&& handler) -> const Char* {
+  struct id_adapter {
+    Handler& handler;
+    int arg_id;
+
+    FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); }
+    FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
+    FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
+      arg_id = handler.on_arg_id(id);
+    }
+  };
+
+  ++begin;
+  if (begin == end) return handler.on_error("invalid format string"), end;
+  if (*begin == '}') {
+    handler.on_replacement_field(handler.on_arg_id(), begin);
+  } else if (*begin == '{') {
+    handler.on_text(begin, begin + 1);
+  } else {
+    auto adapter = id_adapter{handler, 0};
+    begin = parse_arg_id(begin, end, adapter);
+    Char c = begin != end ? *begin : Char();
+    if (c == '}') {
+      handler.on_replacement_field(adapter.arg_id, begin);
+    } else if (c == ':') {
+      begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
+      if (begin == end || *begin != '}')
+        return handler.on_error("unknown format specifier"), end;
+    } else {
+      return handler.on_error("missing '}' in format string"), end;
+    }
+  }
+  return begin + 1;
+}
+
+template <bool IS_CONSTEXPR, typename Char, typename Handler>
+FMT_CONSTEXPR FMT_INLINE void parse_format_string(
+    basic_string_view<Char> format_str, Handler&& handler) {
+  auto begin = format_str.data();
+  auto end = begin + format_str.size();
+  if (end - begin < 32) {
+    // Use a simple loop instead of memchr for small strings.
+    const Char* p = begin;
+    while (p != end) {
+      auto c = *p++;
+      if (c == '{') {
+        handler.on_text(begin, p - 1);
+        begin = p = parse_replacement_field(p - 1, end, handler);
+      } else if (c == '}') {
+        if (p == end || *p != '}')
+          return handler.on_error("unmatched '}' in format string");
+        handler.on_text(begin, p);
+        begin = ++p;
+      }
+    }
+    handler.on_text(begin, end);
+    return;
+  }
+  struct writer {
+    FMT_CONSTEXPR void operator()(const Char* from, const Char* to) {
+      if (from == to) return;
+      for (;;) {
+        const Char* p = nullptr;
+        if (!find<IS_CONSTEXPR>(from, to, Char('}'), p))
+          return handler_.on_text(from, to);
+        ++p;
+        if (p == to || *p != '}')
+          return handler_.on_error("unmatched '}' in format string");
+        handler_.on_text(from, p);
+        from = p + 1;
+      }
+    }
+    Handler& handler_;
+  } write = {handler};
+  while (begin != end) {
+    // Doing two passes with memchr (one for '{' and another for '}') is up to
+    // 2.5x faster than the naive one-pass implementation on big format strings.
+    const Char* p = begin;
+    if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
+      return write(begin, end);
+    write(begin, p);
+    begin = parse_replacement_field(p, end, handler);
+  }
+}
+
+template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg {
+  using type = T;
+};
+template <typename T> struct strip_named_arg<T, true> {
+  using type = remove_cvref_t<decltype(T::value)>;
+};
+
+template <typename T, typename ParseContext>
+FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
+    -> decltype(ctx.begin()) {
+  using char_type = typename ParseContext::char_type;
+  using context = buffer_context<char_type>;
+  using mapped_type = conditional_t<
+      mapped_type_constant<T, context>::value != type::custom_type,
+      decltype(arg_mapper<context>().map(std::declval<const T&>())),
+      typename strip_named_arg<T>::type>;
+#if defined(__cpp_if_constexpr)
+  if constexpr (std::is_default_constructible_v<
+                    formatter<mapped_type, char_type>>) {
+    return formatter<mapped_type, char_type>().parse(ctx);
+  } else {
+    type_is_unformattable_for<T, char_type> _;
+    return ctx.begin();
+  }
+#else
+  return formatter<mapped_type, char_type>().parse(ctx);
+#endif
+}
+
+// Checks char specs and returns true iff the presentation type is char-like.
+template <typename Char>
+FMT_CONSTEXPR auto check_char_specs(const format_specs<Char>& specs) -> bool {
+  if (specs.type != presentation_type::none &&
+      specs.type != presentation_type::chr &&
+      specs.type != presentation_type::debug) {
+    return false;
+  }
+  if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
+    throw_format_error("invalid format specifier for char");
+  return true;
+}
+
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <int N, typename T, typename... Args, typename Char>
+constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
+  if constexpr (is_statically_named_arg<T>()) {
+    if (name == T::name) return N;
+  }
+  if constexpr (sizeof...(Args) > 0)
+    return get_arg_index_by_name<N + 1, Args...>(name);
+  (void)name;  // Workaround an MSVC bug about "unused" parameter.
+  return -1;
+}
+#endif
+
+template <typename... Args, typename Char>
+FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+  if constexpr (sizeof...(Args) > 0)
+    return get_arg_index_by_name<0, Args...>(name);
+#endif
+  (void)name;
+  return -1;
+}
+
+template <typename Char, typename... Args> class format_string_checker {
+ private:
+  using parse_context_type = compile_parse_context<Char>;
+  static constexpr int num_args = sizeof...(Args);
+
+  // Format specifier parsing function.
+  // In the future basic_format_parse_context will replace compile_parse_context
+  // here and will use is_constant_evaluated and downcasting to access the data
+  // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
+  using parse_func = const Char* (*)(parse_context_type&);
+
+  type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
+  parse_context_type context_;
+  parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
+
+ public:
+  explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt)
+      : types_{mapped_type_constant<Args, buffer_context<Char>>::value...},
+        context_(fmt, num_args, types_),
+        parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
+
+  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+
+  FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
+  FMT_CONSTEXPR auto on_arg_id(int id) -> int {
+    return context_.check_arg_id(id), id;
+  }
+  FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+    auto index = get_arg_index_by_name<Args...>(id);
+    if (index < 0) on_error("named argument is not found");
+    return index;
+#else
+    (void)id;
+    on_error("compile-time checks for named arguments require C++20 support");
+    return 0;
+#endif
+  }
+
+  FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) {
+    on_format_specs(id, begin, begin);  // Call parse() on empty specs.
+  }
+
+  FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
+      -> const Char* {
+    context_.advance_to(begin);
+    // id >= 0 check is a workaround for gcc 10 bug (#2065).
+    return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
+  }
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    throw_format_error(message);
+  }
+};
+
+// Reports a compile-time error if S is not a valid format string.
+template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
+FMT_INLINE void check_format_string(const S&) {
+#ifdef FMT_ENFORCE_COMPILE_STRING
+  static_assert(is_compile_string<S>::value,
+                "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
+                "FMT_STRING.");
+#endif
+}
+template <typename... Args, typename S,
+          FMT_ENABLE_IF(is_compile_string<S>::value)>
+void check_format_string(S format_str) {
+  using char_t = typename S::char_type;
+  FMT_CONSTEXPR auto s = basic_string_view<char_t>(format_str);
+  using checker = format_string_checker<char_t, remove_cvref_t<Args>...>;
+  FMT_CONSTEXPR bool error = (parse_format_string<true>(s, checker(s)), true);
+  ignore_unused(error);
+}
+
+template <typename Char = char> struct vformat_args {
+  using type = basic_format_args<
+      basic_format_context<std::back_insert_iterator<buffer<Char>>, Char>>;
+};
+template <> struct vformat_args<char> { using type = format_args; };
+
+// Use vformat_args and avoid type_identity to keep symbols short.
+template <typename Char>
+void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
+                typename vformat_args<Char>::type args, locale_ref loc = {});
+
+FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
+#ifndef _WIN32
+inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
+#endif
+}  // namespace detail
+
+FMT_BEGIN_EXPORT
+
+// A formatter specialization for natively supported types.
+template <typename T, typename Char>
+struct formatter<T, Char,
+                 enable_if_t<detail::type_constant<T, Char>::value !=
+                             detail::type::custom_type>> {
+ private:
+  detail::dynamic_format_specs<Char> specs_;
+
+ public:
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
+    auto type = detail::type_constant<T, Char>::value;
+    auto end =
+        detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type);
+    if (type == detail::type::char_type) detail::check_char_specs(specs_);
+    return end;
+  }
+
+  template <detail::type U = detail::type_constant<T, Char>::value,
+            FMT_ENABLE_IF(U == detail::type::string_type ||
+                          U == detail::type::cstring_type ||
+                          U == detail::type::char_type)>
+  FMT_CONSTEXPR void set_debug_format(bool set = true) {
+    specs_.type = set ? presentation_type::debug : presentation_type::none;
+  }
+
+  template <typename FormatContext>
+  FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
+      -> decltype(ctx.out());
+};
+
+template <typename Char = char> struct runtime_format_string {
+  basic_string_view<Char> str;
+};
+
+/** A compile-time format string. */
+template <typename Char, typename... Args> class basic_format_string {
+ private:
+  basic_string_view<Char> str_;
+
+ public:
+  template <typename S,
+            FMT_ENABLE_IF(
+                std::is_convertible<const S&, basic_string_view<Char>>::value)>
+  FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) {
+    static_assert(
+        detail::count<
+            (std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
+             std::is_reference<Args>::value)...>() == 0,
+        "passing views as lvalues is disallowed");
+#ifdef FMT_HAS_CONSTEVAL
+    if constexpr (detail::count_named_args<Args...>() ==
+                  detail::count_statically_named_args<Args...>()) {
+      using checker =
+          detail::format_string_checker<Char, remove_cvref_t<Args>...>;
+      detail::parse_format_string<true>(str_, checker(s));
+    }
+#else
+    detail::check_format_string<Args...>(s);
+#endif
+  }
+  basic_format_string(runtime_format_string<Char> fmt) : str_(fmt.str) {}
+
+  FMT_INLINE operator basic_string_view<Char>() const { return str_; }
+  FMT_INLINE auto get() const -> basic_string_view<Char> { return str_; }
+};
+
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+// Workaround broken conversion on older gcc.
+template <typename...> using format_string = string_view;
+inline auto runtime(string_view s) -> string_view { return s; }
+#else
+template <typename... Args>
+using format_string = basic_format_string<char, type_identity_t<Args>...>;
+/**
+  \rst
+  Creates a runtime format string.
+
+  **Example**::
+
+    // Check format string at runtime instead of compile-time.
+    fmt::print(fmt::runtime("{:d}"), "I am not a number");
+  \endrst
+ */
+inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
+#endif
+
+FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``fmt`` and returns the result
+  as a string.
+
+  **Example**::
+
+    #include <fmt/core.h>
+    std::string message = fmt::format("The answer is {}.", 42);
+  \endrst
+*/
+template <typename... T>
+FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
+    -> std::string {
+  return vformat(fmt, fmt::make_format_args(args...));
+}
+
+/** Formats a string and writes the output to ``out``. */
+template <typename OutputIt,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
+  auto&& buf = detail::get_buffer<char>(out);
+  detail::vformat_to(buf, fmt, args, {});
+  return detail::get_iterator(buf, out);
+}
+
+/**
+ \rst
+ Formats ``args`` according to specifications in ``fmt``, writes the result to
+ the output iterator ``out`` and returns the iterator past the end of the output
+ range. `format_to` does not append a terminating null character.
+
+ **Example**::
+
+   auto out = std::vector<char>();
+   fmt::format_to(std::back_inserter(out), "{}", 42);
+ \endrst
+ */
+template <typename OutputIt, typename... T,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
+    -> OutputIt {
+  return vformat_to(out, fmt, fmt::make_format_args(args...));
+}
+
+template <typename OutputIt> struct format_to_n_result {
+  /** Iterator past the end of the output range. */
+  OutputIt out;
+  /** Total (not truncated) output size. */
+  size_t size;
+};
+
+template <typename OutputIt, typename... T,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
+    -> format_to_n_result<OutputIt> {
+  using traits = detail::fixed_buffer_traits;
+  auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
+  detail::vformat_to(buf, fmt, args, {});
+  return {buf.out(), buf.count()};
+}
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``fmt``, writes up to ``n``
+  characters of the result to the output iterator ``out`` and returns the total
+  (not truncated) output size and the iterator past the end of the output range.
+  `format_to_n` does not append a terminating null character.
+  \endrst
+ */
+template <typename OutputIt, typename... T,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
+                            T&&... args) -> format_to_n_result<OutputIt> {
+  return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
+}
+
+/** Returns the number of chars in the output of ``format(fmt, args...)``. */
+template <typename... T>
+FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
+                                             T&&... args) -> size_t {
+  auto buf = detail::counting_buffer<>();
+  detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...), {});
+  return buf.count();
+}
+
+FMT_API void vprint(string_view fmt, format_args args);
+FMT_API void vprint(std::FILE* f, string_view fmt, format_args args);
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``fmt`` and writes the output
+  to ``stdout``.
+
+  **Example**::
+
+    fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
+  \endrst
+ */
+template <typename... T>
+FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
+  const auto& vargs = fmt::make_format_args(args...);
+  return detail::is_utf8() ? vprint(fmt, vargs)
+                           : detail::vprint_mojibake(stdout, fmt, vargs);
+}
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``fmt`` and writes the
+  output to the file ``f``.
+
+  **Example**::
+
+    fmt::print(stderr, "Don't {}!", "panic");
+  \endrst
+ */
+template <typename... T>
+FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
+  const auto& vargs = fmt::make_format_args(args...);
+  return detail::is_utf8() ? vprint(f, fmt, vargs)
+                           : detail::vprint_mojibake(f, fmt, vargs);
+}
+
+/**
+  Formats ``args`` according to specifications in ``fmt`` and writes the
+  output to the file ``f`` followed by a newline.
+ */
+template <typename... T>
+FMT_INLINE void println(std::FILE* f, format_string<T...> fmt, T&&... args) {
+  return fmt::print(f, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
+}
+
+/**
+  Formats ``args`` according to specifications in ``fmt`` and writes the output
+  to ``stdout`` followed by a newline.
+ */
+template <typename... T>
+FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
+  return fmt::println(stdout, fmt, std::forward<T>(args)...);
+}
+
+FMT_END_EXPORT
+FMT_GCC_PRAGMA("GCC pop_options")
+FMT_END_NAMESPACE
+
+#ifdef FMT_HEADER_ONLY
+#  include "format.h"
+#endif
+#endif  // FMT_CORE_H_
diff --git a/src/cpp-common/vendor/fmt/format-inl.h b/src/cpp-common/vendor/fmt/format-inl.h
new file mode 100644 (file)
index 0000000..dac2d43
--- /dev/null
@@ -0,0 +1,1662 @@
+// Formatting library for C++ - implementation
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_FORMAT_INL_H_
+#define FMT_FORMAT_INL_H_
+
+#include <algorithm>
+#include <cerrno>  // errno
+#include <climits>
+#include <cmath>
+#include <exception>
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+#  include <locale>
+#endif
+
+#ifdef _WIN32
+#  include <io.h>  // _isatty
+#endif
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
+  // Use unchecked std::fprintf to avoid triggering another assertion when
+  // writing to stderr fails
+  std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
+  // Chosen instead of std::abort to satisfy Clang in CUDA mode during device
+  // code pass.
+  std::terminate();
+}
+
+FMT_FUNC void throw_format_error(const char* message) {
+  FMT_THROW(format_error(message));
+}
+
+FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
+                                string_view message) noexcept {
+  // Report error code making sure that the output fits into
+  // inline_buffer_size to avoid dynamic memory allocation and potential
+  // bad_alloc.
+  out.try_resize(0);
+  static const char SEP[] = ": ";
+  static const char ERROR_STR[] = "error ";
+  // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
+  size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
+  auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
+  if (detail::is_negative(error_code)) {
+    abs_value = 0 - abs_value;
+    ++error_code_size;
+  }
+  error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
+  auto it = buffer_appender<char>(out);
+  if (message.size() <= inline_buffer_size - error_code_size)
+    format_to(it, FMT_STRING("{}{}"), message, SEP);
+  format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
+  FMT_ASSERT(out.size() <= inline_buffer_size, "");
+}
+
+FMT_FUNC void report_error(format_func func, int error_code,
+                           const char* message) noexcept {
+  memory_buffer full_message;
+  func(full_message, error_code, message);
+  // Don't use fwrite_fully because the latter may throw.
+  if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
+    std::fputc('\n', stderr);
+}
+
+// A wrapper around fwrite that throws on error.
+inline void fwrite_fully(const void* ptr, size_t size, size_t count,
+                         FILE* stream) {
+  size_t written = std::fwrite(ptr, size, count, stream);
+  if (written < count)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+}
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+template <typename Locale>
+locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
+  static_assert(std::is_same<Locale, std::locale>::value, "");
+}
+
+template <typename Locale> Locale locale_ref::get() const {
+  static_assert(std::is_same<Locale, std::locale>::value, "");
+  return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
+}
+
+template <typename Char>
+FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
+  auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
+  auto grouping = facet.grouping();
+  auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
+  return {std::move(grouping), thousands_sep};
+}
+template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
+  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
+      .decimal_point();
+}
+#else
+template <typename Char>
+FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
+  return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
+}
+template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
+  return '.';
+}
+#endif
+
+FMT_FUNC auto write_loc(appender out, loc_value value,
+                        const format_specs<>& specs, locale_ref loc) -> bool {
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+  auto locale = loc.get<std::locale>();
+  // We cannot use the num_put<char> facet because it may produce output in
+  // a wrong encoding.
+  using facet = format_facet<std::locale>;
+  if (std::has_facet<facet>(locale))
+    return std::use_facet<facet>(locale).put(out, value, specs);
+  return facet(locale).put(out, value, specs);
+#endif
+  return false;
+}
+}  // namespace detail
+
+template <typename Locale> typename Locale::id format_facet<Locale>::id;
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
+  auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
+  grouping_ = numpunct.grouping();
+  if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
+}
+
+template <>
+FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
+    appender out, loc_value val, const format_specs<>& specs) const -> bool {
+  return val.visit(
+      detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
+}
+#endif
+
+FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt,
+                                         format_args args) {
+  auto ec = std::error_code(error_code, std::generic_category());
+  return std::system_error(ec, vformat(fmt, args));
+}
+
+namespace detail {
+
+template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) {
+  return x.f == y.f && x.e == y.e;
+}
+
+// Compilers should be able to optimize this into the ror instruction.
+FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept {
+  r &= 31;
+  return (n >> r) | (n << (32 - r));
+}
+FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
+  r &= 63;
+  return (n >> r) | (n << (64 - r));
+}
+
+// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
+namespace dragonbox {
+// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
+// 64-bit unsigned integer.
+inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
+  return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
+}
+
+// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
+// 128-bit unsigned integer.
+inline uint128_fallback umul192_lower128(uint64_t x,
+                                         uint128_fallback y) noexcept {
+  uint64_t high = x * y.high();
+  uint128_fallback high_low = umul128(x, y.low());
+  return {high + high_low.high(), high_low.low()};
+}
+
+// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
+// 64-bit unsigned integer.
+inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
+  return x * y;
+}
+
+// Various fast log computations.
+inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
+  FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
+  return (e * 631305 - 261663) >> 21;
+}
+
+FMT_INLINE_VARIABLE constexpr struct {
+  uint32_t divisor;
+  int shift_amount;
+} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
+
+// Replaces n by floor(n / pow(10, N)) returning true if and only if n is
+// divisible by pow(10, N).
+// Precondition: n <= pow(10, N + 1).
+template <int N>
+bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
+  // The numbers below are chosen such that:
+  //   1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
+  //   2. nm mod 2^k < m if and only if n is divisible by d,
+  // where m is magic_number, k is shift_amount
+  // and d is divisor.
+  //
+  // Item 1 is a common technique of replacing division by a constant with
+  // multiplication, see e.g. "Division by Invariant Integers Using
+  // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set
+  // to ceil(2^k/d) for large enough k.
+  // The idea for item 2 originates from Schubfach.
+  constexpr auto info = div_small_pow10_infos[N - 1];
+  FMT_ASSERT(n <= info.divisor * 10, "n is too large");
+  constexpr uint32_t magic_number =
+      (1u << info.shift_amount) / info.divisor + 1;
+  n *= magic_number;
+  const uint32_t comparison_mask = (1u << info.shift_amount) - 1;
+  bool result = (n & comparison_mask) < magic_number;
+  n >>= info.shift_amount;
+  return result;
+}
+
+// Computes floor(n / pow(10, N)) for small n and N.
+// Precondition: n <= pow(10, N + 1).
+template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
+  constexpr auto info = div_small_pow10_infos[N - 1];
+  FMT_ASSERT(n <= info.divisor * 10, "n is too large");
+  constexpr uint32_t magic_number =
+      (1u << info.shift_amount) / info.divisor + 1;
+  return (n * magic_number) >> info.shift_amount;
+}
+
+// Computes floor(n / 10^(kappa + 1)) (float)
+inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept {
+  // 1374389535 = ceil(2^37/100)
+  return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
+}
+// Computes floor(n / 10^(kappa + 1)) (double)
+inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
+  // 2361183241434822607 = ceil(2^(64+7)/1000)
+  return umul128_upper64(n, 2361183241434822607ull) >> 7;
+}
+
+// Various subroutines using pow10 cache
+template <typename T> struct cache_accessor;
+
+template <> struct cache_accessor<float> {
+  using carrier_uint = float_info<float>::carrier_uint;
+  using cache_entry_type = uint64_t;
+
+  static uint64_t get_cached_power(int k) noexcept {
+    FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
+               "k is out of range");
+    static constexpr const uint64_t pow10_significands[] = {
+        0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
+        0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
+        0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
+        0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb,
+        0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a,
+        0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810,
+        0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff,
+        0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd,
+        0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424,
+        0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b,
+        0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000,
+        0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000,
+        0xc350000000000000, 0xf424000000000000, 0x9896800000000000,
+        0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000,
+        0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000,
+        0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000,
+        0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000,
+        0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000,
+        0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0,
+        0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985,
+        0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297,
+        0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7,
+        0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21,
+        0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe,
+        0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a,
+        0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f};
+    return pow10_significands[k - float_info<float>::min_k];
+  }
+
+  struct compute_mul_result {
+    carrier_uint result;
+    bool is_integer;
+  };
+  struct compute_mul_parity_result {
+    bool parity;
+    bool is_integer;
+  };
+
+  static compute_mul_result compute_mul(
+      carrier_uint u, const cache_entry_type& cache) noexcept {
+    auto r = umul96_upper64(u, cache);
+    return {static_cast<carrier_uint>(r >> 32),
+            static_cast<carrier_uint>(r) == 0};
+  }
+
+  static uint32_t compute_delta(const cache_entry_type& cache,
+                                int beta) noexcept {
+    return static_cast<uint32_t>(cache >> (64 - 1 - beta));
+  }
+
+  static compute_mul_parity_result compute_mul_parity(
+      carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
+    FMT_ASSERT(beta >= 1, "");
+    FMT_ASSERT(beta < 64, "");
+
+    auto r = umul96_lower64(two_f, cache);
+    return {((r >> (64 - beta)) & 1) != 0,
+            static_cast<uint32_t>(r >> (32 - beta)) == 0};
+  }
+
+  static carrier_uint compute_left_endpoint_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return static_cast<carrier_uint>(
+        (cache - (cache >> (num_significand_bits<float>() + 2))) >>
+        (64 - num_significand_bits<float>() - 1 - beta));
+  }
+
+  static carrier_uint compute_right_endpoint_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return static_cast<carrier_uint>(
+        (cache + (cache >> (num_significand_bits<float>() + 1))) >>
+        (64 - num_significand_bits<float>() - 1 - beta));
+  }
+
+  static carrier_uint compute_round_up_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return (static_cast<carrier_uint>(
+                cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
+            1) /
+           2;
+  }
+};
+
+template <> struct cache_accessor<double> {
+  using carrier_uint = float_info<double>::carrier_uint;
+  using cache_entry_type = uint128_fallback;
+
+  static uint128_fallback get_cached_power(int k) noexcept {
+    FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
+               "k is out of range");
+
+    static constexpr const uint128_fallback pow10_significands[] = {
+#if FMT_USE_FULL_CACHE_DRAGONBOX
+      {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
+      {0x9faacf3df73609b1, 0x77b191618c54e9ad},
+      {0xc795830d75038c1d, 0xd59df5b9ef6a2418},
+      {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e},
+      {0x9becce62836ac577, 0x4ee367f9430aec33},
+      {0xc2e801fb244576d5, 0x229c41f793cda740},
+      {0xf3a20279ed56d48a, 0x6b43527578c11110},
+      {0x9845418c345644d6, 0x830a13896b78aaaa},
+      {0xbe5691ef416bd60c, 0x23cc986bc656d554},
+      {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9},
+      {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa},
+      {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54},
+      {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69},
+      {0x91376c36d99995be, 0x23100809b9c21fa2},
+      {0xb58547448ffffb2d, 0xabd40a0c2832a78b},
+      {0xe2e69915b3fff9f9, 0x16c90c8f323f516d},
+      {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4},
+      {0xb1442798f49ffb4a, 0x99cd11cfdf41779d},
+      {0xdd95317f31c7fa1d, 0x40405643d711d584},
+      {0x8a7d3eef7f1cfc52, 0x482835ea666b2573},
+      {0xad1c8eab5ee43b66, 0xda3243650005eed0},
+      {0xd863b256369d4a40, 0x90bed43e40076a83},
+      {0x873e4f75e2224e68, 0x5a7744a6e804a292},
+      {0xa90de3535aaae202, 0x711515d0a205cb37},
+      {0xd3515c2831559a83, 0x0d5a5b44ca873e04},
+      {0x8412d9991ed58091, 0xe858790afe9486c3},
+      {0xa5178fff668ae0b6, 0x626e974dbe39a873},
+      {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
+      {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a},
+      {0xa139029f6a239f72, 0x1c1fffc1ebc44e81},
+      {0xc987434744ac874e, 0xa327ffb266b56221},
+      {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9},
+      {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa},
+      {0xc4ce17b399107c22, 0xcb550fb4384d21d4},
+      {0xf6019da07f549b2b, 0x7e2a53a146606a49},
+      {0x99c102844f94e0fb, 0x2eda7444cbfc426e},
+      {0xc0314325637a1939, 0xfa911155fefb5309},
+      {0xf03d93eebc589f88, 0x793555ab7eba27cb},
+      {0x96267c7535b763b5, 0x4bc1558b2f3458df},
+      {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17},
+      {0xea9c227723ee8bcb, 0x465e15a979c1cadd},
+      {0x92a1958a7675175f, 0x0bfacd89ec191eca},
+      {0xb749faed14125d36, 0xcef980ec671f667c},
+      {0xe51c79a85916f484, 0x82b7e12780e7401b},
+      {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811},
+      {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16},
+      {0xdfbdcece67006ac9, 0x67a791e093e1d49b},
+      {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1},
+      {0xaecc49914078536d, 0x58fae9f773886e19},
+      {0xda7f5bf590966848, 0xaf39a475506a899f},
+      {0x888f99797a5e012d, 0x6d8406c952429604},
+      {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84},
+      {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65},
+      {0x855c3be0a17fcd26, 0x5cf2eea09a550680},
+      {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
+      {0xd0601d8efc57b08b, 0xf13b94daf124da27},
+      {0x823c12795db6ce57, 0x76c53d08d6b70859},
+      {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f},
+      {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a},
+      {0xfe5d54150b090b02, 0xd3f93b35435d7c4d},
+      {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0},
+      {0xc6b8e9b0709f109a, 0x359ab6419ca1091c},
+      {0xf867241c8cc6d4c0, 0xc30163d203c94b63},
+      {0x9b407691d7fc44f8, 0x79e0de63425dcf1e},
+      {0xc21094364dfb5636, 0x985915fc12f542e5},
+      {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e},
+      {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43},
+      {0xbd8430bd08277231, 0x50c6ff782a838354},
+      {0xece53cec4a314ebd, 0xa4f8bf5635246429},
+      {0x940f4613ae5ed136, 0x871b7795e136be9a},
+      {0xb913179899f68584, 0x28e2557b59846e40},
+      {0xe757dd7ec07426e5, 0x331aeada2fe589d0},
+      {0x9096ea6f3848984f, 0x3ff0d2c85def7622},
+      {0xb4bca50b065abe63, 0x0fed077a756b53aa},
+      {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895},
+      {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d},
+      {0xb080392cc4349dec, 0xbd8d794d96aacfb4},
+      {0xdca04777f541c567, 0xecf0d7a0fc5583a1},
+      {0x89e42caaf9491b60, 0xf41686c49db57245},
+      {0xac5d37d5b79b6239, 0x311c2875c522ced6},
+      {0xd77485cb25823ac7, 0x7d633293366b828c},
+      {0x86a8d39ef77164bc, 0xae5dff9c02033198},
+      {0xa8530886b54dbdeb, 0xd9f57f830283fdfd},
+      {0xd267caa862a12d66, 0xd072df63c324fd7c},
+      {0x8380dea93da4bc60, 0x4247cb9e59f71e6e},
+      {0xa46116538d0deb78, 0x52d9be85f074e609},
+      {0xcd795be870516656, 0x67902e276c921f8c},
+      {0x806bd9714632dff6, 0x00ba1cd8a3db53b7},
+      {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5},
+      {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce},
+      {0xfad2a4b13d1b5d6c, 0x796b805720085f82},
+      {0x9cc3a6eec6311a63, 0xcbe3303674053bb1},
+      {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d},
+      {0xf4f1b4d515acb93b, 0xee92fb5515482d45},
+      {0x991711052d8bf3c5, 0x751bdd152d4d1c4b},
+      {0xbf5cd54678eef0b6, 0xd262d45a78a0635e},
+      {0xef340a98172aace4, 0x86fb897116c87c35},
+      {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1},
+      {0xbae0a846d2195712, 0x8974836059cca10a},
+      {0xe998d258869facd7, 0x2bd1a438703fc94c},
+      {0x91ff83775423cc06, 0x7b6306a34627ddd0},
+      {0xb67f6455292cbf08, 0x1a3bc84c17b1d543},
+      {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94},
+      {0x8e938662882af53e, 0x547eb47b7282ee9d},
+      {0xb23867fb2a35b28d, 0xe99e619a4f23aa44},
+      {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5},
+      {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05},
+      {0xae0b158b4738705e, 0x9624ab50b148d446},
+      {0xd98ddaee19068c76, 0x3badd624dd9b0958},
+      {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7},
+      {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d},
+      {0xd47487cc8470652b, 0x7647c32000696720},
+      {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074},
+      {0xa5fb0a17c777cf09, 0xf468107100525891},
+      {0xcf79cc9db955c2cc, 0x7182148d4066eeb5},
+      {0x81ac1fe293d599bf, 0xc6f14cd848405531},
+      {0xa21727db38cb002f, 0xb8ada00e5a506a7d},
+      {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d},
+      {0xfd442e4688bd304a, 0x908f4a166d1da664},
+      {0x9e4a9cec15763e2e, 0x9a598e4e043287ff},
+      {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe},
+      {0xf7549530e188c128, 0xd12bee59e68ef47d},
+      {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf},
+      {0xc13a148e3032d6e7, 0xe36a52363c1faf02},
+      {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2},
+      {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba},
+      {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8},
+      {0xebdf661791d60f56, 0x111b495b3464ad22},
+      {0x936b9fcebb25c995, 0xcab10dd900beec35},
+      {0xb84687c269ef3bfb, 0x3d5d514f40eea743},
+      {0xe65829b3046b0afa, 0x0cb4a5a3112a5113},
+      {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac},
+      {0xb3f4e093db73a093, 0x59ed216765690f57},
+      {0xe0f218b8d25088b8, 0x306869c13ec3532d},
+      {0x8c974f7383725573, 0x1e414218c73a13fc},
+      {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
+      {0xdbac6c247d62a583, 0xdf45f746b74abf3a},
+      {0x894bc396ce5da772, 0x6b8bba8c328eb784},
+      {0xab9eb47c81f5114f, 0x066ea92f3f326565},
+      {0xd686619ba27255a2, 0xc80a537b0efefebe},
+      {0x8613fd0145877585, 0xbd06742ce95f5f37},
+      {0xa798fc4196e952e7, 0x2c48113823b73705},
+      {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6},
+      {0x82ef85133de648c4, 0x9a984d73dbe722fc},
+      {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb},
+      {0xcc963fee10b7d1b3, 0x318df905079926a9},
+      {0xffbbcfe994e5c61f, 0xfdf17746497f7053},
+      {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634},
+      {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1},
+      {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1},
+      {0x9c1661a651213e2d, 0x06bea10ca65c084f},
+      {0xc31bfa0fe5698db8, 0x486e494fcff30a63},
+      {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb},
+      {0x986ddb5c6b3a76b7, 0xf89629465a75e01d},
+      {0xbe89523386091465, 0xf6bbb397f1135824},
+      {0xee2ba6c0678b597f, 0x746aa07ded582e2d},
+      {0x94db483840b717ef, 0xa8c2a44eb4571cdd},
+      {0xba121a4650e4ddeb, 0x92f34d62616ce414},
+      {0xe896a0d7e51e1566, 0x77b020baf9c81d18},
+      {0x915e2486ef32cd60, 0x0ace1474dc1d122f},
+      {0xb5b5ada8aaff80b8, 0x0d819992132456bb},
+      {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a},
+      {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
+      {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3},
+      {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf},
+      {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c},
+      {0xad4ab7112eb3929d, 0x86c16c98d2c953c7},
+      {0xd89d64d57a607744, 0xe871c7bf077ba8b8},
+      {0x87625f056c7c4a8b, 0x11471cd764ad4973},
+      {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0},
+      {0xd389b47879823479, 0x4aff1d108d4ec2c4},
+      {0x843610cb4bf160cb, 0xcedf722a585139bb},
+      {0xa54394fe1eedb8fe, 0xc2974eb4ee658829},
+      {0xce947a3da6a9273e, 0x733d226229feea33},
+      {0x811ccc668829b887, 0x0806357d5a3f5260},
+      {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8},
+      {0xc9bcff6034c13052, 0xfc89b393dd02f0b6},
+      {0xfc2c3f3841f17c67, 0xbbac2078d443ace3},
+      {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e},
+      {0xc5029163f384a931, 0x0a9e795e65d4df12},
+      {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6},
+      {0x99ea0196163fa42e, 0x504bced1bf8e4e46},
+      {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7},
+      {0xf07da27a82c37088, 0x5d767327bb4e5a4d},
+      {0x964e858c91ba2655, 0x3a6a07f8d510f870},
+      {0xbbe226efb628afea, 0x890489f70a55368c},
+      {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f},
+      {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e},
+      {0xb77ada0617e3bbcb, 0x09ce6ebb40173745},
+      {0xe55990879ddcaabd, 0xcc420a6a101d0516},
+      {0x8f57fa54c2a9eab6, 0x9fa946824a12232e},
+      {0xb32df8e9f3546564, 0x47939822dc96abfa},
+      {0xdff9772470297ebd, 0x59787e2b93bc56f8},
+      {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b},
+      {0xaefae51477a06b03, 0xede622920b6b23f2},
+      {0xdab99e59958885c4, 0xe95fab368e45ecee},
+      {0x88b402f7fd75539b, 0x11dbcb0218ebb415},
+      {0xaae103b5fcd2a881, 0xd652bdc29f26a11a},
+      {0xd59944a37c0752a2, 0x4be76d3346f04960},
+      {0x857fcae62d8493a5, 0x6f70a4400c562ddc},
+      {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953},
+      {0xd097ad07a71f26b2, 0x7e2000a41346a7a8},
+      {0x825ecc24c873782f, 0x8ed400668c0c28c9},
+      {0xa2f67f2dfa90563b, 0x728900802f0f32fb},
+      {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba},
+      {0xfea126b7d78186bc, 0xe2f610c84987bfa9},
+      {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca},
+      {0xc6ede63fa05d3143, 0x91503d1c79720dbc},
+      {0xf8a95fcf88747d94, 0x75a44c6397ce912b},
+      {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb},
+      {0xc24452da229b021b, 0xfbe85badce996169},
+      {0xf2d56790ab41c2a2, 0xfae27299423fb9c4},
+      {0x97c560ba6b0919a5, 0xdccd879fc967d41b},
+      {0xbdb6b8e905cb600f, 0x5400e987bbc1c921},
+      {0xed246723473e3813, 0x290123e9aab23b69},
+      {0x9436c0760c86e30b, 0xf9a0b6720aaf6522},
+      {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
+      {0xe7958cb87392c2c2, 0xb60b1d1230b20e05},
+      {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3},
+      {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4},
+      {0xe2280b6c20dd5232, 0x25c6da63c38de1b1},
+      {0x8d590723948a535f, 0x579c487e5a38ad0f},
+      {0xb0af48ec79ace837, 0x2d835a9df0c6d852},
+      {0xdcdb1b2798182244, 0xf8e431456cf88e66},
+      {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900},
+      {0xac8b2d36eed2dac5, 0xe272467e3d222f40},
+      {0xd7adf884aa879177, 0x5b0ed81dcc6abb10},
+      {0x86ccbb52ea94baea, 0x98e947129fc2b4ea},
+      {0xa87fea27a539e9a5, 0x3f2398d747b36225},
+      {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae},
+      {0x83a3eeeef9153e89, 0x1953cf68300424ad},
+      {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8},
+      {0xcdb02555653131b6, 0x3792f412cb06794e},
+      {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1},
+      {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5},
+      {0xc8de047564d20a8b, 0xf245825a5a445276},
+      {0xfb158592be068d2e, 0xeed6e2f0f0d56713},
+      {0x9ced737bb6c4183d, 0x55464dd69685606c},
+      {0xc428d05aa4751e4c, 0xaa97e14c3c26b887},
+      {0xf53304714d9265df, 0xd53dd99f4b3066a9},
+      {0x993fe2c6d07b7fab, 0xe546a8038efe402a},
+      {0xbf8fdb78849a5f96, 0xde98520472bdd034},
+      {0xef73d256a5c0f77c, 0x963e66858f6d4441},
+      {0x95a8637627989aad, 0xdde7001379a44aa9},
+      {0xbb127c53b17ec159, 0x5560c018580d5d53},
+      {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7},
+      {0x9226712162ab070d, 0xcab3961304ca70e9},
+      {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23},
+      {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b},
+      {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243},
+      {0xb267ed1940f1c61c, 0x55f038b237591ed4},
+      {0xdf01e85f912e37a3, 0x6b6c46dec52f6689},
+      {0x8b61313bbabce2c6, 0x2323ac4b3b3da016},
+      {0xae397d8aa96c1b77, 0xabec975e0a0d081b},
+      {0xd9c7dced53c72255, 0x96e7bd358c904a22},
+      {0x881cea14545c7575, 0x7e50d64177da2e55},
+      {0xaa242499697392d2, 0xdde50bd1d5d0b9ea},
+      {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865},
+      {0x84ec3c97da624ab4, 0xbd5af13bef0b113f},
+      {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f},
+      {0xcfb11ead453994ba, 0x67de18eda5814af3},
+      {0x81ceb32c4b43fcf4, 0x80eacf948770ced8},
+      {0xa2425ff75e14fc31, 0xa1258379a94d028e},
+      {0xcad2f7f5359a3b3e, 0x096ee45813a04331},
+      {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd},
+      {0x9e74d1b791e07e48, 0x775ea264cf55347e},
+      {0xc612062576589dda, 0x95364afe032a819e},
+      {0xf79687aed3eec551, 0x3a83ddbd83f52205},
+      {0x9abe14cd44753b52, 0xc4926a9672793543},
+      {0xc16d9a0095928a27, 0x75b7053c0f178294},
+      {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
+      {0x971da05074da7bee, 0xd3f6fc16ebca5e04},
+      {0xbce5086492111aea, 0x88f4bb1ca6bcf585},
+      {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6},
+      {0x9392ee8e921d5d07, 0x3aff322e62439fd0},
+      {0xb877aa3236a4b449, 0x09befeb9fad487c3},
+      {0xe69594bec44de15b, 0x4c2ebe687989a9b4},
+      {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11},
+      {0xb424dc35095cd80f, 0x538484c19ef38c95},
+      {0xe12e13424bb40e13, 0x2865a5f206b06fba},
+      {0x8cbccc096f5088cb, 0xf93f87b7442e45d4},
+      {0xafebff0bcb24aafe, 0xf78f69a51539d749},
+      {0xdbe6fecebdedd5be, 0xb573440e5a884d1c},
+      {0x89705f4136b4a597, 0x31680a88f8953031},
+      {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e},
+      {0xd6bf94d5e57a42bc, 0x3d32907604691b4d},
+      {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110},
+      {0xa7c5ac471b478423, 0x0fcf80dc33721d54},
+      {0xd1b71758e219652b, 0xd3c36113404ea4a9},
+      {0x83126e978d4fdf3b, 0x645a1cac083126ea},
+      {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4},
+      {0xcccccccccccccccc, 0xcccccccccccccccd},
+      {0x8000000000000000, 0x0000000000000000},
+      {0xa000000000000000, 0x0000000000000000},
+      {0xc800000000000000, 0x0000000000000000},
+      {0xfa00000000000000, 0x0000000000000000},
+      {0x9c40000000000000, 0x0000000000000000},
+      {0xc350000000000000, 0x0000000000000000},
+      {0xf424000000000000, 0x0000000000000000},
+      {0x9896800000000000, 0x0000000000000000},
+      {0xbebc200000000000, 0x0000000000000000},
+      {0xee6b280000000000, 0x0000000000000000},
+      {0x9502f90000000000, 0x0000000000000000},
+      {0xba43b74000000000, 0x0000000000000000},
+      {0xe8d4a51000000000, 0x0000000000000000},
+      {0x9184e72a00000000, 0x0000000000000000},
+      {0xb5e620f480000000, 0x0000000000000000},
+      {0xe35fa931a0000000, 0x0000000000000000},
+      {0x8e1bc9bf04000000, 0x0000000000000000},
+      {0xb1a2bc2ec5000000, 0x0000000000000000},
+      {0xde0b6b3a76400000, 0x0000000000000000},
+      {0x8ac7230489e80000, 0x0000000000000000},
+      {0xad78ebc5ac620000, 0x0000000000000000},
+      {0xd8d726b7177a8000, 0x0000000000000000},
+      {0x878678326eac9000, 0x0000000000000000},
+      {0xa968163f0a57b400, 0x0000000000000000},
+      {0xd3c21bcecceda100, 0x0000000000000000},
+      {0x84595161401484a0, 0x0000000000000000},
+      {0xa56fa5b99019a5c8, 0x0000000000000000},
+      {0xcecb8f27f4200f3a, 0x0000000000000000},
+      {0x813f3978f8940984, 0x4000000000000000},
+      {0xa18f07d736b90be5, 0x5000000000000000},
+      {0xc9f2c9cd04674ede, 0xa400000000000000},
+      {0xfc6f7c4045812296, 0x4d00000000000000},
+      {0x9dc5ada82b70b59d, 0xf020000000000000},
+      {0xc5371912364ce305, 0x6c28000000000000},
+      {0xf684df56c3e01bc6, 0xc732000000000000},
+      {0x9a130b963a6c115c, 0x3c7f400000000000},
+      {0xc097ce7bc90715b3, 0x4b9f100000000000},
+      {0xf0bdc21abb48db20, 0x1e86d40000000000},
+      {0x96769950b50d88f4, 0x1314448000000000},
+      {0xbc143fa4e250eb31, 0x17d955a000000000},
+      {0xeb194f8e1ae525fd, 0x5dcfab0800000000},
+      {0x92efd1b8d0cf37be, 0x5aa1cae500000000},
+      {0xb7abc627050305ad, 0xf14a3d9e40000000},
+      {0xe596b7b0c643c719, 0x6d9ccd05d0000000},
+      {0x8f7e32ce7bea5c6f, 0xe4820023a2000000},
+      {0xb35dbf821ae4f38b, 0xdda2802c8a800000},
+      {0xe0352f62a19e306e, 0xd50b2037ad200000},
+      {0x8c213d9da502de45, 0x4526f422cc340000},
+      {0xaf298d050e4395d6, 0x9670b12b7f410000},
+      {0xdaf3f04651d47b4c, 0x3c0cdd765f114000},
+      {0x88d8762bf324cd0f, 0xa5880a69fb6ac800},
+      {0xab0e93b6efee0053, 0x8eea0d047a457a00},
+      {0xd5d238a4abe98068, 0x72a4904598d6d880},
+      {0x85a36366eb71f041, 0x47a6da2b7f864750},
+      {0xa70c3c40a64e6c51, 0x999090b65f67d924},
+      {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d},
+      {0x82818f1281ed449f, 0xbff8f10e7a8921a5},
+      {0xa321f2d7226895c7, 0xaff72d52192b6a0e},
+      {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491},
+      {0xfee50b7025c36a08, 0x02f236d04753d5b5},
+      {0x9f4f2726179a2245, 0x01d762422c946591},
+      {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6},
+      {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3},
+      {0x9b934c3b330c8577, 0x63cc55f49f88eb30},
+      {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc},
+      {0xf316271c7fc3908a, 0x8bef464e3945ef7b},
+      {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad},
+      {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318},
+      {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde},
+      {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b},
+      {0xb975d6b6ee39e436, 0xb3e2fd538e122b45},
+      {0xe7d34c64a9c85d44, 0x60dbbca87196b617},
+      {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce},
+      {0xb51d13aea4a488dd, 0x6babab6398bdbe42},
+      {0xe264589a4dcdab14, 0xc696963c7eed2dd2},
+      {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3},
+      {0xb0de65388cc8ada8, 0x3b25a55f43294bcc},
+      {0xdd15fe86affad912, 0x49ef0eb713f39ebf},
+      {0x8a2dbf142dfcc7ab, 0x6e3569326c784338},
+      {0xacb92ed9397bf996, 0x49c2c37f07965405},
+      {0xd7e77a8f87daf7fb, 0xdc33745ec97be907},
+      {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4},
+      {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d},
+      {0xd2d80db02aabd62b, 0xf50a3fa490c30191},
+      {0x83c7088e1aab65db, 0x792667c6da79e0fb},
+      {0xa4b8cab1a1563f52, 0x577001b891185939},
+      {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
+      {0x80b05e5ac60b6178, 0x544f8158315b05b5},
+      {0xa0dc75f1778e39d6, 0x696361ae3db1c722},
+      {0xc913936dd571c84c, 0x03bc3a19cd1e38ea},
+      {0xfb5878494ace3a5f, 0x04ab48a04065c724},
+      {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77},
+      {0xc45d1df942711d9a, 0x3ba5d0bd324f8395},
+      {0xf5746577930d6500, 0xca8f44ec7ee3647a},
+      {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc},
+      {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f},
+      {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f},
+      {0x95d04aee3b80ece5, 0xbba1f1d158724a13},
+      {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98},
+      {0xea1575143cf97226, 0xf52d09d71a3293be},
+      {0x924d692ca61be758, 0x593c2626705f9c57},
+      {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d},
+      {0xe498f455c38b997a, 0x0b6dfb9c0f956448},
+      {0x8edf98b59a373fec, 0x4724bd4189bd5ead},
+      {0xb2977ee300c50fe7, 0x58edec91ec2cb658},
+      {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee},
+      {0x8b865b215899f46c, 0xbd79e0d20082ee75},
+      {0xae67f1e9aec07187, 0xecd8590680a3aa12},
+      {0xda01ee641a708de9, 0xe80e6f4820cc9496},
+      {0x884134fe908658b2, 0x3109058d147fdcde},
+      {0xaa51823e34a7eede, 0xbd4b46f0599fd416},
+      {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b},
+      {0x850fadc09923329e, 0x03e2cf6bc604ddb1},
+      {0xa6539930bf6bff45, 0x84db8346b786151d},
+      {0xcfe87f7cef46ff16, 0xe612641865679a64},
+      {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f},
+      {0xa26da3999aef7749, 0xe3be5e330f38f09e},
+      {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6},
+      {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7},
+      {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb},
+      {0xc646d63501a1511d, 0xb281e1fd541501b9},
+      {0xf7d88bc24209a565, 0x1f225a7ca91a4227},
+      {0x9ae757596946075f, 0x3375788de9b06959},
+      {0xc1a12d2fc3978937, 0x0052d6b1641c83af},
+      {0xf209787bb47d6b84, 0xc0678c5dbd23a49b},
+      {0x9745eb4d50ce6332, 0xf840b7ba963646e1},
+      {0xbd176620a501fbff, 0xb650e5a93bc3d899},
+      {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf},
+      {0x93ba47c980e98cdf, 0xc66f336c36b10138},
+      {0xb8a8d9bbe123f017, 0xb80b0047445d4185},
+      {0xe6d3102ad96cec1d, 0xa60dc059157491e6},
+      {0x9043ea1ac7e41392, 0x87c89837ad68db30},
+      {0xb454e4a179dd1877, 0x29babe4598c311fc},
+      {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b},
+      {0x8ce2529e2734bb1d, 0x1899e4a65f58660d},
+      {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90},
+      {0xdc21a1171d42645d, 0x76707543f4fa1f74},
+      {0x899504ae72497eba, 0x6a06494a791c53a9},
+      {0xabfa45da0edbde69, 0x0487db9d17636893},
+      {0xd6f8d7509292d603, 0x45a9d2845d3c42b7},
+      {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
+      {0xa7f26836f282b732, 0x8e6cac7768d7141f},
+      {0xd1ef0244af2364ff, 0x3207d795430cd927},
+      {0x8335616aed761f1f, 0x7f44e6bd49e807b9},
+      {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7},
+      {0xcd036837130890a1, 0x36dba887c37a8c10},
+      {0x802221226be55a64, 0xc2494954da2c978a},
+      {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d},
+      {0xc83553c5c8965d3d, 0x6f92829494e5acc8},
+      {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa},
+      {0x9c69a97284b578d7, 0xff2a760414536efc},
+      {0xc38413cf25e2d70d, 0xfef5138519684abb},
+      {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a},
+      {0x98bf2f79d5993802, 0xef2f773ffbd97a62},
+      {0xbeeefb584aff8603, 0xaafb550ffacfd8fb},
+      {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39},
+      {0x952ab45cfa97a0b2, 0xdd945a747bf26184},
+      {0xba756174393d88df, 0x94f971119aeef9e5},
+      {0xe912b9d1478ceb17, 0x7a37cd5601aab85e},
+      {0x91abb422ccb812ee, 0xac62e055c10ab33b},
+      {0xb616a12b7fe617aa, 0x577b986b314d600a},
+      {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c},
+      {0x8e41ade9fbebc27d, 0x14588f13be847308},
+      {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9},
+      {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc},
+      {0x8aec23d680043bee, 0x25de7bb9480d5855},
+      {0xada72ccc20054ae9, 0xaf561aa79a10ae6b},
+      {0xd910f7ff28069da4, 0x1b2ba1518094da05},
+      {0x87aa9aff79042286, 0x90fb44d2f05d0843},
+      {0xa99541bf57452b28, 0x353a1607ac744a54},
+      {0xd3fa922f2d1675f2, 0x42889b8997915ce9},
+      {0x847c9b5d7c2e09b7, 0x69956135febada12},
+      {0xa59bc234db398c25, 0x43fab9837e699096},
+      {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc},
+      {0x8161afb94b44f57d, 0x1d1be0eebac278f6},
+      {0xa1ba1ba79e1632dc, 0x6462d92a69731733},
+      {0xca28a291859bbf93, 0x7d7b8f7503cfdcff},
+      {0xfcb2cb35e702af78, 0x5cda735244c3d43f},
+      {0x9defbf01b061adab, 0x3a0888136afa64a8},
+      {0xc56baec21c7a1916, 0x088aaa1845b8fdd1},
+      {0xf6c69a72a3989f5b, 0x8aad549e57273d46},
+      {0x9a3c2087a63f6399, 0x36ac54e2f678864c},
+      {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de},
+      {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6},
+      {0x969eb7c47859e743, 0x9f644ae5a4b1b326},
+      {0xbc4665b596706114, 0x873d5d9f0dde1fef},
+      {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb},
+      {0x9316ff75dd87cbd8, 0x09a7f12442d588f3},
+      {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30},
+      {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb},
+      {0x8fa475791a569d10, 0xf96e017d694487bd},
+      {0xb38d92d760ec4455, 0x37c981dcc395a9ad},
+      {0xe070f78d3927556a, 0x85bbe253f47b1418},
+      {0x8c469ab843b89562, 0x93956d7478ccec8f},
+      {0xaf58416654a6babb, 0x387ac8d1970027b3},
+      {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f},
+      {0x88fcf317f22241e2, 0x441fece3bdf81f04},
+      {0xab3c2fddeeaad25a, 0xd527e81cad7626c4},
+      {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075},
+      {0x85c7056562757456, 0xf6872d5667844e4a},
+      {0xa738c6bebb12d16c, 0xb428f8ac016561dc},
+      {0xd106f86e69d785c7, 0xe13336d701beba53},
+      {0x82a45b450226b39c, 0xecc0024661173474},
+      {0xa34d721642b06084, 0x27f002d7f95d0191},
+      {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5},
+      {0xff290242c83396ce, 0x7e67047175a15272},
+      {0x9f79a169bd203e41, 0x0f0062c6e984d387},
+      {0xc75809c42c684dd1, 0x52c07b78a3e60869},
+      {0xf92e0c3537826145, 0xa7709a56ccdf8a83},
+      {0x9bbcc7a142b17ccb, 0x88a66076400bb692},
+      {0xc2abf989935ddbfe, 0x6acff893d00ea436},
+      {0xf356f7ebf83552fe, 0x0583f6b8c4124d44},
+      {0x98165af37b2153de, 0xc3727a337a8b704b},
+      {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d},
+      {0xeda2ee1c7064130c, 0x1162def06f79df74},
+      {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9},
+      {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693},
+      {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438},
+      {0x910ab1d4db9914a0, 0x1d9c9892400a22a3},
+      {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c},
+      {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e},
+      {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
+      {0xb10d8e1456105dad, 0x7425a83e872c5f48},
+      {0xdd50f1996b947518, 0xd12f124e28f7771a},
+      {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70},
+      {0xace73cbfdc0bfb7b, 0x636cc64d1001550c},
+      {0xd8210befd30efa5a, 0x3c47f7e05401aa4f},
+      {0x8714a775e3e95c78, 0x65acfaec34810a72},
+      {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e},
+      {0xd31045a8341ca07c, 0x1ede48111209a051},
+      {0x83ea2b892091e44d, 0x934aed0aab460433},
+      {0xa4e4b66b68b65d60, 0xf81da84d56178540},
+      {0xce1de40642e3f4b9, 0x36251260ab9d668f},
+      {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a},
+      {0xa1075a24e4421730, 0xb24cf65b8612f820},
+      {0xc94930ae1d529cfc, 0xdee033f26797b628},
+      {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2},
+      {0x9d412e0806e88aa5, 0x8e1f289560ee864f},
+      {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3},
+      {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc},
+      {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a},
+      {0xbff610b0cc6edd3f, 0x17fd090a58d32af4},
+      {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1},
+      {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f},
+      {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2},
+      {0xea53df5fd18d5513, 0x84c86189216dc5ee},
+      {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5},
+      {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2},
+      {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
+      {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f},
+      {0xb2c71d5bca9023f8, 0x743e20e9ef511013},
+      {0xdf78e4b2bd342cf6, 0x914da9246b255417},
+      {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f},
+      {0xae9672aba3d0c320, 0xa184ac2473b529b2},
+      {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f},
+      {0x8865899617fb1871, 0x7e2fa67c7a658893},
+      {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8},
+      {0xd51ea6fa85785631, 0x552a74227f3ea566},
+      {0x8533285c936b35de, 0xd53a88958f872760},
+      {0xa67ff273b8460356, 0x8a892abaf368f138},
+      {0xd01fef10a657842c, 0x2d2b7569b0432d86},
+      {0x8213f56a67f6b29b, 0x9c3b29620e29fc74},
+      {0xa298f2c501f45f42, 0x8349f3ba91b47b90},
+      {0xcb3f2f7642717713, 0x241c70a936219a74},
+      {0xfe0efb53d30dd4d7, 0xed238cd383aa0111},
+      {0x9ec95d1463e8a506, 0xf4363804324a40ab},
+      {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6},
+      {0xf81aa16fdc1b81da, 0xdd94b7868e94050b},
+      {0x9b10a4e5e9913128, 0xca7cf2b4191c8327},
+      {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1},
+      {0xf24a01a73cf2dccf, 0xbc633b39673c8ced},
+      {0x976e41088617ca01, 0xd5be0503e085d814},
+      {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19},
+      {0xec9c459d51852ba2, 0xddf8e7d60ed1219f},
+      {0x93e1ab8252f33b45, 0xcabb90e5c942b504},
+      {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
+      {0xe7109bfba19c0c9d, 0x0cc512670a783ad5},
+      {0x906a617d450187e2, 0x27fb2b80668b24c6},
+      {0xb484f9dc9641e9da, 0xb1f9f660802dedf7},
+      {0xe1a63853bbd26451, 0x5e7873f8a0396974},
+      {0x8d07e33455637eb2, 0xdb0b487b6423e1e9},
+      {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63},
+      {0xdc5c5301c56b75f7, 0x7641a140cc7810fc},
+      {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e},
+      {0xac2820d9623bf429, 0x546345fa9fbdcd45},
+      {0xd732290fbacaf133, 0xa97c177947ad4096},
+      {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e},
+      {0xa81f301449ee8c70, 0x5c68f256bfff5a75},
+      {0xd226fc195c6a2f8c, 0x73832eec6fff3112},
+      {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac},
+      {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56},
+      {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec},
+      {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4},
+      {0xa0555e361951c366, 0xd7e105bcc3326220},
+      {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8},
+      {0xfa856334878fc150, 0xb14f98f6f0feb952},
+      {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4},
+      {0xc3b8358109e84f07, 0x0a862f80ec4700c9},
+      {0xf4a642e14c6262c8, 0xcd27bb612758c0fb},
+      {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d},
+      {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4},
+      {0xeeea5d5004981478, 0x1858ccfce06cac75},
+      {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
+      {0xbaa718e68396cffd, 0xd30560258f54e6bb},
+      {0xe950df20247c83fd, 0x47c6b82ef32a206a},
+      {0x91d28b7416cdd27e, 0x4cdc331d57fa5442},
+      {0xb6472e511c81471d, 0xe0133fe4adf8e953},
+      {0xe3d8f9e563a198e5, 0x58180fddd97723a7},
+      {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649},
+      {0xb201833b35d63f73, 0x2cd2cc6551e513db},
+      {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2},
+      {0x8b112e86420f6191, 0xfb04afaf27faf783},
+      {0xadd57a27d29339f6, 0x79c5db9af1f9b564},
+      {0xd94ad8b1c7380874, 0x18375281ae7822bd},
+      {0x87cec76f1c830548, 0x8f2293910d0b15b6},
+      {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23},
+      {0xd433179d9c8cb841, 0x5fa60692a46151ec},
+      {0x849feec281d7f328, 0xdbc7c41ba6bcd334},
+      {0xa5c7ea73224deff3, 0x12b9b522906c0801},
+      {0xcf39e50feae16bef, 0xd768226b34870a01},
+      {0x81842f29f2cce375, 0xe6a1158300d46641},
+      {0xa1e53af46f801c53, 0x60495ae3c1097fd1},
+      {0xca5e89b18b602368, 0x385bb19cb14bdfc5},
+      {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
+      {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
+      {0xc5a05277621be293, 0xc7098b7305241886},
+      {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8},
+      {0x9a65406d44a5c903, 0x737f74f1dc043329},
+      {0xc0fe908895cf3b44, 0x505f522e53053ff3},
+      {0xf13e34aabb430a15, 0x647726b9e7c68ff0},
+      {0x96c6e0eab509e64d, 0x5eca783430dc19f6},
+      {0xbc789925624c5fe0, 0xb67d16413d132073},
+      {0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890},
+      {0x933e37a534cbaae7, 0x8e91b962f7b6f15a},
+      {0xb80dc58e81fe95a1, 0x723627bbb5a4adb1},
+      {0xe61136f2227e3b09, 0xcec3b1aaa30dd91d},
+      {0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2},
+      {0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e},
+      {0xe0accfa875af45a7, 0x93eb1b80a33b8606},
+      {0x8c6c01c9498d8b88, 0xbc72f130660533c4},
+      {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
+      { 0xdb68c2ca82ed2a05,
+        0xa67398db9f6820e2 }
+#else
+      {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
+      {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
+      {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
+      {0x86a8d39ef77164bc, 0xae5dff9c02033198},
+      {0xd98ddaee19068c76, 0x3badd624dd9b0958},
+      {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
+      {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
+      {0xe55990879ddcaabd, 0xcc420a6a101d0516},
+      {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
+      {0x95a8637627989aad, 0xdde7001379a44aa9},
+      {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
+      {0xc350000000000000, 0x0000000000000000},
+      {0x9dc5ada82b70b59d, 0xf020000000000000},
+      {0xfee50b7025c36a08, 0x02f236d04753d5b5},
+      {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
+      {0xa6539930bf6bff45, 0x84db8346b786151d},
+      {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
+      {0xd910f7ff28069da4, 0x1b2ba1518094da05},
+      {0xaf58416654a6babb, 0x387ac8d1970027b3},
+      {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
+      {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
+      {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
+      {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
+      {0xf13e34aabb430a15, 0x647726b9e7c68ff0}
+#endif
+    };
+
+#if FMT_USE_FULL_CACHE_DRAGONBOX
+    return pow10_significands[k - float_info<double>::min_k];
+#else
+    static constexpr const uint64_t powers_of_5_64[] = {
+        0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
+        0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
+        0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
+        0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd,
+        0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9,
+        0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5,
+        0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631,
+        0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed,
+        0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9};
+
+    static const int compression_ratio = 27;
+
+    // Compute base index.
+    int cache_index = (k - float_info<double>::min_k) / compression_ratio;
+    int kb = cache_index * compression_ratio + float_info<double>::min_k;
+    int offset = k - kb;
+
+    // Get base cache.
+    uint128_fallback base_cache = pow10_significands[cache_index];
+    if (offset == 0) return base_cache;
+
+    // Compute the required amount of bit-shift.
+    int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset;
+    FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected");
+
+    // Try to recover the real cache.
+    uint64_t pow5 = powers_of_5_64[offset];
+    uint128_fallback recovered_cache = umul128(base_cache.high(), pow5);
+    uint128_fallback middle_low = umul128(base_cache.low(), pow5);
+
+    recovered_cache += middle_low.high();
+
+    uint64_t high_to_middle = recovered_cache.high() << (64 - alpha);
+    uint64_t middle_to_low = recovered_cache.low() << (64 - alpha);
+
+    recovered_cache =
+        uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle,
+                         ((middle_low.low() >> alpha) | middle_to_low)};
+    FMT_ASSERT(recovered_cache.low() + 1 != 0, "");
+    return {recovered_cache.high(), recovered_cache.low() + 1};
+#endif
+  }
+
+  struct compute_mul_result {
+    carrier_uint result;
+    bool is_integer;
+  };
+  struct compute_mul_parity_result {
+    bool parity;
+    bool is_integer;
+  };
+
+  static compute_mul_result compute_mul(
+      carrier_uint u, const cache_entry_type& cache) noexcept {
+    auto r = umul192_upper128(u, cache);
+    return {r.high(), r.low() == 0};
+  }
+
+  static uint32_t compute_delta(cache_entry_type const& cache,
+                                int beta) noexcept {
+    return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
+  }
+
+  static compute_mul_parity_result compute_mul_parity(
+      carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
+    FMT_ASSERT(beta >= 1, "");
+    FMT_ASSERT(beta < 64, "");
+
+    auto r = umul192_lower128(two_f, cache);
+    return {((r.high() >> (64 - beta)) & 1) != 0,
+            ((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
+  }
+
+  static carrier_uint compute_left_endpoint_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return (cache.high() -
+            (cache.high() >> (num_significand_bits<double>() + 2))) >>
+           (64 - num_significand_bits<double>() - 1 - beta);
+  }
+
+  static carrier_uint compute_right_endpoint_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return (cache.high() +
+            (cache.high() >> (num_significand_bits<double>() + 1))) >>
+           (64 - num_significand_bits<double>() - 1 - beta);
+  }
+
+  static carrier_uint compute_round_up_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
+            1) /
+           2;
+  }
+};
+
+FMT_FUNC uint128_fallback get_cached_power(int k) noexcept {
+  return cache_accessor<double>::get_cached_power(k);
+}
+
+// Various integer checks
+template <typename T>
+bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
+  const int case_shorter_interval_left_endpoint_lower_threshold = 2;
+  const int case_shorter_interval_left_endpoint_upper_threshold = 3;
+  return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
+         exponent <= case_shorter_interval_left_endpoint_upper_threshold;
+}
+
+// Remove trailing zeros from n and return the number of zeros removed (float)
+FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
+  FMT_ASSERT(n != 0, "");
+  // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
+  constexpr uint32_t mod_inv_5 = 0xcccccccd;
+  constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5
+
+  while (true) {
+    auto q = rotr(n * mod_inv_25, 2);
+    if (q > max_value<uint32_t>() / 100) break;
+    n = q;
+    s += 2;
+  }
+  auto q = rotr(n * mod_inv_5, 1);
+  if (q <= max_value<uint32_t>() / 10) {
+    n = q;
+    s |= 1;
+  }
+  return s;
+}
+
+// Removes trailing zeros and returns the number of zeros removed (double)
+FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
+  FMT_ASSERT(n != 0, "");
+
+  // This magic number is ceil(2^90 / 10^8).
+  constexpr uint64_t magic_number = 12379400392853802749ull;
+  auto nm = umul128(n, magic_number);
+
+  // Is n is divisible by 10^8?
+  if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
+    // If yes, work with the quotient...
+    auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
+    // ... and use the 32 bit variant of the function
+    int s = remove_trailing_zeros(n32, 8);
+    n = n32;
+    return s;
+  }
+
+  // If n is not divisible by 10^8, work with n itself.
+  constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd;
+  constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // = mod_inv_5 * mod_inv_5
+
+  int s = 0;
+  while (true) {
+    auto q = rotr(n * mod_inv_25, 2);
+    if (q > max_value<uint64_t>() / 100) break;
+    n = q;
+    s += 2;
+  }
+  auto q = rotr(n * mod_inv_5, 1);
+  if (q <= max_value<uint64_t>() / 10) {
+    n = q;
+    s |= 1;
+  }
+
+  return s;
+}
+
+// The main algorithm for shorter interval case
+template <typename T>
+FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
+  decimal_fp<T> ret_value;
+  // Compute k and beta
+  const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
+  const int beta = exponent + floor_log2_pow10(-minus_k);
+
+  // Compute xi and zi
+  using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
+  const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
+
+  auto xi = cache_accessor<T>::compute_left_endpoint_for_shorter_interval_case(
+      cache, beta);
+  auto zi = cache_accessor<T>::compute_right_endpoint_for_shorter_interval_case(
+      cache, beta);
+
+  // If the left endpoint is not an integer, increase it
+  if (!is_left_endpoint_integer_shorter_interval<T>(exponent)) ++xi;
+
+  // Try bigger divisor
+  ret_value.significand = zi / 10;
+
+  // If succeed, remove trailing zeros if necessary and return
+  if (ret_value.significand * 10 >= xi) {
+    ret_value.exponent = minus_k + 1;
+    ret_value.exponent += remove_trailing_zeros(ret_value.significand);
+    return ret_value;
+  }
+
+  // Otherwise, compute the round-up of y
+  ret_value.significand =
+      cache_accessor<T>::compute_round_up_for_shorter_interval_case(cache,
+                                                                    beta);
+  ret_value.exponent = minus_k;
+
+  // When tie occurs, choose one of them according to the rule
+  if (exponent >= float_info<T>::shorter_interval_tie_lower_threshold &&
+      exponent <= float_info<T>::shorter_interval_tie_upper_threshold) {
+    ret_value.significand = ret_value.significand % 2 == 0
+                                ? ret_value.significand
+                                : ret_value.significand - 1;
+  } else if (ret_value.significand < xi) {
+    ++ret_value.significand;
+  }
+  return ret_value;
+}
+
+template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
+  // Step 1: integer promotion & Schubfach multiplier calculation.
+
+  using carrier_uint = typename float_info<T>::carrier_uint;
+  using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
+  auto br = bit_cast<carrier_uint>(x);
+
+  // Extract significand bits and exponent bits.
+  const carrier_uint significand_mask =
+      (static_cast<carrier_uint>(1) << num_significand_bits<T>()) - 1;
+  carrier_uint significand = (br & significand_mask);
+  int exponent =
+      static_cast<int>((br & exponent_mask<T>()) >> num_significand_bits<T>());
+
+  if (exponent != 0) {  // Check if normal.
+    exponent -= exponent_bias<T>() + num_significand_bits<T>();
+
+    // Shorter interval case; proceed like Schubfach.
+    // In fact, when exponent == 1 and significand == 0, the interval is
+    // regular. However, it can be shown that the end-results are anyway same.
+    if (significand == 0) return shorter_interval_case<T>(exponent);
+
+    significand |= (static_cast<carrier_uint>(1) << num_significand_bits<T>());
+  } else {
+    // Subnormal case; the interval is always regular.
+    if (significand == 0) return {0, 0};
+    exponent =
+        std::numeric_limits<T>::min_exponent - num_significand_bits<T>() - 1;
+  }
+
+  const bool include_left_endpoint = (significand % 2 == 0);
+  const bool include_right_endpoint = include_left_endpoint;
+
+  // Compute k and beta.
+  const int minus_k = floor_log10_pow2(exponent) - float_info<T>::kappa;
+  const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
+  const int beta = exponent + floor_log2_pow10(-minus_k);
+
+  // Compute zi and deltai.
+  // 10^kappa <= deltai < 10^(kappa + 1)
+  const uint32_t deltai = cache_accessor<T>::compute_delta(cache, beta);
+  const carrier_uint two_fc = significand << 1;
+
+  // For the case of binary32, the result of integer check is not correct for
+  // 29711844 * 2^-82
+  // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18
+  // and 29711844 * 2^-81
+  // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17,
+  // and they are the unique counterexamples. However, since 29711844 is even,
+  // this does not cause any problem for the endpoints calculations; it can only
+  // cause a problem when we need to perform integer check for the center.
+  // Fortunately, with these inputs, that branch is never executed, so we are
+  // fine.
+  const typename cache_accessor<T>::compute_mul_result z_mul =
+      cache_accessor<T>::compute_mul((two_fc | 1) << beta, cache);
+
+  // Step 2: Try larger divisor; remove trailing zeros if necessary.
+
+  // Using an upper bound on zi, we might be able to optimize the division
+  // better than the compiler; we are computing zi / big_divisor here.
+  decimal_fp<T> ret_value;
+  ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result);
+  uint32_t r = static_cast<uint32_t>(z_mul.result - float_info<T>::big_divisor *
+                                                        ret_value.significand);
+
+  if (r < deltai) {
+    // Exclude the right endpoint if necessary.
+    if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) {
+      --ret_value.significand;
+      r = float_info<T>::big_divisor;
+      goto small_divisor_case_label;
+    }
+  } else if (r > deltai) {
+    goto small_divisor_case_label;
+  } else {
+    // r == deltai; compare fractional parts.
+    const typename cache_accessor<T>::compute_mul_parity_result x_mul =
+        cache_accessor<T>::compute_mul_parity(two_fc - 1, cache, beta);
+
+    if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint)))
+      goto small_divisor_case_label;
+  }
+  ret_value.exponent = minus_k + float_info<T>::kappa + 1;
+
+  // We may need to remove trailing zeros.
+  ret_value.exponent += remove_trailing_zeros(ret_value.significand);
+  return ret_value;
+
+  // Step 3: Find the significand with the smaller divisor.
+
+small_divisor_case_label:
+  ret_value.significand *= 10;
+  ret_value.exponent = minus_k + float_info<T>::kappa;
+
+  uint32_t dist = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
+  const bool approx_y_parity =
+      ((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0;
+
+  // Is dist divisible by 10^kappa?
+  const bool divisible_by_small_divisor =
+      check_divisibility_and_divide_by_pow10<float_info<T>::kappa>(dist);
+
+  // Add dist / 10^kappa to the significand.
+  ret_value.significand += dist;
+
+  if (!divisible_by_small_divisor) return ret_value;
+
+  // Check z^(f) >= epsilon^(f).
+  // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
+  // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).
+  // Since there are only 2 possibilities, we only need to care about the
+  // parity. Also, zi and r should have the same parity since the divisor
+  // is an even number.
+  const auto y_mul = cache_accessor<T>::compute_mul_parity(two_fc, cache, beta);
+
+  // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f),
+  // or equivalently, when y is an integer.
+  if (y_mul.parity != approx_y_parity)
+    --ret_value.significand;
+  else if (y_mul.is_integer & (ret_value.significand % 2 != 0))
+    --ret_value.significand;
+  return ret_value;
+}
+}  // namespace dragonbox
+}  // namespace detail
+
+template <> struct formatter<detail::bigint> {
+  FMT_CONSTEXPR auto parse(format_parse_context& ctx)
+      -> format_parse_context::iterator {
+    return ctx.begin();
+  }
+
+  auto format(const detail::bigint& n, format_context& ctx) const
+      -> format_context::iterator {
+    auto out = ctx.out();
+    bool first = true;
+    for (auto i = n.bigits_.size(); i > 0; --i) {
+      auto value = n.bigits_[i - 1u];
+      if (first) {
+        out = format_to(out, FMT_STRING("{:x}"), value);
+        first = false;
+        continue;
+      }
+      out = format_to(out, FMT_STRING("{:08x}"), value);
+    }
+    if (n.exp_ > 0)
+      out = format_to(out, FMT_STRING("p{}"),
+                      n.exp_ * detail::bigint::bigit_bits);
+    return out;
+  }
+};
+
+FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
+  for_each_codepoint(s, [this](uint32_t cp, string_view) {
+    if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8"));
+    if (cp <= 0xFFFF) {
+      buffer_.push_back(static_cast<wchar_t>(cp));
+    } else {
+      cp -= 0x10000;
+      buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
+      buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
+    }
+    return true;
+  });
+  buffer_.push_back(0);
+}
+
+FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
+                                  const char* message) noexcept {
+  FMT_TRY {
+    auto ec = std::error_code(error_code, std::generic_category());
+    write(std::back_inserter(out), std::system_error(ec, message).what());
+    return;
+  }
+  FMT_CATCH(...) {}
+  format_error_code(out, error_code, message);
+}
+
+FMT_FUNC void report_system_error(int error_code,
+                                  const char* message) noexcept {
+  report_error(format_system_error, error_code, message);
+}
+
+FMT_FUNC std::string vformat(string_view fmt, format_args args) {
+  // Don't optimize the "{}" case to keep the binary size small and because it
+  // can be better optimized in fmt::format anyway.
+  auto buffer = memory_buffer();
+  detail::vformat_to(buffer, fmt, args);
+  return to_string(buffer);
+}
+
+namespace detail {
+#ifndef _WIN32
+FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
+#else
+using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
+extern "C" __declspec(dllimport) int __stdcall WriteConsoleW(  //
+    void*, const void*, dword, dword*, void*);
+
+FMT_FUNC bool write_console(std::FILE* f, string_view text) {
+  auto fd = _fileno(f);
+  if (!_isatty(fd)) return false;
+  auto u16 = utf8_to_utf16(text);
+  auto written = dword();
+  return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
+                       static_cast<uint32_t>(u16.size()), &written, nullptr) != 0;
+}
+
+// Print assuming legacy (non-Unicode) encoding.
+FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
+  auto buffer = memory_buffer();
+  detail::vformat_to(buffer, fmt,
+                     basic_format_args<buffer_context<char>>(args));
+  fwrite_fully(buffer.data(), 1, buffer.size(), f);
+}
+#endif
+
+FMT_FUNC void print(std::FILE* f, string_view text) {
+  if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
+}
+}  // namespace detail
+
+FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
+  auto buffer = memory_buffer();
+  detail::vformat_to(buffer, fmt, args);
+  detail::print(f, {buffer.data(), buffer.size()});
+}
+
+FMT_FUNC void vprint(string_view fmt, format_args args) {
+  vprint(stdout, fmt, args);
+}
+
+namespace detail {
+
+struct singleton {
+  unsigned char upper;
+  unsigned char lower_count;
+};
+
+inline auto is_printable(uint16_t x, const singleton* singletons,
+                         size_t singletons_size,
+                         const unsigned char* singleton_lowers,
+                         const unsigned char* normal, size_t normal_size)
+    -> bool {
+  auto upper = x >> 8;
+  auto lower_start = 0;
+  for (size_t i = 0; i < singletons_size; ++i) {
+    auto s = singletons[i];
+    auto lower_end = lower_start + s.lower_count;
+    if (upper < s.upper) break;
+    if (upper == s.upper) {
+      for (auto j = lower_start; j < lower_end; ++j) {
+        if (singleton_lowers[j] == (x & 0xff)) return false;
+      }
+    }
+    lower_start = lower_end;
+  }
+
+  auto xsigned = static_cast<int>(x);
+  auto current = true;
+  for (size_t i = 0; i < normal_size; ++i) {
+    auto v = static_cast<int>(normal[i]);
+    auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
+    xsigned -= len;
+    if (xsigned < 0) break;
+    current = !current;
+  }
+  return current;
+}
+
+// This code is generated by support/printable.py.
+FMT_FUNC auto is_printable(uint32_t cp) -> bool {
+  static constexpr singleton singletons0[] = {
+      {0x00, 1},  {0x03, 5},  {0x05, 6},  {0x06, 3},  {0x07, 6},  {0x08, 8},
+      {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
+      {0x0f, 4},  {0x10, 3},  {0x12, 18}, {0x13, 9},  {0x16, 1},  {0x17, 5},
+      {0x18, 2},  {0x19, 3},  {0x1a, 7},  {0x1c, 2},  {0x1d, 1},  {0x1f, 22},
+      {0x20, 3},  {0x2b, 3},  {0x2c, 2},  {0x2d, 11}, {0x2e, 1},  {0x30, 3},
+      {0x31, 2},  {0x32, 1},  {0xa7, 2},  {0xa9, 2},  {0xaa, 4},  {0xab, 8},
+      {0xfa, 2},  {0xfb, 5},  {0xfd, 4},  {0xfe, 3},  {0xff, 9},
+  };
+  static constexpr unsigned char singletons0_lower[] = {
+      0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
+      0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
+      0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
+      0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
+      0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
+      0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
+      0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
+      0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
+      0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
+      0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
+      0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
+      0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
+      0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
+      0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
+      0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
+      0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
+      0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
+      0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
+      0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
+      0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
+      0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
+      0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
+      0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
+      0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
+      0xfe, 0xff,
+  };
+  static constexpr singleton singletons1[] = {
+      {0x00, 6},  {0x01, 1}, {0x03, 1},  {0x04, 2}, {0x08, 8},  {0x09, 2},
+      {0x0a, 5},  {0x0b, 2}, {0x0e, 4},  {0x10, 1}, {0x11, 2},  {0x12, 5},
+      {0x13, 17}, {0x14, 1}, {0x15, 2},  {0x17, 2}, {0x19, 13}, {0x1c, 5},
+      {0x1d, 8},  {0x24, 1}, {0x6a, 3},  {0x6b, 2}, {0xbc, 2},  {0xd1, 2},
+      {0xd4, 12}, {0xd5, 9}, {0xd6, 2},  {0xd7, 2}, {0xda, 1},  {0xe0, 5},
+      {0xe1, 2},  {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2},  {0xf9, 2},
+      {0xfa, 2},  {0xfb, 1},
+  };
+  static constexpr unsigned char singletons1_lower[] = {
+      0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
+      0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
+      0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
+      0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
+      0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
+      0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
+      0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
+      0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
+      0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
+      0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
+      0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
+      0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
+      0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
+      0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
+      0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
+  };
+  static constexpr unsigned char normal0[] = {
+      0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
+      0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
+      0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
+      0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
+      0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
+      0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
+      0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
+      0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
+      0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
+      0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
+      0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
+      0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
+      0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
+      0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
+      0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
+      0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
+      0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
+      0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
+      0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
+      0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
+      0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
+      0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
+      0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
+      0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
+      0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
+      0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
+  };
+  static constexpr unsigned char normal1[] = {
+      0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
+      0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
+      0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
+      0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
+      0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
+      0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
+      0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
+      0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
+      0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
+      0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
+      0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
+      0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
+      0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
+      0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
+      0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
+      0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
+      0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
+      0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
+      0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
+      0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
+      0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
+      0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
+      0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
+      0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
+      0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
+      0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
+      0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
+      0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
+      0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
+      0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
+      0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
+      0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
+      0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
+      0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
+      0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
+  };
+  auto lower = static_cast<uint16_t>(cp);
+  if (cp < 0x10000) {
+    return is_printable(lower, singletons0,
+                        sizeof(singletons0) / sizeof(*singletons0),
+                        singletons0_lower, normal0, sizeof(normal0));
+  }
+  if (cp < 0x20000) {
+    return is_printable(lower, singletons1,
+                        sizeof(singletons1) / sizeof(*singletons1),
+                        singletons1_lower, normal1, sizeof(normal1));
+  }
+  if (0x2a6de <= cp && cp < 0x2a700) return false;
+  if (0x2b735 <= cp && cp < 0x2b740) return false;
+  if (0x2b81e <= cp && cp < 0x2b820) return false;
+  if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
+  if (0x2ebe1 <= cp && cp < 0x2f800) return false;
+  if (0x2fa1e <= cp && cp < 0x30000) return false;
+  if (0x3134b <= cp && cp < 0xe0100) return false;
+  if (0xe01f0 <= cp && cp < 0x110000) return false;
+  return cp < 0x110000;
+}
+
+}  // namespace detail
+
+FMT_END_NAMESPACE
+
+#endif  // FMT_FORMAT_INL_H_
diff --git a/src/cpp-common/vendor/fmt/format.cc b/src/cpp-common/vendor/fmt/format.cc
new file mode 100644 (file)
index 0000000..5d8f1b3
--- /dev/null
@@ -0,0 +1,43 @@
+// Formatting library for C++
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#include "cpp-common/vendor/fmt/format-inl.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+template FMT_API auto dragonbox::to_decimal(float x) noexcept
+    -> dragonbox::decimal_fp<float>;
+template FMT_API auto dragonbox::to_decimal(double x) noexcept
+    -> dragonbox::decimal_fp<double>;
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+template FMT_API locale_ref::locale_ref(const std::locale& loc);
+template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
+#endif
+
+// Explicit instantiations for char.
+
+template FMT_API auto thousands_sep_impl(locale_ref)
+    -> thousands_sep_result<char>;
+template FMT_API auto decimal_point_impl(locale_ref) -> char;
+
+template FMT_API void buffer<char>::append(const char*, const char*);
+
+template FMT_API void vformat_to(buffer<char>&, string_view,
+                                 typename vformat_args<>::type, locale_ref);
+
+// Explicit instantiations for wchar_t.
+
+template FMT_API auto thousands_sep_impl(locale_ref)
+    -> thousands_sep_result<wchar_t>;
+template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
+
+template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
+
+}  // namespace detail
+FMT_END_NAMESPACE
diff --git a/src/cpp-common/vendor/fmt/format.h b/src/cpp-common/vendor/fmt/format.h
new file mode 100644 (file)
index 0000000..87a34b9
--- /dev/null
@@ -0,0 +1,4510 @@
+/*
+  Formatting library for C++
+
+  Copyright (c) 2012 - present, Victor Zverovich
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+  --- Optional exception to the license ---
+
+  As an exception, if, as a result of your compiling your source code, portions
+  of this Software are embedded into a machine-executable object form of such
+  source code, you may redistribute such embedded portions in such object form
+  without including the above copyright and permission notices.
+ */
+
+#ifndef FMT_FORMAT_H_
+#define FMT_FORMAT_H_
+
+#include <cmath>             // std::signbit
+#include <cstdint>           // uint32_t
+#include <cstring>           // std::memcpy
+#include <initializer_list>  // std::initializer_list
+#include <limits>            // std::numeric_limits
+#include <memory>            // std::uninitialized_copy
+#include <stdexcept>         // std::runtime_error
+#include <system_error>      // std::system_error
+
+#ifdef __cpp_lib_bit_cast
+#  include <bit>  // std::bitcast
+#endif
+
+#include "core.h"
+
+#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L
+#  define FMT_INLINE_VARIABLE inline
+#else
+#  define FMT_INLINE_VARIABLE
+#endif
+
+#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
+#  define FMT_FALLTHROUGH [[fallthrough]]
+#elif defined(__clang__)
+#  define FMT_FALLTHROUGH [[clang::fallthrough]]
+#elif FMT_GCC_VERSION >= 700 && \
+    (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
+#  define FMT_FALLTHROUGH [[gnu::fallthrough]]
+#else
+#  define FMT_FALLTHROUGH
+#endif
+
+#ifndef FMT_DEPRECATED
+#  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
+#    define FMT_DEPRECATED [[deprecated]]
+#  else
+#    if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
+#      define FMT_DEPRECATED __attribute__((deprecated))
+#    elif FMT_MSC_VERSION
+#      define FMT_DEPRECATED __declspec(deprecated)
+#    else
+#      define FMT_DEPRECATED /* deprecated */
+#    endif
+#  endif
+#endif
+
+#ifndef FMT_NO_UNIQUE_ADDRESS
+#  if FMT_CPLUSPLUS >= 202002L
+#    if FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
+#      define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]]
+// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485)
+#    elif (FMT_MSC_VERSION >= 1929) && !FMT_CLANG_VERSION
+#      define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
+#    endif
+#  endif
+#endif
+#ifndef FMT_NO_UNIQUE_ADDRESS
+#  define FMT_NO_UNIQUE_ADDRESS
+#endif
+
+#if FMT_GCC_VERSION || defined(__clang__)
+#  define FMT_VISIBILITY(value) __attribute__((visibility(value)))
+#else
+#  define FMT_VISIBILITY(value)
+#endif
+
+#ifdef __has_builtin
+#  define FMT_HAS_BUILTIN(x) __has_builtin(x)
+#else
+#  define FMT_HAS_BUILTIN(x) 0
+#endif
+
+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#  define FMT_NOINLINE __attribute__((noinline))
+#else
+#  define FMT_NOINLINE
+#endif
+
+#ifndef FMT_THROW
+#  if FMT_EXCEPTIONS
+#    if FMT_MSC_VERSION || defined(__NVCC__)
+FMT_BEGIN_NAMESPACE
+namespace detail {
+template <typename Exception> inline void do_throw(const Exception& x) {
+  // Silence unreachable code warnings in MSVC and NVCC because these
+  // are nearly impossible to fix in a generic code.
+  volatile bool b = true;
+  if (b) throw x;
+}
+}  // namespace detail
+FMT_END_NAMESPACE
+#      define FMT_THROW(x) detail::do_throw(x)
+#    else
+#      define FMT_THROW(x) throw x
+#    endif
+#  else
+#    define FMT_THROW(x) \
+      ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what())
+#  endif
+#endif
+
+#if FMT_EXCEPTIONS
+#  define FMT_TRY try
+#  define FMT_CATCH(x) catch (x)
+#else
+#  define FMT_TRY if (true)
+#  define FMT_CATCH(x) if (false)
+#endif
+
+#ifndef FMT_MAYBE_UNUSED
+#  if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
+#    define FMT_MAYBE_UNUSED [[maybe_unused]]
+#  else
+#    define FMT_MAYBE_UNUSED
+#  endif
+#endif
+
+#ifndef FMT_USE_USER_DEFINED_LITERALS
+// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
+#  if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
+       FMT_MSC_VERSION >= 1900) &&                                     \
+      (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
+#    define FMT_USE_USER_DEFINED_LITERALS 1
+#  else
+#    define FMT_USE_USER_DEFINED_LITERALS 0
+#  endif
+#endif
+
+// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
+// integer formatter template instantiations to just one by only using the
+// largest integer type. This results in a reduction in binary size but will
+// cause a decrease in integer formatting performance.
+#if !defined(FMT_REDUCE_INT_INSTANTIATIONS)
+#  define FMT_REDUCE_INT_INSTANTIATIONS 0
+#endif
+
+// __builtin_clz is broken in clang with Microsoft CodeGen:
+// https://github.com/fmtlib/fmt/issues/519.
+#if !FMT_MSC_VERSION
+#  if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION
+#    define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
+#  endif
+#  if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION
+#    define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
+#  endif
+#endif
+
+// __builtin_ctz is broken in Intel Compiler Classic on Windows:
+// https://github.com/fmtlib/fmt/issues/2510.
+#ifndef __ICL
+#  if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \
+      defined(__NVCOMPILER)
+#    define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
+#  endif
+#  if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \
+      FMT_ICC_VERSION || defined(__NVCOMPILER)
+#    define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
+#  endif
+#endif
+
+#if FMT_MSC_VERSION
+#  include <intrin.h>  // _BitScanReverse[64], _BitScanForward[64], _umul128
+#endif
+
+// Some compilers masquerade as both MSVC and GCC-likes or otherwise support
+// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the
+// MSVC intrinsics if the clz and clzll builtins are not available.
+#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \
+    !defined(FMT_BUILTIN_CTZLL)
+FMT_BEGIN_NAMESPACE
+namespace detail {
+// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
+#  if !defined(__clang__)
+#    pragma intrinsic(_BitScanForward)
+#    pragma intrinsic(_BitScanReverse)
+#    if defined(_WIN64)
+#      pragma intrinsic(_BitScanForward64)
+#      pragma intrinsic(_BitScanReverse64)
+#    endif
+#  endif
+
+inline auto clz(uint32_t x) -> int {
+  unsigned long r = 0;
+  _BitScanReverse(&r, x);
+  FMT_ASSERT(x != 0, "");
+  // Static analysis complains about using uninitialized data
+  // "r", but the only way that can happen is if "x" is 0,
+  // which the callers guarantee to not happen.
+  FMT_MSC_WARNING(suppress : 6102)
+  return 31 ^ static_cast<int>(r);
+}
+#  define FMT_BUILTIN_CLZ(n) detail::clz(n)
+
+inline auto clzll(uint64_t x) -> int {
+  unsigned long r = 0;
+#  ifdef _WIN64
+  _BitScanReverse64(&r, x);
+#  else
+  // Scan the high 32 bits.
+  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
+    return 63 ^ static_cast<int>(r + 32);
+  // Scan the low 32 bits.
+  _BitScanReverse(&r, static_cast<uint32_t>(x));
+#  endif
+  FMT_ASSERT(x != 0, "");
+  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
+  return 63 ^ static_cast<int>(r);
+}
+#  define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
+
+inline auto ctz(uint32_t x) -> int {
+  unsigned long r = 0;
+  _BitScanForward(&r, x);
+  FMT_ASSERT(x != 0, "");
+  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
+  return static_cast<int>(r);
+}
+#  define FMT_BUILTIN_CTZ(n) detail::ctz(n)
+
+inline auto ctzll(uint64_t x) -> int {
+  unsigned long r = 0;
+  FMT_ASSERT(x != 0, "");
+  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
+#  ifdef _WIN64
+  _BitScanForward64(&r, x);
+#  else
+  // Scan the low 32 bits.
+  if (_BitScanForward(&r, static_cast<uint32_t>(x))) return static_cast<int>(r);
+  // Scan the high 32 bits.
+  _BitScanForward(&r, static_cast<uint32_t>(x >> 32));
+  r += 32;
+#  endif
+  return static_cast<int>(r);
+}
+#  define FMT_BUILTIN_CTZLL(n) detail::ctzll(n)
+}  // namespace detail
+FMT_END_NAMESPACE
+#endif
+
+FMT_BEGIN_NAMESPACE
+
+template <typename...> struct disjunction : std::false_type {};
+template <typename P> struct disjunction<P> : P {};
+template <typename P1, typename... Pn>
+struct disjunction<P1, Pn...>
+    : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
+
+template <typename...> struct conjunction : std::true_type {};
+template <typename P> struct conjunction<P> : P {};
+template <typename P1, typename... Pn>
+struct conjunction<P1, Pn...>
+    : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
+
+namespace detail {
+
+FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
+  ignore_unused(condition);
+#ifdef FMT_FUZZ
+  if (condition) throw std::runtime_error("fuzzing limit reached");
+#endif
+}
+
+template <typename CharT, CharT... C> struct string_literal {
+  static constexpr CharT value[sizeof...(C)] = {C...};
+  constexpr operator basic_string_view<CharT>() const {
+    return {value, sizeof...(C)};
+  }
+};
+
+#if FMT_CPLUSPLUS < 201703L
+template <typename CharT, CharT... C>
+constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)];
+#endif
+
+template <typename Streambuf> class formatbuf : public Streambuf {
+ private:
+  using char_type = typename Streambuf::char_type;
+  using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
+  using int_type = typename Streambuf::int_type;
+  using traits_type = typename Streambuf::traits_type;
+
+  buffer<char_type>& buffer_;
+
+ public:
+  explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
+
+ protected:
+  // The put area is always empty. This makes the implementation simpler and has
+  // the advantage that the streambuf and the buffer are always in sync and
+  // sputc never writes into uninitialized memory. A disadvantage is that each
+  // call to sputc always results in a (virtual) call to overflow. There is no
+  // disadvantage here for sputn since this always results in a call to xsputn.
+
+  auto overflow(int_type ch) -> int_type override {
+    if (!traits_type::eq_int_type(ch, traits_type::eof()))
+      buffer_.push_back(static_cast<char_type>(ch));
+    return ch;
+  }
+
+  auto xsputn(const char_type* s, streamsize count) -> streamsize override {
+    buffer_.append(s, s + count);
+    return count;
+  }
+};
+
+// Implementation of std::bit_cast for pre-C++20.
+template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
+FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
+#ifdef __cpp_lib_bit_cast
+  if (is_constant_evaluated()) return std::bit_cast<To>(from);
+#endif
+  auto to = To();
+  // The cast suppresses a bogus -Wclass-memaccess on GCC.
+  std::memcpy(static_cast<void*>(&to), &from, sizeof(to));
+  return to;
+}
+
+inline auto is_big_endian() -> bool {
+#ifdef _WIN32
+  return false;
+#elif defined(__BIG_ENDIAN__)
+  return true;
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
+  return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__;
+#else
+  struct bytes {
+    char data[sizeof(int)];
+  };
+  return bit_cast<bytes>(1).data[0] == 0;
+#endif
+}
+
+class uint128_fallback {
+ private:
+  uint64_t lo_, hi_;
+
+ public:
+  constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
+  constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
+
+  constexpr uint64_t high() const noexcept { return hi_; }
+  constexpr uint64_t low() const noexcept { return lo_; }
+
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  constexpr explicit operator T() const {
+    return static_cast<T>(lo_);
+  }
+
+  friend constexpr auto operator==(const uint128_fallback& lhs,
+                                   const uint128_fallback& rhs) -> bool {
+    return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
+  }
+  friend constexpr auto operator!=(const uint128_fallback& lhs,
+                                   const uint128_fallback& rhs) -> bool {
+    return !(lhs == rhs);
+  }
+  friend constexpr auto operator>(const uint128_fallback& lhs,
+                                  const uint128_fallback& rhs) -> bool {
+    return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
+  }
+  friend constexpr auto operator|(const uint128_fallback& lhs,
+                                  const uint128_fallback& rhs)
+      -> uint128_fallback {
+    return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
+  }
+  friend constexpr auto operator&(const uint128_fallback& lhs,
+                                  const uint128_fallback& rhs)
+      -> uint128_fallback {
+    return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
+  }
+  friend constexpr auto operator~(const uint128_fallback& n)
+      -> uint128_fallback {
+    return {~n.hi_, ~n.lo_};
+  }
+  friend auto operator+(const uint128_fallback& lhs,
+                        const uint128_fallback& rhs) -> uint128_fallback {
+    auto result = uint128_fallback(lhs);
+    result += rhs;
+    return result;
+  }
+  friend auto operator*(const uint128_fallback& lhs, uint32_t rhs)
+      -> uint128_fallback {
+    FMT_ASSERT(lhs.hi_ == 0, "");
+    uint64_t hi = (lhs.lo_ >> 32) * rhs;
+    uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs;
+    uint64_t new_lo = (hi << 32) + lo;
+    return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
+  }
+  friend auto operator-(const uint128_fallback& lhs, uint64_t rhs)
+      -> uint128_fallback {
+    return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
+  }
+  FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
+    if (shift == 64) return {0, hi_};
+    if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);
+    return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
+  }
+  FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
+    if (shift == 64) return {lo_, 0};
+    if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);
+    return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
+  }
+  FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
+    return *this = *this >> shift;
+  }
+  FMT_CONSTEXPR void operator+=(uint128_fallback n) {
+    uint64_t new_lo = lo_ + n.lo_;
+    uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
+    FMT_ASSERT(new_hi >= hi_, "");
+    lo_ = new_lo;
+    hi_ = new_hi;
+  }
+  FMT_CONSTEXPR void operator&=(uint128_fallback n) {
+    lo_ &= n.lo_;
+    hi_ &= n.hi_;
+  }
+
+  FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept {
+    if (is_constant_evaluated()) {
+      lo_ += n;
+      hi_ += (lo_ < n ? 1 : 0);
+      return *this;
+    }
+#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
+    unsigned long long carry;
+    lo_ = __builtin_addcll(lo_, n, 0, &carry);
+    hi_ += carry;
+#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
+    unsigned long long result;
+    auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
+    lo_ = result;
+    hi_ += carry;
+#elif defined(_MSC_VER) && defined(_M_X64)
+    auto carry = _addcarry_u64(0, lo_, n, &lo_);
+    _addcarry_u64(carry, hi_, 0, &hi_);
+#else
+    lo_ += n;
+    hi_ += (lo_ < n ? 1 : 0);
+#endif
+    return *this;
+  }
+};
+
+using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>;
+
+#ifdef UINTPTR_MAX
+using uintptr_t = ::uintptr_t;
+#else
+using uintptr_t = uint128_t;
+#endif
+
+// Returns the largest possible value for type T. Same as
+// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
+template <typename T> constexpr auto max_value() -> T {
+  return (std::numeric_limits<T>::max)();
+}
+template <typename T> constexpr auto num_bits() -> int {
+  return std::numeric_limits<T>::digits;
+}
+// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
+template <> constexpr auto num_bits<int128_opt>() -> int { return 128; }
+template <> constexpr auto num_bits<uint128_t>() -> int { return 128; }
+
+// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
+// and 128-bit pointers to uint128_fallback.
+template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
+inline auto bit_cast(const From& from) -> To {
+  constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
+  struct data_t {
+    unsigned value[static_cast<unsigned>(size)];
+  } data = bit_cast<data_t>(from);
+  auto result = To();
+  if (const_check(is_big_endian())) {
+    for (int i = 0; i < size; ++i)
+      result = (result << num_bits<unsigned>()) | data.value[i];
+  } else {
+    for (int i = size - 1; i >= 0; --i)
+      result = (result << num_bits<unsigned>()) | data.value[i];
+  }
+  return result;
+}
+
+template <typename UInt>
+FMT_CONSTEXPR20 inline auto countl_zero_fallback(UInt n) -> int {
+  int lz = 0;
+  constexpr UInt msb_mask = static_cast<UInt>(1) << (num_bits<UInt>() - 1);
+  for (; (n & msb_mask) == 0; n <<= 1) lz++;
+  return lz;
+}
+
+FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int {
+#ifdef FMT_BUILTIN_CLZ
+  if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n);
+#endif
+  return countl_zero_fallback(n);
+}
+
+FMT_CONSTEXPR20 inline auto countl_zero(uint64_t n) -> int {
+#ifdef FMT_BUILTIN_CLZLL
+  if (!is_constant_evaluated()) return FMT_BUILTIN_CLZLL(n);
+#endif
+  return countl_zero_fallback(n);
+}
+
+FMT_INLINE void assume(bool condition) {
+  (void)condition;
+#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
+  __builtin_assume(condition);
+#elif FMT_GCC_VERSION
+  if (!condition) __builtin_unreachable();
+#endif
+}
+
+// An approximation of iterator_t for pre-C++20 systems.
+template <typename T>
+using iterator_t = decltype(std::begin(std::declval<T&>()));
+template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
+
+// A workaround for std::string not having mutable data() until C++17.
+template <typename Char>
+inline auto get_data(std::basic_string<Char>& s) -> Char* {
+  return &s[0];
+}
+template <typename Container>
+inline auto get_data(Container& c) -> typename Container::value_type* {
+  return c.data();
+}
+
+// Attempts to reserve space for n extra characters in the output range.
+// Returns a pointer to the reserved range or a reference to it.
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
+__attribute__((no_sanitize("undefined")))
+#endif
+inline auto
+reserve(std::back_insert_iterator<Container> it, size_t n) ->
+    typename Container::value_type* {
+  Container& c = get_container(it);
+  size_t size = c.size();
+  c.resize(size + n);
+  return get_data(c) + size;
+}
+
+template <typename T>
+inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T> {
+  buffer<T>& buf = get_container(it);
+  buf.try_reserve(buf.size() + n);
+  return it;
+}
+
+template <typename Iterator>
+constexpr auto reserve(Iterator& it, size_t) -> Iterator& {
+  return it;
+}
+
+template <typename OutputIt>
+using reserve_iterator =
+    remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
+
+template <typename T, typename OutputIt>
+constexpr auto to_pointer(OutputIt, size_t) -> T* {
+  return nullptr;
+}
+template <typename T> auto to_pointer(buffer_appender<T> it, size_t n) -> T* {
+  buffer<T>& buf = get_container(it);
+  auto size = buf.size();
+  if (buf.capacity() < size + n) return nullptr;
+  buf.try_resize(size + n);
+  return buf.data() + size;
+}
+
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+inline auto base_iterator(std::back_insert_iterator<Container> it,
+                          typename Container::value_type*)
+    -> std::back_insert_iterator<Container> {
+  return it;
+}
+
+template <typename Iterator>
+constexpr auto base_iterator(Iterator, Iterator it) -> Iterator {
+  return it;
+}
+
+// <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n
+// instead (#1998).
+template <typename OutputIt, typename Size, typename T>
+FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value)
+    -> OutputIt {
+  for (Size i = 0; i < count; ++i) *out++ = value;
+  return out;
+}
+template <typename T, typename Size>
+FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
+  if (is_constant_evaluated()) {
+    return fill_n<T*, Size, T>(out, count, value);
+  }
+  std::memset(out, value, to_unsigned(count));
+  return out + count;
+}
+
+#ifdef __cpp_char8_t
+using char8_type = char8_t;
+#else
+enum char8_type : unsigned char {};
+#endif
+
+template <typename OutChar, typename InputIt, typename OutputIt>
+FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end,
+                                                  OutputIt out) -> OutputIt {
+  return copy_str<OutChar>(begin, end, out);
+}
+
+// A public domain branchless UTF-8 decoder by Christopher Wellons:
+// https://github.com/skeeto/branchless-utf8
+/* Decode the next character, c, from s, reporting errors in e.
+ *
+ * Since this is a branchless decoder, four bytes will be read from the
+ * buffer regardless of the actual length of the next character. This
+ * means the buffer _must_ have at least three bytes of zero padding
+ * following the end of the data stream.
+ *
+ * Errors are reported in e, which will be non-zero if the parsed
+ * character was somehow invalid: invalid byte sequence, non-canonical
+ * encoding, or a surrogate half.
+ *
+ * The function returns a pointer to the next character. When an error
+ * occurs, this pointer will be a guess that depends on the particular
+ * error, but it will always advance at least one byte.
+ */
+FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
+    -> const char* {
+  constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
+  constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
+  constexpr const int shiftc[] = {0, 18, 12, 6, 0};
+  constexpr const int shifte[] = {0, 6, 4, 2, 0};
+
+  int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"
+      [static_cast<unsigned char>(*s) >> 3];
+  // Compute the pointer to the next character early so that the next
+  // iteration can start working on the next character. Neither Clang
+  // nor GCC figure out this reordering on their own.
+  const char* next = s + len + !len;
+
+  using uchar = unsigned char;
+
+  // Assume a four-byte character and load four bytes. Unused bits are
+  // shifted out.
+  *c = uint32_t(uchar(s[0]) & masks[len]) << 18;
+  *c |= uint32_t(uchar(s[1]) & 0x3f) << 12;
+  *c |= uint32_t(uchar(s[2]) & 0x3f) << 6;
+  *c |= uint32_t(uchar(s[3]) & 0x3f) << 0;
+  *c >>= shiftc[len];
+
+  // Accumulate the various error conditions.
+  *e = (*c < mins[len]) << 6;       // non-canonical encoding
+  *e |= ((*c >> 11) == 0x1b) << 7;  // surrogate half?
+  *e |= (*c > 0x10FFFF) << 8;       // out of range?
+  *e |= (uchar(s[1]) & 0xc0) >> 2;
+  *e |= (uchar(s[2]) & 0xc0) >> 4;
+  *e |= uchar(s[3]) >> 6;
+  *e ^= 0x2a;  // top two bits of each tail byte correct?
+  *e >>= shifte[len];
+
+  return next;
+}
+
+constexpr FMT_INLINE_VARIABLE uint32_t invalid_code_point = ~uint32_t();
+
+// Invokes f(cp, sv) for every code point cp in s with sv being the string view
+// corresponding to the code point. cp is invalid_code_point on error.
+template <typename F>
+FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
+  auto decode = [f](const char* buf_ptr, const char* ptr) {
+    auto cp = uint32_t();
+    auto error = 0;
+    auto end = utf8_decode(buf_ptr, &cp, &error);
+    bool result = f(error ? invalid_code_point : cp,
+                    string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
+    return result ? (error ? buf_ptr + 1 : end) : nullptr;
+  };
+  auto p = s.data();
+  const size_t block_size = 4;  // utf8_decode always reads blocks of 4 chars.
+  if (s.size() >= block_size) {
+    for (auto end = p + s.size() - block_size + 1; p < end;) {
+      p = decode(p, p);
+      if (!p) return;
+    }
+  }
+  if (auto num_chars_left = s.data() + s.size() - p) {
+    char buf[2 * block_size - 1] = {};
+    copy_str<char>(p, p + num_chars_left, buf);
+    const char* buf_ptr = buf;
+    do {
+      auto end = decode(buf_ptr, p);
+      if (!end) return;
+      p += end - buf_ptr;
+      buf_ptr = end;
+    } while (buf_ptr - buf < num_chars_left);
+  }
+}
+
+template <typename Char>
+inline auto compute_width(basic_string_view<Char> s) -> size_t {
+  return s.size();
+}
+
+// Computes approximate display width of a UTF-8 string.
+FMT_CONSTEXPR inline size_t compute_width(string_view s) {
+  size_t num_code_points = 0;
+  // It is not a lambda for compatibility with C++14.
+  struct count_code_points {
+    size_t* count;
+    FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool {
+      *count += detail::to_unsigned(
+          1 +
+          (cp >= 0x1100 &&
+           (cp <= 0x115f ||  // Hangul Jamo init. consonants
+            cp == 0x2329 ||  // LEFT-POINTING ANGLE BRACKET
+            cp == 0x232a ||  // RIGHT-POINTING ANGLE BRACKET
+            // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE:
+            (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) ||
+            (cp >= 0xac00 && cp <= 0xd7a3) ||    // Hangul Syllables
+            (cp >= 0xf900 && cp <= 0xfaff) ||    // CJK Compatibility Ideographs
+            (cp >= 0xfe10 && cp <= 0xfe19) ||    // Vertical Forms
+            (cp >= 0xfe30 && cp <= 0xfe6f) ||    // CJK Compatibility Forms
+            (cp >= 0xff00 && cp <= 0xff60) ||    // Fullwidth Forms
+            (cp >= 0xffe0 && cp <= 0xffe6) ||    // Fullwidth Forms
+            (cp >= 0x20000 && cp <= 0x2fffd) ||  // CJK
+            (cp >= 0x30000 && cp <= 0x3fffd) ||
+            // Miscellaneous Symbols and Pictographs + Emoticons:
+            (cp >= 0x1f300 && cp <= 0x1f64f) ||
+            // Supplemental Symbols and Pictographs:
+            (cp >= 0x1f900 && cp <= 0x1f9ff))));
+      return true;
+    }
+  };
+  // We could avoid branches by using utf8_decode directly.
+  for_each_codepoint(s, count_code_points{&num_code_points});
+  return num_code_points;
+}
+
+inline auto compute_width(basic_string_view<char8_type> s) -> size_t {
+  return compute_width(
+      string_view(reinterpret_cast<const char*>(s.data()), s.size()));
+}
+
+template <typename Char>
+inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t {
+  size_t size = s.size();
+  return n < size ? n : size;
+}
+
+// Calculates the index of the nth code point in a UTF-8 string.
+inline auto code_point_index(string_view s, size_t n) -> size_t {
+  const char* data = s.data();
+  size_t num_code_points = 0;
+  for (size_t i = 0, size = s.size(); i != size; ++i) {
+    if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i;
+  }
+  return s.size();
+}
+
+inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
+    -> size_t {
+  return code_point_index(
+      string_view(reinterpret_cast<const char*>(s.data()), s.size()), n);
+}
+
+template <typename T> struct is_integral : std::is_integral<T> {};
+template <> struct is_integral<int128_opt> : std::true_type {};
+template <> struct is_integral<uint128_t> : std::true_type {};
+
+template <typename T>
+using is_signed =
+    std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
+                                     std::is_same<T, int128_opt>::value>;
+
+template <typename T>
+using is_integer =
+    bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
+                  !std::is_same<T, char>::value &&
+                  !std::is_same<T, wchar_t>::value>;
+
+#ifndef FMT_USE_FLOAT
+#  define FMT_USE_FLOAT 1
+#endif
+#ifndef FMT_USE_DOUBLE
+#  define FMT_USE_DOUBLE 1
+#endif
+#ifndef FMT_USE_LONG_DOUBLE
+#  define FMT_USE_LONG_DOUBLE 1
+#endif
+
+#ifndef FMT_USE_FLOAT128
+#  ifdef __clang__
+// Clang emulates GCC, so it has to appear early.
+#    if FMT_HAS_INCLUDE(<quadmath.h>)
+#      define FMT_USE_FLOAT128 1
+#    endif
+#  elif defined(__GNUC__)
+// GNU C++:
+#    if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__)
+#      define FMT_USE_FLOAT128 1
+#    endif
+#  endif
+#  ifndef FMT_USE_FLOAT128
+#    define FMT_USE_FLOAT128 0
+#  endif
+#endif
+
+#if FMT_USE_FLOAT128
+using float128 = __float128;
+#else
+using float128 = void;
+#endif
+template <typename T> using is_float128 = std::is_same<T, float128>;
+
+template <typename T>
+using is_floating_point =
+    bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>;
+
+template <typename T, bool = std::is_floating_point<T>::value>
+struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 &&
+                                     sizeof(T) <= sizeof(double)> {};
+template <typename T> struct is_fast_float<T, false> : std::false_type {};
+
+template <typename T>
+using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
+
+#ifndef FMT_USE_FULL_CACHE_DRAGONBOX
+#  define FMT_USE_FULL_CACHE_DRAGONBOX 0
+#endif
+
+template <typename T>
+template <typename U>
+void buffer<T>::append(const U* begin, const U* end) {
+  while (begin != end) {
+    auto count = to_unsigned(end - begin);
+    try_reserve(size_ + count);
+    auto free_cap = capacity_ - size_;
+    if (free_cap < count) count = free_cap;
+    std::uninitialized_copy_n(begin, count, ptr_ + size_);
+    size_ += count;
+    begin += count;
+  }
+}
+
+template <typename T, typename Enable = void>
+struct is_locale : std::false_type {};
+template <typename T>
+struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
+}  // namespace detail
+
+FMT_BEGIN_EXPORT
+
+// The number of characters to store in the basic_memory_buffer object itself
+// to avoid dynamic memory allocation.
+enum { inline_buffer_size = 500 };
+
+/**
+  \rst
+  A dynamically growing memory buffer for trivially copyable/constructible types
+  with the first ``SIZE`` elements stored in the object itself.
+
+  You can use the ``memory_buffer`` type alias for ``char`` instead.
+
+  **Example**::
+
+     auto out = fmt::memory_buffer();
+     format_to(std::back_inserter(out), "The answer is {}.", 42);
+
+  This will append the following output to the ``out`` object:
+
+  .. code-block:: none
+
+     The answer is 42.
+
+  The output can be converted to an ``std::string`` with ``to_string(out)``.
+  \endrst
+ */
+template <typename T, size_t SIZE = inline_buffer_size,
+          typename Allocator = std::allocator<T>>
+class basic_memory_buffer final : public detail::buffer<T> {
+ private:
+  T store_[SIZE];
+
+  // Don't inherit from Allocator to avoid generating type_info for it.
+  FMT_NO_UNIQUE_ADDRESS Allocator alloc_;
+
+  // Deallocate memory allocated by the buffer.
+  FMT_CONSTEXPR20 void deallocate() {
+    T* data = this->data();
+    if (data != store_) alloc_.deallocate(data, this->capacity());
+  }
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t size) override {
+    detail::abort_fuzzing_if(size > 5000);
+    const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_);
+    size_t old_capacity = this->capacity();
+    size_t new_capacity = old_capacity + old_capacity / 2;
+    if (size > new_capacity)
+      new_capacity = size;
+    else if (new_capacity > max_size)
+      new_capacity = size > max_size ? size : max_size;
+    T* old_data = this->data();
+    T* new_data =
+        std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
+    // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481).
+    detail::assume(this->size() <= new_capacity);
+    // The following code doesn't throw, so the raw pointer above doesn't leak.
+    std::uninitialized_copy_n(old_data, this->size(), new_data);
+    this->set(new_data, new_capacity);
+    // deallocate must not throw according to the standard, but even if it does,
+    // the buffer already uses the new storage and will deallocate it in
+    // destructor.
+    if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
+  }
+
+ public:
+  using value_type = T;
+  using const_reference = const T&;
+
+  FMT_CONSTEXPR20 explicit basic_memory_buffer(
+      const Allocator& alloc = Allocator())
+      : alloc_(alloc) {
+    this->set(store_, SIZE);
+    if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T());
+  }
+  FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); }
+
+ private:
+  // Move data from other to this buffer.
+  FMT_CONSTEXPR20 void move(basic_memory_buffer& other) {
+    alloc_ = std::move(other.alloc_);
+    T* data = other.data();
+    size_t size = other.size(), capacity = other.capacity();
+    if (data == other.store_) {
+      this->set(store_, capacity);
+      detail::copy_str<T>(other.store_, other.store_ + size, store_);
+    } else {
+      this->set(data, capacity);
+      // Set pointer to the inline array so that delete is not called
+      // when deallocating.
+      other.set(other.store_, 0);
+      other.clear();
+    }
+    this->resize(size);
+  }
+
+ public:
+  /**
+    \rst
+    Constructs a :class:`fmt::basic_memory_buffer` object moving the content
+    of the other object to it.
+    \endrst
+   */
+  FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept {
+    move(other);
+  }
+
+  /**
+    \rst
+    Moves the content of the other ``basic_memory_buffer`` object to this one.
+    \endrst
+   */
+  auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& {
+    FMT_ASSERT(this != &other, "");
+    deallocate();
+    move(other);
+    return *this;
+  }
+
+  // Returns a copy of the allocator associated with this buffer.
+  auto get_allocator() const -> Allocator { return alloc_; }
+
+  /**
+    Resizes the buffer to contain *count* elements. If T is a POD type new
+    elements may not be initialized.
+   */
+  FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); }
+
+  /** Increases the buffer capacity to *new_capacity*. */
+  void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
+
+  // Directly append data into the buffer
+  using detail::buffer<T>::append;
+  template <typename ContiguousRange>
+  void append(const ContiguousRange& range) {
+    append(range.data(), range.data() + range.size());
+  }
+};
+
+using memory_buffer = basic_memory_buffer<char>;
+
+template <typename T, size_t SIZE, typename Allocator>
+struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
+};
+
+FMT_END_EXPORT
+namespace detail {
+FMT_API bool write_console(std::FILE* f, string_view text);
+FMT_API void print(std::FILE*, string_view);
+}  // namespace detail
+
+FMT_BEGIN_EXPORT
+
+// Suppress a misleading warning in older versions of clang.
+#if FMT_CLANG_VERSION
+#  pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+/** An error reported from a formatting function. */
+class FMT_VISIBILITY("default") format_error : public std::runtime_error {
+ public:
+  using std::runtime_error::runtime_error;
+};
+
+namespace detail_exported {
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <typename Char, size_t N> struct fixed_string {
+  constexpr fixed_string(const Char (&str)[N]) {
+    detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str),
+                                               str + N, data);
+  }
+  Char data[N] = {};
+};
+#endif
+
+// Converts a compile-time string to basic_string_view.
+template <typename Char, size_t N>
+constexpr auto compile_string_to_view(const Char (&s)[N])
+    -> basic_string_view<Char> {
+  // Remove trailing NUL character if needed. Won't be present if this is used
+  // with a raw character array (i.e. not defined as a string).
+  return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
+}
+template <typename Char>
+constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
+    -> basic_string_view<Char> {
+  return {s.data(), s.size()};
+}
+}  // namespace detail_exported
+
+class loc_value {
+ private:
+  basic_format_arg<format_context> value_;
+
+ public:
+  template <typename T, FMT_ENABLE_IF(!detail::is_float128<T>::value)>
+  loc_value(T value) : value_(detail::make_arg<format_context>(value)) {}
+
+  template <typename T, FMT_ENABLE_IF(detail::is_float128<T>::value)>
+  loc_value(T) {}
+
+  template <typename Visitor> auto visit(Visitor&& vis) -> decltype(vis(0)) {
+    return visit_format_arg(vis, value_);
+  }
+};
+
+// A locale facet that formats values in UTF-8.
+// It is parameterized on the locale to avoid the heavy <locale> include.
+template <typename Locale> class format_facet : public Locale::facet {
+ private:
+  std::string separator_;
+  std::string grouping_;
+  std::string decimal_point_;
+
+ protected:
+  virtual auto do_put(appender out, loc_value val,
+                      const format_specs<>& specs) const -> bool;
+
+ public:
+  static FMT_API typename Locale::id id;
+
+  explicit format_facet(Locale& loc);
+  explicit format_facet(string_view sep = "",
+                        std::initializer_list<unsigned char> g = {3},
+                        std::string decimal_point = ".")
+      : separator_(sep.data(), sep.size()),
+        grouping_(g.begin(), g.end()),
+        decimal_point_(decimal_point) {}
+
+  auto put(appender out, loc_value val, const format_specs<>& specs) const
+      -> bool {
+    return do_put(out, val, specs);
+  }
+};
+
+namespace detail {
+
+// Returns true if value is negative, false otherwise.
+// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
+template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
+constexpr auto is_negative(T value) -> bool {
+  return value < 0;
+}
+template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
+constexpr auto is_negative(T) -> bool {
+  return false;
+}
+
+template <typename T>
+FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool {
+  if (std::is_same<T, float>()) return FMT_USE_FLOAT;
+  if (std::is_same<T, double>()) return FMT_USE_DOUBLE;
+  if (std::is_same<T, long double>()) return FMT_USE_LONG_DOUBLE;
+  return true;
+}
+
+// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
+// represent all values of an integral type T.
+template <typename T>
+using uint32_or_64_or_128_t =
+    conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
+                  uint32_t,
+                  conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
+template <typename T>
+using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
+
+#define FMT_POWERS_OF_10(factor)                                             \
+  factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
+      (factor)*1000000, (factor)*10000000, (factor)*100000000,               \
+      (factor)*1000000000
+
+// Converts value in the range [0, 100) to a string.
+constexpr const char* digits2(size_t value) {
+  // GCC generates slightly better code when value is pointer-size.
+  return &"0001020304050607080910111213141516171819"
+         "2021222324252627282930313233343536373839"
+         "4041424344454647484950515253545556575859"
+         "6061626364656667686970717273747576777879"
+         "8081828384858687888990919293949596979899"[value * 2];
+}
+
+// Sign is a template parameter to workaround a bug in gcc 4.8.
+template <typename Char, typename Sign> constexpr Char sign(Sign s) {
+#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604
+  static_assert(std::is_same<Sign, sign_t>::value, "");
+#endif
+  return static_cast<Char>("\0-+ "[s]);
+}
+
+template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
+  int count = 1;
+  for (;;) {
+    // Integer division is slow so do it for a group of four digits instead
+    // of for every digit. The idea comes from the talk by Alexandrescu
+    // "Three Optimization Tips for C++". See speed-test for a comparison.
+    if (n < 10) return count;
+    if (n < 100) return count + 1;
+    if (n < 1000) return count + 2;
+    if (n < 10000) return count + 3;
+    n /= 10000u;
+    count += 4;
+  }
+}
+#if FMT_USE_INT128
+FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int {
+  return count_digits_fallback(n);
+}
+#endif
+
+#ifdef FMT_BUILTIN_CLZLL
+// It is a separate function rather than a part of count_digits to workaround
+// the lack of static constexpr in constexpr functions.
+inline auto do_count_digits(uint64_t n) -> int {
+  // This has comparable performance to the version by Kendall Willets
+  // (https://github.com/fmtlib/format-benchmark/blob/master/digits10)
+  // but uses smaller tables.
+  // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
+  static constexpr uint8_t bsr2log10[] = {
+      1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,
+      6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10, 10,
+      10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
+      15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
+  auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
+  static constexpr const uint64_t zero_or_powers_of_10[] = {
+      0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
+      10000000000000000000ULL};
+  return t - (n < zero_or_powers_of_10[t]);
+}
+#endif
+
+// Returns the number of decimal digits in n. Leading zeros are not counted
+// except for n == 0 in which case count_digits returns 1.
+FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
+#ifdef FMT_BUILTIN_CLZLL
+  if (!is_constant_evaluated()) {
+    return do_count_digits(n);
+  }
+#endif
+  return count_digits_fallback(n);
+}
+
+// Counts the number of digits in n. BITS = log2(radix).
+template <int BITS, typename UInt>
+FMT_CONSTEXPR auto count_digits(UInt n) -> int {
+#ifdef FMT_BUILTIN_CLZ
+  if (!is_constant_evaluated() && num_bits<UInt>() == 32)
+    return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1;
+#endif
+  // Lambda avoids unreachable code warnings from NVHPC.
+  return [](UInt m) {
+    int num_digits = 0;
+    do {
+      ++num_digits;
+    } while ((m >>= BITS) != 0);
+    return num_digits;
+  }(n);
+}
+
+#ifdef FMT_BUILTIN_CLZ
+// It is a separate function rather than a part of count_digits to workaround
+// the lack of static constexpr in constexpr functions.
+FMT_INLINE auto do_count_digits(uint32_t n) -> int {
+// An optimization by Kendall Willets from https://bit.ly/3uOIQrB.
+// This increments the upper 32 bits (log10(T) - 1) when >= T is added.
+#  define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T)
+  static constexpr uint64_t table[] = {
+      FMT_INC(0),          FMT_INC(0),          FMT_INC(0),           // 8
+      FMT_INC(10),         FMT_INC(10),         FMT_INC(10),          // 64
+      FMT_INC(100),        FMT_INC(100),        FMT_INC(100),         // 512
+      FMT_INC(1000),       FMT_INC(1000),       FMT_INC(1000),        // 4096
+      FMT_INC(10000),      FMT_INC(10000),      FMT_INC(10000),       // 32k
+      FMT_INC(100000),     FMT_INC(100000),     FMT_INC(100000),      // 256k
+      FMT_INC(1000000),    FMT_INC(1000000),    FMT_INC(1000000),     // 2048k
+      FMT_INC(10000000),   FMT_INC(10000000),   FMT_INC(10000000),    // 16M
+      FMT_INC(100000000),  FMT_INC(100000000),  FMT_INC(100000000),   // 128M
+      FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000),  // 1024M
+      FMT_INC(1000000000), FMT_INC(1000000000)                        // 4B
+  };
+  auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31];
+  return static_cast<int>((n + inc) >> 32);
+}
+#endif
+
+// Optional version of count_digits for better performance on 32-bit platforms.
+FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
+#ifdef FMT_BUILTIN_CLZ
+  if (!is_constant_evaluated()) {
+    return do_count_digits(n);
+  }
+#endif
+  return count_digits_fallback(n);
+}
+
+template <typename Int> constexpr auto digits10() noexcept -> int {
+  return std::numeric_limits<Int>::digits10;
+}
+template <> constexpr auto digits10<int128_opt>() noexcept -> int { return 38; }
+template <> constexpr auto digits10<uint128_t>() noexcept -> int { return 38; }
+
+template <typename Char> struct thousands_sep_result {
+  std::string grouping;
+  Char thousands_sep;
+};
+
+template <typename Char>
+FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>;
+template <typename Char>
+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char> {
+  auto result = thousands_sep_impl<char>(loc);
+  return {result.grouping, Char(result.thousands_sep)};
+}
+template <>
+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t> {
+  return thousands_sep_impl<wchar_t>(loc);
+}
+
+template <typename Char>
+FMT_API auto decimal_point_impl(locale_ref loc) -> Char;
+template <typename Char> inline auto decimal_point(locale_ref loc) -> Char {
+  return Char(decimal_point_impl<char>(loc));
+}
+template <> inline auto decimal_point(locale_ref loc) -> wchar_t {
+  return decimal_point_impl<wchar_t>(loc);
+}
+
+// Compares two characters for equality.
+template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
+  return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
+}
+inline auto equal2(const char* lhs, const char* rhs) -> bool {
+  return memcmp(lhs, rhs, 2) == 0;
+}
+
+// Copies two characters from src to dst.
+template <typename Char>
+FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) {
+  if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) {
+    memcpy(dst, src, 2);
+    return;
+  }
+  *dst++ = static_cast<Char>(*src++);
+  *dst = static_cast<Char>(*src);
+}
+
+template <typename Iterator> struct format_decimal_result {
+  Iterator begin;
+  Iterator end;
+};
+
+// Formats a decimal unsigned integer value writing into out pointing to a
+// buffer of specified size. The caller must ensure that the buffer is large
+// enough.
+template <typename Char, typename UInt>
+FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size)
+    -> format_decimal_result<Char*> {
+  FMT_ASSERT(size >= count_digits(value), "invalid digit count");
+  out += size;
+  Char* end = out;
+  while (value >= 100) {
+    // Integer division is slow so do it for a group of two digits instead
+    // of for every digit. The idea comes from the talk by Alexandrescu
+    // "Three Optimization Tips for C++". See speed-test for a comparison.
+    out -= 2;
+    copy2(out, digits2(static_cast<size_t>(value % 100)));
+    value /= 100;
+  }
+  if (value < 10) {
+    *--out = static_cast<Char>('0' + value);
+    return {out, end};
+  }
+  out -= 2;
+  copy2(out, digits2(static_cast<size_t>(value)));
+  return {out, end};
+}
+
+template <typename Char, typename UInt, typename Iterator,
+          FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
+FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size)
+    -> format_decimal_result<Iterator> {
+  // Buffer is large enough to hold all digits (digits10 + 1).
+  Char buffer[digits10<UInt>() + 1] = {};
+  auto end = format_decimal(buffer, value, size).end;
+  return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
+}
+
+template <unsigned BASE_BITS, typename Char, typename UInt>
+FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits,
+                               bool upper = false) -> Char* {
+  buffer += num_digits;
+  Char* end = buffer;
+  do {
+    const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
+    unsigned digit = static_cast<unsigned>(value & ((1 << BASE_BITS) - 1));
+    *--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
+                                                : digits[digit]);
+  } while ((value >>= BASE_BITS) != 0);
+  return end;
+}
+
+template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
+FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits,
+                                      bool upper = false) -> It {
+  if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
+    format_uint<BASE_BITS>(ptr, value, num_digits, upper);
+    return out;
+  }
+  // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
+  char buffer[num_bits<UInt>() / BASE_BITS + 1];
+  format_uint<BASE_BITS>(buffer, value, num_digits, upper);
+  return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out);
+}
+
+// A converter from UTF-8 to UTF-16.
+class utf8_to_utf16 {
+ private:
+  basic_memory_buffer<wchar_t> buffer_;
+
+ public:
+  FMT_API explicit utf8_to_utf16(string_view s);
+  operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
+  auto size() const -> size_t { return buffer_.size() - 1; }
+  auto c_str() const -> const wchar_t* { return &buffer_[0]; }
+  auto str() const -> std::wstring { return {&buffer_[0], size()}; }
+};
+
+enum class to_utf8_error_policy { abort, replace };
+
+// A converter from UTF-16/UTF-32 (host endian) to UTF-8.
+template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
+ private:
+  Buffer buffer_;
+
+ public:
+  to_utf8() {}
+  explicit to_utf8(basic_string_view<WChar> s,
+                   to_utf8_error_policy policy = to_utf8_error_policy::abort) {
+    static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4,
+                  "Expect utf16 or utf32");
+    if (!convert(s, policy))
+      FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16"
+                                                      : "invalid utf32"));
+  }
+  operator string_view() const { return string_view(&buffer_[0], size()); }
+  size_t size() const { return buffer_.size() - 1; }
+  const char* c_str() const { return &buffer_[0]; }
+  std::string str() const { return std::string(&buffer_[0], size()); }
+
+  // Performs conversion returning a bool instead of throwing exception on
+  // conversion error. This method may still throw in case of memory allocation
+  // error.
+  bool convert(basic_string_view<WChar> s,
+               to_utf8_error_policy policy = to_utf8_error_policy::abort) {
+    if (!convert(buffer_, s, policy)) return false;
+    buffer_.push_back(0);
+    return true;
+  }
+  static bool convert(
+      Buffer& buf, basic_string_view<WChar> s,
+      to_utf8_error_policy policy = to_utf8_error_policy::abort) {
+    for (auto p = s.begin(); p != s.end(); ++p) {
+      uint32_t c = static_cast<uint32_t>(*p);
+      if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) {
+        // Handle a surrogate pair.
+        ++p;
+        if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
+          if (policy == to_utf8_error_policy::abort) return false;
+          buf.append(string_view("\xEF\xBF\xBD"));
+          --p;
+        } else {
+          c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
+        }
+      } else if (c < 0x80) {
+        buf.push_back(static_cast<char>(c));
+      } else if (c < 0x800) {
+        buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
+        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+      } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
+        buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
+        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+      } else if (c >= 0x10000 && c <= 0x10ffff) {
+        buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
+        buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
+        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+      } else {
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
+inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
+#if FMT_USE_INT128
+  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
+  return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
+#elif defined(_MSC_VER) && defined(_M_X64)
+  auto hi = uint64_t();
+  auto lo = _umul128(x, y, &hi);
+  return {hi, lo};
+#else
+  const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
+
+  uint64_t a = x >> 32;
+  uint64_t b = x & mask;
+  uint64_t c = y >> 32;
+  uint64_t d = y & mask;
+
+  uint64_t ac = a * c;
+  uint64_t bc = b * c;
+  uint64_t ad = a * d;
+  uint64_t bd = b * d;
+
+  uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
+
+  return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
+          (intermediate << 32) + (bd & mask)};
+#endif
+}
+
+namespace dragonbox {
+// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
+// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
+inline int floor_log10_pow2(int e) noexcept {
+  FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
+  static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
+  return (e * 315653) >> 20;
+}
+
+inline int floor_log2_pow10(int e) noexcept {
+  FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
+  return (e * 1741647) >> 19;
+}
+
+// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
+inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
+#if FMT_USE_INT128
+  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
+  return static_cast<uint64_t>(p >> 64);
+#elif defined(_MSC_VER) && defined(_M_X64)
+  return __umulh(x, y);
+#else
+  return umul128(x, y).high();
+#endif
+}
+
+// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
+// 128-bit unsigned integer.
+inline uint128_fallback umul192_upper128(uint64_t x,
+                                         uint128_fallback y) noexcept {
+  uint128_fallback r = umul128(x, y.high());
+  r += umul128_upper64(x, y.low());
+  return r;
+}
+
+FMT_API uint128_fallback get_cached_power(int k) noexcept;
+
+// Type-specific information that Dragonbox uses.
+template <typename T, typename Enable = void> struct float_info;
+
+template <> struct float_info<float> {
+  using carrier_uint = uint32_t;
+  static const int exponent_bits = 8;
+  static const int kappa = 1;
+  static const int big_divisor = 100;
+  static const int small_divisor = 10;
+  static const int min_k = -31;
+  static const int max_k = 46;
+  static const int shorter_interval_tie_lower_threshold = -35;
+  static const int shorter_interval_tie_upper_threshold = -35;
+};
+
+template <> struct float_info<double> {
+  using carrier_uint = uint64_t;
+  static const int exponent_bits = 11;
+  static const int kappa = 2;
+  static const int big_divisor = 1000;
+  static const int small_divisor = 100;
+  static const int min_k = -292;
+  static const int max_k = 341;
+  static const int shorter_interval_tie_lower_threshold = -77;
+  static const int shorter_interval_tie_upper_threshold = -77;
+};
+
+// An 80- or 128-bit floating point number.
+template <typename T>
+struct float_info<T, enable_if_t<std::numeric_limits<T>::digits == 64 ||
+                                 std::numeric_limits<T>::digits == 113 ||
+                                 is_float128<T>::value>> {
+  using carrier_uint = detail::uint128_t;
+  static const int exponent_bits = 15;
+};
+
+// A double-double floating point number.
+template <typename T>
+struct float_info<T, enable_if_t<is_double_double<T>::value>> {
+  using carrier_uint = detail::uint128_t;
+};
+
+template <typename T> struct decimal_fp {
+  using significand_type = typename float_info<T>::carrier_uint;
+  significand_type significand;
+  int exponent;
+};
+
+template <typename T> FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
+}  // namespace dragonbox
+
+// Returns true iff Float has the implicit bit which is not stored.
+template <typename Float> constexpr bool has_implicit_bit() {
+  // An 80-bit FP number has a 64-bit significand an no implicit bit.
+  return std::numeric_limits<Float>::digits != 64;
+}
+
+// Returns the number of significand bits stored in Float. The implicit bit is
+// not counted since it is not stored.
+template <typename Float> constexpr int num_significand_bits() {
+  // std::numeric_limits may not support __float128.
+  return is_float128<Float>() ? 112
+                              : (std::numeric_limits<Float>::digits -
+                                 (has_implicit_bit<Float>() ? 1 : 0));
+}
+
+template <typename Float>
+constexpr auto exponent_mask() ->
+    typename dragonbox::float_info<Float>::carrier_uint {
+  using float_uint = typename dragonbox::float_info<Float>::carrier_uint;
+  return ((float_uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1)
+         << num_significand_bits<Float>();
+}
+template <typename Float> constexpr auto exponent_bias() -> int {
+  // std::numeric_limits may not support __float128.
+  return is_float128<Float>() ? 16383
+                              : std::numeric_limits<Float>::max_exponent - 1;
+}
+
+// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
+template <typename Char, typename It>
+FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It {
+  FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
+  if (exp < 0) {
+    *it++ = static_cast<Char>('-');
+    exp = -exp;
+  } else {
+    *it++ = static_cast<Char>('+');
+  }
+  if (exp >= 100) {
+    const char* top = digits2(to_unsigned(exp / 100));
+    if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
+    *it++ = static_cast<Char>(top[1]);
+    exp %= 100;
+  }
+  const char* d = digits2(to_unsigned(exp));
+  *it++ = static_cast<Char>(d[0]);
+  *it++ = static_cast<Char>(d[1]);
+  return it;
+}
+
+// A floating-point number f * pow(2, e) where F is an unsigned type.
+template <typename F> struct basic_fp {
+  F f;
+  int e;
+
+  static constexpr const int num_significand_bits =
+      static_cast<int>(sizeof(F) * num_bits<unsigned char>());
+
+  constexpr basic_fp() : f(0), e(0) {}
+  constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
+
+  // Constructs fp from an IEEE754 floating-point number.
+  template <typename Float> FMT_CONSTEXPR basic_fp(Float n) { assign(n); }
+
+  // Assigns n to this and return true iff predecessor is closer than successor.
+  template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
+  FMT_CONSTEXPR auto assign(Float n) -> bool {
+    static_assert(std::numeric_limits<Float>::digits <= 113, "unsupported FP");
+    // Assume Float is in the format [sign][exponent][significand].
+    using carrier_uint = typename dragonbox::float_info<Float>::carrier_uint;
+    const auto num_float_significand_bits =
+        detail::num_significand_bits<Float>();
+    const auto implicit_bit = carrier_uint(1) << num_float_significand_bits;
+    const auto significand_mask = implicit_bit - 1;
+    auto u = bit_cast<carrier_uint>(n);
+    f = static_cast<F>(u & significand_mask);
+    auto biased_e = static_cast<int>((u & exponent_mask<Float>()) >>
+                                     num_float_significand_bits);
+    // The predecessor is closer if n is a normalized power of 2 (f == 0)
+    // other than the smallest normalized number (biased_e > 1).
+    auto is_predecessor_closer = f == 0 && biased_e > 1;
+    if (biased_e == 0)
+      biased_e = 1;  // Subnormals use biased exponent 1 (min exponent).
+    else if (has_implicit_bit<Float>())
+      f += static_cast<F>(implicit_bit);
+    e = biased_e - exponent_bias<Float>() - num_float_significand_bits;
+    if (!has_implicit_bit<Float>()) ++e;
+    return is_predecessor_closer;
+  }
+
+  template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
+  FMT_CONSTEXPR auto assign(Float n) -> bool {
+    static_assert(std::numeric_limits<double>::is_iec559, "unsupported FP");
+    return assign(static_cast<double>(n));
+  }
+};
+
+using fp = basic_fp<unsigned long long>;
+
+// Normalizes the value converted from double and multiplied by (1 << SHIFT).
+template <int SHIFT = 0, typename F>
+FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
+  // Handle subnormals.
+  const auto implicit_bit = F(1) << num_significand_bits<double>();
+  const auto shifted_implicit_bit = implicit_bit << SHIFT;
+  while ((value.f & shifted_implicit_bit) == 0) {
+    value.f <<= 1;
+    --value.e;
+  }
+  // Subtract 1 to account for hidden bit.
+  const auto offset = basic_fp<F>::num_significand_bits -
+                      num_significand_bits<double>() - SHIFT - 1;
+  value.f <<= offset;
+  value.e -= offset;
+  return value;
+}
+
+// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
+FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
+#if FMT_USE_INT128
+  auto product = static_cast<__uint128_t>(lhs) * rhs;
+  auto f = static_cast<uint64_t>(product >> 64);
+  return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
+#else
+  // Multiply 32-bit parts of significands.
+  uint64_t mask = (1ULL << 32) - 1;
+  uint64_t a = lhs >> 32, b = lhs & mask;
+  uint64_t c = rhs >> 32, d = rhs & mask;
+  uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
+  // Compute mid 64-bit of result and round.
+  uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
+  return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
+#endif
+}
+
+FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
+  return {multiply(x.f, y.f), x.e + y.e + 64};
+}
+
+template <typename T = void> struct basic_data {
+  // For checking rounding thresholds.
+  // The kth entry is chosen to be the smallest integer such that the
+  // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
+  static constexpr uint32_t fractional_part_rounding_thresholds[8] = {
+      2576980378U,  // ceil(2^31 + 2^32/10^1)
+      2190433321U,  // ceil(2^31 + 2^32/10^2)
+      2151778616U,  // ceil(2^31 + 2^32/10^3)
+      2147913145U,  // ceil(2^31 + 2^32/10^4)
+      2147526598U,  // ceil(2^31 + 2^32/10^5)
+      2147487943U,  // ceil(2^31 + 2^32/10^6)
+      2147484078U,  // ceil(2^31 + 2^32/10^7)
+      2147483691U   // ceil(2^31 + 2^32/10^8)
+  };
+};
+// This is a struct rather than an alias to avoid shadowing warnings in gcc.
+struct data : basic_data<> {};
+
+#if FMT_CPLUSPLUS < 201703L
+template <typename T>
+constexpr uint32_t basic_data<T>::fractional_part_rounding_thresholds[];
+#endif
+
+template <typename T, bool doublish = num_bits<T>() == num_bits<double>()>
+using convert_float_result =
+    conditional_t<std::is_same<T, float>::value || doublish, double, T>;
+
+template <typename T>
+constexpr auto convert_float(T value) -> convert_float_result<T> {
+  return static_cast<convert_float_result<T>>(value);
+}
+
+template <typename OutputIt, typename Char>
+FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
+                                     const fill_t<Char>& fill) -> OutputIt {
+  auto fill_size = fill.size();
+  if (fill_size == 1) return detail::fill_n(it, n, fill[0]);
+  auto data = fill.data();
+  for (size_t i = 0; i < n; ++i)
+    it = copy_str<Char>(data, data + fill_size, it);
+  return it;
+}
+
+// Writes the output of f, padded according to format specifications in specs.
+// size: output size in code units.
+// width: output display width in (terminal) column positions.
+template <align::type align = align::left, typename OutputIt, typename Char,
+          typename F>
+FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs<Char>& specs,
+                                size_t size, size_t width, F&& f) -> OutputIt {
+  static_assert(align == align::left || align == align::right, "");
+  unsigned spec_width = to_unsigned(specs.width);
+  size_t padding = spec_width > width ? spec_width - width : 0;
+  // Shifts are encoded as string literals because static constexpr is not
+  // supported in constexpr functions.
+  auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
+  size_t left_padding = padding >> shifts[specs.align];
+  size_t right_padding = padding - left_padding;
+  auto it = reserve(out, size + padding * specs.fill.size());
+  if (left_padding != 0) it = fill(it, left_padding, specs.fill);
+  it = f(it);
+  if (right_padding != 0) it = fill(it, right_padding, specs.fill);
+  return base_iterator(out, it);
+}
+
+template <align::type align = align::left, typename OutputIt, typename Char,
+          typename F>
+constexpr auto write_padded(OutputIt out, const format_specs<Char>& specs,
+                            size_t size, F&& f) -> OutputIt {
+  return write_padded<align>(out, specs, size, size, f);
+}
+
+template <align::type align = align::left, typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
+                               const format_specs<Char>& specs) -> OutputIt {
+  return write_padded<align>(
+      out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
+        const char* data = bytes.data();
+        return copy_str<Char>(data, data + bytes.size(), it);
+      });
+}
+
+template <typename Char, typename OutputIt, typename UIntPtr>
+auto write_ptr(OutputIt out, UIntPtr value, const format_specs<Char>* specs)
+    -> OutputIt {
+  int num_digits = count_digits<4>(value);
+  auto size = to_unsigned(num_digits) + size_t(2);
+  auto write = [=](reserve_iterator<OutputIt> it) {
+    *it++ = static_cast<Char>('0');
+    *it++ = static_cast<Char>('x');
+    return format_uint<4, Char>(it, value, num_digits);
+  };
+  return specs ? write_padded<align::right>(out, *specs, size, write)
+               : base_iterator(out, write(reserve(out, size)));
+}
+
+// Returns true iff the code point cp is printable.
+FMT_API auto is_printable(uint32_t cp) -> bool;
+
+inline auto needs_escape(uint32_t cp) -> bool {
+  return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
+         !is_printable(cp);
+}
+
+template <typename Char> struct find_escape_result {
+  const Char* begin;
+  const Char* end;
+  uint32_t cp;
+};
+
+template <typename Char>
+using make_unsigned_char =
+    typename conditional_t<std::is_integral<Char>::value,
+                           std::make_unsigned<Char>,
+                           type_identity<uint32_t>>::type;
+
+template <typename Char>
+auto find_escape(const Char* begin, const Char* end)
+    -> find_escape_result<Char> {
+  for (; begin != end; ++begin) {
+    uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin);
+    if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
+    if (needs_escape(cp)) return {begin, begin + 1, cp};
+  }
+  return {begin, nullptr, 0};
+}
+
+inline auto find_escape(const char* begin, const char* end)
+    -> find_escape_result<char> {
+  if (!is_utf8()) return find_escape<char>(begin, end);
+  auto result = find_escape_result<char>{end, nullptr, 0};
+  for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
+                     [&](uint32_t cp, string_view sv) {
+                       if (needs_escape(cp)) {
+                         result = {sv.begin(), sv.end(), cp};
+                         return false;
+                       }
+                       return true;
+                     });
+  return result;
+}
+
+#define FMT_STRING_IMPL(s, base, explicit)                                    \
+  [] {                                                                        \
+    /* Use the hidden visibility as a workaround for a GCC bug (#1973). */    \
+    /* Use a macro-like name to avoid shadowing warnings. */                  \
+    struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base {               \
+      using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
+      FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit                                 \
+      operator fmt::basic_string_view<char_type>() const {                    \
+        return fmt::detail_exported::compile_string_to_view<char_type>(s);    \
+      }                                                                       \
+    };                                                                        \
+    return FMT_COMPILE_STRING();                                              \
+  }()
+
+/**
+  \rst
+  Constructs a compile-time format string from a string literal *s*.
+
+  **Example**::
+
+    // A compile-time error because 'd' is an invalid specifier for strings.
+    std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
+  \endrst
+ */
+#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, )
+
+template <size_t width, typename Char, typename OutputIt>
+auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {
+  *out++ = static_cast<Char>('\\');
+  *out++ = static_cast<Char>(prefix);
+  Char buf[width];
+  fill_n(buf, width, static_cast<Char>('0'));
+  format_uint<4>(buf, cp, width);
+  return copy_str<Char>(buf, buf + width, out);
+}
+
+template <typename OutputIt, typename Char>
+auto write_escaped_cp(OutputIt out, const find_escape_result<Char>& escape)
+    -> OutputIt {
+  auto c = static_cast<Char>(escape.cp);
+  switch (escape.cp) {
+  case '\n':
+    *out++ = static_cast<Char>('\\');
+    c = static_cast<Char>('n');
+    break;
+  case '\r':
+    *out++ = static_cast<Char>('\\');
+    c = static_cast<Char>('r');
+    break;
+  case '\t':
+    *out++ = static_cast<Char>('\\');
+    c = static_cast<Char>('t');
+    break;
+  case '"':
+    FMT_FALLTHROUGH;
+  case '\'':
+    FMT_FALLTHROUGH;
+  case '\\':
+    *out++ = static_cast<Char>('\\');
+    break;
+  default:
+    if (escape.cp < 0x100) {
+      return write_codepoint<2, Char>(out, 'x', escape.cp);
+    }
+    if (escape.cp < 0x10000) {
+      return write_codepoint<4, Char>(out, 'u', escape.cp);
+    }
+    if (escape.cp < 0x110000) {
+      return write_codepoint<8, Char>(out, 'U', escape.cp);
+    }
+    for (Char escape_char : basic_string_view<Char>(
+             escape.begin, to_unsigned(escape.end - escape.begin))) {
+      out = write_codepoint<2, Char>(out, 'x',
+                                     static_cast<uint32_t>(escape_char) & 0xFF);
+    }
+    return out;
+  }
+  *out++ = c;
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
+    -> OutputIt {
+  *out++ = static_cast<Char>('"');
+  auto begin = str.begin(), end = str.end();
+  do {
+    auto escape = find_escape(begin, end);
+    out = copy_str<Char>(begin, escape.begin, out);
+    begin = escape.end;
+    if (!begin) break;
+    out = write_escaped_cp<OutputIt, Char>(out, escape);
+  } while (begin != end);
+  *out++ = static_cast<Char>('"');
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
+  *out++ = static_cast<Char>('\'');
+  if ((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) ||
+      v == static_cast<Char>('\'')) {
+    out = write_escaped_cp(
+        out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
+  } else {
+    *out++ = v;
+  }
+  *out++ = static_cast<Char>('\'');
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
+                              const format_specs<Char>& specs) -> OutputIt {
+  bool is_debug = specs.type == presentation_type::debug;
+  return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
+    if (is_debug) return write_escaped_char(it, value);
+    *it++ = value;
+    return it;
+  });
+}
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, Char value,
+                         const format_specs<Char>& specs, locale_ref loc = {})
+    -> OutputIt {
+  // char is formatted as unsigned char for consistency across platforms.
+  using unsigned_type =
+      conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>;
+  return check_char_specs(specs)
+             ? write_char(out, value, specs)
+             : write(out, static_cast<unsigned_type>(value), specs, loc);
+}
+
+// Data for write_int that doesn't depend on output iterator type. It is used to
+// avoid template code bloat.
+template <typename Char> struct write_int_data {
+  size_t size;
+  size_t padding;
+
+  FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
+                               const format_specs<Char>& specs)
+      : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
+    if (specs.align == align::numeric) {
+      auto width = to_unsigned(specs.width);
+      if (width > size) {
+        padding = width - size;
+        size = width;
+      }
+    } else if (specs.precision > num_digits) {
+      size = (prefix >> 24) + to_unsigned(specs.precision);
+      padding = to_unsigned(specs.precision - num_digits);
+    }
+  }
+};
+
+// Writes an integer in the format
+//   <left-padding><prefix><numeric-padding><digits><right-padding>
+// where <digits> are written by write_digits(it).
+// prefix contains chars in three lower bytes and the size in the fourth byte.
+template <typename OutputIt, typename Char, typename W>
+FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
+                                        unsigned prefix,
+                                        const format_specs<Char>& specs,
+                                        W write_digits) -> OutputIt {
+  // Slightly faster check for specs.width == 0 && specs.precision == -1.
+  if ((specs.width | (specs.precision + 1)) == 0) {
+    auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
+    if (prefix != 0) {
+      for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+        *it++ = static_cast<Char>(p & 0xff);
+    }
+    return base_iterator(out, write_digits(it));
+  }
+  auto data = write_int_data<Char>(num_digits, prefix, specs);
+  return write_padded<align::right>(
+      out, specs, data.size, [=](reserve_iterator<OutputIt> it) {
+        for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+          *it++ = static_cast<Char>(p & 0xff);
+        it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
+        return write_digits(it);
+      });
+}
+
+template <typename Char> class digit_grouping {
+ private:
+  std::string grouping_;
+  std::basic_string<Char> thousands_sep_;
+
+  struct next_state {
+    std::string::const_iterator group;
+    int pos;
+  };
+  next_state initial_state() const { return {grouping_.begin(), 0}; }
+
+  // Returns the next digit group separator position.
+  int next(next_state& state) const {
+    if (thousands_sep_.empty()) return max_value<int>();
+    if (state.group == grouping_.end()) return state.pos += grouping_.back();
+    if (*state.group <= 0 || *state.group == max_value<char>())
+      return max_value<int>();
+    state.pos += *state.group++;
+    return state.pos;
+  }
+
+ public:
+  explicit digit_grouping(locale_ref loc, bool localized = true) {
+    if (!localized) return;
+    auto sep = thousands_sep<Char>(loc);
+    grouping_ = sep.grouping;
+    if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep);
+  }
+  digit_grouping(std::string grouping, std::basic_string<Char> sep)
+      : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {}
+
+  bool has_separator() const { return !thousands_sep_.empty(); }
+
+  int count_separators(int num_digits) const {
+    int count = 0;
+    auto state = initial_state();
+    while (num_digits > next(state)) ++count;
+    return count;
+  }
+
+  // Applies grouping to digits and write the output to out.
+  template <typename Out, typename C>
+  Out apply(Out out, basic_string_view<C> digits) const {
+    auto num_digits = static_cast<int>(digits.size());
+    auto separators = basic_memory_buffer<int>();
+    separators.push_back(0);
+    auto state = initial_state();
+    while (int i = next(state)) {
+      if (i >= num_digits) break;
+      separators.push_back(i);
+    }
+    for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
+         i < num_digits; ++i) {
+      if (num_digits - i == separators[sep_index]) {
+        out =
+            copy_str<Char>(thousands_sep_.data(),
+                           thousands_sep_.data() + thousands_sep_.size(), out);
+        --sep_index;
+      }
+      *out++ = static_cast<Char>(digits[to_unsigned(i)]);
+    }
+    return out;
+  }
+};
+
+// Writes a decimal integer with digit grouping.
+template <typename OutputIt, typename UInt, typename Char>
+auto write_int(OutputIt out, UInt value, unsigned prefix,
+               const format_specs<Char>& specs,
+               const digit_grouping<Char>& grouping) -> OutputIt {
+  static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
+  int num_digits = count_digits(value);
+  char digits[40];
+  format_decimal(digits, value, num_digits);
+  unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits +
+                              grouping.count_separators(num_digits));
+  return write_padded<align::right>(
+      out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
+        if (prefix != 0) {
+          char sign = static_cast<char>(prefix);
+          *it++ = static_cast<Char>(sign);
+        }
+        return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
+      });
+}
+
+// Writes a localized value.
+FMT_API auto write_loc(appender out, loc_value value,
+                       const format_specs<>& specs, locale_ref loc) -> bool;
+template <typename OutputIt, typename Char>
+inline auto write_loc(OutputIt, loc_value, const format_specs<Char>&,
+                      locale_ref) -> bool {
+  return false;
+}
+
+FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
+  prefix |= prefix != 0 ? value << 8 : value;
+  prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
+}
+
+template <typename UInt> struct write_int_arg {
+  UInt abs_value;
+  unsigned prefix;
+};
+
+template <typename T>
+FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
+    -> write_int_arg<uint32_or_64_or_128_t<T>> {
+  auto prefix = 0u;
+  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
+  if (is_negative(value)) {
+    prefix = 0x01000000 | '-';
+    abs_value = 0 - abs_value;
+  } else {
+    constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
+                                            0x1000000u | ' '};
+    prefix = prefixes[sign];
+  }
+  return {abs_value, prefix};
+}
+
+template <typename Char = char> struct loc_writer {
+  buffer_appender<Char> out;
+  const format_specs<Char>& specs;
+  std::basic_string<Char> sep;
+  std::string grouping;
+  std::basic_string<Char> decimal_point;
+
+  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+  auto operator()(T value) -> bool {
+    auto arg = make_write_int_arg(value, specs.sign);
+    write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
+              specs, digit_grouping<Char>(grouping, sep));
+    return true;
+  }
+
+  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+  auto operator()(T) -> bool {
+    return false;
+  }
+};
+
+template <typename Char, typename OutputIt, typename T>
+FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
+                                        const format_specs<Char>& specs,
+                                        locale_ref) -> OutputIt {
+  static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
+  auto abs_value = arg.abs_value;
+  auto prefix = arg.prefix;
+  switch (specs.type) {
+  case presentation_type::none:
+  case presentation_type::dec: {
+    auto num_digits = count_digits(abs_value);
+    return write_int(
+        out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
+          return format_decimal<Char>(it, abs_value, num_digits).end;
+        });
+  }
+  case presentation_type::hex_lower:
+  case presentation_type::hex_upper: {
+    bool upper = specs.type == presentation_type::hex_upper;
+    if (specs.alt)
+      prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
+    int num_digits = count_digits<4>(abs_value);
+    return write_int(
+        out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
+          return format_uint<4, Char>(it, abs_value, num_digits, upper);
+        });
+  }
+  case presentation_type::bin_lower:
+  case presentation_type::bin_upper: {
+    bool upper = specs.type == presentation_type::bin_upper;
+    if (specs.alt)
+      prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
+    int num_digits = count_digits<1>(abs_value);
+    return write_int(out, num_digits, prefix, specs,
+                     [=](reserve_iterator<OutputIt> it) {
+                       return format_uint<1, Char>(it, abs_value, num_digits);
+                     });
+  }
+  case presentation_type::oct: {
+    int num_digits = count_digits<3>(abs_value);
+    // Octal prefix '0' is counted as a digit, so only add it if precision
+    // is not greater than the number of digits.
+    if (specs.alt && specs.precision <= num_digits && abs_value != 0)
+      prefix_append(prefix, '0');
+    return write_int(out, num_digits, prefix, specs,
+                     [=](reserve_iterator<OutputIt> it) {
+                       return format_uint<3, Char>(it, abs_value, num_digits);
+                     });
+  }
+  case presentation_type::chr:
+    return write_char(out, static_cast<Char>(abs_value), specs);
+  default:
+    throw_format_error("invalid format specifier");
+  }
+  return out;
+}
+template <typename Char, typename OutputIt, typename T>
+FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(
+    OutputIt out, write_int_arg<T> arg, const format_specs<Char>& specs,
+    locale_ref loc) -> OutputIt {
+  return write_int(out, arg, specs, loc);
+}
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_integral<T>::value &&
+                        !std::is_same<T, bool>::value &&
+                        std::is_same<OutputIt, buffer_appender<Char>>::value)>
+FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
+                                    const format_specs<Char>& specs,
+                                    locale_ref loc) -> OutputIt {
+  if (specs.localized && write_loc(out, value, specs, loc)) return out;
+  return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
+                            loc);
+}
+// An inlined version of write used in format string compilation.
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_integral<T>::value &&
+                        !std::is_same<T, bool>::value &&
+                        !std::is_same<OutputIt, buffer_appender<Char>>::value)>
+FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
+                                    const format_specs<Char>& specs,
+                                    locale_ref loc) -> OutputIt {
+  if (specs.localized && write_loc(out, value, specs, loc)) return out;
+  return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
+}
+
+// An output iterator that counts the number of objects written to it and
+// discards them.
+class counting_iterator {
+ private:
+  size_t count_;
+
+ public:
+  using iterator_category = std::output_iterator_tag;
+  using difference_type = std::ptrdiff_t;
+  using pointer = void;
+  using reference = void;
+  FMT_UNCHECKED_ITERATOR(counting_iterator);
+
+  struct value_type {
+    template <typename T> FMT_CONSTEXPR void operator=(const T&) {}
+  };
+
+  FMT_CONSTEXPR counting_iterator() : count_(0) {}
+
+  FMT_CONSTEXPR size_t count() const { return count_; }
+
+  FMT_CONSTEXPR counting_iterator& operator++() {
+    ++count_;
+    return *this;
+  }
+  FMT_CONSTEXPR counting_iterator operator++(int) {
+    auto it = *this;
+    ++*this;
+    return it;
+  }
+
+  FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it,
+                                                   difference_type n) {
+    it.count_ += static_cast<size_t>(n);
+    return it;
+  }
+
+  FMT_CONSTEXPR value_type operator*() const { return {}; }
+};
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
+                         const format_specs<Char>& specs) -> OutputIt {
+  auto data = s.data();
+  auto size = s.size();
+  if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
+    size = code_point_index(s, to_unsigned(specs.precision));
+  bool is_debug = specs.type == presentation_type::debug;
+  size_t width = 0;
+  if (specs.width != 0) {
+    if (is_debug)
+      width = write_escaped_string(counting_iterator{}, s).count();
+    else
+      width = compute_width(basic_string_view<Char>(data, size));
+  }
+  return write_padded(out, specs, size, width,
+                      [=](reserve_iterator<OutputIt> it) {
+                        if (is_debug) return write_escaped_string(it, s);
+                        return copy_str<Char>(data, data + size, it);
+                      });
+}
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out,
+                         basic_string_view<type_identity_t<Char>> s,
+                         const format_specs<Char>& specs, locale_ref)
+    -> OutputIt {
+  return write(out, s, specs);
+}
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
+                         const format_specs<Char>& specs, locale_ref)
+    -> OutputIt {
+  return specs.type != presentation_type::pointer
+             ? write(out, basic_string_view<Char>(s), specs, {})
+             : write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_integral<T>::value &&
+                        !std::is_same<T, bool>::value &&
+                        !std::is_same<T, Char>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
+  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
+  bool negative = is_negative(value);
+  // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
+  if (negative) abs_value = ~abs_value + 1;
+  int num_digits = count_digits(abs_value);
+  auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
+  auto it = reserve(out, size);
+  if (auto ptr = to_pointer<Char>(it, size)) {
+    if (negative) *ptr++ = static_cast<Char>('-');
+    format_decimal<Char>(ptr, abs_value, num_digits);
+    return out;
+  }
+  if (negative) *it++ = static_cast<Char>('-');
+  it = format_decimal<Char>(it, abs_value, num_digits).end;
+  return base_iterator(out, it);
+}
+
+// DEPRECATED!
+template <typename Char>
+FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
+                               format_specs<Char>& specs) -> const Char* {
+  FMT_ASSERT(begin != end, "");
+  auto align = align::none;
+  auto p = begin + code_point_length(begin);
+  if (end - p <= 0) p = begin;
+  for (;;) {
+    switch (to_ascii(*p)) {
+    case '<':
+      align = align::left;
+      break;
+    case '>':
+      align = align::right;
+      break;
+    case '^':
+      align = align::center;
+      break;
+    }
+    if (align != align::none) {
+      if (p != begin) {
+        auto c = *begin;
+        if (c == '}') return begin;
+        if (c == '{') {
+          throw_format_error("invalid fill character '{'");
+          return begin;
+        }
+        specs.fill = {begin, to_unsigned(p - begin)};
+        begin = p + 1;
+      } else {
+        ++begin;
+      }
+      break;
+    } else if (p == begin) {
+      break;
+    }
+    p = begin;
+  }
+  specs.align = align;
+  return begin;
+}
+
+// A floating-point presentation format.
+enum class float_format : unsigned char {
+  general,  // General: exponent notation or fixed point based on magnitude.
+  exp,      // Exponent notation with the default precision of 6, e.g. 1.2e-3.
+  fixed,    // Fixed point with the default precision of 6, e.g. 0.0012.
+  hex
+};
+
+struct float_specs {
+  int precision;
+  float_format format : 8;
+  sign_t sign : 8;
+  bool upper : 1;
+  bool locale : 1;
+  bool binary32 : 1;
+  bool showpoint : 1;
+};
+
+template <typename ErrorHandler = error_handler, typename Char>
+FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs,
+                                         ErrorHandler&& eh = {})
+    -> float_specs {
+  auto result = float_specs();
+  result.showpoint = specs.alt;
+  result.locale = specs.localized;
+  switch (specs.type) {
+  case presentation_type::none:
+    result.format = float_format::general;
+    break;
+  case presentation_type::general_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::general_lower:
+    result.format = float_format::general;
+    break;
+  case presentation_type::exp_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::exp_lower:
+    result.format = float_format::exp;
+    result.showpoint |= specs.precision != 0;
+    break;
+  case presentation_type::fixed_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::fixed_lower:
+    result.format = float_format::fixed;
+    result.showpoint |= specs.precision != 0;
+    break;
+  case presentation_type::hexfloat_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::hexfloat_lower:
+    result.format = float_format::hex;
+    break;
+  default:
+    eh.on_error("invalid format specifier");
+    break;
+  }
+  return result;
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
+                                     format_specs<Char> specs,
+                                     const float_specs& fspecs) -> OutputIt {
+  auto str =
+      isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
+  constexpr size_t str_size = 3;
+  auto sign = fspecs.sign;
+  auto size = str_size + (sign ? 1 : 0);
+  // Replace '0'-padding with space for non-finite values.
+  const bool is_zero_fill =
+      specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0');
+  if (is_zero_fill) specs.fill[0] = static_cast<Char>(' ');
+  return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) {
+    if (sign) *it++ = detail::sign<Char>(sign);
+    return copy_str<Char>(str, str + str_size, it);
+  });
+}
+
+// A decimal floating-point number significand * pow(10, exp).
+struct big_decimal_fp {
+  const char* significand;
+  int significand_size;
+  int exponent;
+};
+
+constexpr auto get_significand_size(const big_decimal_fp& f) -> int {
+  return f.significand_size;
+}
+template <typename T>
+inline auto get_significand_size(const dragonbox::decimal_fp<T>& f) -> int {
+  return count_digits(f.significand);
+}
+
+template <typename Char, typename OutputIt>
+constexpr auto write_significand(OutputIt out, const char* significand,
+                                 int significand_size) -> OutputIt {
+  return copy_str<Char>(significand, significand + significand_size, out);
+}
+template <typename Char, typename OutputIt, typename UInt>
+inline auto write_significand(OutputIt out, UInt significand,
+                              int significand_size) -> OutputIt {
+  return format_decimal<Char>(out, significand, significand_size).end;
+}
+template <typename Char, typename OutputIt, typename T, typename Grouping>
+FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
+                                       int significand_size, int exponent,
+                                       const Grouping& grouping) -> OutputIt {
+  if (!grouping.has_separator()) {
+    out = write_significand<Char>(out, significand, significand_size);
+    return detail::fill_n(out, exponent, static_cast<Char>('0'));
+  }
+  auto buffer = memory_buffer();
+  write_significand<char>(appender(buffer), significand, significand_size);
+  detail::fill_n(appender(buffer), exponent, '0');
+  return grouping.apply(out, string_view(buffer.data(), buffer.size()));
+}
+
+template <typename Char, typename UInt,
+          FMT_ENABLE_IF(std::is_integral<UInt>::value)>
+inline auto write_significand(Char* out, UInt significand, int significand_size,
+                              int integral_size, Char decimal_point) -> Char* {
+  if (!decimal_point)
+    return format_decimal(out, significand, significand_size).end;
+  out += significand_size + 1;
+  Char* end = out;
+  int floating_size = significand_size - integral_size;
+  for (int i = floating_size / 2; i > 0; --i) {
+    out -= 2;
+    copy2(out, digits2(static_cast<std::size_t>(significand % 100)));
+    significand /= 100;
+  }
+  if (floating_size % 2 != 0) {
+    *--out = static_cast<Char>('0' + significand % 10);
+    significand /= 10;
+  }
+  *--out = decimal_point;
+  format_decimal(out - integral_size, significand, integral_size);
+  return end;
+}
+
+template <typename OutputIt, typename UInt, typename Char,
+          FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
+inline auto write_significand(OutputIt out, UInt significand,
+                              int significand_size, int integral_size,
+                              Char decimal_point) -> OutputIt {
+  // Buffer is large enough to hold digits (digits10 + 1) and a decimal point.
+  Char buffer[digits10<UInt>() + 2];
+  auto end = write_significand(buffer, significand, significand_size,
+                               integral_size, decimal_point);
+  return detail::copy_str_noinline<Char>(buffer, end, out);
+}
+
+template <typename OutputIt, typename Char>
+FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand,
+                                     int significand_size, int integral_size,
+                                     Char decimal_point) -> OutputIt {
+  out = detail::copy_str_noinline<Char>(significand,
+                                        significand + integral_size, out);
+  if (!decimal_point) return out;
+  *out++ = decimal_point;
+  return detail::copy_str_noinline<Char>(significand + integral_size,
+                                         significand + significand_size, out);
+}
+
+template <typename OutputIt, typename Char, typename T, typename Grouping>
+FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
+                                       int significand_size, int integral_size,
+                                       Char decimal_point,
+                                       const Grouping& grouping) -> OutputIt {
+  if (!grouping.has_separator()) {
+    return write_significand(out, significand, significand_size, integral_size,
+                             decimal_point);
+  }
+  auto buffer = basic_memory_buffer<Char>();
+  write_significand(buffer_appender<Char>(buffer), significand,
+                    significand_size, integral_size, decimal_point);
+  grouping.apply(
+      out, basic_string_view<Char>(buffer.data(), to_unsigned(integral_size)));
+  return detail::copy_str_noinline<Char>(buffer.data() + integral_size,
+                                         buffer.end(), out);
+}
+
+template <typename OutputIt, typename DecimalFP, typename Char,
+          typename Grouping = digit_grouping<Char>>
+FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
+                                    const format_specs<Char>& specs,
+                                    float_specs fspecs, locale_ref loc)
+    -> OutputIt {
+  auto significand = f.significand;
+  int significand_size = get_significand_size(f);
+  const Char zero = static_cast<Char>('0');
+  auto sign = fspecs.sign;
+  size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
+  using iterator = reserve_iterator<OutputIt>;
+
+  Char decimal_point =
+      fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
+
+  int output_exp = f.exponent + significand_size - 1;
+  auto use_exp_format = [=]() {
+    if (fspecs.format == float_format::exp) return true;
+    if (fspecs.format != float_format::general) return false;
+    // Use the fixed notation if the exponent is in [exp_lower, exp_upper),
+    // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
+    const int exp_lower = -4, exp_upper = 16;
+    return output_exp < exp_lower ||
+           output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper);
+  };
+  if (use_exp_format()) {
+    int num_zeros = 0;
+    if (fspecs.showpoint) {
+      num_zeros = fspecs.precision - significand_size;
+      if (num_zeros < 0) num_zeros = 0;
+      size += to_unsigned(num_zeros);
+    } else if (significand_size == 1) {
+      decimal_point = Char();
+    }
+    auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
+    int exp_digits = 2;
+    if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
+
+    size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
+    char exp_char = fspecs.upper ? 'E' : 'e';
+    auto write = [=](iterator it) {
+      if (sign) *it++ = detail::sign<Char>(sign);
+      // Insert a decimal point after the first digit and add an exponent.
+      it = write_significand(it, significand, significand_size, 1,
+                             decimal_point);
+      if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero);
+      *it++ = static_cast<Char>(exp_char);
+      return write_exponent<Char>(output_exp, it);
+    };
+    return specs.width > 0 ? write_padded<align::right>(out, specs, size, write)
+                           : base_iterator(out, write(reserve(out, size)));
+  }
+
+  int exp = f.exponent + significand_size;
+  if (f.exponent >= 0) {
+    // 1234e5 -> 123400000[.0+]
+    size += to_unsigned(f.exponent);
+    int num_zeros = fspecs.precision - exp;
+    abort_fuzzing_if(num_zeros > 5000);
+    if (fspecs.showpoint) {
+      ++size;
+      if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0;
+      if (num_zeros > 0) size += to_unsigned(num_zeros);
+    }
+    auto grouping = Grouping(loc, fspecs.locale);
+    size += to_unsigned(grouping.count_separators(exp));
+    return write_padded<align::right>(out, specs, size, [&](iterator it) {
+      if (sign) *it++ = detail::sign<Char>(sign);
+      it = write_significand<Char>(it, significand, significand_size,
+                                   f.exponent, grouping);
+      if (!fspecs.showpoint) return it;
+      *it++ = decimal_point;
+      return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
+    });
+  } else if (exp > 0) {
+    // 1234e-2 -> 12.34[0+]
+    int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
+    size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
+    auto grouping = Grouping(loc, fspecs.locale);
+    size += to_unsigned(grouping.count_separators(exp));
+    return write_padded<align::right>(out, specs, size, [&](iterator it) {
+      if (sign) *it++ = detail::sign<Char>(sign);
+      it = write_significand(it, significand, significand_size, exp,
+                             decimal_point, grouping);
+      return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
+    });
+  }
+  // 1234e-6 -> 0.001234
+  int num_zeros = -exp;
+  if (significand_size == 0 && fspecs.precision >= 0 &&
+      fspecs.precision < num_zeros) {
+    num_zeros = fspecs.precision;
+  }
+  bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint;
+  size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
+  return write_padded<align::right>(out, specs, size, [&](iterator it) {
+    if (sign) *it++ = detail::sign<Char>(sign);
+    *it++ = zero;
+    if (!pointy) return it;
+    *it++ = decimal_point;
+    it = detail::fill_n(it, num_zeros, zero);
+    return write_significand<Char>(it, significand, significand_size);
+  });
+}
+
+template <typename Char> class fallback_digit_grouping {
+ public:
+  constexpr fallback_digit_grouping(locale_ref, bool) {}
+
+  constexpr bool has_separator() const { return false; }
+
+  constexpr int count_separators(int) const { return 0; }
+
+  template <typename Out, typename C>
+  constexpr Out apply(Out out, basic_string_view<C>) const {
+    return out;
+  }
+};
+
+template <typename OutputIt, typename DecimalFP, typename Char>
+FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
+                                 const format_specs<Char>& specs,
+                                 float_specs fspecs, locale_ref loc)
+    -> OutputIt {
+  if (is_constant_evaluated()) {
+    return do_write_float<OutputIt, DecimalFP, Char,
+                          fallback_digit_grouping<Char>>(out, f, specs, fspecs,
+                                                         loc);
+  } else {
+    return do_write_float(out, f, specs, fspecs, loc);
+  }
+}
+
+template <typename T> constexpr bool isnan(T value) {
+  return !(value >= value);  // std::isnan doesn't support __float128.
+}
+
+template <typename T, typename Enable = void>
+struct has_isfinite : std::false_type {};
+
+template <typename T>
+struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
+    : std::true_type {};
+
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&&
+                                        has_isfinite<T>::value)>
+FMT_CONSTEXPR20 bool isfinite(T value) {
+  constexpr T inf = T(std::numeric_limits<double>::infinity());
+  if (is_constant_evaluated())
+    return !detail::isnan(value) && value < inf && value > -inf;
+  return std::isfinite(value);
+}
+template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)>
+FMT_CONSTEXPR bool isfinite(T value) {
+  T inf = T(std::numeric_limits<double>::infinity());
+  // std::isfinite doesn't support __float128.
+  return !detail::isnan(value) && value < inf && value > -inf;
+}
+
+template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
+FMT_INLINE FMT_CONSTEXPR bool signbit(T value) {
+  if (is_constant_evaluated()) {
+#ifdef __cpp_if_constexpr
+    if constexpr (std::numeric_limits<double>::is_iec559) {
+      auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
+      return (bits >> (num_bits<uint64_t>() - 1)) != 0;
+    }
+#endif
+  }
+  return std::signbit(static_cast<double>(value));
+}
+
+inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
+  // Adjust fixed precision by exponent because it is relative to decimal
+  // point.
+  if (exp10 > 0 && precision > max_value<int>() - exp10)
+    FMT_THROW(format_error("number is too big"));
+  precision += exp10;
+}
+
+class bigint {
+ private:
+  // A bigint is stored as an array of bigits (big digits), with bigit at index
+  // 0 being the least significant one.
+  using bigit = uint32_t;
+  using double_bigit = uint64_t;
+  enum { bigits_capacity = 32 };
+  basic_memory_buffer<bigit, bigits_capacity> bigits_;
+  int exp_;
+
+  FMT_CONSTEXPR20 bigit operator[](int index) const {
+    return bigits_[to_unsigned(index)];
+  }
+  FMT_CONSTEXPR20 bigit& operator[](int index) {
+    return bigits_[to_unsigned(index)];
+  }
+
+  static constexpr const int bigit_bits = num_bits<bigit>();
+
+  friend struct formatter<bigint>;
+
+  FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
+    auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
+    (*this)[index] = static_cast<bigit>(result);
+    borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
+  }
+
+  FMT_CONSTEXPR20 void remove_leading_zeros() {
+    int num_bigits = static_cast<int>(bigits_.size()) - 1;
+    while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
+    bigits_.resize(to_unsigned(num_bigits + 1));
+  }
+
+  // Computes *this -= other assuming aligned bigints and *this >= other.
+  FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
+    FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
+    FMT_ASSERT(compare(*this, other) >= 0, "");
+    bigit borrow = 0;
+    int i = other.exp_ - exp_;
+    for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
+      subtract_bigits(i, other.bigits_[j], borrow);
+    while (borrow > 0) subtract_bigits(i, 0, borrow);
+    remove_leading_zeros();
+  }
+
+  FMT_CONSTEXPR20 void multiply(uint32_t value) {
+    const double_bigit wide_value = value;
+    bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      double_bigit result = bigits_[i] * wide_value + carry;
+      bigits_[i] = static_cast<bigit>(result);
+      carry = static_cast<bigit>(result >> bigit_bits);
+    }
+    if (carry != 0) bigits_.push_back(carry);
+  }
+
+  template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
+                                         std::is_same<UInt, uint128_t>::value)>
+  FMT_CONSTEXPR20 void multiply(UInt value) {
+    using half_uint =
+        conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>;
+    const int shift = num_bits<half_uint>() - bigit_bits;
+    const UInt lower = static_cast<half_uint>(value);
+    const UInt upper = value >> num_bits<half_uint>();
+    UInt carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      UInt result = lower * bigits_[i] + static_cast<bigit>(carry);
+      carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) +
+              (carry >> bigit_bits);
+      bigits_[i] = static_cast<bigit>(result);
+    }
+    while (carry != 0) {
+      bigits_.push_back(static_cast<bigit>(carry));
+      carry >>= bigit_bits;
+    }
+  }
+
+  template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
+                                         std::is_same<UInt, uint128_t>::value)>
+  FMT_CONSTEXPR20 void assign(UInt n) {
+    size_t num_bigits = 0;
+    do {
+      bigits_[num_bigits++] = static_cast<bigit>(n);
+      n >>= bigit_bits;
+    } while (n != 0);
+    bigits_.resize(num_bigits);
+    exp_ = 0;
+  }
+
+ public:
+  FMT_CONSTEXPR20 bigint() : exp_(0) {}
+  explicit bigint(uint64_t n) { assign(n); }
+
+  bigint(const bigint&) = delete;
+  void operator=(const bigint&) = delete;
+
+  FMT_CONSTEXPR20 void assign(const bigint& other) {
+    auto size = other.bigits_.size();
+    bigits_.resize(size);
+    auto data = other.bigits_.data();
+    copy_str<bigit>(data, data + size, bigits_.data());
+    exp_ = other.exp_;
+  }
+
+  template <typename Int> FMT_CONSTEXPR20 void operator=(Int n) {
+    FMT_ASSERT(n > 0, "");
+    assign(uint64_or_128_t<Int>(n));
+  }
+
+  FMT_CONSTEXPR20 int num_bigits() const {
+    return static_cast<int>(bigits_.size()) + exp_;
+  }
+
+  FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
+    FMT_ASSERT(shift >= 0, "");
+    exp_ += shift / bigit_bits;
+    shift %= bigit_bits;
+    if (shift == 0) return *this;
+    bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      bigit c = bigits_[i] >> (bigit_bits - shift);
+      bigits_[i] = (bigits_[i] << shift) + carry;
+      carry = c;
+    }
+    if (carry != 0) bigits_.push_back(carry);
+    return *this;
+  }
+
+  template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
+    FMT_ASSERT(value > 0, "");
+    multiply(uint32_or_64_or_128_t<Int>(value));
+    return *this;
+  }
+
+  friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
+    int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
+    if (num_lhs_bigits != num_rhs_bigits)
+      return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
+    int i = static_cast<int>(lhs.bigits_.size()) - 1;
+    int j = static_cast<int>(rhs.bigits_.size()) - 1;
+    int end = i - j;
+    if (end < 0) end = 0;
+    for (; i >= end; --i, --j) {
+      bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
+      if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
+    }
+    if (i != j) return i > j ? 1 : -1;
+    return 0;
+  }
+
+  // Returns compare(lhs1 + lhs2, rhs).
+  friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
+                                         const bigint& rhs) {
+    auto minimum = [](int a, int b) { return a < b ? a : b; };
+    auto maximum = [](int a, int b) { return a > b ? a : b; };
+    int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
+    int num_rhs_bigits = rhs.num_bigits();
+    if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
+    if (max_lhs_bigits > num_rhs_bigits) return 1;
+    auto get_bigit = [](const bigint& n, int i) -> bigit {
+      return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
+    };
+    double_bigit borrow = 0;
+    int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_);
+    for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
+      double_bigit sum =
+          static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
+      bigit rhs_bigit = get_bigit(rhs, i);
+      if (sum > rhs_bigit + borrow) return 1;
+      borrow = rhs_bigit + borrow - sum;
+      if (borrow > 1) return -1;
+      borrow <<= bigit_bits;
+    }
+    return borrow != 0 ? -1 : 0;
+  }
+
+  // Assigns pow(10, exp) to this bigint.
+  FMT_CONSTEXPR20 void assign_pow10(int exp) {
+    FMT_ASSERT(exp >= 0, "");
+    if (exp == 0) return *this = 1;
+    // Find the top bit.
+    int bitmask = 1;
+    while (exp >= bitmask) bitmask <<= 1;
+    bitmask >>= 1;
+    // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
+    // repeated squaring and multiplication.
+    *this = 5;
+    bitmask >>= 1;
+    while (bitmask != 0) {
+      square();
+      if ((exp & bitmask) != 0) *this *= 5;
+      bitmask >>= 1;
+    }
+    *this <<= exp;  // Multiply by pow(2, exp) by shifting.
+  }
+
+  FMT_CONSTEXPR20 void square() {
+    int num_bigits = static_cast<int>(bigits_.size());
+    int num_result_bigits = 2 * num_bigits;
+    basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
+    bigits_.resize(to_unsigned(num_result_bigits));
+    auto sum = uint128_t();
+    for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
+      // Compute bigit at position bigit_index of the result by adding
+      // cross-product terms n[i] * n[j] such that i + j == bigit_index.
+      for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
+        // Most terms are multiplied twice which can be optimized in the future.
+        sum += static_cast<double_bigit>(n[i]) * n[j];
+      }
+      (*this)[bigit_index] = static_cast<bigit>(sum);
+      sum >>= num_bits<bigit>();  // Compute the carry.
+    }
+    // Do the same for the top half.
+    for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
+         ++bigit_index) {
+      for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
+        sum += static_cast<double_bigit>(n[i++]) * n[j--];
+      (*this)[bigit_index] = static_cast<bigit>(sum);
+      sum >>= num_bits<bigit>();
+    }
+    remove_leading_zeros();
+    exp_ *= 2;
+  }
+
+  // If this bigint has a bigger exponent than other, adds trailing zero to make
+  // exponents equal. This simplifies some operations such as subtraction.
+  FMT_CONSTEXPR20 void align(const bigint& other) {
+    int exp_difference = exp_ - other.exp_;
+    if (exp_difference <= 0) return;
+    int num_bigits = static_cast<int>(bigits_.size());
+    bigits_.resize(to_unsigned(num_bigits + exp_difference));
+    for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
+      bigits_[j] = bigits_[i];
+    std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
+    exp_ -= exp_difference;
+  }
+
+  // Divides this bignum by divisor, assigning the remainder to this and
+  // returning the quotient.
+  FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
+    FMT_ASSERT(this != &divisor, "");
+    if (compare(*this, divisor) < 0) return 0;
+    FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
+    align(divisor);
+    int quotient = 0;
+    do {
+      subtract_aligned(divisor);
+      ++quotient;
+    } while (compare(*this, divisor) >= 0);
+    return quotient;
+  }
+};
+
+// format_dragon flags.
+enum dragon {
+  predecessor_closer = 1,
+  fixup = 2,  // Run fixup to correct exp10 which can be off by one.
+  fixed = 4,
+};
+
+// Formats a floating-point number using a variation of the Fixed-Precision
+// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
+// https://fmt.dev/papers/p372-steele.pdf.
+FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
+                                          unsigned flags, int num_digits,
+                                          buffer<char>& buf, int& exp10) {
+  bigint numerator;    // 2 * R in (FPP)^2.
+  bigint denominator;  // 2 * S in (FPP)^2.
+  // lower and upper are differences between value and corresponding boundaries.
+  bigint lower;             // (M^- in (FPP)^2).
+  bigint upper_store;       // upper's value if different from lower.
+  bigint* upper = nullptr;  // (M^+ in (FPP)^2).
+  // Shift numerator and denominator by an extra bit or two (if lower boundary
+  // is closer) to make lower and upper integers. This eliminates multiplication
+  // by 2 during later computations.
+  bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0;
+  int shift = is_predecessor_closer ? 2 : 1;
+  if (value.e >= 0) {
+    numerator = value.f;
+    numerator <<= value.e + shift;
+    lower = 1;
+    lower <<= value.e;
+    if (is_predecessor_closer) {
+      upper_store = 1;
+      upper_store <<= value.e + 1;
+      upper = &upper_store;
+    }
+    denominator.assign_pow10(exp10);
+    denominator <<= shift;
+  } else if (exp10 < 0) {
+    numerator.assign_pow10(-exp10);
+    lower.assign(numerator);
+    if (is_predecessor_closer) {
+      upper_store.assign(numerator);
+      upper_store <<= 1;
+      upper = &upper_store;
+    }
+    numerator *= value.f;
+    numerator <<= shift;
+    denominator = 1;
+    denominator <<= shift - value.e;
+  } else {
+    numerator = value.f;
+    numerator <<= shift;
+    denominator.assign_pow10(exp10);
+    denominator <<= shift - value.e;
+    lower = 1;
+    if (is_predecessor_closer) {
+      upper_store = 1ULL << 1;
+      upper = &upper_store;
+    }
+  }
+  int even = static_cast<int>((value.f & 1) == 0);
+  if (!upper) upper = &lower;
+  bool shortest = num_digits < 0;
+  if ((flags & dragon::fixup) != 0) {
+    if (add_compare(numerator, *upper, denominator) + even <= 0) {
+      --exp10;
+      numerator *= 10;
+      if (num_digits < 0) {
+        lower *= 10;
+        if (upper != &lower) *upper *= 10;
+      }
+    }
+    if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1);
+  }
+  // Invariant: value == (numerator / denominator) * pow(10, exp10).
+  if (shortest) {
+    // Generate the shortest representation.
+    num_digits = 0;
+    char* data = buf.data();
+    for (;;) {
+      int digit = numerator.divmod_assign(denominator);
+      bool low = compare(numerator, lower) - even < 0;  // numerator <[=] lower.
+      // numerator + upper >[=] pow10:
+      bool high = add_compare(numerator, *upper, denominator) + even > 0;
+      data[num_digits++] = static_cast<char>('0' + digit);
+      if (low || high) {
+        if (!low) {
+          ++data[num_digits - 1];
+        } else if (high) {
+          int result = add_compare(numerator, numerator, denominator);
+          // Round half to even.
+          if (result > 0 || (result == 0 && (digit % 2) != 0))
+            ++data[num_digits - 1];
+        }
+        buf.try_resize(to_unsigned(num_digits));
+        exp10 -= num_digits - 1;
+        return;
+      }
+      numerator *= 10;
+      lower *= 10;
+      if (upper != &lower) *upper *= 10;
+    }
+  }
+  // Generate the given number of digits.
+  exp10 -= num_digits - 1;
+  if (num_digits <= 0) {
+    denominator *= 10;
+    auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
+    buf.push_back(digit);
+    return;
+  }
+  buf.try_resize(to_unsigned(num_digits));
+  for (int i = 0; i < num_digits - 1; ++i) {
+    int digit = numerator.divmod_assign(denominator);
+    buf[i] = static_cast<char>('0' + digit);
+    numerator *= 10;
+  }
+  int digit = numerator.divmod_assign(denominator);
+  auto result = add_compare(numerator, numerator, denominator);
+  if (result > 0 || (result == 0 && (digit % 2) != 0)) {
+    if (digit == 9) {
+      const auto overflow = '0' + 10;
+      buf[num_digits - 1] = overflow;
+      // Propagate the carry.
+      for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) {
+        buf[i] = '0';
+        ++buf[i - 1];
+      }
+      if (buf[0] == overflow) {
+        buf[0] = '1';
+        if ((flags & dragon::fixed) != 0) buf.push_back('0');
+        else ++exp10;
+      }
+      return;
+    }
+    ++digit;
+  }
+  buf[num_digits - 1] = static_cast<char>('0' + digit);
+}
+
+// Formats a floating-point number using the hexfloat format.
+template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
+FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
+                                     float_specs specs, buffer<char>& buf) {
+  // float is passed as double to reduce the number of instantiations and to
+  // simplify implementation.
+  static_assert(!std::is_same<Float, float>::value, "");
+
+  using info = dragonbox::float_info<Float>;
+
+  // Assume Float is in the format [sign][exponent][significand].
+  using carrier_uint = typename info::carrier_uint;
+
+  constexpr auto num_float_significand_bits =
+      detail::num_significand_bits<Float>();
+
+  basic_fp<carrier_uint> f(value);
+  f.e += num_float_significand_bits;
+  if (!has_implicit_bit<Float>()) --f.e;
+
+  constexpr auto num_fraction_bits =
+      num_float_significand_bits + (has_implicit_bit<Float>() ? 1 : 0);
+  constexpr auto num_xdigits = (num_fraction_bits + 3) / 4;
+
+  constexpr auto leading_shift = ((num_xdigits - 1) * 4);
+  const auto leading_mask = carrier_uint(0xF) << leading_shift;
+  const auto leading_xdigit =
+      static_cast<uint32_t>((f.f & leading_mask) >> leading_shift);
+  if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1);
+
+  int print_xdigits = num_xdigits - 1;
+  if (precision >= 0 && print_xdigits > precision) {
+    const int shift = ((print_xdigits - precision - 1) * 4);
+    const auto mask = carrier_uint(0xF) << shift;
+    const auto v = static_cast<uint32_t>((f.f & mask) >> shift);
+
+    if (v >= 8) {
+      const auto inc = carrier_uint(1) << (shift + 4);
+      f.f += inc;
+      f.f &= ~(inc - 1);
+    }
+
+    // Check long double overflow
+    if (!has_implicit_bit<Float>()) {
+      const auto implicit_bit = carrier_uint(1) << num_float_significand_bits;
+      if ((f.f & implicit_bit) == implicit_bit) {
+        f.f >>= 4;
+        f.e += 4;
+      }
+    }
+
+    print_xdigits = precision;
+  }
+
+  char xdigits[num_bits<carrier_uint>() / 4];
+  detail::fill_n(xdigits, sizeof(xdigits), '0');
+  format_uint<4>(xdigits, f.f, num_xdigits, specs.upper);
+
+  // Remove zero tail
+  while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits;
+
+  buf.push_back('0');
+  buf.push_back(specs.upper ? 'X' : 'x');
+  buf.push_back(xdigits[0]);
+  if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision)
+    buf.push_back('.');
+  buf.append(xdigits + 1, xdigits + 1 + print_xdigits);
+  for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0');
+
+  buf.push_back(specs.upper ? 'P' : 'p');
+
+  uint32_t abs_e;
+  if (f.e < 0) {
+    buf.push_back('-');
+    abs_e = static_cast<uint32_t>(-f.e);
+  } else {
+    buf.push_back('+');
+    abs_e = static_cast<uint32_t>(f.e);
+  }
+  format_decimal<char>(appender(buf), abs_e, detail::count_digits(abs_e));
+}
+
+template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
+FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
+                                     float_specs specs, buffer<char>& buf) {
+  format_hexfloat(static_cast<double>(value), precision, specs, buf);
+}
+
+template <typename Float>
+FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
+                                  buffer<char>& buf) -> int {
+  // float is passed as double to reduce the number of instantiations.
+  static_assert(!std::is_same<Float, float>::value, "");
+  FMT_ASSERT(value >= 0, "value is negative");
+  auto converted_value = convert_float(value);
+
+  const bool fixed = specs.format == float_format::fixed;
+  if (value <= 0) {  // <= instead of == to silence a warning.
+    if (precision <= 0 || !fixed) {
+      buf.push_back('0');
+      return 0;
+    }
+    buf.try_resize(to_unsigned(precision));
+    fill_n(buf.data(), precision, '0');
+    return -precision;
+  }
+
+  int exp = 0;
+  bool use_dragon = true;
+  unsigned dragon_flags = 0;
+  if (!is_fast_float<Float>() || is_constant_evaluated()) {
+    const auto inv_log2_10 = 0.3010299956639812;  // 1 / log2(10)
+    using info = dragonbox::float_info<decltype(converted_value)>;
+    const auto f = basic_fp<typename info::carrier_uint>(converted_value);
+    // Compute exp, an approximate power of 10, such that
+    //   10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
+    // This is based on log10(value) == log2(value) / log2(10) and approximation
+    // of log2(value) by e + num_fraction_bits idea from double-conversion.
+    auto e = (f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10;
+    exp = static_cast<int>(e);
+    if (e > exp) ++exp;  // Compute ceil.
+    dragon_flags = dragon::fixup;
+  } else if (precision < 0) {
+    // Use Dragonbox for the shortest format.
+    if (specs.binary32) {
+      auto dec = dragonbox::to_decimal(static_cast<float>(value));
+      write<char>(buffer_appender<char>(buf), dec.significand);
+      return dec.exponent;
+    }
+    auto dec = dragonbox::to_decimal(static_cast<double>(value));
+    write<char>(buffer_appender<char>(buf), dec.significand);
+    return dec.exponent;
+  } else {
+    // Extract significand bits and exponent bits.
+    using info = dragonbox::float_info<double>;
+    auto br = bit_cast<uint64_t>(static_cast<double>(value));
+
+    const uint64_t significand_mask =
+        (static_cast<uint64_t>(1) << num_significand_bits<double>()) - 1;
+    uint64_t significand = (br & significand_mask);
+    int exponent = static_cast<int>((br & exponent_mask<double>()) >>
+                                    num_significand_bits<double>());
+
+    if (exponent != 0) {  // Check if normal.
+      exponent -= exponent_bias<double>() + num_significand_bits<double>();
+      significand |=
+          (static_cast<uint64_t>(1) << num_significand_bits<double>());
+      significand <<= 1;
+    } else {
+      // Normalize subnormal inputs.
+      FMT_ASSERT(significand != 0, "zeros should not appear here");
+      int shift = countl_zero(significand);
+      FMT_ASSERT(shift >= num_bits<uint64_t>() - num_significand_bits<double>(),
+                 "");
+      shift -= (num_bits<uint64_t>() - num_significand_bits<double>() - 2);
+      exponent = (std::numeric_limits<double>::min_exponent -
+                  num_significand_bits<double>()) -
+                 shift;
+      significand <<= shift;
+    }
+
+    // Compute the first several nonzero decimal significand digits.
+    // We call the number we get the first segment.
+    const int k = info::kappa - dragonbox::floor_log10_pow2(exponent);
+    exp = -k;
+    const int beta = exponent + dragonbox::floor_log2_pow10(k);
+    uint64_t first_segment;
+    bool has_more_segments;
+    int digits_in_the_first_segment;
+    {
+      const auto r = dragonbox::umul192_upper128(
+          significand << beta, dragonbox::get_cached_power(k));
+      first_segment = r.high();
+      has_more_segments = r.low() != 0;
+
+      // The first segment can have 18 ~ 19 digits.
+      if (first_segment >= 1000000000000000000ULL) {
+        digits_in_the_first_segment = 19;
+      } else {
+        // When it is of 18-digits, we align it to 19-digits by adding a bogus
+        // zero at the end.
+        digits_in_the_first_segment = 18;
+        first_segment *= 10;
+      }
+    }
+
+    // Compute the actual number of decimal digits to print.
+    if (fixed) adjust_precision(precision, exp + digits_in_the_first_segment);
+
+    // Use Dragon4 only when there might be not enough digits in the first
+    // segment.
+    if (digits_in_the_first_segment > precision) {
+      use_dragon = false;
+
+      if (precision <= 0) {
+        exp += digits_in_the_first_segment;
+
+        if (precision < 0) {
+          // Nothing to do, since all we have are just leading zeros.
+          buf.try_resize(0);
+        } else {
+          // We may need to round-up.
+          buf.try_resize(1);
+          if ((first_segment | static_cast<uint64_t>(has_more_segments)) >
+              5000000000000000000ULL) {
+            buf[0] = '1';
+          } else {
+            buf[0] = '0';
+          }
+        }
+      }  // precision <= 0
+      else {
+        exp += digits_in_the_first_segment - precision;
+
+        // When precision > 0, we divide the first segment into three
+        // subsegments, each with 9, 9, and 0 ~ 1 digits so that each fits
+        // in 32-bits which usually allows faster calculation than in
+        // 64-bits. Since some compiler (e.g. MSVC) doesn't know how to optimize
+        // division-by-constant for large 64-bit divisors, we do it here
+        // manually. The magic number 7922816251426433760 below is equal to
+        // ceil(2^(64+32) / 10^10).
+        const uint32_t first_subsegment = static_cast<uint32_t>(
+            dragonbox::umul128_upper64(first_segment, 7922816251426433760ULL) >>
+            32);
+        const uint64_t second_third_subsegments =
+            first_segment - first_subsegment * 10000000000ULL;
+
+        uint64_t prod;
+        uint32_t digits;
+        bool should_round_up;
+        int number_of_digits_to_print = precision > 9 ? 9 : precision;
+
+        // Print a 9-digits subsegment, either the first or the second.
+        auto print_subsegment = [&](uint32_t subsegment, char* buffer) {
+          int number_of_digits_printed = 0;
+
+          // If we want to print an odd number of digits from the subsegment,
+          if ((number_of_digits_to_print & 1) != 0) {
+            // Convert to 64-bit fixed-point fractional form with 1-digit
+            // integer part. The magic number 720575941 is a good enough
+            // approximation of 2^(32 + 24) / 10^8; see
+            // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case
+            // for details.
+            prod = ((subsegment * static_cast<uint64_t>(720575941)) >> 24) + 1;
+            digits = static_cast<uint32_t>(prod >> 32);
+            *buffer = static_cast<char>('0' + digits);
+            number_of_digits_printed++;
+          }
+          // If we want to print an even number of digits from the
+          // first_subsegment,
+          else {
+            // Convert to 64-bit fixed-point fractional form with 2-digits
+            // integer part. The magic number 450359963 is a good enough
+            // approximation of 2^(32 + 20) / 10^7; see
+            // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case
+            // for details.
+            prod = ((subsegment * static_cast<uint64_t>(450359963)) >> 20) + 1;
+            digits = static_cast<uint32_t>(prod >> 32);
+            copy2(buffer, digits2(digits));
+            number_of_digits_printed += 2;
+          }
+
+          // Print all digit pairs.
+          while (number_of_digits_printed < number_of_digits_to_print) {
+            prod = static_cast<uint32_t>(prod) * static_cast<uint64_t>(100);
+            digits = static_cast<uint32_t>(prod >> 32);
+            copy2(buffer + number_of_digits_printed, digits2(digits));
+            number_of_digits_printed += 2;
+          }
+        };
+
+        // Print first subsegment.
+        print_subsegment(first_subsegment, buf.data());
+
+        // Perform rounding if the first subsegment is the last subsegment to
+        // print.
+        if (precision <= 9) {
+          // Rounding inside the subsegment.
+          // We round-up if:
+          //  - either the fractional part is strictly larger than 1/2, or
+          //  - the fractional part is exactly 1/2 and the last digit is odd.
+          // We rely on the following observations:
+          //  - If fractional_part >= threshold, then the fractional part is
+          //    strictly larger than 1/2.
+          //  - If the MSB of fractional_part is set, then the fractional part
+          //    must be at least 1/2.
+          //  - When the MSB of fractional_part is set, either
+          //    second_third_subsegments being nonzero or has_more_segments
+          //    being true means there are further digits not printed, so the
+          //    fractional part is strictly larger than 1/2.
+          if (precision < 9) {
+            uint32_t fractional_part = static_cast<uint32_t>(prod);
+            should_round_up = fractional_part >=
+                                  data::fractional_part_rounding_thresholds
+                                      [8 - number_of_digits_to_print] ||
+                              ((fractional_part >> 31) &
+                               ((digits & 1) | (second_third_subsegments != 0) |
+                                has_more_segments)) != 0;
+          }
+          // Rounding at the subsegment boundary.
+          // In this case, the fractional part is at least 1/2 if and only if
+          // second_third_subsegments >= 5000000000ULL, and is strictly larger
+          // than 1/2 if we further have either second_third_subsegments >
+          // 5000000000ULL or has_more_segments == true.
+          else {
+            should_round_up = second_third_subsegments > 5000000000ULL ||
+                              (second_third_subsegments == 5000000000ULL &&
+                               ((digits & 1) != 0 || has_more_segments));
+          }
+        }
+        // Otherwise, print the second subsegment.
+        else {
+          // Compilers are not aware of how to leverage the maximum value of
+          // second_third_subsegments to find out a better magic number which
+          // allows us to eliminate an additional shift. 1844674407370955162 =
+          // ceil(2^64/10) < ceil(2^64*(10^9/(10^10 - 1))).
+          const uint32_t second_subsegment =
+              static_cast<uint32_t>(dragonbox::umul128_upper64(
+                  second_third_subsegments, 1844674407370955162ULL));
+          const uint32_t third_subsegment =
+              static_cast<uint32_t>(second_third_subsegments) -
+              second_subsegment * 10;
+
+          number_of_digits_to_print = precision - 9;
+          print_subsegment(second_subsegment, buf.data() + 9);
+
+          // Rounding inside the subsegment.
+          if (precision < 18) {
+            // The condition third_subsegment != 0 implies that the segment was
+            // of 19 digits, so in this case the third segment should be
+            // consisting of a genuine digit from the input.
+            uint32_t fractional_part = static_cast<uint32_t>(prod);
+            should_round_up = fractional_part >=
+                                  data::fractional_part_rounding_thresholds
+                                      [8 - number_of_digits_to_print] ||
+                              ((fractional_part >> 31) &
+                               ((digits & 1) | (third_subsegment != 0) |
+                                has_more_segments)) != 0;
+          }
+          // Rounding at the subsegment boundary.
+          else {
+            // In this case, the segment must be of 19 digits, thus
+            // the third subsegment should be consisting of a genuine digit from
+            // the input.
+            should_round_up = third_subsegment > 5 ||
+                              (third_subsegment == 5 &&
+                               ((digits & 1) != 0 || has_more_segments));
+          }
+        }
+
+        // Round-up if necessary.
+        if (should_round_up) {
+          ++buf[precision - 1];
+          for (int i = precision - 1; i > 0 && buf[i] > '9'; --i) {
+            buf[i] = '0';
+            ++buf[i - 1];
+          }
+          if (buf[0] > '9') {
+            buf[0] = '1';
+            if (fixed)
+              buf[precision++] = '0';
+            else
+              ++exp;
+          }
+        }
+        buf.try_resize(to_unsigned(precision));
+      }
+    }  // if (digits_in_the_first_segment > precision)
+    else {
+      // Adjust the exponent for its use in Dragon4.
+      exp += digits_in_the_first_segment - 1;
+    }
+  }
+  if (use_dragon) {
+    auto f = basic_fp<uint128_t>();
+    bool is_predecessor_closer = specs.binary32
+                                     ? f.assign(static_cast<float>(value))
+                                     : f.assign(converted_value);
+    if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer;
+    if (fixed) dragon_flags |= dragon::fixed;
+    // Limit precision to the maximum possible number of significant digits in
+    // an IEEE754 double because we don't need to generate zeros.
+    const int max_double_digits = 767;
+    if (precision > max_double_digits) precision = max_double_digits;
+    format_dragon(f, dragon_flags, precision, buf, exp);
+  }
+  if (!fixed && !specs.showpoint) {
+    // Remove trailing zeros.
+    auto num_digits = buf.size();
+    while (num_digits > 0 && buf[num_digits - 1] == '0') {
+      --num_digits;
+      ++exp;
+    }
+    buf.try_resize(num_digits);
+  }
+  return exp;
+}
+template <typename Char, typename OutputIt, typename T>
+FMT_CONSTEXPR20 auto write_float(OutputIt out, T value,
+                                 format_specs<Char> specs, locale_ref loc)
+    -> OutputIt {
+  float_specs fspecs = parse_float_type_spec(specs);
+  fspecs.sign = specs.sign;
+  if (detail::signbit(value)) {  // value < 0 is false for NaN so use signbit.
+    fspecs.sign = sign::minus;
+    value = -value;
+  } else if (fspecs.sign == sign::minus) {
+    fspecs.sign = sign::none;
+  }
+
+  if (!detail::isfinite(value))
+    return write_nonfinite(out, detail::isnan(value), specs, fspecs);
+
+  if (specs.align == align::numeric && fspecs.sign) {
+    auto it = reserve(out, 1);
+    *it++ = detail::sign<Char>(fspecs.sign);
+    out = base_iterator(out, it);
+    fspecs.sign = sign::none;
+    if (specs.width != 0) --specs.width;
+  }
+
+  memory_buffer buffer;
+  if (fspecs.format == float_format::hex) {
+    if (fspecs.sign) buffer.push_back(detail::sign<char>(fspecs.sign));
+    format_hexfloat(convert_float(value), specs.precision, fspecs, buffer);
+    return write_bytes<align::right>(out, {buffer.data(), buffer.size()},
+                                     specs);
+  }
+  int precision = specs.precision >= 0 || specs.type == presentation_type::none
+                      ? specs.precision
+                      : 6;
+  if (fspecs.format == float_format::exp) {
+    if (precision == max_value<int>())
+      throw_format_error("number is too big");
+    else
+      ++precision;
+  } else if (fspecs.format != float_format::fixed && precision == 0) {
+    precision = 1;
+  }
+  if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+  int exp = format_float(convert_float(value), precision, fspecs, buffer);
+  fspecs.precision = precision;
+  auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
+  return write_float(out, f, specs, fspecs, loc);
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_floating_point<T>::value)>
+FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs<Char> specs,
+                           locale_ref loc = {}) -> OutputIt {
+  if (const_check(!is_supported_floating_point(value))) return out;
+  return specs.localized && write_loc(out, value, specs, loc)
+             ? out
+             : write_float(out, value, specs, loc);
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_fast_float<T>::value)>
+FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
+  if (is_constant_evaluated()) return write(out, value, format_specs<Char>());
+  if (const_check(!is_supported_floating_point(value))) return out;
+
+  auto fspecs = float_specs();
+  if (detail::signbit(value)) {
+    fspecs.sign = sign::minus;
+    value = -value;
+  }
+
+  constexpr auto specs = format_specs<Char>();
+  using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
+  using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint;
+  floaty_uint mask = exponent_mask<floaty>();
+  if ((bit_cast<floaty_uint>(value) & mask) == mask)
+    return write_nonfinite(out, std::isnan(value), specs, fspecs);
+
+  auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
+  return write_float(out, dec, specs, fspecs, {});
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_floating_point<T>::value &&
+                        !is_fast_float<T>::value)>
+inline auto write(OutputIt out, T value) -> OutputIt {
+  return write(out, value, format_specs<Char>());
+}
+
+template <typename Char, typename OutputIt>
+auto write(OutputIt out, monostate, format_specs<Char> = {}, locale_ref = {})
+    -> OutputIt {
+  FMT_ASSERT(false, "");
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value)
+    -> OutputIt {
+  auto it = reserve(out, value.size());
+  it = copy_str_noinline<Char>(value.begin(), value.end(), it);
+  return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_string<T>::value)>
+constexpr auto write(OutputIt out, const T& value) -> OutputIt {
+  return write<Char>(out, to_string_view(value));
+}
+
+// FMT_ENABLE_IF() condition separated to workaround an MSVC bug.
+template <
+    typename Char, typename OutputIt, typename T,
+    bool check =
+        std::is_enum<T>::value && !std::is_same<T, Char>::value &&
+        mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value !=
+            type::custom_type,
+    FMT_ENABLE_IF(check)>
+FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
+  return write<Char>(out, static_cast<underlying_t<T>>(value));
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(std::is_same<T, bool>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, T value,
+                         const format_specs<Char>& specs = {}, locale_ref = {})
+    -> OutputIt {
+  return specs.type != presentation_type::none &&
+                 specs.type != presentation_type::string
+             ? write(out, value ? 1 : 0, specs, {})
+             : write_bytes(out, value ? "true" : "false", specs);
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt {
+  auto it = reserve(out, 1);
+  *it++ = value;
+  return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value)
+    -> OutputIt {
+  if (value) return write(out, basic_string_view<Char>(value));
+  throw_format_error("string pointer is null");
+  return out;
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(std::is_same<T, void>::value)>
+auto write(OutputIt out, const T* value, const format_specs<Char>& specs = {},
+           locale_ref = {}) -> OutputIt {
+  return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
+}
+
+// A write overload that handles implicit conversions.
+template <typename Char, typename OutputIt, typename T,
+          typename Context = basic_format_context<OutputIt, Char>>
+FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
+    std::is_class<T>::value && !is_string<T>::value &&
+        !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
+        !std::is_same<T, remove_cvref_t<decltype(arg_mapper<Context>().map(
+                             value))>>::value,
+    OutputIt> {
+  return write<Char>(out, arg_mapper<Context>().map(value));
+}
+
+template <typename Char, typename OutputIt, typename T,
+          typename Context = basic_format_context<OutputIt, Char>>
+FMT_CONSTEXPR auto write(OutputIt out, const T& value)
+    -> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
+                   OutputIt> {
+  auto ctx = Context(out, {}, {});
+  return typename Context::template formatter_type<T>().format(value, ctx);
+}
+
+// An argument visitor that formats the argument and writes it via the output
+// iterator. It's a class and not a generic lambda for compatibility with C++11.
+template <typename Char> struct default_arg_formatter {
+  using iterator = buffer_appender<Char>;
+  using context = buffer_context<Char>;
+
+  iterator out;
+  basic_format_args<context> args;
+  locale_ref loc;
+
+  template <typename T> auto operator()(T value) -> iterator {
+    return write<Char>(out, value);
+  }
+  auto operator()(typename basic_format_arg<context>::handle h) -> iterator {
+    basic_format_parse_context<Char> parse_ctx({});
+    context format_ctx(out, args, loc);
+    h.format(parse_ctx, format_ctx);
+    return format_ctx.out();
+  }
+};
+
+template <typename Char> struct arg_formatter {
+  using iterator = buffer_appender<Char>;
+  using context = buffer_context<Char>;
+
+  iterator out;
+  const format_specs<Char>& specs;
+  locale_ref locale;
+
+  template <typename T>
+  FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
+    return detail::write(out, value, specs, locale);
+  }
+  auto operator()(typename basic_format_arg<context>::handle) -> iterator {
+    // User-defined types are handled separately because they require access
+    // to the parse context.
+    return out;
+  }
+};
+
+template <typename Char> struct custom_formatter {
+  basic_format_parse_context<Char>& parse_ctx;
+  buffer_context<Char>& ctx;
+
+  void operator()(
+      typename basic_format_arg<buffer_context<Char>>::handle h) const {
+    h.format(parse_ctx, ctx);
+  }
+  template <typename T> void operator()(T) const {}
+};
+
+template <typename ErrorHandler> class width_checker {
+ public:
+  explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
+
+  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+  FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
+    if (is_negative(value)) handler_.on_error("negative width");
+    return static_cast<unsigned long long>(value);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+  FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
+    handler_.on_error("width is not integer");
+    return 0;
+  }
+
+ private:
+  ErrorHandler& handler_;
+};
+
+template <typename ErrorHandler> class precision_checker {
+ public:
+  explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
+
+  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+  FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
+    if (is_negative(value)) handler_.on_error("negative precision");
+    return static_cast<unsigned long long>(value);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+  FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
+    handler_.on_error("precision is not integer");
+    return 0;
+  }
+
+ private:
+  ErrorHandler& handler_;
+};
+
+template <template <typename> class Handler, typename FormatArg,
+          typename ErrorHandler>
+FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int {
+  unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
+  if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
+  return static_cast<int>(value);
+}
+
+template <typename Context, typename ID>
+FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) {
+  auto arg = ctx.arg(id);
+  if (!arg) ctx.on_error("argument not found");
+  return arg;
+}
+
+template <template <typename> class Handler, typename Context>
+FMT_CONSTEXPR void handle_dynamic_spec(int& value,
+                                       arg_ref<typename Context::char_type> ref,
+                                       Context& ctx) {
+  switch (ref.kind) {
+  case arg_id_kind::none:
+    break;
+  case arg_id_kind::index:
+    value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index),
+                                              ctx.error_handler());
+    break;
+  case arg_id_kind::name:
+    value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name),
+                                              ctx.error_handler());
+    break;
+  }
+}
+
+#if FMT_USE_USER_DEFINED_LITERALS
+#  if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <typename T, typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct statically_named_arg : view {
+  static constexpr auto name = Str.data;
+
+  const T& value;
+  statically_named_arg(const T& v) : value(v) {}
+};
+
+template <typename T, typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {};
+
+template <typename T, typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>>
+    : std::true_type {};
+
+template <typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct udl_arg {
+  template <typename T> auto operator=(T&& value) const {
+    return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
+  }
+};
+#  else
+template <typename Char> struct udl_arg {
+  const Char* str;
+
+  template <typename T> auto operator=(T&& value) const -> named_arg<Char, T> {
+    return {str, std::forward<T>(value)};
+  }
+};
+#  endif
+#endif  // FMT_USE_USER_DEFINED_LITERALS
+
+template <typename Locale, typename Char>
+auto vformat(const Locale& loc, basic_string_view<Char> fmt,
+             basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> std::basic_string<Char> {
+  auto buf = basic_memory_buffer<Char>();
+  detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
+  return {buf.data(), buf.size()};
+}
+
+using format_func = void (*)(detail::buffer<char>&, int, const char*);
+
+FMT_API void format_error_code(buffer<char>& out, int error_code,
+                               string_view message) noexcept;
+
+FMT_API void report_error(format_func func, int error_code,
+                          const char* message) noexcept;
+}  // namespace detail
+
+FMT_API auto vsystem_error(int error_code, string_view format_str,
+                           format_args args) -> std::system_error;
+
+/**
+  \rst
+  Constructs :class:`std::system_error` with a message formatted with
+  ``fmt::format(fmt, args...)``.
+  *error_code* is a system error code as given by ``errno``.
+
+  **Example**::
+
+    // This throws std::system_error with the description
+    //   cannot open file 'madeup': No such file or directory
+    // or similar (system message may vary).
+    const char* filename = "madeup";
+    std::FILE* file = std::fopen(filename, "r");
+    if (!file)
+      throw fmt::system_error(errno, "cannot open file '{}'", filename);
+  \endrst
+ */
+template <typename... T>
+auto system_error(int error_code, format_string<T...> fmt, T&&... args)
+    -> std::system_error {
+  return vsystem_error(error_code, fmt, fmt::make_format_args(args...));
+}
+
+/**
+  \rst
+  Formats an error message for an error returned by an operating system or a
+  language runtime, for example a file opening error, and writes it to *out*.
+  The format is the same as the one used by ``std::system_error(ec, message)``
+  where ``ec`` is ``std::error_code(error_code, std::generic_category()})``.
+  It is implementation-defined but normally looks like:
+
+  .. parsed-literal::
+     *<message>*: *<system-message>*
+
+  where *<message>* is the passed message and *<system-message>* is the system
+  message corresponding to the error code.
+  *error_code* is a system error code as given by ``errno``.
+  \endrst
+ */
+FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
+                                 const char* message) noexcept;
+
+// Reports a system error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_system_error(int error_code, const char* message) noexcept;
+
+/** Fast integer formatter. */
+class format_int {
+ private:
+  // Buffer should be large enough to hold all digits (digits10 + 1),
+  // a sign and a null character.
+  enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
+  mutable char buffer_[buffer_size];
+  char* str_;
+
+  template <typename UInt> auto format_unsigned(UInt value) -> char* {
+    auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
+    return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
+  }
+
+  template <typename Int> auto format_signed(Int value) -> char* {
+    auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
+    bool negative = value < 0;
+    if (negative) abs_value = 0 - abs_value;
+    auto begin = format_unsigned(abs_value);
+    if (negative) *--begin = '-';
+    return begin;
+  }
+
+ public:
+  explicit format_int(int value) : str_(format_signed(value)) {}
+  explicit format_int(long value) : str_(format_signed(value)) {}
+  explicit format_int(long long value) : str_(format_signed(value)) {}
+  explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
+  explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
+  explicit format_int(unsigned long long value)
+      : str_(format_unsigned(value)) {}
+
+  /** Returns the number of characters written to the output buffer. */
+  auto size() const -> size_t {
+    return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
+  }
+
+  /**
+    Returns a pointer to the output buffer content. No terminating null
+    character is appended.
+   */
+  auto data() const -> const char* { return str_; }
+
+  /**
+    Returns a pointer to the output buffer content with terminating null
+    character appended.
+   */
+  auto c_str() const -> const char* {
+    buffer_[buffer_size - 1] = '\0';
+    return str_;
+  }
+
+  /**
+    \rst
+    Returns the content of the output buffer as an ``std::string``.
+    \endrst
+   */
+  auto str() const -> std::string { return std::string(str_, size()); }
+};
+
+template <typename T, typename Char>
+struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
+    : private formatter<detail::format_as_t<T>, Char> {
+  using base = formatter<detail::format_as_t<T>, Char>;
+  using base::parse;
+
+  template <typename FormatContext>
+  auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
+    return base::format(format_as(value), ctx);
+  }
+};
+
+#define FMT_FORMAT_AS(Type, Base) \
+  template <typename Char>        \
+  struct formatter<Type, Char> : formatter<Base, Char> {}
+
+FMT_FORMAT_AS(signed char, int);
+FMT_FORMAT_AS(unsigned char, unsigned);
+FMT_FORMAT_AS(short, int);
+FMT_FORMAT_AS(unsigned short, unsigned);
+FMT_FORMAT_AS(long, detail::long_type);
+FMT_FORMAT_AS(unsigned long, detail::ulong_type);
+FMT_FORMAT_AS(Char*, const Char*);
+FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(std::nullptr_t, const void*);
+FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(void*, const void*);
+
+template <typename Char, size_t N>
+struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {};
+
+/**
+  \rst
+  Converts ``p`` to ``const void*`` for pointer formatting.
+
+  **Example**::
+
+    auto s = fmt::format("{}", fmt::ptr(p));
+  \endrst
+ */
+template <typename T> auto ptr(T p) -> const void* {
+  static_assert(std::is_pointer<T>::value, "");
+  return detail::bit_cast<const void*>(p);
+}
+template <typename T, typename Deleter>
+auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
+  return p.get();
+}
+template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
+  return p.get();
+}
+
+/**
+  \rst
+  Converts ``e`` to the underlying type.
+
+  **Example**::
+
+    enum class color { red, green, blue };
+    auto s = fmt::format("{}", fmt::underlying(color::red));
+  \endrst
+ */
+template <typename Enum>
+constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum> {
+  return static_cast<underlying_t<Enum>>(e);
+}
+
+namespace enums {
+template <typename Enum, FMT_ENABLE_IF(std::is_enum<Enum>::value)>
+constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> {
+  return static_cast<underlying_t<Enum>>(e);
+}
+}  // namespace enums
+
+class bytes {
+ private:
+  string_view data_;
+  friend struct formatter<bytes>;
+
+ public:
+  explicit bytes(string_view data) : data_(data) {}
+};
+
+template <> struct formatter<bytes> {
+ private:
+  detail::dynamic_format_specs<> specs_;
+
+ public:
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
+    return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
+                              detail::type::string_type);
+  }
+
+  template <typename FormatContext>
+  auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
+    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+                                                       specs_.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
+        specs_.precision, specs_.precision_ref, ctx);
+    return detail::write_bytes(ctx.out(), b.data_, specs_);
+  }
+};
+
+// group_digits_view is not derived from view because it copies the argument.
+template <typename T> struct group_digits_view {
+  T value;
+};
+
+/**
+  \rst
+  Returns a view that formats an integer value using ',' as a locale-independent
+  thousands separator.
+
+  **Example**::
+
+    fmt::print("{}", fmt::group_digits(12345));
+    // Output: "12,345"
+  \endrst
+ */
+template <typename T> auto group_digits(T value) -> group_digits_view<T> {
+  return {value};
+}
+
+template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
+ private:
+  detail::dynamic_format_specs<> specs_;
+
+ public:
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* {
+    return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
+                              detail::type::int_type);
+  }
+
+  template <typename FormatContext>
+  auto format(group_digits_view<T> t, FormatContext& ctx)
+      -> decltype(ctx.out()) {
+    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+                                                       specs_.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
+        specs_.precision, specs_.precision_ref, ctx);
+    return detail::write_int(
+        ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
+        detail::digit_grouping<char>("\3", ","));
+  }
+};
+
+// DEPRECATED! join_view will be moved to ranges.h.
+template <typename It, typename Sentinel, typename Char = char>
+struct join_view : detail::view {
+  It begin;
+  Sentinel end;
+  basic_string_view<Char> sep;
+
+  join_view(It b, Sentinel e, basic_string_view<Char> s)
+      : begin(b), end(e), sep(s) {}
+};
+
+template <typename It, typename Sentinel, typename Char>
+struct formatter<join_view<It, Sentinel, Char>, Char> {
+ private:
+  using value_type =
+#ifdef __cpp_lib_ranges
+      std::iter_value_t<It>;
+#else
+      typename std::iterator_traits<It>::value_type;
+#endif
+  formatter<remove_cvref_t<value_type>, Char> value_formatter_;
+
+ public:
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
+    return value_formatter_.parse(ctx);
+  }
+
+  template <typename FormatContext>
+  auto format(const join_view<It, Sentinel, Char>& value,
+              FormatContext& ctx) const -> decltype(ctx.out()) {
+    auto it = value.begin;
+    auto out = ctx.out();
+    if (it != value.end) {
+      out = value_formatter_.format(*it, ctx);
+      ++it;
+      while (it != value.end) {
+        out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
+        ctx.advance_to(out);
+        out = value_formatter_.format(*it, ctx);
+        ++it;
+      }
+    }
+    return out;
+  }
+};
+
+/**
+  Returns a view that formats the iterator range `[begin, end)` with elements
+  separated by `sep`.
+ */
+template <typename It, typename Sentinel>
+auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
+  return {begin, end, sep};
+}
+
+/**
+  \rst
+  Returns a view that formats `range` with elements separated by `sep`.
+
+  **Example**::
+
+    std::vector<int> v = {1, 2, 3};
+    fmt::print("{}", fmt::join(v, ", "));
+    // Output: "1, 2, 3"
+
+  ``fmt::join`` applies passed format specifiers to the range elements::
+
+    fmt::print("{:02}", fmt::join(v, ", "));
+    // Output: "01, 02, 03"
+  \endrst
+ */
+template <typename Range>
+auto join(Range&& range, string_view sep)
+    -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
+  return join(std::begin(range), std::end(range), sep);
+}
+
+/**
+  \rst
+  Converts *value* to ``std::string`` using the default format for type *T*.
+
+  **Example**::
+
+    #include <fmt/format.h>
+
+    std::string answer = fmt::to_string(42);
+  \endrst
+ */
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
+                                    !detail::has_format_as<T>::value)>
+inline auto to_string(const T& value) -> std::string {
+  auto buffer = memory_buffer();
+  detail::write<char>(appender(buffer), value);
+  return {buffer.data(), buffer.size()};
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+FMT_NODISCARD inline auto to_string(T value) -> std::string {
+  // The buffer should be large enough to store the number including the sign
+  // or "false" for bool.
+  constexpr int max_size = detail::digits10<T>() + 2;
+  char buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
+  char* begin = buffer;
+  return std::string(begin, detail::write<char>(begin, value));
+}
+
+template <typename Char, size_t SIZE>
+FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE>& buf)
+    -> std::basic_string<Char> {
+  auto size = buf.size();
+  detail::assume(size < std::basic_string<Char>().max_size());
+  return std::basic_string<Char>(buf.data(), size);
+}
+
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value &&
+                                    detail::has_format_as<T>::value)>
+inline auto to_string(const T& value) -> std::string {
+  return to_string(format_as(value));
+}
+
+FMT_END_EXPORT
+
+namespace detail {
+
+template <typename Char>
+void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
+                typename vformat_args<Char>::type args, locale_ref loc) {
+  auto out = buffer_appender<Char>(buf);
+  if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
+    auto arg = args.get(0);
+    if (!arg) error_handler().on_error("argument not found");
+    visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
+    return;
+  }
+
+  struct format_handler : error_handler {
+    basic_format_parse_context<Char> parse_context;
+    buffer_context<Char> context;
+
+    format_handler(buffer_appender<Char> p_out, basic_string_view<Char> str,
+                   basic_format_args<buffer_context<Char>> p_args,
+                   locale_ref p_loc)
+        : parse_context(str), context(p_out, p_args, p_loc) {}
+
+    void on_text(const Char* begin, const Char* end) {
+      auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
+      context.advance_to(write<Char>(context.out(), text));
+    }
+
+    FMT_CONSTEXPR auto on_arg_id() -> int {
+      return parse_context.next_arg_id();
+    }
+    FMT_CONSTEXPR auto on_arg_id(int id) -> int {
+      return parse_context.check_arg_id(id), id;
+    }
+    FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
+      int arg_id = context.arg_id(id);
+      if (arg_id < 0) on_error("argument not found");
+      return arg_id;
+    }
+
+    FMT_INLINE void on_replacement_field(int id, const Char*) {
+      auto arg = get_arg(context, id);
+      context.advance_to(visit_format_arg(
+          default_arg_formatter<Char>{context.out(), context.args(),
+                                      context.locale()},
+          arg));
+    }
+
+    auto on_format_specs(int id, const Char* begin, const Char* end)
+        -> const Char* {
+      auto arg = get_arg(context, id);
+      if (arg.type() == type::custom_type) {
+        parse_context.advance_to(begin);
+        visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
+        return parse_context.begin();
+      }
+      auto specs = detail::dynamic_format_specs<Char>();
+      begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
+      detail::handle_dynamic_spec<detail::width_checker>(
+          specs.width, specs.width_ref, context);
+      detail::handle_dynamic_spec<detail::precision_checker>(
+          specs.precision, specs.precision_ref, context);
+      if (begin == end || *begin != '}')
+        on_error("missing '}' in format string");
+      auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
+      context.advance_to(visit_format_arg(f, arg));
+      return begin;
+    }
+  };
+  detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
+}
+
+FMT_BEGIN_EXPORT
+
+#ifndef FMT_HEADER_ONLY
+extern template FMT_API void vformat_to(buffer<char>&, string_view,
+                                        typename vformat_args<>::type,
+                                        locale_ref);
+extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
+    -> thousands_sep_result<char>;
+extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
+    -> thousands_sep_result<wchar_t>;
+extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
+extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
+#endif  // FMT_HEADER_ONLY
+
+}  // namespace detail
+
+#if FMT_USE_USER_DEFINED_LITERALS
+inline namespace literals {
+/**
+  \rst
+  User-defined literal equivalent of :func:`fmt::arg`.
+
+  **Example**::
+
+    using namespace fmt::literals;
+    fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
+  \endrst
+ */
+#  if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <detail_exported::fixed_string Str> constexpr auto operator""_a() {
+  using char_t = remove_cvref_t<decltype(Str.data[0])>;
+  return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
+}
+#  else
+constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> {
+  return {s};
+}
+#  endif
+}  // namespace literals
+#endif  // FMT_USE_USER_DEFINED_LITERALS
+
+template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
+inline auto vformat(const Locale& loc, string_view fmt, format_args args)
+    -> std::string {
+  return detail::vformat(loc, fmt, args);
+}
+
+template <typename Locale, typename... T,
+          FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
+inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
+    -> std::string {
+  return fmt::vformat(loc, string_view(fmt), fmt::make_format_args(args...));
+}
+
+template <typename OutputIt, typename Locale,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
+                            detail::is_locale<Locale>::value)>
+auto vformat_to(OutputIt out, const Locale& loc, string_view fmt,
+                format_args args) -> OutputIt {
+  using detail::get_buffer;
+  auto&& buf = get_buffer<char>(out);
+  detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
+  return detail::get_iterator(buf, out);
+}
+
+template <typename OutputIt, typename Locale, typename... T,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
+                            detail::is_locale<Locale>::value)>
+FMT_INLINE auto format_to(OutputIt out, const Locale& loc,
+                          format_string<T...> fmt, T&&... args) -> OutputIt {
+  return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
+}
+
+template <typename Locale, typename... T,
+          FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
+FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc,
+                                             format_string<T...> fmt,
+                                             T&&... args) -> size_t {
+  auto buf = detail::counting_buffer<>();
+  detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...),
+                           detail::locale_ref(loc));
+  return buf.count();
+}
+
+FMT_END_EXPORT
+
+template <typename T, typename Char>
+template <typename FormatContext>
+FMT_CONSTEXPR FMT_INLINE auto
+formatter<T, Char,
+          enable_if_t<detail::type_constant<T, Char>::value !=
+                      detail::type::custom_type>>::format(const T& val,
+                                                          FormatContext& ctx)
+    const -> decltype(ctx.out()) {
+  if (specs_.width_ref.kind != detail::arg_id_kind::none ||
+      specs_.precision_ref.kind != detail::arg_id_kind::none) {
+    auto specs = specs_;
+    detail::handle_dynamic_spec<detail::width_checker>(specs.width,
+                                                       specs.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
+        specs.precision, specs.precision_ref, ctx);
+    return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
+  }
+  return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
+}
+
+FMT_END_NAMESPACE
+
+#ifdef FMT_HEADER_ONLY
+#  define FMT_FUNC inline
+#  include "format-inl.h"
+#else
+#  define FMT_FUNC
+#endif
+
+#endif  // FMT_FORMAT_H_
diff --git a/src/cpp-common/vendor/fmt/os.cc b/src/cpp-common/vendor/fmt/os.cc
new file mode 100644 (file)
index 0000000..0e26d80
--- /dev/null
@@ -0,0 +1,398 @@
+// Formatting library for C++ - optional OS-specific functionality
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+// Disable bogus MSVC warnings.
+#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
+#  define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "cpp-common/vendor/fmt/os.h"
+
+#include <climits>
+
+#if FMT_USE_FCNTL
+#  include <sys/stat.h>
+#  include <sys/types.h>
+
+#  ifdef _WRS_KERNEL   // VxWorks7 kernel
+#    include <ioLib.h> // getpagesize
+#  endif
+
+#  ifndef _WIN32
+#    include <unistd.h>
+#  else
+#    ifndef WIN32_LEAN_AND_MEAN
+#      define WIN32_LEAN_AND_MEAN
+#    endif
+#    include <io.h>
+
+#    ifndef S_IRUSR
+#      define S_IRUSR _S_IREAD
+#    endif
+#    ifndef S_IWUSR
+#      define S_IWUSR _S_IWRITE
+#    endif
+#    ifndef S_IRGRP
+#      define S_IRGRP 0
+#    endif
+#    ifndef S_IWGRP
+#      define S_IWGRP 0
+#    endif
+#    ifndef S_IROTH
+#      define S_IROTH 0
+#    endif
+#    ifndef S_IWOTH
+#      define S_IWOTH 0
+#    endif
+#  endif  // _WIN32
+#endif    // FMT_USE_FCNTL
+
+#ifdef _WIN32
+#  include <windows.h>
+#endif
+
+namespace {
+#ifdef _WIN32
+// Return type of read and write functions.
+using rwresult = int;
+
+// On Windows the count argument to read and write is unsigned, so convert
+// it from size_t preventing integer overflow.
+inline unsigned convert_rwcount(std::size_t count) {
+  return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
+}
+#elif FMT_USE_FCNTL
+// Return type of read and write functions.
+using rwresult = ssize_t;
+
+inline std::size_t convert_rwcount(std::size_t count) { return count; }
+#endif
+}  // namespace
+
+FMT_BEGIN_NAMESPACE
+
+#ifdef _WIN32
+namespace detail {
+
+class system_message {
+  system_message(const system_message&) = delete;
+  void operator=(const system_message&) = delete;
+
+  unsigned long result_;
+  wchar_t* message_;
+
+  static bool is_whitespace(wchar_t c) noexcept {
+    return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
+  }
+
+ public:
+  explicit system_message(unsigned long error_code)
+      : result_(0), message_(nullptr) {
+    result_ = FormatMessageW(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+            FORMAT_MESSAGE_IGNORE_INSERTS,
+        nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        reinterpret_cast<wchar_t*>(&message_), 0, nullptr);
+    if (result_ != 0) {
+      while (result_ != 0 && is_whitespace(message_[result_ - 1])) {
+        --result_;
+      }
+    }
+  }
+  ~system_message() { LocalFree(message_); }
+  explicit operator bool() const noexcept { return result_ != 0; }
+  operator basic_string_view<wchar_t>() const noexcept {
+    return basic_string_view<wchar_t>(message_, result_);
+  }
+};
+
+class utf8_system_category final : public std::error_category {
+ public:
+  const char* name() const noexcept override { return "system"; }
+  std::string message(int error_code) const override {
+    auto&& msg = system_message(error_code);
+    if (msg) {
+      auto utf8_message = to_utf8<wchar_t>();
+      if (utf8_message.convert(msg)) {
+        return utf8_message.str();
+      }
+    }
+    return "unknown error";
+  }
+};
+
+}  // namespace detail
+
+FMT_API const std::error_category& system_category() noexcept {
+  static const detail::utf8_system_category category;
+  return category;
+}
+
+std::system_error vwindows_error(int err_code, string_view format_str,
+                                 format_args args) {
+  auto ec = std::error_code(err_code, system_category());
+  return std::system_error(ec, vformat(format_str, args));
+}
+
+void detail::format_windows_error(detail::buffer<char>& out, int error_code,
+                                  const char* message) noexcept {
+  FMT_TRY {
+    auto&& msg = system_message(error_code);
+    if (msg) {
+      auto utf8_message = to_utf8<wchar_t>();
+      if (utf8_message.convert(msg)) {
+        fmt::format_to(appender(out), FMT_STRING("{}: {}"), message,
+                       string_view(utf8_message));
+        return;
+      }
+    }
+  }
+  FMT_CATCH(...) {}
+  format_error_code(out, error_code, message);
+}
+
+void report_windows_error(int error_code, const char* message) noexcept {
+  report_error(detail::format_windows_error, error_code, message);
+}
+#endif  // _WIN32
+
+buffered_file::~buffered_file() noexcept {
+  if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
+    report_system_error(errno, "cannot close file");
+}
+
+buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
+  FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
+                nullptr);
+  if (!file_)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot open file {}"),
+                           filename.c_str()));
+}
+
+void buffered_file::close() {
+  if (!file_) return;
+  int result = FMT_SYSTEM(fclose(file_));
+  file_ = nullptr;
+  if (result != 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
+}
+
+int buffered_file::descriptor() const {
+#ifdef fileno  // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
+  int fd = fileno(file_);
+#else
+  int fd = FMT_POSIX_CALL(fileno(file_));
+#endif
+  if (fd == -1)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor")));
+  return fd;
+}
+
+#if FMT_USE_FCNTL
+#  ifdef _WIN32
+using mode_t = int;
+#  endif
+constexpr mode_t default_open_mode =
+    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+
+file::file(cstring_view path, int oflag) {
+#  if defined(_WIN32) && !defined(__MINGW32__)
+  fd_ = -1;
+  auto converted = detail::utf8_to_utf16(string_view(path.c_str()));
+  *this = file::open_windows_file(converted.c_str(), oflag);
+#  else
+  FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, default_open_mode)));
+  if (fd_ == -1)
+    FMT_THROW(
+        system_error(errno, FMT_STRING("cannot open file {}"), path.c_str()));
+#  endif
+}
+
+file::~file() noexcept {
+  // Don't retry close in case of EINTR!
+  // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
+  if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
+    report_system_error(errno, "cannot close file");
+}
+
+void file::close() {
+  if (fd_ == -1) return;
+  // Don't retry close in case of EINTR!
+  // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
+  int result = FMT_POSIX_CALL(close(fd_));
+  fd_ = -1;
+  if (result != 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
+}
+
+long long file::size() const {
+#  ifdef _WIN32
+  // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
+  // is less than 0x0500 as is the case with some default MinGW builds.
+  // Both functions support large file sizes.
+  DWORD size_upper = 0;
+  HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
+  DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
+  if (size_lower == INVALID_FILE_SIZE) {
+    DWORD error = GetLastError();
+    if (error != NO_ERROR)
+      FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
+  }
+  unsigned long long long_size = size_upper;
+  return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
+#  else
+  using Stat = struct stat;
+  Stat file_stat = Stat();
+  if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot get file attributes")));
+  static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
+                "return type of file::size is not large enough");
+  return file_stat.st_size;
+#  endif
+}
+
+std::size_t file::read(void* buffer, std::size_t count) {
+  rwresult result = 0;
+  FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
+  if (result < 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot read from file")));
+  return detail::to_unsigned(result);
+}
+
+std::size_t file::write(const void* buffer, std::size_t count) {
+  rwresult result = 0;
+  FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
+  if (result < 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+  return detail::to_unsigned(result);
+}
+
+file file::dup(int fd) {
+  // Don't retry as dup doesn't return EINTR.
+  // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
+  int new_fd = FMT_POSIX_CALL(dup(fd));
+  if (new_fd == -1)
+    FMT_THROW(system_error(
+        errno, FMT_STRING("cannot duplicate file descriptor {}"), fd));
+  return file(new_fd);
+}
+
+void file::dup2(int fd) {
+  int result = 0;
+  FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
+  if (result == -1) {
+    FMT_THROW(system_error(
+        errno, FMT_STRING("cannot duplicate file descriptor {} to {}"), fd_,
+        fd));
+  }
+}
+
+void file::dup2(int fd, std::error_code& ec) noexcept {
+  int result = 0;
+  FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
+  if (result == -1) ec = std::error_code(errno, std::generic_category());
+}
+
+void file::pipe(file& read_end, file& write_end) {
+  // Close the descriptors first to make sure that assignments don't throw
+  // and there are no leaks.
+  read_end.close();
+  write_end.close();
+  int fds[2] = {};
+#  ifdef _WIN32
+  // Make the default pipe capacity same as on Linux 2.6.11+.
+  enum { DEFAULT_CAPACITY = 65536 };
+  int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
+#  else
+  // Don't retry as the pipe function doesn't return EINTR.
+  // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
+  int result = FMT_POSIX_CALL(pipe(fds));
+#  endif
+  if (result != 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe")));
+  // The following assignments don't throw because read_fd and write_fd
+  // are closed.
+  read_end = file(fds[0]);
+  write_end = file(fds[1]);
+}
+
+buffered_file file::fdopen(const char* mode) {
+// Don't retry as fdopen doesn't return EINTR.
+#  if defined(__MINGW32__) && defined(_POSIX_)
+  FILE* f = ::fdopen(fd_, mode);
+#  else
+  FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
+#  endif
+  if (!f) {
+    FMT_THROW(system_error(
+        errno, FMT_STRING("cannot associate stream with file descriptor")));
+  }
+  buffered_file bf(f);
+  fd_ = -1;
+  return bf;
+}
+
+#  if defined(_WIN32) && !defined(__MINGW32__)
+file file::open_windows_file(wcstring_view path, int oflag) {
+  int fd = -1;
+  auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode);
+  if (fd == -1) {
+    FMT_THROW(system_error(err, FMT_STRING("cannot open file {}"),
+                           detail::to_utf8<wchar_t>(path.c_str()).c_str()));
+  }
+  return file(fd);
+}
+#  endif
+
+#  if !defined(__MSDOS__)
+long getpagesize() {
+#    ifdef _WIN32
+  SYSTEM_INFO si;
+  GetSystemInfo(&si);
+  return si.dwPageSize;
+#    else
+#      ifdef _WRS_KERNEL
+  long size = FMT_POSIX_CALL(getpagesize());
+#      else
+  long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
+#      endif
+
+  if (size < 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size")));
+  return size;
+#    endif
+}
+#  endif
+
+namespace detail {
+
+void file_buffer::grow(size_t) {
+  if (this->size() == this->capacity()) flush();
+}
+
+file_buffer::file_buffer(cstring_view path,
+                         const detail::ostream_params& params)
+    : file_(path, params.oflag) {
+  set(new char[params.buffer_size], params.buffer_size);
+}
+
+file_buffer::file_buffer(file_buffer&& other)
+    : detail::buffer<char>(other.data(), other.size(), other.capacity()),
+      file_(std::move(other.file_)) {
+  other.clear();
+  other.set(nullptr, 0);
+}
+
+file_buffer::~file_buffer() {
+  flush();
+  delete[] data();
+}
+}  // namespace detail
+
+ostream::~ostream() = default;
+#endif  // FMT_USE_FCNTL
+FMT_END_NAMESPACE
diff --git a/src/cpp-common/vendor/fmt/os.h b/src/cpp-common/vendor/fmt/os.h
new file mode 100644 (file)
index 0000000..2126424
--- /dev/null
@@ -0,0 +1,451 @@
+// Formatting library for C++ - optional OS-specific functionality
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_OS_H_
+#define FMT_OS_H_
+
+#include <cerrno>
+#include <cstddef>
+#include <cstdio>
+#include <system_error>  // std::system_error
+
+#if defined __APPLE__ || defined(__FreeBSD__)
+#  include <xlocale.h>  // for LC_NUMERIC_MASK on OS X
+#endif
+
+#include "format.h"
+
+#ifndef FMT_USE_FCNTL
+// UWP doesn't provide _pipe.
+#  if FMT_HAS_INCLUDE("winapifamily.h")
+#    include <winapifamily.h>
+#  endif
+#  if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
+       defined(__linux__)) &&                              \
+      (!defined(WINAPI_FAMILY) ||                          \
+       (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
+#    include <fcntl.h>  // for O_RDONLY
+#    define FMT_USE_FCNTL 1
+#  else
+#    define FMT_USE_FCNTL 0
+#  endif
+#endif
+
+#ifndef FMT_POSIX
+#  if defined(_WIN32) && !defined(__MINGW32__)
+// Fix warnings about deprecated symbols.
+#    define FMT_POSIX(call) _##call
+#  else
+#    define FMT_POSIX(call) call
+#  endif
+#endif
+
+// Calls to system functions are wrapped in FMT_SYSTEM for testability.
+#ifdef FMT_SYSTEM
+#  define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
+#else
+#  define FMT_SYSTEM(call) ::call
+#  ifdef _WIN32
+// Fix warnings about deprecated symbols.
+#    define FMT_POSIX_CALL(call) ::_##call
+#  else
+#    define FMT_POSIX_CALL(call) ::call
+#  endif
+#endif
+
+// Retries the expression while it evaluates to error_result and errno
+// equals to EINTR.
+#ifndef _WIN32
+#  define FMT_RETRY_VAL(result, expression, error_result) \
+    do {                                                  \
+      (result) = (expression);                            \
+    } while ((result) == (error_result) && errno == EINTR)
+#else
+#  define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
+#endif
+
+#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
+
+FMT_BEGIN_NAMESPACE
+FMT_BEGIN_EXPORT
+
+/**
+  \rst
+  A reference to a null-terminated string. It can be constructed from a C
+  string or ``std::string``.
+
+  You can use one of the following type aliases for common character types:
+
+  +---------------+-----------------------------+
+  | Type          | Definition                  |
+  +===============+=============================+
+  | cstring_view  | basic_cstring_view<char>    |
+  +---------------+-----------------------------+
+  | wcstring_view | basic_cstring_view<wchar_t> |
+  +---------------+-----------------------------+
+
+  This class is most useful as a parameter type to allow passing
+  different types of strings to a function, for example::
+
+    template <typename... Args>
+    std::string format(cstring_view format_str, const Args & ... args);
+
+    format("{}", 42);
+    format(std::string("{}"), 42);
+  \endrst
+ */
+template <typename Char> class basic_cstring_view {
+ private:
+  const Char* data_;
+
+ public:
+  /** Constructs a string reference object from a C string. */
+  basic_cstring_view(const Char* s) : data_(s) {}
+
+  /**
+    \rst
+    Constructs a string reference from an ``std::string`` object.
+    \endrst
+   */
+  basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
+
+  /** Returns the pointer to a C string. */
+  const Char* c_str() const { return data_; }
+};
+
+using cstring_view = basic_cstring_view<char>;
+using wcstring_view = basic_cstring_view<wchar_t>;
+
+#ifdef _WIN32
+FMT_API const std::error_category& system_category() noexcept;
+
+namespace detail {
+FMT_API void format_windows_error(buffer<char>& out, int error_code,
+                                  const char* message) noexcept;
+}
+
+FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
+                                         format_args args);
+
+/**
+ \rst
+ Constructs a :class:`std::system_error` object with the description
+ of the form
+
+ .. parsed-literal::
+   *<message>*: *<system-message>*
+
+ where *<message>* is the formatted message and *<system-message>* is the
+ system message corresponding to the error code.
+ *error_code* is a Windows error code as given by ``GetLastError``.
+ If *error_code* is not a valid error code such as -1, the system message
+ will look like "error -1".
+
+ **Example**::
+
+   // This throws a system_error with the description
+   //   cannot open file 'madeup': The system cannot find the file specified.
+   // or similar (system message may vary).
+   const char *filename = "madeup";
+   LPOFSTRUCT of = LPOFSTRUCT();
+   HFILE file = OpenFile(filename, &of, OF_READ);
+   if (file == HFILE_ERROR) {
+     throw fmt::windows_error(GetLastError(),
+                              "cannot open file '{}'", filename);
+   }
+ \endrst
+*/
+template <typename... Args>
+std::system_error windows_error(int error_code, string_view message,
+                                const Args&... args) {
+  return vwindows_error(error_code, message, fmt::make_format_args(args...));
+}
+
+// Reports a Windows error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_windows_error(int error_code, const char* message) noexcept;
+#else
+inline const std::error_category& system_category() noexcept {
+  return std::system_category();
+}
+#endif  // _WIN32
+
+// std::system is not available on some platforms such as iOS (#2248).
+#ifdef __OSX__
+template <typename S, typename... Args, typename Char = char_t<S>>
+void say(const S& format_str, Args&&... args) {
+  std::system(format("say \"{}\"", format(format_str, args...)).c_str());
+}
+#endif
+
+// A buffered file.
+class buffered_file {
+ private:
+  FILE* file_;
+
+  friend class file;
+
+  explicit buffered_file(FILE* f) : file_(f) {}
+
+ public:
+  buffered_file(const buffered_file&) = delete;
+  void operator=(const buffered_file&) = delete;
+
+  // Constructs a buffered_file object which doesn't represent any file.
+  buffered_file() noexcept : file_(nullptr) {}
+
+  // Destroys the object closing the file it represents if any.
+  FMT_API ~buffered_file() noexcept;
+
+ public:
+  buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
+    other.file_ = nullptr;
+  }
+
+  buffered_file& operator=(buffered_file&& other) {
+    close();
+    file_ = other.file_;
+    other.file_ = nullptr;
+    return *this;
+  }
+
+  // Opens a file.
+  FMT_API buffered_file(cstring_view filename, cstring_view mode);
+
+  // Closes the file.
+  FMT_API void close();
+
+  // Returns the pointer to a FILE object representing this file.
+  FILE* get() const noexcept { return file_; }
+
+  FMT_API int descriptor() const;
+
+  void vprint(string_view format_str, format_args args) {
+    fmt::vprint(file_, format_str, args);
+  }
+
+  template <typename... Args>
+  inline void print(string_view format_str, const Args&... args) {
+    vprint(format_str, fmt::make_format_args(args...));
+  }
+};
+
+#if FMT_USE_FCNTL
+// A file. Closed file is represented by a file object with descriptor -1.
+// Methods that are not declared with noexcept may throw
+// fmt::system_error in case of failure. Note that some errors such as
+// closing the file multiple times will cause a crash on Windows rather
+// than an exception. You can get standard behavior by overriding the
+// invalid parameter handler with _set_invalid_parameter_handler.
+class FMT_API file {
+ private:
+  int fd_;  // File descriptor.
+
+  // Constructs a file object with a given descriptor.
+  explicit file(int fd) : fd_(fd) {}
+
+ public:
+  // Possible values for the oflag argument to the constructor.
+  enum {
+    RDONLY = FMT_POSIX(O_RDONLY),  // Open for reading only.
+    WRONLY = FMT_POSIX(O_WRONLY),  // Open for writing only.
+    RDWR = FMT_POSIX(O_RDWR),      // Open for reading and writing.
+    CREATE = FMT_POSIX(O_CREAT),   // Create if the file doesn't exist.
+    APPEND = FMT_POSIX(O_APPEND),  // Open in append mode.
+    TRUNC = FMT_POSIX(O_TRUNC)     // Truncate the content of the file.
+  };
+
+  // Constructs a file object which doesn't represent any file.
+  file() noexcept : fd_(-1) {}
+
+  // Opens a file and constructs a file object representing this file.
+  file(cstring_view path, int oflag);
+
+ public:
+  file(const file&) = delete;
+  void operator=(const file&) = delete;
+
+  file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
+
+  // Move assignment is not noexcept because close may throw.
+  file& operator=(file&& other) {
+    close();
+    fd_ = other.fd_;
+    other.fd_ = -1;
+    return *this;
+  }
+
+  // Destroys the object closing the file it represents if any.
+  ~file() noexcept;
+
+  // Returns the file descriptor.
+  int descriptor() const noexcept { return fd_; }
+
+  // Closes the file.
+  void close();
+
+  // Returns the file size. The size has signed type for consistency with
+  // stat::st_size.
+  long long size() const;
+
+  // Attempts to read count bytes from the file into the specified buffer.
+  size_t read(void* buffer, size_t count);
+
+  // Attempts to write count bytes from the specified buffer to the file.
+  size_t write(const void* buffer, size_t count);
+
+  // Duplicates a file descriptor with the dup function and returns
+  // the duplicate as a file object.
+  static file dup(int fd);
+
+  // Makes fd be the copy of this file descriptor, closing fd first if
+  // necessary.
+  void dup2(int fd);
+
+  // Makes fd be the copy of this file descriptor, closing fd first if
+  // necessary.
+  void dup2(int fd, std::error_code& ec) noexcept;
+
+  // Creates a pipe setting up read_end and write_end file objects for reading
+  // and writing respectively.
+  static void pipe(file& read_end, file& write_end);
+
+  // Creates a buffered_file object associated with this file and detaches
+  // this file object from the file.
+  buffered_file fdopen(const char* mode);
+
+#  if defined(_WIN32) && !defined(__MINGW32__)
+  // Opens a file and constructs a file object representing this file by
+  // wcstring_view filename. Windows only.
+  static file open_windows_file(wcstring_view path, int oflag);
+#  endif
+};
+
+// Returns the memory page size.
+long getpagesize();
+
+namespace detail {
+
+struct buffer_size {
+  buffer_size() = default;
+  size_t value = 0;
+  buffer_size operator=(size_t val) const {
+    auto bs = buffer_size();
+    bs.value = val;
+    return bs;
+  }
+};
+
+struct ostream_params {
+  int oflag = file::WRONLY | file::CREATE | file::TRUNC;
+  size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
+
+  ostream_params() {}
+
+  template <typename... T>
+  ostream_params(T... params, int new_oflag) : ostream_params(params...) {
+    oflag = new_oflag;
+  }
+
+  template <typename... T>
+  ostream_params(T... params, detail::buffer_size bs)
+      : ostream_params(params...) {
+    this->buffer_size = bs.value;
+  }
+
+// Intel has a bug that results in failure to deduce a constructor
+// for empty parameter packs.
+#  if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
+  ostream_params(int new_oflag) : oflag(new_oflag) {}
+  ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
+#  endif
+};
+
+class file_buffer final : public buffer<char> {
+  file file_;
+
+  FMT_API void grow(size_t) override;
+
+ public:
+  FMT_API file_buffer(cstring_view path, const ostream_params& params);
+  FMT_API file_buffer(file_buffer&& other);
+  FMT_API ~file_buffer();
+
+  void flush() {
+    if (size() == 0) return;
+    file_.write(data(), size() * sizeof(data()[0]));
+    clear();
+  }
+
+  void close() {
+    flush();
+    file_.close();
+  }
+};
+
+}  // namespace detail
+
+// Added {} below to work around default constructor error known to
+// occur in Xcode versions 7.2.1 and 8.2.1.
+constexpr detail::buffer_size buffer_size{};
+
+/** A fast output stream which is not thread-safe. */
+class FMT_API ostream {
+ private:
+  FMT_MSC_WARNING(suppress : 4251)
+  detail::file_buffer buffer_;
+
+  ostream(cstring_view path, const detail::ostream_params& params)
+      : buffer_(path, params) {}
+
+ public:
+  ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
+
+  ~ostream();
+
+  void flush() { buffer_.flush(); }
+
+  template <typename... T>
+  friend ostream output_file(cstring_view path, T... params);
+
+  void close() { buffer_.close(); }
+
+  /**
+    Formats ``args`` according to specifications in ``fmt`` and writes the
+    output to the file.
+   */
+  template <typename... T> void print(format_string<T...> fmt, T&&... args) {
+    vformat_to(detail::buffer_appender<char>(buffer_), fmt,
+               fmt::make_format_args(args...));
+  }
+};
+
+/**
+  \rst
+  Opens a file for writing. Supported parameters passed in *params*:
+
+  * ``<integer>``: Flags passed to `open
+    <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
+    (``file::WRONLY | file::CREATE | file::TRUNC`` by default)
+  * ``buffer_size=<integer>``: Output buffer size
+
+  **Example**::
+
+    auto out = fmt::output_file("guide.txt");
+    out.print("Don't {}", "Panic");
+  \endrst
+ */
+template <typename... T>
+inline ostream output_file(cstring_view path, T... params) {
+  return {path, detail::ostream_params(params...)};
+}
+#endif  // FMT_USE_FCNTL
+
+FMT_END_EXPORT
+FMT_END_NAMESPACE
+
+#endif  // FMT_OS_H_
diff --git a/src/cpp-common/vendor/fmt/ostream.h b/src/cpp-common/vendor/fmt/ostream.h
new file mode 100644 (file)
index 0000000..a112fe7
--- /dev/null
@@ -0,0 +1,209 @@
+// Formatting library for C++ - std::ostream support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_OSTREAM_H_
+#define FMT_OSTREAM_H_
+
+#include <fstream>  // std::filebuf
+
+#if defined(_WIN32) && defined(__GLIBCXX__)
+#  include <ext/stdio_filebuf.h>
+#  include <ext/stdio_sync_filebuf.h>
+#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
+#  include <__std_stream>
+#endif
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+namespace detail {
+
+// Generate a unique explicit instantion in every translation unit using a tag
+// type in an anonymous namespace.
+namespace {
+struct file_access_tag {};
+}  // namespace
+template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
+class file_access {
+  friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
+};
+
+#if FMT_MSC_VERSION
+template class file_access<file_access_tag, std::filebuf,
+                           &std::filebuf::_Myfile>;
+auto get_file(std::filebuf&) -> FILE*;
+#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
+template class file_access<file_access_tag, std::__stdoutbuf<char>,
+                           &std::__stdoutbuf<char>::__file_>;
+auto get_file(std::__stdoutbuf<char>&) -> FILE*;
+#endif
+
+inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
+#if FMT_MSC_VERSION
+  if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
+    if (FILE* f = get_file(*buf)) return write_console(f, data);
+#elif defined(_WIN32) && defined(__GLIBCXX__)
+  auto* rdbuf = os.rdbuf();
+  FILE* c_file;
+  if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
+    c_file = sfbuf->file();
+  else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
+    c_file = fbuf->file();
+  else
+    return false;
+  if (c_file) return write_console(c_file, data);
+#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
+  if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
+    if (FILE* f = get_file(*buf)) return write_console(f, data);
+#else
+  ignore_unused(os, data);
+#endif
+  return false;
+}
+inline bool write_ostream_unicode(std::wostream&,
+                                  fmt::basic_string_view<wchar_t>) {
+  return false;
+}
+
+// Write the content of buf to os.
+// It is a separate function rather than a part of vprint to simplify testing.
+template <typename Char>
+void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
+  const Char* buf_data = buf.data();
+  using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
+  unsigned_streamsize size = buf.size();
+  unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
+  do {
+    unsigned_streamsize n = size <= max_size ? size : max_size;
+    os.write(buf_data, static_cast<std::streamsize>(n));
+    buf_data += n;
+    size -= n;
+  } while (size != 0);
+}
+
+template <typename Char, typename T>
+void format_value(buffer<Char>& buf, const T& value,
+                  locale_ref loc = locale_ref()) {
+  auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
+  auto&& output = std::basic_ostream<Char>(&format_buf);
+#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
+  if (loc) output.imbue(loc.get<std::locale>());
+#endif
+  output << value;
+  output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
+}
+
+template <typename T> struct streamed_view { const T& value; };
+
+}  // namespace detail
+
+// Formats an object of type T that has an overloaded ostream operator<<.
+template <typename Char>
+struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
+  void set_debug_format() = delete;
+
+  template <typename T, typename OutputIt>
+  auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
+      -> OutputIt {
+    auto buffer = basic_memory_buffer<Char>();
+    detail::format_value(buffer, value, ctx.locale());
+    return formatter<basic_string_view<Char>, Char>::format(
+        {buffer.data(), buffer.size()}, ctx);
+  }
+};
+
+using ostream_formatter = basic_ostream_formatter<char>;
+
+template <typename T, typename Char>
+struct formatter<detail::streamed_view<T>, Char>
+    : basic_ostream_formatter<Char> {
+  template <typename OutputIt>
+  auto format(detail::streamed_view<T> view,
+              basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
+    return basic_ostream_formatter<Char>::format(view.value, ctx);
+  }
+};
+
+/**
+  \rst
+  Returns a view that formats `value` via an ostream ``operator<<``.
+
+  **Example**::
+
+    fmt::print("Current thread id: {}\n",
+               fmt::streamed(std::this_thread::get_id()));
+  \endrst
+ */
+template <typename T>
+auto streamed(const T& value) -> detail::streamed_view<T> {
+  return {value};
+}
+
+namespace detail {
+
+inline void vprint_directly(std::ostream& os, string_view format_str,
+                            format_args args) {
+  auto buffer = memory_buffer();
+  detail::vformat_to(buffer, format_str, args);
+  detail::write_buffer(os, buffer);
+}
+
+}  // namespace detail
+
+FMT_EXPORT template <typename Char>
+void vprint(std::basic_ostream<Char>& os,
+            basic_string_view<type_identity_t<Char>> format_str,
+            basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  auto buffer = basic_memory_buffer<Char>();
+  detail::vformat_to(buffer, format_str, args);
+  if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
+  detail::write_buffer(os, buffer);
+}
+
+/**
+  \rst
+  Prints formatted data to the stream *os*.
+
+  **Example**::
+
+    fmt::print(cerr, "Don't {}!", "panic");
+  \endrst
+ */
+FMT_EXPORT template <typename... T>
+void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
+  const auto& vargs = fmt::make_format_args(args...);
+  if (detail::is_utf8())
+    vprint(os, fmt, vargs);
+  else
+    detail::vprint_directly(os, fmt, vargs);
+}
+
+FMT_EXPORT
+template <typename... Args>
+void print(std::wostream& os,
+           basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
+           Args&&... args) {
+  vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
+}
+
+FMT_EXPORT template <typename... T>
+void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
+  fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
+}
+
+FMT_EXPORT
+template <typename... Args>
+void println(std::wostream& os,
+             basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
+             Args&&... args) {
+  print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
+}
+
+FMT_END_NAMESPACE
+
+#endif  // FMT_OSTREAM_H_
diff --git a/src/cpp-common/vendor/fmt/printf.h b/src/cpp-common/vendor/fmt/printf.h
new file mode 100644 (file)
index 0000000..adef6ad
--- /dev/null
@@ -0,0 +1,667 @@
+// Formatting library for C++ - legacy printf implementation
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_PRINTF_H_
+#define FMT_PRINTF_H_
+
+#include <algorithm>  // std::max
+#include <limits>     // std::numeric_limits
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+FMT_BEGIN_EXPORT
+
+template <typename T> struct printf_formatter { printf_formatter() = delete; };
+
+template <typename Char> class basic_printf_context {
+ private:
+  detail::buffer_appender<Char> out_;
+  basic_format_args<basic_printf_context> args_;
+
+ public:
+  using char_type = Char;
+  using parse_context_type = basic_format_parse_context<Char>;
+  template <typename T> using formatter_type = printf_formatter<T>;
+
+  /**
+    \rst
+    Constructs a ``printf_context`` object. References to the arguments are
+    stored in the context object so make sure they have appropriate lifetimes.
+    \endrst
+   */
+  basic_printf_context(detail::buffer_appender<Char> out,
+                       basic_format_args<basic_printf_context> args)
+      : out_(out), args_(args) {}
+
+  auto out() -> detail::buffer_appender<Char> { return out_; }
+  void advance_to(detail::buffer_appender<Char>) {}
+
+  auto locale() -> detail::locale_ref { return {}; }
+
+  auto arg(int id) const -> basic_format_arg<basic_printf_context> {
+    return args_.get(id);
+  }
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    detail::error_handler().on_error(message);
+  }
+};
+
+namespace detail {
+
+// Checks if a value fits in int - used to avoid warnings about comparing
+// signed and unsigned integers.
+template <bool IsSigned> struct int_checker {
+  template <typename T> static auto fits_in_int(T value) -> bool {
+    unsigned max = max_value<int>();
+    return value <= max;
+  }
+  static auto fits_in_int(bool) -> bool { return true; }
+};
+
+template <> struct int_checker<true> {
+  template <typename T> static auto fits_in_int(T value) -> bool {
+    return value >= (std::numeric_limits<int>::min)() &&
+           value <= max_value<int>();
+  }
+  static auto fits_in_int(int) -> bool { return true; }
+};
+
+struct printf_precision_handler {
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  auto operator()(T value) -> int {
+    if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
+      throw_format_error("number is too big");
+    return (std::max)(static_cast<int>(value), 0);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+  auto operator()(T) -> int {
+    throw_format_error("precision is not integer");
+    return 0;
+  }
+};
+
+// An argument visitor that returns true iff arg is a zero integer.
+struct is_zero_int {
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  auto operator()(T value) -> bool {
+    return value == 0;
+  }
+
+  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+  auto operator()(T) -> bool {
+    return false;
+  }
+};
+
+template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
+
+template <> struct make_unsigned_or_bool<bool> { using type = bool; };
+
+template <typename T, typename Context> class arg_converter {
+ private:
+  using char_type = typename Context::char_type;
+
+  basic_format_arg<Context>& arg_;
+  char_type type_;
+
+ public:
+  arg_converter(basic_format_arg<Context>& arg, char_type type)
+      : arg_(arg), type_(type) {}
+
+  void operator()(bool value) {
+    if (type_ != 's') operator()<bool>(value);
+  }
+
+  template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
+  void operator()(U value) {
+    bool is_signed = type_ == 'd' || type_ == 'i';
+    using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
+    if (const_check(sizeof(target_type) <= sizeof(int))) {
+      // Extra casts are used to silence warnings.
+      if (is_signed) {
+        auto n = static_cast<int>(static_cast<target_type>(value));
+        arg_ = detail::make_arg<Context>(n);
+      } else {
+        using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
+        auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
+        arg_ = detail::make_arg<Context>(n);
+      }
+    } else {
+      if (is_signed) {
+        // glibc's printf doesn't sign extend arguments of smaller types:
+        //   std::printf("%lld", -42);  // prints "4294967254"
+        // but we don't have to do the same because it's a UB.
+        auto n = static_cast<long long>(value);
+        arg_ = detail::make_arg<Context>(n);
+      } else {
+        auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
+        arg_ = detail::make_arg<Context>(n);
+      }
+    }
+  }
+
+  template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
+  void operator()(U) {}  // No conversion needed for non-integral types.
+};
+
+// Converts an integer argument to T for printf, if T is an integral type.
+// If T is void, the argument is converted to corresponding signed or unsigned
+// type depending on the type specifier: 'd' and 'i' - signed, other -
+// unsigned).
+template <typename T, typename Context, typename Char>
+void convert_arg(basic_format_arg<Context>& arg, Char type) {
+  visit_format_arg(arg_converter<T, Context>(arg, type), arg);
+}
+
+// Converts an integer argument to char for printf.
+template <typename Context> class char_converter {
+ private:
+  basic_format_arg<Context>& arg_;
+
+ public:
+  explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
+
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  void operator()(T value) {
+    auto c = static_cast<typename Context::char_type>(value);
+    arg_ = detail::make_arg<Context>(c);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+  void operator()(T) {}  // No conversion needed for non-integral types.
+};
+
+// An argument visitor that return a pointer to a C string if argument is a
+// string or null otherwise.
+template <typename Char> struct get_cstring {
+  template <typename T> auto operator()(T) -> const Char* { return nullptr; }
+  auto operator()(const Char* s) -> const Char* { return s; }
+};
+
+// Checks if an argument is a valid printf width specifier and sets
+// left alignment if it is negative.
+template <typename Char> class printf_width_handler {
+ private:
+  format_specs<Char>& specs_;
+
+ public:
+  explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
+
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  auto operator()(T value) -> unsigned {
+    auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
+    if (detail::is_negative(value)) {
+      specs_.align = align::left;
+      width = 0 - width;
+    }
+    unsigned int_max = max_value<int>();
+    if (width > int_max) throw_format_error("number is too big");
+    return static_cast<unsigned>(width);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+  auto operator()(T) -> unsigned {
+    throw_format_error("width is not integer");
+    return 0;
+  }
+};
+
+// Workaround for a bug with the XL compiler when initializing
+// printf_arg_formatter's base class.
+template <typename Char>
+auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
+    -> arg_formatter<Char> {
+  return {iter, s, locale_ref()};
+}
+
+// The ``printf`` argument formatter.
+template <typename Char>
+class printf_arg_formatter : public arg_formatter<Char> {
+ private:
+  using base = arg_formatter<Char>;
+  using context_type = basic_printf_context<Char>;
+
+  context_type& context_;
+
+  void write_null_pointer(bool is_string = false) {
+    auto s = this->specs;
+    s.type = presentation_type::none;
+    write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
+  }
+
+ public:
+  printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
+                       context_type& ctx)
+      : base(make_arg_formatter(iter, s)), context_(ctx) {}
+
+  void operator()(monostate value) { base::operator()(value); }
+
+  template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
+  void operator()(T value) {
+    // MSVC2013 fails to compile separate overloads for bool and Char so use
+    // std::is_same instead.
+    if (!std::is_same<T, Char>::value) {
+      base::operator()(value);
+      return;
+    }
+    format_specs<Char> fmt_specs = this->specs;
+    if (fmt_specs.type != presentation_type::none &&
+        fmt_specs.type != presentation_type::chr) {
+      return (*this)(static_cast<int>(value));
+    }
+    fmt_specs.sign = sign::none;
+    fmt_specs.alt = false;
+    fmt_specs.fill[0] = ' ';  // Ignore '0' flag for char types.
+    // align::numeric needs to be overwritten here since the '0' flag is
+    // ignored for non-numeric types
+    if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
+      fmt_specs.align = align::right;
+    write<Char>(this->out, static_cast<Char>(value), fmt_specs);
+  }
+
+  template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+  void operator()(T value) {
+    base::operator()(value);
+  }
+
+  /** Formats a null-terminated C string. */
+  void operator()(const char* value) {
+    if (value)
+      base::operator()(value);
+    else
+      write_null_pointer(this->specs.type != presentation_type::pointer);
+  }
+
+  /** Formats a null-terminated wide C string. */
+  void operator()(const wchar_t* value) {
+    if (value)
+      base::operator()(value);
+    else
+      write_null_pointer(this->specs.type != presentation_type::pointer);
+  }
+
+  void operator()(basic_string_view<Char> value) { base::operator()(value); }
+
+  /** Formats a pointer. */
+  void operator()(const void* value) {
+    if (value)
+      base::operator()(value);
+    else
+      write_null_pointer();
+  }
+
+  /** Formats an argument of a custom (user-defined) type. */
+  void operator()(typename basic_format_arg<context_type>::handle handle) {
+    auto parse_ctx = basic_format_parse_context<Char>({});
+    handle.format(parse_ctx, context_);
+  }
+};
+
+template <typename Char>
+void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
+  for (; it != end; ++it) {
+    switch (*it) {
+    case '-':
+      specs.align = align::left;
+      break;
+    case '+':
+      specs.sign = sign::plus;
+      break;
+    case '0':
+      specs.fill[0] = '0';
+      break;
+    case ' ':
+      if (specs.sign != sign::plus) specs.sign = sign::space;
+      break;
+    case '#':
+      specs.alt = true;
+      break;
+    default:
+      return;
+    }
+  }
+}
+
+template <typename Char, typename GetArg>
+auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
+                  GetArg get_arg) -> int {
+  int arg_index = -1;
+  Char c = *it;
+  if (c >= '0' && c <= '9') {
+    // Parse an argument index (if followed by '$') or a width possibly
+    // preceded with '0' flag(s).
+    int value = parse_nonnegative_int(it, end, -1);
+    if (it != end && *it == '$') {  // value is an argument index
+      ++it;
+      arg_index = value != -1 ? value : max_value<int>();
+    } else {
+      if (c == '0') specs.fill[0] = '0';
+      if (value != 0) {
+        // Nonzero value means that we parsed width and don't need to
+        // parse it or flags again, so return now.
+        if (value == -1) throw_format_error("number is too big");
+        specs.width = value;
+        return arg_index;
+      }
+    }
+  }
+  parse_flags(specs, it, end);
+  // Parse width.
+  if (it != end) {
+    if (*it >= '0' && *it <= '9') {
+      specs.width = parse_nonnegative_int(it, end, -1);
+      if (specs.width == -1) throw_format_error("number is too big");
+    } else if (*it == '*') {
+      ++it;
+      specs.width = static_cast<int>(visit_format_arg(
+          detail::printf_width_handler<Char>(specs), get_arg(-1)));
+    }
+  }
+  return arg_index;
+}
+
+inline auto parse_printf_presentation_type(char c, type t)
+    -> presentation_type {
+  using pt = presentation_type;
+  constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
+  switch (c) {
+  case 'd':
+    return in(t, integral_set) ? pt::dec : pt::none;
+  case 'o':
+    return in(t, integral_set) ? pt::oct : pt::none;
+  case 'x':
+    return in(t, integral_set) ? pt::hex_lower : pt::none;
+  case 'X':
+    return in(t, integral_set) ? pt::hex_upper : pt::none;
+  case 'a':
+    return in(t, float_set) ? pt::hexfloat_lower : pt::none;
+  case 'A':
+    return in(t, float_set) ? pt::hexfloat_upper : pt::none;
+  case 'e':
+    return in(t, float_set) ? pt::exp_lower : pt::none;
+  case 'E':
+    return in(t, float_set) ? pt::exp_upper : pt::none;
+  case 'f':
+    return in(t, float_set) ? pt::fixed_lower : pt::none;
+  case 'F':
+    return in(t, float_set) ? pt::fixed_upper : pt::none;
+  case 'g':
+    return in(t, float_set) ? pt::general_lower : pt::none;
+  case 'G':
+    return in(t, float_set) ? pt::general_upper : pt::none;
+  case 'c':
+    return in(t, integral_set) ? pt::chr : pt::none;
+  case 's':
+    return in(t, string_set | cstring_set) ? pt::string : pt::none;
+  case 'p':
+    return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
+  default:
+    return pt::none;
+  }
+}
+
+template <typename Char, typename Context>
+void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
+             basic_format_args<Context> args) {
+  using iterator = buffer_appender<Char>;
+  auto out = iterator(buf);
+  auto context = basic_printf_context<Char>(out, args);
+  auto parse_ctx = basic_format_parse_context<Char>(format);
+
+  // Returns the argument with specified index or, if arg_index is -1, the next
+  // argument.
+  auto get_arg = [&](int arg_index) {
+    if (arg_index < 0)
+      arg_index = parse_ctx.next_arg_id();
+    else
+      parse_ctx.check_arg_id(--arg_index);
+    return detail::get_arg(context, arg_index);
+  };
+
+  const Char* start = parse_ctx.begin();
+  const Char* end = parse_ctx.end();
+  auto it = start;
+  while (it != end) {
+    if (!find<false, Char>(it, end, '%', it)) {
+      it = end;  // find leaves it == nullptr if it doesn't find '%'.
+      break;
+    }
+    Char c = *it++;
+    if (it != end && *it == c) {
+      write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
+      start = ++it;
+      continue;
+    }
+    write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
+
+    auto specs = format_specs<Char>();
+    specs.align = align::right;
+
+    // Parse argument index, flags and width.
+    int arg_index = parse_header(it, end, specs, get_arg);
+    if (arg_index == 0) throw_format_error("argument not found");
+
+    // Parse precision.
+    if (it != end && *it == '.') {
+      ++it;
+      c = it != end ? *it : 0;
+      if ('0' <= c && c <= '9') {
+        specs.precision = parse_nonnegative_int(it, end, 0);
+      } else if (c == '*') {
+        ++it;
+        specs.precision = static_cast<int>(
+            visit_format_arg(printf_precision_handler(), get_arg(-1)));
+      } else {
+        specs.precision = 0;
+      }
+    }
+
+    auto arg = get_arg(arg_index);
+    // For d, i, o, u, x, and X conversion specifiers, if a precision is
+    // specified, the '0' flag is ignored
+    if (specs.precision >= 0 && arg.is_integral()) {
+      // Ignore '0' for non-numeric types or if '-' present.
+      specs.fill[0] = ' ';
+    }
+    if (specs.precision >= 0 && arg.type() == type::cstring_type) {
+      auto str = visit_format_arg(get_cstring<Char>(), arg);
+      auto str_end = str + specs.precision;
+      auto nul = std::find(str, str_end, Char());
+      auto sv = basic_string_view<Char>(
+          str, to_unsigned(nul != str_end ? nul - str : specs.precision));
+      arg = make_arg<basic_printf_context<Char>>(sv);
+    }
+    if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
+    if (specs.fill[0] == '0') {
+      if (arg.is_arithmetic() && specs.align != align::left)
+        specs.align = align::numeric;
+      else
+        specs.fill[0] = ' ';  // Ignore '0' flag for non-numeric types or if '-'
+                              // flag is also present.
+    }
+
+    // Parse length and convert the argument to the required type.
+    c = it != end ? *it++ : 0;
+    Char t = it != end ? *it : 0;
+    switch (c) {
+    case 'h':
+      if (t == 'h') {
+        ++it;
+        t = it != end ? *it : 0;
+        convert_arg<signed char>(arg, t);
+      } else {
+        convert_arg<short>(arg, t);
+      }
+      break;
+    case 'l':
+      if (t == 'l') {
+        ++it;
+        t = it != end ? *it : 0;
+        convert_arg<long long>(arg, t);
+      } else {
+        convert_arg<long>(arg, t);
+      }
+      break;
+    case 'j':
+      convert_arg<intmax_t>(arg, t);
+      break;
+    case 'z':
+      convert_arg<size_t>(arg, t);
+      break;
+    case 't':
+      convert_arg<std::ptrdiff_t>(arg, t);
+      break;
+    case 'L':
+      // printf produces garbage when 'L' is omitted for long double, no
+      // need to do the same.
+      break;
+    default:
+      --it;
+      convert_arg<void>(arg, c);
+    }
+
+    // Parse type.
+    if (it == end) throw_format_error("invalid format string");
+    char type = static_cast<char>(*it++);
+    if (arg.is_integral()) {
+      // Normalize type.
+      switch (type) {
+      case 'i':
+      case 'u':
+        type = 'd';
+        break;
+      case 'c':
+        visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
+        break;
+      }
+    }
+    specs.type = parse_printf_presentation_type(type, arg.type());
+    if (specs.type == presentation_type::none)
+      throw_format_error("invalid format specifier");
+
+    start = it;
+
+    // Format argument.
+    visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
+  }
+  write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
+}
+}  // namespace detail
+
+using printf_context = basic_printf_context<char>;
+using wprintf_context = basic_printf_context<wchar_t>;
+
+using printf_args = basic_format_args<printf_context>;
+using wprintf_args = basic_format_args<wprintf_context>;
+
+/**
+  \rst
+  Constructs an `~fmt::format_arg_store` object that contains references to
+  arguments and can be implicitly converted to `~fmt::printf_args`.
+  \endrst
+ */
+template <typename... T>
+inline auto make_printf_args(const T&... args)
+    -> format_arg_store<printf_context, T...> {
+  return {args...};
+}
+
+// DEPRECATED!
+template <typename... T>
+inline auto make_wprintf_args(const T&... args)
+    -> format_arg_store<wprintf_context, T...> {
+  return {args...};
+}
+
+template <typename Char>
+inline auto vsprintf(
+    basic_string_view<Char> fmt,
+    basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
+    -> std::basic_string<Char> {
+  auto buf = basic_memory_buffer<Char>();
+  detail::vprintf(buf, fmt, args);
+  return to_string(buf);
+}
+
+/**
+  \rst
+  Formats arguments and returns the result as a string.
+
+  **Example**::
+
+    std::string message = fmt::sprintf("The answer is %d", 42);
+  \endrst
+*/
+template <typename S, typename... T,
+          typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
+inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
+  return vsprintf(detail::to_string_view(fmt),
+                  fmt::make_format_args<basic_printf_context<Char>>(args...));
+}
+
+template <typename Char>
+inline auto vfprintf(
+    std::FILE* f, basic_string_view<Char> fmt,
+    basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
+    -> int {
+  auto buf = basic_memory_buffer<Char>();
+  detail::vprintf(buf, fmt, args);
+  size_t size = buf.size();
+  return std::fwrite(buf.data(), sizeof(Char), size, f) < size
+             ? -1
+             : static_cast<int>(size);
+}
+
+/**
+  \rst
+  Prints formatted data to the file *f*.
+
+  **Example**::
+
+    fmt::fprintf(stderr, "Don't %s!", "panic");
+  \endrst
+ */
+template <typename S, typename... T, typename Char = char_t<S>>
+inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
+  return vfprintf(f, detail::to_string_view(fmt),
+                  fmt::make_format_args<basic_printf_context<Char>>(args...));
+}
+
+template <typename Char>
+FMT_DEPRECATED inline auto vprintf(
+    basic_string_view<Char> fmt,
+    basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
+    -> int {
+  return vfprintf(stdout, fmt, args);
+}
+
+/**
+  \rst
+  Prints formatted data to ``stdout``.
+
+  **Example**::
+
+    fmt::printf("Elapsed time: %.2f seconds", 1.23);
+  \endrst
+ */
+template <typename... T>
+inline auto printf(string_view fmt, const T&... args) -> int {
+  return vfprintf(stdout, fmt, make_printf_args(args...));
+}
+template <typename... T>
+FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
+                                  const T&... args) -> int {
+  return vfprintf(stdout, fmt, make_wprintf_args(args...));
+}
+
+FMT_END_EXPORT
+FMT_END_NAMESPACE
+
+#endif  // FMT_PRINTF_H_
diff --git a/src/cpp-common/vendor/fmt/ranges.h b/src/cpp-common/vendor/fmt/ranges.h
new file mode 100644 (file)
index 0000000..65beba5
--- /dev/null
@@ -0,0 +1,735 @@
+// Formatting library for C++ - experimental range support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+//
+// Copyright (c) 2018 - present, Remotion (Igor Schulz)
+// All Rights Reserved
+// {fmt} support for ranges, containers and types tuple interface.
+
+#ifndef FMT_RANGES_H_
+#define FMT_RANGES_H_
+
+#include <initializer_list>
+#include <tuple>
+#include <type_traits>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+namespace detail {
+
+template <typename Range, typename OutputIt>
+auto copy(const Range& range, OutputIt out) -> OutputIt {
+  for (auto it = range.begin(), end = range.end(); it != end; ++it)
+    *out++ = *it;
+  return out;
+}
+
+template <typename OutputIt>
+auto copy(const char* str, OutputIt out) -> OutputIt {
+  while (*str) *out++ = *str++;
+  return out;
+}
+
+template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
+  *out++ = ch;
+  return out;
+}
+
+template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
+  *out++ = ch;
+  return out;
+}
+
+// Returns true if T has a std::string-like interface, like std::string_view.
+template <typename T> class is_std_string_like {
+  template <typename U>
+  static auto check(U* p)
+      -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
+  template <typename> static void check(...);
+
+ public:
+  static constexpr const bool value =
+      is_string<T>::value ||
+      std::is_convertible<T, std_string_view<char>>::value ||
+      !std::is_void<decltype(check<T>(nullptr))>::value;
+};
+
+template <typename Char>
+struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
+
+template <typename T> class is_map {
+  template <typename U> static auto check(U*) -> typename U::mapped_type;
+  template <typename> static void check(...);
+
+ public:
+#ifdef FMT_FORMAT_MAP_AS_LIST  // DEPRECATED!
+  static constexpr const bool value = false;
+#else
+  static constexpr const bool value =
+      !std::is_void<decltype(check<T>(nullptr))>::value;
+#endif
+};
+
+template <typename T> class is_set {
+  template <typename U> static auto check(U*) -> typename U::key_type;
+  template <typename> static void check(...);
+
+ public:
+#ifdef FMT_FORMAT_SET_AS_LIST  // DEPRECATED!
+  static constexpr const bool value = false;
+#else
+  static constexpr const bool value =
+      !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
+#endif
+};
+
+template <typename... Ts> struct conditional_helper {};
+
+template <typename T, typename _ = void> struct is_range_ : std::false_type {};
+
+#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
+
+#  define FMT_DECLTYPE_RETURN(val)  \
+    ->decltype(val) { return val; } \
+    static_assert(                  \
+        true, "")  // This makes it so that a semicolon is required after the
+                   // macro, which helps clang-format handle the formatting.
+
+// C array overload
+template <typename T, std::size_t N>
+auto range_begin(const T (&arr)[N]) -> const T* {
+  return arr;
+}
+template <typename T, std::size_t N>
+auto range_end(const T (&arr)[N]) -> const T* {
+  return arr + N;
+}
+
+template <typename T, typename Enable = void>
+struct has_member_fn_begin_end_t : std::false_type {};
+
+template <typename T>
+struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
+                                           decltype(std::declval<T>().end())>>
+    : std::true_type {};
+
+// Member function overload
+template <typename T>
+auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
+template <typename T>
+auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
+
+// ADL overload. Only participates in overload resolution if member functions
+// are not found.
+template <typename T>
+auto range_begin(T&& rng)
+    -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
+                   decltype(begin(static_cast<T&&>(rng)))> {
+  return begin(static_cast<T&&>(rng));
+}
+template <typename T>
+auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
+                                       decltype(end(static_cast<T&&>(rng)))> {
+  return end(static_cast<T&&>(rng));
+}
+
+template <typename T, typename Enable = void>
+struct has_const_begin_end : std::false_type {};
+template <typename T, typename Enable = void>
+struct has_mutable_begin_end : std::false_type {};
+
+template <typename T>
+struct has_const_begin_end<
+    T,
+    void_t<
+        decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
+        decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
+    : std::true_type {};
+
+template <typename T>
+struct has_mutable_begin_end<
+    T, void_t<decltype(detail::range_begin(std::declval<T>())),
+              decltype(detail::range_end(std::declval<T>())),
+              // the extra int here is because older versions of MSVC don't
+              // SFINAE properly unless there are distinct types
+              int>> : std::true_type {};
+
+template <typename T>
+struct is_range_<T, void>
+    : std::integral_constant<bool, (has_const_begin_end<T>::value ||
+                                    has_mutable_begin_end<T>::value)> {};
+#  undef FMT_DECLTYPE_RETURN
+#endif
+
+// tuple_size and tuple_element check.
+template <typename T> class is_tuple_like_ {
+  template <typename U>
+  static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
+  template <typename> static void check(...);
+
+ public:
+  static constexpr const bool value =
+      !std::is_void<decltype(check<T>(nullptr))>::value;
+};
+
+// Check for integer_sequence
+#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
+template <typename T, T... N>
+using integer_sequence = std::integer_sequence<T, N...>;
+template <size_t... N> using index_sequence = std::index_sequence<N...>;
+template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
+#else
+template <typename T, T... N> struct integer_sequence {
+  using value_type = T;
+
+  static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
+};
+
+template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
+
+template <typename T, size_t N, T... Ns>
+struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
+template <typename T, T... Ns>
+struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
+
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+#endif
+
+template <typename T>
+using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
+
+template <typename T, typename C, bool = is_tuple_like_<T>::value>
+class is_tuple_formattable_ {
+ public:
+  static constexpr const bool value = false;
+};
+template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
+  template <std::size_t... Is>
+  static std::true_type check2(index_sequence<Is...>,
+                               integer_sequence<bool, (Is == Is)...>);
+  static std::false_type check2(...);
+  template <std::size_t... Is>
+  static decltype(check2(
+      index_sequence<Is...>{},
+      integer_sequence<
+          bool, (is_formattable<typename std::tuple_element<Is, T>::type,
+                                C>::value)...>{})) check(index_sequence<Is...>);
+
+ public:
+  static constexpr const bool value =
+      decltype(check(tuple_index_sequence<T>{}))::value;
+};
+
+template <typename Tuple, typename F, size_t... Is>
+FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
+  using std::get;
+  // Using a free function get<Is>(Tuple) now.
+  const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
+  ignore_unused(unused);
+}
+
+template <typename Tuple, typename F>
+FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
+  for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
+           std::forward<Tuple>(t), std::forward<F>(f));
+}
+
+template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
+void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
+  using std::get;
+  const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
+  ignore_unused(unused);
+}
+
+template <typename Tuple1, typename Tuple2, typename F>
+void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
+  for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
+            std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
+            std::forward<F>(f));
+}
+
+namespace tuple {
+// Workaround a bug in MSVC 2019 (v140).
+template <typename Char, typename... T>
+using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
+
+using std::get;
+template <typename Tuple, typename Char, std::size_t... Is>
+auto get_formatters(index_sequence<Is...>)
+    -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
+}  // namespace tuple
+
+#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
+// Older MSVC doesn't get the reference type correctly for arrays.
+template <typename R> struct range_reference_type_impl {
+  using type = decltype(*detail::range_begin(std::declval<R&>()));
+};
+
+template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
+  using type = T&;
+};
+
+template <typename T>
+using range_reference_type = typename range_reference_type_impl<T>::type;
+#else
+template <typename Range>
+using range_reference_type =
+    decltype(*detail::range_begin(std::declval<Range&>()));
+#endif
+
+// We don't use the Range's value_type for anything, but we do need the Range's
+// reference type, with cv-ref stripped.
+template <typename Range>
+using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
+
+template <typename Formatter>
+FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
+    -> decltype(f.set_debug_format(set)) {
+  f.set_debug_format(set);
+}
+template <typename Formatter>
+FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
+
+// These are not generic lambdas for compatibility with C++11.
+template <typename ParseContext> struct parse_empty_specs {
+  template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
+    f.parse(ctx);
+    detail::maybe_set_debug_format(f, true);
+  }
+  ParseContext& ctx;
+};
+template <typename FormatContext> struct format_tuple_element {
+  using char_type = typename FormatContext::char_type;
+
+  template <typename T>
+  void operator()(const formatter<T, char_type>& f, const T& v) {
+    if (i > 0)
+      ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
+    ctx.advance_to(f.format(v, ctx));
+    ++i;
+  }
+
+  int i;
+  FormatContext& ctx;
+  basic_string_view<char_type> separator;
+};
+
+}  // namespace detail
+
+template <typename T> struct is_tuple_like {
+  static constexpr const bool value =
+      detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
+};
+
+template <typename T, typename C> struct is_tuple_formattable {
+  static constexpr const bool value =
+      detail::is_tuple_formattable_<T, C>::value;
+};
+
+template <typename Tuple, typename Char>
+struct formatter<Tuple, Char,
+                 enable_if_t<fmt::is_tuple_like<Tuple>::value &&
+                             fmt::is_tuple_formattable<Tuple, Char>::value>> {
+ private:
+  decltype(detail::tuple::get_formatters<Tuple, Char>(
+      detail::tuple_index_sequence<Tuple>())) formatters_;
+
+  basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
+  basic_string_view<Char> opening_bracket_ =
+      detail::string_literal<Char, '('>{};
+  basic_string_view<Char> closing_bracket_ =
+      detail::string_literal<Char, ')'>{};
+
+ public:
+  FMT_CONSTEXPR formatter() {}
+
+  FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
+    separator_ = sep;
+  }
+
+  FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
+                                  basic_string_view<Char> close) {
+    opening_bracket_ = open;
+    closing_bracket_ = close;
+  }
+
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    auto it = ctx.begin();
+    if (it != ctx.end() && *it != '}')
+      FMT_THROW(format_error("invalid format specifier"));
+    detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
+    return it;
+  }
+
+  template <typename FormatContext>
+  auto format(const Tuple& value, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
+    detail::for_each2(
+        formatters_, value,
+        detail::format_tuple_element<FormatContext>{0, ctx, separator_});
+    return detail::copy_str<Char>(closing_bracket_, ctx.out());
+  }
+};
+
+template <typename T, typename Char> struct is_range {
+  static constexpr const bool value =
+      detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
+      !std::is_convertible<T, std::basic_string<Char>>::value &&
+      !std::is_convertible<T, detail::std_string_view<Char>>::value;
+};
+
+namespace detail {
+template <typename Context> struct range_mapper {
+  using mapper = arg_mapper<Context>;
+
+  template <typename T,
+            FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
+  static auto map(T&& value) -> T&& {
+    return static_cast<T&&>(value);
+  }
+  template <typename T,
+            FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
+  static auto map(T&& value)
+      -> decltype(mapper().map(static_cast<T&&>(value))) {
+    return mapper().map(static_cast<T&&>(value));
+  }
+};
+
+template <typename Char, typename Element>
+using range_formatter_type =
+    formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
+                  std::declval<Element>()))>,
+              Char>;
+
+template <typename R>
+using maybe_const_range =
+    conditional_t<has_const_begin_end<R>::value, const R, R>;
+
+// Workaround a bug in MSVC 2015 and earlier.
+#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
+template <typename R, typename Char>
+struct is_formattable_delayed
+    : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
+#endif
+}  // namespace detail
+
+template <typename T, typename Char, typename Enable = void>
+struct range_formatter;
+
+template <typename T, typename Char>
+struct range_formatter<
+    T, Char,
+    enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
+                            is_formattable<T, Char>>::value>> {
+ private:
+  detail::range_formatter_type<Char, T> underlying_;
+  basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
+  basic_string_view<Char> opening_bracket_ =
+      detail::string_literal<Char, '['>{};
+  basic_string_view<Char> closing_bracket_ =
+      detail::string_literal<Char, ']'>{};
+
+ public:
+  FMT_CONSTEXPR range_formatter() {}
+
+  FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
+    return underlying_;
+  }
+
+  FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
+    separator_ = sep;
+  }
+
+  FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
+                                  basic_string_view<Char> close) {
+    opening_bracket_ = open;
+    closing_bracket_ = close;
+  }
+
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    auto it = ctx.begin();
+    auto end = ctx.end();
+
+    if (it != end && *it == 'n') {
+      set_brackets({}, {});
+      ++it;
+    }
+
+    if (it != end && *it != '}') {
+      if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
+      ++it;
+    } else {
+      detail::maybe_set_debug_format(underlying_, true);
+    }
+
+    ctx.advance_to(it);
+    return underlying_.parse(ctx);
+  }
+
+  template <typename R, typename FormatContext>
+  auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
+    detail::range_mapper<buffer_context<Char>> mapper;
+    auto out = ctx.out();
+    out = detail::copy_str<Char>(opening_bracket_, out);
+    int i = 0;
+    auto it = detail::range_begin(range);
+    auto end = detail::range_end(range);
+    for (; it != end; ++it) {
+      if (i > 0) out = detail::copy_str<Char>(separator_, out);
+      ctx.advance_to(out);
+      out = underlying_.format(mapper.map(*it), ctx);
+      ++i;
+    }
+    out = detail::copy_str<Char>(closing_bracket_, out);
+    return out;
+  }
+};
+
+enum class range_format { disabled, map, set, sequence, string, debug_string };
+
+namespace detail {
+template <typename T>
+struct range_format_kind_
+    : std::integral_constant<range_format,
+                             std::is_same<uncvref_type<T>, T>::value
+                                 ? range_format::disabled
+                             : is_map<T>::value ? range_format::map
+                             : is_set<T>::value ? range_format::set
+                                                : range_format::sequence> {};
+
+template <range_format K, typename R, typename Char, typename Enable = void>
+struct range_default_formatter;
+
+template <range_format K>
+using range_format_constant = std::integral_constant<range_format, K>;
+
+template <range_format K, typename R, typename Char>
+struct range_default_formatter<
+    K, R, Char,
+    enable_if_t<(K == range_format::sequence || K == range_format::map ||
+                 K == range_format::set)>> {
+  using range_type = detail::maybe_const_range<R>;
+  range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
+
+  FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
+
+  FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
+    underlying_.set_brackets(detail::string_literal<Char, '{'>{},
+                             detail::string_literal<Char, '}'>{});
+  }
+
+  FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
+    underlying_.set_brackets(detail::string_literal<Char, '{'>{},
+                             detail::string_literal<Char, '}'>{});
+    underlying_.underlying().set_brackets({}, {});
+    underlying_.underlying().set_separator(
+        detail::string_literal<Char, ':', ' '>{});
+  }
+
+  FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
+
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return underlying_.parse(ctx);
+  }
+
+  template <typename FormatContext>
+  auto format(range_type& range, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return underlying_.format(range, ctx);
+  }
+};
+}  // namespace detail
+
+template <typename T, typename Char, typename Enable = void>
+struct range_format_kind
+    : conditional_t<
+          is_range<T, Char>::value, detail::range_format_kind_<T>,
+          std::integral_constant<range_format, range_format::disabled>> {};
+
+template <typename R, typename Char>
+struct formatter<
+    R, Char,
+    enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
+                                          range_format::disabled>
+// Workaround a bug in MSVC 2015 and earlier.
+#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
+                            ,
+                            detail::is_formattable_delayed<R, Char>
+#endif
+                            >::value>>
+    : detail::range_default_formatter<range_format_kind<R, Char>::value, R,
+                                      Char> {
+};
+
+template <typename Char, typename... T> struct tuple_join_view : detail::view {
+  const std::tuple<T...>& tuple;
+  basic_string_view<Char> sep;
+
+  tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
+      : tuple(t), sep{s} {}
+};
+
+// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
+// support in tuple_join. It is disabled by default because of issues with
+// the dynamic width and precision.
+#ifndef FMT_TUPLE_JOIN_SPECIFIERS
+#  define FMT_TUPLE_JOIN_SPECIFIERS 0
+#endif
+
+template <typename Char, typename... T>
+struct formatter<tuple_join_view<Char, T...>, Char> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
+  }
+
+  template <typename FormatContext>
+  auto format(const tuple_join_view<Char, T...>& value,
+              FormatContext& ctx) const -> typename FormatContext::iterator {
+    return do_format(value, ctx,
+                     std::integral_constant<size_t, sizeof...(T)>());
+  }
+
+ private:
+  std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
+
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
+                              std::integral_constant<size_t, 0>)
+      -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename ParseContext, size_t N>
+  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
+                              std::integral_constant<size_t, N>)
+      -> decltype(ctx.begin()) {
+    auto end = ctx.begin();
+#if FMT_TUPLE_JOIN_SPECIFIERS
+    end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
+    if (N > 1) {
+      auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
+      if (end != end1)
+        FMT_THROW(format_error("incompatible format specs for tuple elements"));
+    }
+#endif
+    return end;
+  }
+
+  template <typename FormatContext>
+  auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
+                 std::integral_constant<size_t, 0>) const ->
+      typename FormatContext::iterator {
+    return ctx.out();
+  }
+
+  template <typename FormatContext, size_t N>
+  auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
+                 std::integral_constant<size_t, N>) const ->
+      typename FormatContext::iterator {
+    auto out = std::get<sizeof...(T) - N>(formatters_)
+                   .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
+    if (N > 1) {
+      out = std::copy(value.sep.begin(), value.sep.end(), out);
+      ctx.advance_to(out);
+      return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
+    }
+    return out;
+  }
+};
+
+namespace detail {
+// Check if T has an interface like a container adaptor (e.g. std::stack,
+// std::queue, std::priority_queue).
+template <typename T> class is_container_adaptor_like {
+  template <typename U> static auto check(U* p) -> typename U::container_type;
+  template <typename> static void check(...);
+
+ public:
+  static constexpr const bool value =
+      !std::is_void<decltype(check<T>(nullptr))>::value;
+};
+
+template <typename Container> struct all {
+  const Container& c;
+  auto begin() const -> typename Container::const_iterator { return c.begin(); }
+  auto end() const -> typename Container::const_iterator { return c.end(); }
+};
+}  // namespace detail
+
+template <typename T, typename Char>
+struct formatter<
+    T, Char,
+    enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
+                            bool_constant<range_format_kind<T, Char>::value ==
+                                          range_format::disabled>>::value>>
+    : formatter<detail::all<typename T::container_type>, Char> {
+  using all = detail::all<typename T::container_type>;
+  template <typename FormatContext>
+  auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
+    struct getter : T {
+      static auto get(const T& t) -> all {
+        return {t.*(&getter::c)};  // Access c through the derived class.
+      }
+    };
+    return formatter<all>::format(getter::get(t), ctx);
+  }
+};
+
+FMT_BEGIN_EXPORT
+
+/**
+  \rst
+  Returns an object that formats `tuple` with elements separated by `sep`.
+
+  **Example**::
+
+    std::tuple<int, char> t = {1, 'a'};
+    fmt::print("{}", fmt::join(t, ", "));
+    // Output: "1, a"
+  \endrst
+ */
+template <typename... T>
+FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
+    -> tuple_join_view<char, T...> {
+  return {tuple, sep};
+}
+
+template <typename... T>
+FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
+                        basic_string_view<wchar_t> sep)
+    -> tuple_join_view<wchar_t, T...> {
+  return {tuple, sep};
+}
+
+/**
+  \rst
+  Returns an object that formats `initializer_list` with elements separated by
+  `sep`.
+
+  **Example**::
+
+    fmt::print("{}", fmt::join({1, 2, 3}, ", "));
+    // Output: "1, 2, 3"
+  \endrst
+ */
+template <typename T>
+auto join(std::initializer_list<T> list, string_view sep)
+    -> join_view<const T*, const T*> {
+  return join(std::begin(list), std::end(list), sep);
+}
+
+FMT_END_EXPORT
+FMT_END_NAMESPACE
+
+#endif  // FMT_RANGES_H_
diff --git a/src/cpp-common/vendor/fmt/std.h b/src/cpp-common/vendor/fmt/std.h
new file mode 100644 (file)
index 0000000..b4e055c
--- /dev/null
@@ -0,0 +1,465 @@
+// Formatting library for C++ - formatters for standard library types
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_STD_H_
+#define FMT_STD_H_
+
+#include <atomic>
+#include <bitset>
+#include <cstdlib>
+#include <exception>
+#include <memory>
+#include <thread>
+#include <type_traits>
+#include <typeinfo>
+#include <utility>
+#include <vector>
+
+#include "format.h"
+#include "ostream.h"
+
+#if FMT_HAS_INCLUDE(<version>)
+#  include <version>
+#endif
+// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
+#if FMT_CPLUSPLUS >= 201703L
+#  if FMT_HAS_INCLUDE(<filesystem>)
+#    include <filesystem>
+#  endif
+#  if FMT_HAS_INCLUDE(<variant>)
+#    include <variant>
+#  endif
+#  if FMT_HAS_INCLUDE(<optional>)
+#    include <optional>
+#  endif
+#endif
+
+// GCC 4 does not support FMT_HAS_INCLUDE.
+#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
+#  include <cxxabi.h>
+// Android NDK with gabi++ library on some architectures does not implement
+// abi::__cxa_demangle().
+#  ifndef __GABIXX_CXXABI_H__
+#    define FMT_HAS_ABI_CXA_DEMANGLE
+#  endif
+#endif
+
+// Check if typeid is available.
+#ifndef FMT_USE_TYPEID
+// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
+#  if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
+      defined(__INTEL_RTTI__) || defined(__RTTI)
+#    define FMT_USE_TYPEID 1
+#  else
+#    define FMT_USE_TYPEID 0
+#  endif
+#endif
+
+#ifdef __cpp_lib_filesystem
+FMT_BEGIN_NAMESPACE
+
+namespace detail {
+
+template <typename Char> auto get_path_string(const std::filesystem::path& p) {
+  return p.string<Char>();
+}
+
+template <typename Char>
+void write_escaped_path(basic_memory_buffer<Char>& quoted,
+                        const std::filesystem::path& p) {
+  write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
+}
+
+#  ifdef _WIN32
+template <>
+inline auto get_path_string<char>(const std::filesystem::path& p) {
+  return to_utf8<wchar_t>(p.native(), to_utf8_error_policy::replace);
+}
+
+template <>
+inline void write_escaped_path<char>(memory_buffer& quoted,
+                                     const std::filesystem::path& p) {
+  auto buf = basic_memory_buffer<wchar_t>();
+  write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
+  bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
+  FMT_ASSERT(valid, "invalid utf16");
+}
+#  endif  // _WIN32
+
+template <>
+inline void write_escaped_path<std::filesystem::path::value_type>(
+    basic_memory_buffer<std::filesystem::path::value_type>& quoted,
+    const std::filesystem::path& p) {
+  write_escaped_string<std::filesystem::path::value_type>(
+      std::back_inserter(quoted), p.native());
+}
+
+}  // namespace detail
+
+FMT_EXPORT
+template <typename Char> struct formatter<std::filesystem::path, Char> {
+ private:
+  format_specs<Char> specs_;
+  detail::arg_ref<Char> width_ref_;
+  bool debug_ = false;
+
+ public:
+  FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
+
+  template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
+    auto it = ctx.begin(), end = ctx.end();
+    if (it == end) return it;
+
+    it = detail::parse_align(it, end, specs_);
+    if (it == end) return it;
+
+    it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
+    if (it != end && *it == '?') {
+      debug_ = true;
+      ++it;
+    }
+    return it;
+  }
+
+  template <typename FormatContext>
+  auto format(const std::filesystem::path& p, FormatContext& ctx) const {
+    auto specs = specs_;
+    detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
+                                                       ctx);
+    if (!debug_) {
+      auto s = detail::get_path_string<Char>(p);
+      return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
+    }
+    auto quoted = basic_memory_buffer<Char>();
+    detail::write_escaped_path(quoted, p);
+    return detail::write(ctx.out(),
+                         basic_string_view<Char>(quoted.data(), quoted.size()),
+                         specs);
+  }
+};
+FMT_END_NAMESPACE
+#endif
+
+FMT_BEGIN_NAMESPACE
+FMT_EXPORT
+template <typename Char>
+struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
+FMT_END_NAMESPACE
+
+#ifdef __cpp_lib_optional
+FMT_BEGIN_NAMESPACE
+FMT_EXPORT
+template <typename T, typename Char>
+struct formatter<std::optional<T>, Char,
+                 std::enable_if_t<is_formattable<T, Char>::value>> {
+ private:
+  formatter<T, Char> underlying_;
+  static constexpr basic_string_view<Char> optional =
+      detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
+                             '('>{};
+  static constexpr basic_string_view<Char> none =
+      detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
+
+  template <class U>
+  FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
+      -> decltype(u.set_debug_format(set)) {
+    u.set_debug_format(set);
+  }
+
+  template <class U>
+  FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
+
+ public:
+  template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
+    maybe_set_debug_format(underlying_, true);
+    return underlying_.parse(ctx);
+  }
+
+  template <typename FormatContext>
+  auto format(std::optional<T> const& opt, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    if (!opt) return detail::write<Char>(ctx.out(), none);
+
+    auto out = ctx.out();
+    out = detail::write<Char>(out, optional);
+    ctx.advance_to(out);
+    out = underlying_.format(*opt, ctx);
+    return detail::write(out, ')');
+  }
+};
+FMT_END_NAMESPACE
+#endif  // __cpp_lib_optional
+
+#ifdef __cpp_lib_variant
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+template <typename T>
+using variant_index_sequence =
+    std::make_index_sequence<std::variant_size<T>::value>;
+
+template <typename> struct is_variant_like_ : std::false_type {};
+template <typename... Types>
+struct is_variant_like_<std::variant<Types...>> : std::true_type {};
+
+// formattable element check.
+template <typename T, typename C> class is_variant_formattable_ {
+  template <std::size_t... Is>
+  static std::conjunction<
+      is_formattable<std::variant_alternative_t<Is, T>, C>...>
+      check(std::index_sequence<Is...>);
+
+ public:
+  static constexpr const bool value =
+      decltype(check(variant_index_sequence<T>{}))::value;
+};
+
+template <typename Char, typename OutputIt, typename T>
+auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
+  if constexpr (is_string<T>::value)
+    return write_escaped_string<Char>(out, detail::to_string_view(v));
+  else if constexpr (std::is_same_v<T, Char>)
+    return write_escaped_char(out, v);
+  else
+    return write<Char>(out, v);
+}
+
+}  // namespace detail
+
+template <typename T> struct is_variant_like {
+  static constexpr const bool value = detail::is_variant_like_<T>::value;
+};
+
+template <typename T, typename C> struct is_variant_formattable {
+  static constexpr const bool value =
+      detail::is_variant_formattable_<T, C>::value;
+};
+
+FMT_EXPORT
+template <typename Char> struct formatter<std::monostate, Char> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext>
+  auto format(const std::monostate&, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return detail::write<Char>(ctx.out(), "monostate");
+  }
+};
+
+FMT_EXPORT
+template <typename Variant, typename Char>
+struct formatter<
+    Variant, Char,
+    std::enable_if_t<std::conjunction_v<
+        is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext>
+  auto format(const Variant& value, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    auto out = ctx.out();
+
+    out = detail::write<Char>(out, "variant(");
+    FMT_TRY {
+      std::visit(
+          [&](const auto& v) {
+            out = detail::write_variant_alternative<Char>(out, v);
+          },
+          value);
+    }
+    FMT_CATCH(const std::bad_variant_access&) {
+      detail::write<Char>(out, "valueless by exception");
+    }
+    *out++ = ')';
+    return out;
+  }
+};
+FMT_END_NAMESPACE
+#endif  // __cpp_lib_variant
+
+FMT_BEGIN_NAMESPACE
+FMT_EXPORT
+template <typename Char> struct formatter<std::error_code, Char> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext>
+  FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    auto out = ctx.out();
+    out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
+    out = detail::write<Char>(out, Char(':'));
+    out = detail::write<Char>(out, ec.value());
+    return out;
+  }
+};
+
+FMT_EXPORT
+template <typename T, typename Char>
+struct formatter<
+    T, Char,
+    typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
+ private:
+  bool with_typename_ = false;
+
+ public:
+  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
+      -> decltype(ctx.begin()) {
+    auto it = ctx.begin();
+    auto end = ctx.end();
+    if (it == end || *it == '}') return it;
+    if (*it == 't') {
+      ++it;
+      with_typename_ = FMT_USE_TYPEID != 0;
+    }
+    return it;
+  }
+
+  template <typename OutputIt>
+  auto format(const std::exception& ex,
+              basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
+    format_specs<Char> spec;
+    auto out = ctx.out();
+    if (!with_typename_)
+      return detail::write_bytes(out, string_view(ex.what()), spec);
+
+#if FMT_USE_TYPEID
+    const std::type_info& ti = typeid(ex);
+#  ifdef FMT_HAS_ABI_CXA_DEMANGLE
+    int status = 0;
+    std::size_t size = 0;
+    std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
+        abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
+
+    string_view demangled_name_view;
+    if (demangled_name_ptr) {
+      demangled_name_view = demangled_name_ptr.get();
+
+      // Normalization of stdlib inline namespace names.
+      // libc++ inline namespaces.
+      //  std::__1::*       -> std::*
+      //  std::__1::__fs::* -> std::*
+      // libstdc++ inline namespaces.
+      //  std::__cxx11::*             -> std::*
+      //  std::filesystem::__cxx11::* -> std::filesystem::*
+      if (demangled_name_view.starts_with("std::")) {
+        char* begin = demangled_name_ptr.get();
+        char* to = begin + 5;  // std::
+        for (char *from = to, *end = begin + demangled_name_view.size();
+             from < end;) {
+          // This is safe, because demangled_name is NUL-terminated.
+          if (from[0] == '_' && from[1] == '_') {
+            char* next = from + 1;
+            while (next < end && *next != ':') next++;
+            if (next[0] == ':' && next[1] == ':') {
+              from = next + 2;
+              continue;
+            }
+          }
+          *to++ = *from++;
+        }
+        demangled_name_view = {begin, detail::to_unsigned(to - begin)};
+      }
+    } else {
+      demangled_name_view = string_view(ti.name());
+    }
+    out = detail::write_bytes(out, demangled_name_view, spec);
+#  elif FMT_MSC_VERSION
+    string_view demangled_name_view(ti.name());
+    if (demangled_name_view.starts_with("class "))
+      demangled_name_view.remove_prefix(6);
+    else if (demangled_name_view.starts_with("struct "))
+      demangled_name_view.remove_prefix(7);
+    out = detail::write_bytes(out, demangled_name_view, spec);
+#  else
+    out = detail::write_bytes(out, string_view(ti.name()), spec);
+#  endif
+    *out++ = ':';
+    *out++ = ' ';
+    return detail::write_bytes(out, string_view(ex.what()), spec);
+#endif
+  }
+};
+
+namespace detail {
+
+template <typename T, typename Enable = void>
+struct has_flip : std::false_type {};
+
+template <typename T>
+struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
+    : std::true_type {};
+
+template <typename T> struct is_bit_reference_like {
+  static constexpr const bool value =
+      std::is_convertible<T, bool>::value &&
+      std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
+};
+
+#ifdef _LIBCPP_VERSION
+
+// Workaround for libc++ incompatibility with C++ standard.
+// According to the Standard, `bitset::operator[] const` returns bool.
+template <typename C>
+struct is_bit_reference_like<std::__bit_const_reference<C>> {
+  static constexpr const bool value = true;
+};
+
+#endif
+
+}  // namespace detail
+
+// We can't use std::vector<bool, Allocator>::reference and
+// std::bitset<N>::reference because the compiler can't deduce Allocator and N
+// in partial specialization.
+FMT_EXPORT
+template <typename BitRef, typename Char>
+struct formatter<BitRef, Char,
+                 enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
+    : formatter<bool, Char> {
+  template <typename FormatContext>
+  FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return formatter<bool, Char>::format(v, ctx);
+  }
+};
+
+FMT_EXPORT
+template <typename T, typename Char>
+struct formatter<std::atomic<T>, Char,
+                 enable_if_t<is_formattable<T, Char>::value>>
+    : formatter<T, Char> {
+  template <typename FormatContext>
+  auto format(const std::atomic<T>& v, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return formatter<T, Char>::format(v.load(), ctx);
+  }
+};
+
+#ifdef __cpp_lib_atomic_flag_test
+FMT_EXPORT
+template <typename Char>
+struct formatter<std::atomic_flag, Char>
+    : formatter<bool, Char> {
+  template <typename FormatContext>
+  auto format(const std::atomic_flag& v, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return formatter<bool, Char>::format(v.test(), ctx);
+  }
+};
+#endif // __cpp_lib_atomic_flag_test
+
+FMT_END_NAMESPACE
+#endif  // FMT_STD_H_
diff --git a/src/cpp-common/vendor/fmt/xchar.h b/src/cpp-common/vendor/fmt/xchar.h
new file mode 100644 (file)
index 0000000..625ec36
--- /dev/null
@@ -0,0 +1,258 @@
+// Formatting library for C++ - optional wchar_t and exotic character support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_XCHAR_H_
+#define FMT_XCHAR_H_
+
+#include <cwchar>
+
+#include "format.h"
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+#  include <locale>
+#endif
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+template <typename T>
+using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
+
+inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
+                      loc_value value, const format_specs<wchar_t>& specs,
+                      locale_ref loc) -> bool {
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+  auto& numpunct =
+      std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
+  auto separator = std::wstring();
+  auto grouping = numpunct.grouping();
+  if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
+  return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
+#endif
+  return false;
+}
+}  // namespace detail
+
+FMT_BEGIN_EXPORT
+
+using wstring_view = basic_string_view<wchar_t>;
+using wformat_parse_context = basic_format_parse_context<wchar_t>;
+using wformat_context = buffer_context<wchar_t>;
+using wformat_args = basic_format_args<wformat_context>;
+using wmemory_buffer = basic_memory_buffer<wchar_t>;
+
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+// Workaround broken conversion on older gcc.
+template <typename... Args> using wformat_string = wstring_view;
+inline auto runtime(wstring_view s) -> wstring_view { return s; }
+#else
+template <typename... Args>
+using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
+inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
+  return {{s}};
+}
+#endif
+
+template <> struct is_char<wchar_t> : std::true_type {};
+template <> struct is_char<detail::char8_type> : std::true_type {};
+template <> struct is_char<char16_t> : std::true_type {};
+template <> struct is_char<char32_t> : std::true_type {};
+
+template <typename... T>
+constexpr format_arg_store<wformat_context, T...> make_wformat_args(
+    const T&... args) {
+  return {args...};
+}
+
+inline namespace literals {
+#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
+constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
+  return {s};
+}
+#endif
+}  // namespace literals
+
+template <typename It, typename Sentinel>
+auto join(It begin, Sentinel end, wstring_view sep)
+    -> join_view<It, Sentinel, wchar_t> {
+  return {begin, end, sep};
+}
+
+template <typename Range>
+auto join(Range&& range, wstring_view sep)
+    -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
+                 wchar_t> {
+  return join(std::begin(range), std::end(range), sep);
+}
+
+template <typename T>
+auto join(std::initializer_list<T> list, wstring_view sep)
+    -> join_view<const T*, const T*, wchar_t> {
+  return join(std::begin(list), std::end(list), sep);
+}
+
+template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+auto vformat(basic_string_view<Char> format_str,
+             basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> std::basic_string<Char> {
+  auto buf = basic_memory_buffer<Char>();
+  detail::vformat_to(buf, format_str, args);
+  return to_string(buf);
+}
+
+template <typename... T>
+auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
+  return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
+}
+
+// Pass char_t as a default template parameter instead of using
+// std::basic_string<char_t<S>> to reduce the symbol size.
+template <typename S, typename... T, typename Char = char_t<S>,
+          FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
+                        !std::is_same<Char, wchar_t>::value)>
+auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
+  return vformat(detail::to_string_view(format_str),
+                 fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename Locale, typename S, typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto vformat(
+    const Locale& loc, const S& format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> std::basic_string<Char> {
+  return detail::vformat(loc, detail::to_string_view(format_str), args);
+}
+
+template <typename Locale, typename S, typename... T, typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto format(const Locale& loc, const S& format_str, T&&... args)
+    -> std::basic_string<Char> {
+  return detail::vformat(loc, detail::to_string_view(format_str),
+                         fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename OutputIt, typename S, typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_exotic_char<Char>::value)>
+auto vformat_to(OutputIt out, const S& format_str,
+                basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> OutputIt {
+  auto&& buf = detail::get_buffer<Char>(out);
+  detail::vformat_to(buf, detail::to_string_view(format_str), args);
+  return detail::get_iterator(buf, out);
+}
+
+template <typename OutputIt, typename S, typename... T,
+          typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
+  return vformat_to(out, detail::to_string_view(fmt),
+                    fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename Locale, typename S, typename OutputIt, typename... Args,
+          typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_locale<Locale>::value&&
+                                detail::is_exotic_char<Char>::value)>
+inline auto vformat_to(
+    OutputIt out, const Locale& loc, const S& format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
+  auto&& buf = detail::get_buffer<Char>(out);
+  vformat_to(buf, detail::to_string_view(format_str), args,
+             detail::locale_ref(loc));
+  return detail::get_iterator(buf, out);
+}
+
+template <
+    typename OutputIt, typename Locale, typename S, typename... T,
+    typename Char = char_t<S>,
+    bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
+        detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
+inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
+                      T&&... args) ->
+    typename std::enable_if<enable, OutputIt>::type {
+  return vformat_to(out, loc, detail::to_string_view(format_str),
+                    fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename OutputIt, typename Char, typename... Args,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto vformat_to_n(
+    OutputIt out, size_t n, basic_string_view<Char> format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> format_to_n_result<OutputIt> {
+  using traits = detail::fixed_buffer_traits;
+  auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
+  detail::vformat_to(buf, format_str, args);
+  return {buf.out(), buf.count()};
+}
+
+template <typename OutputIt, typename S, typename... T,
+          typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
+    -> format_to_n_result<OutputIt> {
+  return vformat_to_n(out, n, detail::to_string_view(fmt),
+                      fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename S, typename... T, typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
+inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
+  auto buf = detail::counting_buffer<Char>();
+  detail::vformat_to(buf, detail::to_string_view(fmt),
+                     fmt::make_format_args<buffer_context<Char>>(args...));
+  return buf.count();
+}
+
+inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
+  auto buf = wmemory_buffer();
+  detail::vformat_to(buf, fmt, args);
+  buf.push_back(L'\0');
+  if (std::fputws(buf.data(), f) == -1)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+}
+
+inline void vprint(wstring_view fmt, wformat_args args) {
+  vprint(stdout, fmt, args);
+}
+
+template <typename... T>
+void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
+  return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
+}
+
+template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
+  return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
+}
+
+template <typename... T>
+void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
+  return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
+}
+
+template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
+  return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
+}
+
+/**
+  Converts *value* to ``std::wstring`` using the default format for type *T*.
+ */
+template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
+  return format(FMT_STRING(L"{}"), value);
+}
+FMT_END_EXPORT
+FMT_END_NAMESPACE
+
+#endif  // FMT_XCHAR_H_
diff --git a/src/cpp-common/vendor/nlohmann/json.hpp b/src/cpp-common/vendor/nlohmann/json.hpp
new file mode 100644 (file)
index 0000000..443a69f
--- /dev/null
@@ -0,0 +1,24674 @@
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+/****************************************************************************\
+ * Note on documentation: The source files contain links to the online      *
+ * documentation of the public API at https://json.nlohmann.me. This URL    *
+ * contains the most recent documentation and should also be applicable to  *
+ * previous versions; documentation for deprecated functions is not         *
+ * removed, but marked deprecated. See "Generate documentation" section in  *
+ * file docs/README.md.                                                     *
+\****************************************************************************/
+
+#ifndef INCLUDE_NLOHMANN_JSON_HPP_
+#define INCLUDE_NLOHMANN_JSON_HPP_
+
+#include <algorithm> // all_of, find, for_each
+#include <cstddef> // nullptr_t, ptrdiff_t, size_t
+#include <functional> // hash, less
+#include <initializer_list> // initializer_list
+#ifndef JSON_NO_IO
+    #include <iosfwd> // istream, ostream
+#endif  // JSON_NO_IO
+#include <iterator> // random_access_iterator_tag
+#include <memory> // unique_ptr
+#include <string> // string, stoi, to_string
+#include <utility> // declval, forward, move, pair, swap
+#include <vector> // vector
+
+// #include <nlohmann/adl_serializer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <utility>
+
+// #include <nlohmann/detail/abi_macros.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// This file contains all macro definitions affecting or depending on the ABI
+
+#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
+    #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
+        #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2
+            #warning "Already included a different version of the library!"
+        #endif
+    #endif
+#endif
+
+#define NLOHMANN_JSON_VERSION_MAJOR 3   // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_MINOR 11  // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_PATCH 2   // NOLINT(modernize-macro-to-enum)
+
+#ifndef JSON_DIAGNOSTICS
+    #define JSON_DIAGNOSTICS 0
+#endif
+
+#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
+#endif
+
+#if JSON_DIAGNOSTICS
+    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
+#else
+    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
+#endif
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
+#else
+    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
+    #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
+#endif
+
+// Construct the namespace ABI tags component
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
+    NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
+
+#define NLOHMANN_JSON_ABI_TAGS                                       \
+    NLOHMANN_JSON_ABI_TAGS_CONCAT(                                   \
+            NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS,                       \
+            NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
+
+// Construct the namespace version component
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
+    _v ## major ## _ ## minor ## _ ## patch
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
+    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
+
+#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
+#define NLOHMANN_JSON_NAMESPACE_VERSION
+#else
+#define NLOHMANN_JSON_NAMESPACE_VERSION                                 \
+    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
+                                           NLOHMANN_JSON_VERSION_MINOR, \
+                                           NLOHMANN_JSON_VERSION_PATCH)
+#endif
+
+// Combine namespace components
+#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
+#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
+    NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
+
+#ifndef NLOHMANN_JSON_NAMESPACE
+#define NLOHMANN_JSON_NAMESPACE               \
+    nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
+            NLOHMANN_JSON_ABI_TAGS,           \
+            NLOHMANN_JSON_NAMESPACE_VERSION)
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
+#define NLOHMANN_JSON_NAMESPACE_BEGIN                \
+    namespace nlohmann                               \
+    {                                                \
+    inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
+                NLOHMANN_JSON_ABI_TAGS,              \
+                NLOHMANN_JSON_NAMESPACE_VERSION)     \
+    {
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_END
+#define NLOHMANN_JSON_NAMESPACE_END                                     \
+    }  /* namespace (inline namespace) NOLINT(readability/namespace) */ \
+    }  // namespace nlohmann
+#endif
+
+// #include <nlohmann/detail/conversions/from_json.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // transform
+#include <array> // array
+#include <forward_list> // forward_list
+#include <iterator> // inserter, front_inserter, end
+#include <map> // map
+#include <string> // string
+#include <tuple> // tuple, make_tuple
+#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
+#include <unordered_map> // unordered_map
+#include <utility> // pair, declval
+#include <valarray> // valarray
+
+// #include <nlohmann/detail/exceptions.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // nullptr_t
+#include <exception> // exception
+#if JSON_DIAGNOSTICS
+    #include <numeric> // accumulate
+#endif
+#include <stdexcept> // runtime_error
+#include <string> // to_string
+#include <vector> // vector
+
+// #include <nlohmann/detail/value_t.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t
+#include <string> // string
+
+// #include <nlohmann/detail/macro_scope.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <utility> // declval, pair
+// #include <nlohmann/detail/meta/detected.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <type_traits>
+
+// #include <nlohmann/detail/meta/void_t.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename ...Ts> struct make_void
+{
+    using type = void;
+};
+template<typename ...Ts> using void_t = typename make_void<Ts...>::type;
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// https://en.cppreference.com/w/cpp/experimental/is_detected
+struct nonesuch
+{
+    nonesuch() = delete;
+    ~nonesuch() = delete;
+    nonesuch(nonesuch const&) = delete;
+    nonesuch(nonesuch const&&) = delete;
+    void operator=(nonesuch const&) = delete;
+    void operator=(nonesuch&&) = delete;
+};
+
+template<class Default,
+         class AlwaysVoid,
+         template<class...> class Op,
+         class... Args>
+struct detector
+{
+    using value_t = std::false_type;
+    using type = Default;
+};
+
+template<class Default, template<class...> class Op, class... Args>
+struct detector<Default, void_t<Op<Args...>>, Op, Args...>
+{
+    using value_t = std::true_type;
+    using type = Op<Args...>;
+};
+
+template<template<class...> class Op, class... Args>
+using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
+
+template<template<class...> class Op, class... Args>
+struct is_detected_lazy : is_detected<Op, Args...> { };
+
+template<template<class...> class Op, class... Args>
+using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or = detector<Default, void, Op, Args...>;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or_t = typename detected_or<Default, Op, Args...>::type;
+
+template<class Expected, template<class...> class Op, class... Args>
+using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
+
+template<class To, template<class...> class Op, class... Args>
+using is_detected_convertible =
+    std::is_convertible<detected_t<Op, Args...>, To>;
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/thirdparty/hedley/hedley.hpp>
+
+
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson <evan@nemerson.com>
+// SPDX-License-Identifier: MIT
+
+/* Hedley - https://nemequ.github.io/hedley
+ * Created by Evan Nemerson <evan@nemerson.com>
+ */
+
+#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)
+#if defined(JSON_HEDLEY_VERSION)
+    #undef JSON_HEDLEY_VERSION
+#endif
+#define JSON_HEDLEY_VERSION 15
+
+#if defined(JSON_HEDLEY_STRINGIFY_EX)
+    #undef JSON_HEDLEY_STRINGIFY_EX
+#endif
+#define JSON_HEDLEY_STRINGIFY_EX(x) #x
+
+#if defined(JSON_HEDLEY_STRINGIFY)
+    #undef JSON_HEDLEY_STRINGIFY
+#endif
+#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)
+
+#if defined(JSON_HEDLEY_CONCAT_EX)
+    #undef JSON_HEDLEY_CONCAT_EX
+#endif
+#define JSON_HEDLEY_CONCAT_EX(a,b) a##b
+
+#if defined(JSON_HEDLEY_CONCAT)
+    #undef JSON_HEDLEY_CONCAT
+#endif
+#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)
+
+#if defined(JSON_HEDLEY_CONCAT3_EX)
+    #undef JSON_HEDLEY_CONCAT3_EX
+#endif
+#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c
+
+#if defined(JSON_HEDLEY_CONCAT3)
+    #undef JSON_HEDLEY_CONCAT3
+#endif
+#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)
+
+#if defined(JSON_HEDLEY_VERSION_ENCODE)
+    #undef JSON_HEDLEY_VERSION_ENCODE
+#endif
+#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)
+    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)
+    #undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)
+    #undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)
+
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+    #undef JSON_HEDLEY_GNUC_VERSION
+#endif
+#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)
+    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#elif defined(__GNUC__)
+    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)
+    #undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION)
+    #undef JSON_HEDLEY_MSVC_VERSION
+#endif
+#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)
+    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)
+#elif defined(_MSC_FULL_VER) && !defined(__ICL)
+    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)
+#elif defined(_MSC_VER) && !defined(__ICL)
+    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)
+    #undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#endif
+#if !defined(JSON_HEDLEY_MSVC_VERSION)
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)
+#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))
+#else
+    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+    #undef JSON_HEDLEY_INTEL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)
+    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)
+#elif defined(__INTEL_COMPILER) && !defined(__ICL)
+    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)
+    #undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+    #undef JSON_HEDLEY_INTEL_CL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)
+    #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)
+    #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION)
+    #undef JSON_HEDLEY_PGI_VERSION
+#endif
+#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)
+    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)
+    #undef JSON_HEDLEY_PGI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PGI_VERSION)
+    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+    #undef JSON_HEDLEY_SUNPRO_VERSION
+#endif
+#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)
+#elif defined(__SUNPRO_C)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)
+#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)
+#elif defined(__SUNPRO_CC)
+    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)
+    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#endif
+#if defined(__EMSCRIPTEN__)
+    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)
+    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION)
+    #undef JSON_HEDLEY_ARM_VERSION
+#endif
+#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)
+    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)
+#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)
+    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)
+    #undef JSON_HEDLEY_ARM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_ARM_VERSION)
+    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION)
+    #undef JSON_HEDLEY_IBM_VERSION
+#endif
+#if defined(__ibmxl__)
+    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)
+#elif defined(__xlC__) && defined(__xlC_ver__)
+    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)
+#elif defined(__xlC__)
+    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)
+    #undef JSON_HEDLEY_IBM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IBM_VERSION)
+    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION)
+    #undef JSON_HEDLEY_TI_VERSION
+#endif
+#if \
+    defined(__TI_COMPILER_VERSION__) && \
+    ( \
+      defined(__TMS470__) || defined(__TI_ARM__) || \
+      defined(__MSP430__) || \
+      defined(__TMS320C2000__) \
+    )
+#if (__TI_COMPILER_VERSION__ >= 16000000)
+    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_VERSION)
+    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+    #undef JSON_HEDLEY_TI_CL2000_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)
+    #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+    #undef JSON_HEDLEY_TI_CL430_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)
+    #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+    #undef JSON_HEDLEY_TI_ARMCL_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))
+    #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+    #undef JSON_HEDLEY_TI_CL6X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)
+    #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+    #undef JSON_HEDLEY_TI_CL7X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)
+    #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+    #undef JSON_HEDLEY_TI_CLPRU_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)
+    #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)
+    #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+    #undef JSON_HEDLEY_CRAY_VERSION
+#endif
+#if defined(_CRAYC)
+    #if defined(_RELEASE_PATCHLEVEL)
+        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)
+    #else
+        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)
+    #endif
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)
+    #undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION)
+    #undef JSON_HEDLEY_IAR_VERSION
+#endif
+#if defined(__IAR_SYSTEMS_ICC__)
+    #if __VER__ > 1000
+        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))
+    #else
+        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)
+    #endif
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)
+    #undef JSON_HEDLEY_IAR_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IAR_VERSION)
+    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+    #undef JSON_HEDLEY_TINYC_VERSION
+#endif
+#if defined(__TINYC__)
+    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)
+    #undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION)
+    #undef JSON_HEDLEY_DMC_VERSION
+#endif
+#if defined(__DMC__)
+    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)
+    #undef JSON_HEDLEY_DMC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_DMC_VERSION)
+    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+    #undef JSON_HEDLEY_COMPCERT_VERSION
+#endif
+#if defined(__COMPCERT_VERSION__)
+    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)
+    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+    #undef JSON_HEDLEY_PELLES_VERSION
+#endif
+#if defined(__POCC__)
+    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)
+    #undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+    #undef JSON_HEDLEY_MCST_LCC_VERSION
+#endif
+#if defined(__LCC__) && defined(__LCC_MINOR__)
+    #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)
+    #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION)
+    #undef JSON_HEDLEY_GCC_VERSION
+#endif
+#if \
+    defined(JSON_HEDLEY_GNUC_VERSION) && \
+    !defined(__clang__) && \
+    !defined(JSON_HEDLEY_INTEL_VERSION) && \
+    !defined(JSON_HEDLEY_PGI_VERSION) && \
+    !defined(JSON_HEDLEY_ARM_VERSION) && \
+    !defined(JSON_HEDLEY_CRAY_VERSION) && \
+    !defined(JSON_HEDLEY_TI_VERSION) && \
+    !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL430_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \
+    !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \
+    !defined(__COMPCERT__) && \
+    !defined(JSON_HEDLEY_MCST_LCC_VERSION)
+    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)
+    #undef JSON_HEDLEY_GCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GCC_VERSION)
+    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_HAS_ATTRIBUTE
+#endif
+#if \
+  defined(__has_attribute) && \
+  ( \
+    (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \
+  )
+#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)
+#else
+#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#endif
+#if \
+    defined(__has_cpp_attribute) && \
+    defined(__cplusplus) && \
+    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)
+#else
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)
+    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#endif
+#if !defined(__cplusplus) || !defined(__has_cpp_attribute)
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#elif \
+    !defined(JSON_HEDLEY_PGI_VERSION) && \
+    !defined(JSON_HEDLEY_IAR_VERSION) && \
+    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \
+    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)
+#else
+    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_BUILTIN)
+    #undef JSON_HEDLEY_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)
+#else
+    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)
+    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)
+    #undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_FEATURE)
+    #undef JSON_HEDLEY_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)
+#else
+    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)
+    #undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)
+    #undef JSON_HEDLEY_GCC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_EXTENSION)
+    #undef JSON_HEDLEY_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)
+#else
+    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)
+    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)
+    #undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)
+#else
+    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_WARNING)
+    #undef JSON_HEDLEY_HAS_WARNING
+#endif
+#if defined(__has_warning)
+    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)
+#else
+    #define JSON_HEDLEY_HAS_WARNING(warning) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)
+    #undef JSON_HEDLEY_GNUC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_WARNING)
+    #undef JSON_HEDLEY_GCC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if \
+    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+    defined(__clang__) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \
+    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \
+    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))
+    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)
+#else
+    #define JSON_HEDLEY_PRAGMA(value)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)
+    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#endif
+#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)
+    #undef JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+#if defined(__clang__)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))
+    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))
+#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop")
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_PUSH
+    #define JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+
+/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for
+   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#endif
+#if defined(__cplusplus)
+#  if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat")
+#    if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions")
+#      if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions")
+#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+    _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+    _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \
+    xpr \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#      else
+#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+    _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+    xpr \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#      endif
+#    else
+#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+    xpr \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#    endif
+#  endif
+#endif
+#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x
+#endif
+
+#if defined(JSON_HEDLEY_CONST_CAST)
+    #undef JSON_HEDLEY_CONST_CAST
+#endif
+#if defined(__cplusplus)
+#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))
+#elif \
+  JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \
+        JSON_HEDLEY_DIAGNOSTIC_PUSH \
+        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \
+        ((T) (expr)); \
+        JSON_HEDLEY_DIAGNOSTIC_POP \
+    }))
+#else
+#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_REINTERPRET_CAST)
+    #undef JSON_HEDLEY_REINTERPRET_CAST
+#endif
+#if defined(__cplusplus)
+    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))
+#else
+    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_CAST)
+    #undef JSON_HEDLEY_STATIC_CAST
+#endif
+#if defined(__cplusplus)
+    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))
+#else
+    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_CPP_CAST)
+    #undef JSON_HEDLEY_CPP_CAST
+#endif
+#if defined(__cplusplus)
+#  if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast")
+#    define JSON_HEDLEY_CPP_CAST(T, expr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \
+    ((T) (expr)) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#  elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)
+#    define JSON_HEDLEY_CPP_CAST(T, expr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("diag_suppress=Pe137") \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#  else
+#    define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))
+#  endif
+#else
+#  define JSON_HEDLEY_CPP_CAST(T, expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)")
+#elif \
+    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)
+    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunused-function")
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142")
+#else
+    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+
+#if defined(JSON_HEDLEY_DEPRECATED)
+    #undef JSON_HEDLEY_DEPRECATED
+#endif
+#if defined(JSON_HEDLEY_DEPRECATED_FOR)
+    #undef JSON_HEDLEY_DEPRECATED_FOR
+#endif
+#if \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since))
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement))
+#elif \
+    (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since)))
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement)))
+#elif defined(__cplusplus) && (__cplusplus >= 201402L)
+    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]])
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]])
+#elif \
+    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated")
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated")
+#else
+    #define JSON_HEDLEY_DEPRECATED(since)
+    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)
+#endif
+
+#if defined(JSON_HEDLEY_UNAVAILABLE)
+    #undef JSON_HEDLEY_UNAVAILABLE
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since)))
+#else
+    #define JSON_HEDLEY_UNAVAILABLE(available_since)
+#endif
+
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)
+    #undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#endif
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)
+    #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))
+#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+#elif defined(_Check_return_) /* SAL */
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_
+#else
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT
+    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)
+#endif
+
+#if defined(JSON_HEDLEY_SENTINEL)
+    #undef JSON_HEDLEY_SENTINEL
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))
+#else
+    #define JSON_HEDLEY_SENTINEL(position)
+#endif
+
+#if defined(JSON_HEDLEY_NO_RETURN)
+    #undef JSON_HEDLEY_NO_RETURN
+#endif
+#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_NO_RETURN __noreturn
+#elif \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+    #define JSON_HEDLEY_NO_RETURN _Noreturn
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])
+#elif \
+    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+    #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return")
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#else
+    #define JSON_HEDLEY_NO_RETURN
+#endif
+
+#if defined(JSON_HEDLEY_NO_ESCAPE)
+    #undef JSON_HEDLEY_NO_ESCAPE
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)
+    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))
+#else
+    #define JSON_HEDLEY_NO_ESCAPE
+#endif
+
+#if defined(JSON_HEDLEY_UNREACHABLE)
+    #undef JSON_HEDLEY_UNREACHABLE
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)
+    #undef JSON_HEDLEY_UNREACHABLE_RETURN
+#endif
+#if defined(JSON_HEDLEY_ASSUME)
+    #undef JSON_HEDLEY_ASSUME
+#endif
+#if \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)
+#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)
+    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)
+#elif \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+    #if defined(__cplusplus)
+        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)
+    #else
+        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)
+    #endif
+#endif
+#if \
+    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \
+    JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()
+#elif defined(JSON_HEDLEY_ASSUME)
+    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+#if !defined(JSON_HEDLEY_ASSUME)
+    #if defined(JSON_HEDLEY_UNREACHABLE)
+        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))
+    #else
+        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)
+    #endif
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE)
+    #if  \
+        JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))
+    #else
+        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()
+    #endif
+#else
+    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)
+#endif
+#if !defined(JSON_HEDLEY_UNREACHABLE)
+    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+
+JSON_HEDLEY_DIAGNOSTIC_PUSH
+#if JSON_HEDLEY_HAS_WARNING("-Wpedantic")
+    #pragma clang diagnostic ignored "-Wpedantic"
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus)
+    #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#endif
+#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0)
+    #if defined(__clang__)
+        #pragma clang diagnostic ignored "-Wvariadic-macros"
+    #elif defined(JSON_HEDLEY_GCC_VERSION)
+        #pragma GCC diagnostic ignored "-Wvariadic-macros"
+    #endif
+#endif
+#if defined(JSON_HEDLEY_NON_NULL)
+    #undef JSON_HEDLEY_NON_NULL
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))
+#else
+    #define JSON_HEDLEY_NON_NULL(...)
+#endif
+JSON_HEDLEY_DIAGNOSTIC_POP
+
+#if defined(JSON_HEDLEY_PRINTF_FORMAT)
+    #undef JSON_HEDLEY_PRINTF_FORMAT
+#endif
+#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))
+#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))
+#elif \
+    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))
+#else
+    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)
+#endif
+
+#if defined(JSON_HEDLEY_CONSTEXPR)
+    #undef JSON_HEDLEY_CONSTEXPR
+#endif
+#if defined(__cplusplus)
+    #if __cplusplus >= 201103L
+        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)
+    #endif
+#endif
+#if !defined(JSON_HEDLEY_CONSTEXPR)
+    #define JSON_HEDLEY_CONSTEXPR
+#endif
+
+#if defined(JSON_HEDLEY_PREDICT)
+    #undef JSON_HEDLEY_PREDICT
+#endif
+#if defined(JSON_HEDLEY_LIKELY)
+    #undef JSON_HEDLEY_LIKELY
+#endif
+#if defined(JSON_HEDLEY_UNLIKELY)
+    #undef JSON_HEDLEY_UNLIKELY
+#endif
+#if defined(JSON_HEDLEY_UNPREDICTABLE)
+    #undef JSON_HEDLEY_UNPREDICTABLE
+#endif
+#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)
+    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))
+#endif
+#if \
+  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(  (expr), (value), (probability))
+#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability)   __builtin_expect_with_probability(!!(expr),    1   , (probability))
+#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability)  __builtin_expect_with_probability(!!(expr),    0   , (probability))
+#  define JSON_HEDLEY_LIKELY(expr)                      __builtin_expect                 (!!(expr),    1                  )
+#  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )
+#elif \
+  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+  JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \
+  JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \
+    (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))
+#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \
+    (__extension__ ({ \
+        double hedley_probability_ = (probability); \
+        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \
+    }))
+#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \
+    (__extension__ ({ \
+        double hedley_probability_ = (probability); \
+        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \
+    }))
+#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)
+#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
+#else
+#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))
+#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))
+#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))
+#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))
+#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))
+#endif
+#if !defined(JSON_HEDLEY_UNPREDICTABLE)
+    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)
+#endif
+
+#if defined(JSON_HEDLEY_MALLOC)
+    #undef JSON_HEDLEY_MALLOC
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+    #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory")
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_MALLOC __declspec(restrict)
+#else
+    #define JSON_HEDLEY_MALLOC
+#endif
+
+#if defined(JSON_HEDLEY_PURE)
+    #undef JSON_HEDLEY_PURE
+#endif
+#if \
+  JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+  JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#  define JSON_HEDLEY_PURE __attribute__((__pure__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+#  define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data")
+#elif defined(__cplusplus) && \
+    ( \
+      JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \
+      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \
+    )
+#  define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;")
+#else
+#  define JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_CONST)
+    #undef JSON_HEDLEY_CONST
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_CONST __attribute__((__const__))
+#elif \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+    #define JSON_HEDLEY_CONST _Pragma("no_side_effect")
+#else
+    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_RESTRICT)
+    #undef JSON_HEDLEY_RESTRICT
+#endif
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)
+    #define JSON_HEDLEY_RESTRICT restrict
+#elif \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+    defined(__clang__) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_RESTRICT __restrict
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)
+    #define JSON_HEDLEY_RESTRICT _Restrict
+#else
+    #define JSON_HEDLEY_RESTRICT
+#endif
+
+#if defined(JSON_HEDLEY_INLINE)
+    #undef JSON_HEDLEY_INLINE
+#endif
+#if \
+    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+    (defined(__cplusplus) && (__cplusplus >= 199711L))
+    #define JSON_HEDLEY_INLINE inline
+#elif \
+    defined(JSON_HEDLEY_GCC_VERSION) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)
+    #define JSON_HEDLEY_INLINE __inline__
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_INLINE __inline
+#else
+    #define JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_ALWAYS_INLINE)
+    #undef JSON_HEDLEY_ALWAYS_INLINE
+#endif
+#if \
+  JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+  JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+#  define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE
+#elif \
+  JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+#  define JSON_HEDLEY_ALWAYS_INLINE __forceinline
+#elif defined(__cplusplus) && \
+    ( \
+      JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+      JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+      JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+      JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \
+    )
+#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced")
+#else
+#  define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_NEVER_INLINE)
+    #undef JSON_HEDLEY_NEVER_INLINE
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)
+    #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+    #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+    #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#else
+    #define JSON_HEDLEY_NEVER_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_PRIVATE)
+    #undef JSON_HEDLEY_PRIVATE
+#endif
+#if defined(JSON_HEDLEY_PUBLIC)
+    #undef JSON_HEDLEY_PUBLIC
+#endif
+#if defined(JSON_HEDLEY_IMPORT)
+    #undef JSON_HEDLEY_IMPORT
+#endif
+#if defined(_WIN32) || defined(__CYGWIN__)
+#  define JSON_HEDLEY_PRIVATE
+#  define JSON_HEDLEY_PUBLIC   __declspec(dllexport)
+#  define JSON_HEDLEY_IMPORT   __declspec(dllimport)
+#else
+#  if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+    ( \
+      defined(__TI_EABI__) && \
+      ( \
+        (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \
+      ) \
+    ) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+#    define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden")))
+#    define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__("default")))
+#  else
+#    define JSON_HEDLEY_PRIVATE
+#    define JSON_HEDLEY_PUBLIC
+#  endif
+#  define JSON_HEDLEY_IMPORT    extern
+#endif
+
+#if defined(JSON_HEDLEY_NO_THROW)
+    #undef JSON_HEDLEY_NO_THROW
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))
+#elif \
+    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)
+#else
+    #define JSON_HEDLEY_NO_THROW
+#endif
+
+#if defined(JSON_HEDLEY_FALL_THROUGH)
+    #undef JSON_HEDLEY_FALL_THROUGH
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)
+    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)
+    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])
+#elif defined(__fallthrough) /* SAL */
+    #define JSON_HEDLEY_FALL_THROUGH __fallthrough
+#else
+    #define JSON_HEDLEY_FALL_THROUGH
+#endif
+
+#if defined(JSON_HEDLEY_RETURNS_NON_NULL)
+    #undef JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+#if \
+    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))
+#elif defined(_Ret_notnull_) /* SAL */
+    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_
+#else
+    #define JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+
+#if defined(JSON_HEDLEY_ARRAY_PARAM)
+    #undef JSON_HEDLEY_ARRAY_PARAM
+#endif
+#if \
+    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+    !defined(__STDC_NO_VLA__) && \
+    !defined(__cplusplus) && \
+    !defined(JSON_HEDLEY_PGI_VERSION) && \
+    !defined(JSON_HEDLEY_TINYC_VERSION)
+    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)
+#else
+    #define JSON_HEDLEY_ARRAY_PARAM(name)
+#endif
+
+#if defined(JSON_HEDLEY_IS_CONSTANT)
+    #undef JSON_HEDLEY_IS_CONSTANT
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)
+    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#endif
+/* JSON_HEDLEY_IS_CONSTEXPR_ is for
+   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+    #undef JSON_HEDLEY_IS_CONSTEXPR_
+#endif
+#if \
+    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \
+    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \
+    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \
+    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)
+#endif
+#if !defined(__cplusplus)
+#  if \
+       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \
+       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)
+#if defined(__INTPTR_TYPE__)
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)
+#else
+    #include <stdint.h>
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)
+#endif
+#  elif \
+       ( \
+          defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \
+          !defined(JSON_HEDLEY_SUNPRO_VERSION) && \
+          !defined(JSON_HEDLEY_PGI_VERSION) && \
+          !defined(JSON_HEDLEY_IAR_VERSION)) || \
+       (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \
+       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)
+#if defined(__INTPTR_TYPE__)
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)
+#else
+    #include <stdint.h>
+    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)
+#endif
+#  elif \
+       defined(JSON_HEDLEY_GCC_VERSION) || \
+       defined(JSON_HEDLEY_INTEL_VERSION) || \
+       defined(JSON_HEDLEY_TINYC_VERSION) || \
+       defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \
+       JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \
+       defined(JSON_HEDLEY_TI_CL2000_VERSION) || \
+       defined(JSON_HEDLEY_TI_CL6X_VERSION) || \
+       defined(JSON_HEDLEY_TI_CL7X_VERSION) || \
+       defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \
+       defined(__clang__)
+#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \
+        sizeof(void) != \
+        sizeof(*( \
+                  1 ? \
+                  ((void*) ((expr) * 0L) ) : \
+((struct { char v[sizeof(void) * 2]; } *) 1) \
+                ) \
+              ) \
+                                            )
+#  endif
+#endif
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+    #if !defined(JSON_HEDLEY_IS_CONSTANT)
+        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)
+    #endif
+    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))
+#else
+    #if !defined(JSON_HEDLEY_IS_CONSTANT)
+        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)
+    #endif
+    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_BEGIN_C_DECLS)
+    #undef JSON_HEDLEY_BEGIN_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_END_C_DECLS)
+    #undef JSON_HEDLEY_END_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_C_DECL)
+    #undef JSON_HEDLEY_C_DECL
+#endif
+#if defined(__cplusplus)
+    #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" {
+    #define JSON_HEDLEY_END_C_DECLS }
+    #define JSON_HEDLEY_C_DECL extern "C"
+#else
+    #define JSON_HEDLEY_BEGIN_C_DECLS
+    #define JSON_HEDLEY_END_C_DECLS
+    #define JSON_HEDLEY_C_DECL
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_ASSERT)
+    #undef JSON_HEDLEY_STATIC_ASSERT
+#endif
+#if \
+  !defined(__cplusplus) && ( \
+      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \
+      (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \
+      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+      defined(_Static_assert) \
+    )
+#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)
+#elif \
+  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \
+  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \
+  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))
+#else
+#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)
+#endif
+
+#if defined(JSON_HEDLEY_NULL)
+    #undef JSON_HEDLEY_NULL
+#endif
+#if defined(__cplusplus)
+    #if __cplusplus >= 201103L
+        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)
+    #elif defined(NULL)
+        #define JSON_HEDLEY_NULL NULL
+    #else
+        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)
+    #endif
+#elif defined(NULL)
+    #define JSON_HEDLEY_NULL NULL
+#else
+    #define JSON_HEDLEY_NULL ((void*) 0)
+#endif
+
+#if defined(JSON_HEDLEY_MESSAGE)
+    #undef JSON_HEDLEY_MESSAGE
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+#  define JSON_HEDLEY_MESSAGE(msg) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+    JSON_HEDLEY_PRAGMA(message msg) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)
+#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)
+#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+#  define JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_WARNING)
+    #undef JSON_HEDLEY_WARNING
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+#  define JSON_HEDLEY_WARNING(msg) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+    JSON_HEDLEY_PRAGMA(clang warning msg) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \
+  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)
+#elif \
+  JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_REQUIRE)
+    #undef JSON_HEDLEY_REQUIRE
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_MSG)
+    #undef JSON_HEDLEY_REQUIRE_MSG
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)
+#  if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat")
+#    define JSON_HEDLEY_REQUIRE(expr) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+    __attribute__((diagnose_if(!(expr), #expr, "error"))) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \
+    JSON_HEDLEY_DIAGNOSTIC_PUSH \
+    _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+    __attribute__((diagnose_if(!(expr), msg, "error"))) \
+    JSON_HEDLEY_DIAGNOSTIC_POP
+#  else
+#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error")))
+#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error")))
+#  endif
+#else
+#  define JSON_HEDLEY_REQUIRE(expr)
+#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS)
+    #undef JSON_HEDLEY_FLAGS
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion"))
+    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))
+#else
+    #define JSON_HEDLEY_FLAGS
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS_CAST)
+    #undef JSON_HEDLEY_FLAGS_CAST
+#endif
+#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)
+#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \
+        JSON_HEDLEY_DIAGNOSTIC_PUSH \
+        _Pragma("warning(disable:188)") \
+        ((T) (expr)); \
+        JSON_HEDLEY_DIAGNOSTIC_POP \
+    }))
+#else
+#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)
+#endif
+
+#if defined(JSON_HEDLEY_EMPTY_BASES)
+    #undef JSON_HEDLEY_EMPTY_BASES
+#endif
+#if \
+    (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \
+    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)
+#else
+    #define JSON_HEDLEY_EMPTY_BASES
+#endif
+
+/* Remaining macros are deprecated. */
+
+#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)
+    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#endif
+#if defined(__clang__)
+    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)
+#else
+    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)
+    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)
+    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)
+    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#endif
+#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)
+    #undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)
+    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#endif
+#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)
+    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)
+    #undef JSON_HEDLEY_CLANG_HAS_WARNING
+#endif
+#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)
+
+#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */
+
+
+// This file contains all internal macro definitions (except those affecting ABI)
+// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+// exclude unsupported compilers
+#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
+    #if defined(__clang__)
+        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
+            #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
+        #endif
+    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
+        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
+            #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
+        #endif
+    #endif
+#endif
+
+// C++ language standard detection
+// if the user manually specified the used c++ version this is skipped
+#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
+    #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
+        #define JSON_HAS_CPP_20
+        #define JSON_HAS_CPP_17
+        #define JSON_HAS_CPP_14
+    #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
+        #define JSON_HAS_CPP_17
+        #define JSON_HAS_CPP_14
+    #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
+        #define JSON_HAS_CPP_14
+    #endif
+    // the cpp 11 flag is always specified because it is the minimal required version
+    #define JSON_HAS_CPP_11
+#endif
+
+#ifdef __has_include
+    #if __has_include(<version>)
+        #include <version>
+    #endif
+#endif
+
+#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
+    #ifdef JSON_HAS_CPP_17
+        #if defined(__cpp_lib_filesystem)
+            #define JSON_HAS_FILESYSTEM 1
+        #elif defined(__cpp_lib_experimental_filesystem)
+            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+        #elif !defined(__has_include)
+            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+        #elif __has_include(<filesystem>)
+            #define JSON_HAS_FILESYSTEM 1
+        #elif __has_include(<experimental/filesystem>)
+            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+        #endif
+
+        // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/
+        #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support
+        #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support
+        #if defined(__clang_major__) && __clang_major__ < 7
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support
+        #if defined(_MSC_VER) && _MSC_VER < 1914
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before iOS 13
+        #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+
+        // no filesystem support before macOS Catalina
+        #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
+            #undef JSON_HAS_FILESYSTEM
+            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+        #endif
+    #endif
+#endif
+
+#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+    #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_FILESYSTEM
+    #define JSON_HAS_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_THREE_WAY_COMPARISON
+    #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
+        && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
+        #define JSON_HAS_THREE_WAY_COMPARISON 1
+    #else
+        #define JSON_HAS_THREE_WAY_COMPARISON 0
+    #endif
+#endif
+
+#ifndef JSON_HAS_RANGES
+    // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
+    #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
+        #define JSON_HAS_RANGES 0
+    #elif defined(__cpp_lib_ranges)
+        #define JSON_HAS_RANGES 1
+    #else
+        #define JSON_HAS_RANGES 0
+    #endif
+#endif
+
+#ifdef JSON_HAS_CPP_17
+    #define JSON_INLINE_VARIABLE inline
+#else
+    #define JSON_INLINE_VARIABLE
+#endif
+
+#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
+    #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
+#else
+    #define JSON_NO_UNIQUE_ADDRESS
+#endif
+
+// disable documentation warnings on clang
+#if defined(__clang__)
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wdocumentation"
+    #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
+#endif
+
+// allow disabling exceptions
+#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
+    #define JSON_THROW(exception) throw exception
+    #define JSON_TRY try
+    #define JSON_CATCH(exception) catch(exception)
+    #define JSON_INTERNAL_CATCH(exception) catch(exception)
+#else
+    #include <cstdlib>
+    #define JSON_THROW(exception) std::abort()
+    #define JSON_TRY if(true)
+    #define JSON_CATCH(exception) if(false)
+    #define JSON_INTERNAL_CATCH(exception) if(false)
+#endif
+
+// override exception macros
+#if defined(JSON_THROW_USER)
+    #undef JSON_THROW
+    #define JSON_THROW JSON_THROW_USER
+#endif
+#if defined(JSON_TRY_USER)
+    #undef JSON_TRY
+    #define JSON_TRY JSON_TRY_USER
+#endif
+#if defined(JSON_CATCH_USER)
+    #undef JSON_CATCH
+    #define JSON_CATCH JSON_CATCH_USER
+    #undef JSON_INTERNAL_CATCH
+    #define JSON_INTERNAL_CATCH JSON_CATCH_USER
+#endif
+#if defined(JSON_INTERNAL_CATCH_USER)
+    #undef JSON_INTERNAL_CATCH
+    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
+#endif
+
+// allow overriding assert
+#if !defined(JSON_ASSERT)
+    #include <cassert> // assert
+    #define JSON_ASSERT(x) assert(x)
+#endif
+
+// allow to access some private functions (needed by the test suite)
+#if defined(JSON_TESTS_PRIVATE)
+    #define JSON_PRIVATE_UNLESS_TESTED public
+#else
+    #define JSON_PRIVATE_UNLESS_TESTED private
+#endif
+
+/*!
+@brief macro to briefly define a mapping between an enum and JSON
+@def NLOHMANN_JSON_SERIALIZE_ENUM
+@since version 3.4.0
+*/
+#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                            \
+    template<typename BasicJsonType>                                                            \
+    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                   \
+    {                                                                                           \
+        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");          \
+        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \
+        auto it = std::find_if(std::begin(m), std::end(m),                                      \
+                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool  \
+        {                                                                                       \
+            return ej_pair.first == e;                                                          \
+        });                                                                                     \
+        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                 \
+    }                                                                                           \
+    template<typename BasicJsonType>                                                            \
+    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                 \
+    {                                                                                           \
+        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!");          \
+        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \
+        auto it = std::find_if(std::begin(m), std::end(m),                                      \
+                               [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
+        {                                                                                       \
+            return ej_pair.second == j;                                                         \
+        });                                                                                     \
+        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                  \
+    }
+
+// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
+// may be removed in the future once the class is split.
+
+#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \
+    template<template<typename, typename, typename...> class ObjectType,   \
+             template<typename, typename...> class ArrayType,              \
+             class StringType, class BooleanType, class NumberIntegerType, \
+             class NumberUnsignedType, class NumberFloatType,              \
+             template<typename> class AllocatorType,                       \
+             template<typename, typename = void> class JSONSerializer,     \
+             class BinaryType,                                             \
+             class CustomBaseClass>
+
+#define NLOHMANN_BASIC_JSON_TPL                                            \
+    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \
+    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \
+    AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>
+
+// Macros to simplify conversion from/to types
+
+#define NLOHMANN_JSON_EXPAND( x ) x
+#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
+#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
+        NLOHMANN_JSON_PASTE64, \
+        NLOHMANN_JSON_PASTE63, \
+        NLOHMANN_JSON_PASTE62, \
+        NLOHMANN_JSON_PASTE61, \
+        NLOHMANN_JSON_PASTE60, \
+        NLOHMANN_JSON_PASTE59, \
+        NLOHMANN_JSON_PASTE58, \
+        NLOHMANN_JSON_PASTE57, \
+        NLOHMANN_JSON_PASTE56, \
+        NLOHMANN_JSON_PASTE55, \
+        NLOHMANN_JSON_PASTE54, \
+        NLOHMANN_JSON_PASTE53, \
+        NLOHMANN_JSON_PASTE52, \
+        NLOHMANN_JSON_PASTE51, \
+        NLOHMANN_JSON_PASTE50, \
+        NLOHMANN_JSON_PASTE49, \
+        NLOHMANN_JSON_PASTE48, \
+        NLOHMANN_JSON_PASTE47, \
+        NLOHMANN_JSON_PASTE46, \
+        NLOHMANN_JSON_PASTE45, \
+        NLOHMANN_JSON_PASTE44, \
+        NLOHMANN_JSON_PASTE43, \
+        NLOHMANN_JSON_PASTE42, \
+        NLOHMANN_JSON_PASTE41, \
+        NLOHMANN_JSON_PASTE40, \
+        NLOHMANN_JSON_PASTE39, \
+        NLOHMANN_JSON_PASTE38, \
+        NLOHMANN_JSON_PASTE37, \
+        NLOHMANN_JSON_PASTE36, \
+        NLOHMANN_JSON_PASTE35, \
+        NLOHMANN_JSON_PASTE34, \
+        NLOHMANN_JSON_PASTE33, \
+        NLOHMANN_JSON_PASTE32, \
+        NLOHMANN_JSON_PASTE31, \
+        NLOHMANN_JSON_PASTE30, \
+        NLOHMANN_JSON_PASTE29, \
+        NLOHMANN_JSON_PASTE28, \
+        NLOHMANN_JSON_PASTE27, \
+        NLOHMANN_JSON_PASTE26, \
+        NLOHMANN_JSON_PASTE25, \
+        NLOHMANN_JSON_PASTE24, \
+        NLOHMANN_JSON_PASTE23, \
+        NLOHMANN_JSON_PASTE22, \
+        NLOHMANN_JSON_PASTE21, \
+        NLOHMANN_JSON_PASTE20, \
+        NLOHMANN_JSON_PASTE19, \
+        NLOHMANN_JSON_PASTE18, \
+        NLOHMANN_JSON_PASTE17, \
+        NLOHMANN_JSON_PASTE16, \
+        NLOHMANN_JSON_PASTE15, \
+        NLOHMANN_JSON_PASTE14, \
+        NLOHMANN_JSON_PASTE13, \
+        NLOHMANN_JSON_PASTE12, \
+        NLOHMANN_JSON_PASTE11, \
+        NLOHMANN_JSON_PASTE10, \
+        NLOHMANN_JSON_PASTE9, \
+        NLOHMANN_JSON_PASTE8, \
+        NLOHMANN_JSON_PASTE7, \
+        NLOHMANN_JSON_PASTE6, \
+        NLOHMANN_JSON_PASTE5, \
+        NLOHMANN_JSON_PASTE4, \
+        NLOHMANN_JSON_PASTE3, \
+        NLOHMANN_JSON_PASTE2, \
+        NLOHMANN_JSON_PASTE1)(__VA_ARGS__))
+#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)
+#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)
+#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)
+#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)
+#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)
+#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)
+#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
+#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
+#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)
+#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
+#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
+#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)
+#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)
+#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)
+#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
+#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)
+#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
+#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)
+#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)
+#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
+#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)
+#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)
+#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)
+#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)
+#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)
+#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)
+#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)
+#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)
+#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)
+#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)
+#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)
+#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)
+#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)
+#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)
+#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)
+#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)
+#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)
+#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)
+#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)
+#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)
+#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)
+#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)
+#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)
+#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)
+#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)
+#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)
+#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)
+#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)
+#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)
+#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)
+#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)
+#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)
+#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)
+#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)
+#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)
+#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)
+#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)
+#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)
+#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)
+#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)
+#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)
+#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)
+#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)
+
+#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
+#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
+#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \
+    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
+    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \
+    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
+    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+
+// inspired from https://stackoverflow.com/a/26745591
+// allows to call any std function as if (e.g. with begin):
+// using std::begin; begin(x);
+//
+// it allows using the detected idiom to retrieve the return type
+// of such an expression
+#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name)                                 \
+    namespace detail {                                                            \
+    using std::std_name;                                                          \
+    \
+    template<typename... T>                                                       \
+    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \
+    }                                                                             \
+    \
+    namespace detail2 {                                                           \
+    struct std_name##_tag                                                         \
+    {                                                                             \
+    };                                                                            \
+    \
+    template<typename... T>                                                       \
+    std_name##_tag std_name(T&&...);                                              \
+    \
+    template<typename... T>                                                       \
+    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \
+    \
+    template<typename... T>                                                       \
+    struct would_call_std_##std_name                                              \
+    {                                                                             \
+        static constexpr auto const value = ::nlohmann::detail::                  \
+                                            is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \
+    };                                                                            \
+    } /* namespace detail2 */ \
+    \
+    template<typename... T>                                                       \
+    struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...>   \
+    {                                                                             \
+    }
+
+#ifndef JSON_USE_IMPLICIT_CONVERSIONS
+    #define JSON_USE_IMPLICIT_CONVERSIONS 1
+#endif
+
+#if JSON_USE_IMPLICIT_CONVERSIONS
+    #define JSON_EXPLICIT
+#else
+    #define JSON_EXPLICIT explicit
+#endif
+
+#ifndef JSON_DISABLE_ENUM_SERIALIZATION
+    #define JSON_DISABLE_ENUM_SERIALIZATION 0
+#endif
+
+#ifndef JSON_USE_GLOBAL_UDLS
+    #define JSON_USE_GLOBAL_UDLS 1
+#endif
+
+#if JSON_HAS_THREE_WAY_COMPARISON
+    #include <compare> // partial_ordering
+#endif
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
+
+/*!
+@brief the JSON type enumeration
+
+This enumeration collects the different JSON types. It is internally used to
+distinguish the stored values, and the functions @ref basic_json::is_null(),
+@ref basic_json::is_object(), @ref basic_json::is_array(),
+@ref basic_json::is_string(), @ref basic_json::is_boolean(),
+@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
+@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
+@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
+@ref basic_json::is_structured() rely on it.
+
+@note There are three enumeration entries (number_integer, number_unsigned, and
+number_float), because the library distinguishes these three types for numbers:
+@ref basic_json::number_unsigned_t is used for unsigned integers,
+@ref basic_json::number_integer_t is used for signed integers, and
+@ref basic_json::number_float_t is used for floating-point numbers or to
+approximate integers which do not fit in the limits of their respective type.
+
+@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
+value with the default value for a given type
+
+@since version 1.0.0
+*/
+enum class value_t : std::uint8_t
+{
+    null,             ///< null value
+    object,           ///< object (unordered set of name/value pairs)
+    array,            ///< array (ordered collection of values)
+    string,           ///< string value
+    boolean,          ///< boolean value
+    number_integer,   ///< number value (signed integer)
+    number_unsigned,  ///< number value (unsigned integer)
+    number_float,     ///< number value (floating-point)
+    binary,           ///< binary array (ordered collection of bytes)
+    discarded         ///< discarded by the parser callback function
+};
+
+/*!
+@brief comparison operator for JSON types
+
+Returns an ordering that is similar to Python:
+- order: null < boolean < number < object < array < string < binary
+- furthermore, each type is not smaller than itself
+- discarded values are not comparable
+- binary is represented as a b"" string in python and directly comparable to a
+  string; however, making a binary array directly comparable with a string would
+  be surprising behavior in a JSON file.
+
+@since version 1.0.0
+*/
+#if JSON_HAS_THREE_WAY_COMPARISON
+    inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
+#else
+    inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+#endif
+{
+    static constexpr std::array<std::uint8_t, 9> order = {{
+            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
+            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
+            6 /* binary */
+        }
+    };
+
+    const auto l_index = static_cast<std::size_t>(lhs);
+    const auto r_index = static_cast<std::size_t>(rhs);
+#if JSON_HAS_THREE_WAY_COMPARISON
+    if (l_index < order.size() && r_index < order.size())
+    {
+        return order[l_index] <=> order[r_index]; // *NOPAD*
+    }
+    return std::partial_ordering::unordered;
+#else
+    return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
+#endif
+}
+
+// GCC selects the built-in operator< over an operator rewritten from
+// a user-defined spaceship operator
+// Clang, MSVC, and ICC select the rewritten candidate
+// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
+#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+    return std::is_lt(lhs <=> rhs); // *NOPAD*
+}
+#endif
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_escape.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief replace all occurrences of a substring by another string
+
+@param[in,out] s  the string to manipulate; changed so that all
+               occurrences of @a f are replaced with @a t
+@param[in]     f  the substring to replace with @a t
+@param[in]     t  the string to replace @a f
+
+@pre The search string @a f must not be empty. **This precondition is
+enforced with an assertion.**
+
+@since version 2.0.0
+*/
+template<typename StringType>
+inline void replace_substring(StringType& s, const StringType& f,
+                              const StringType& t)
+{
+    JSON_ASSERT(!f.empty());
+    for (auto pos = s.find(f);                // find first occurrence of f
+            pos != StringType::npos;          // make sure f was found
+            s.replace(pos, f.size(), t),      // replace with t, and
+            pos = s.find(f, pos + t.size()))  // find next occurrence of f
+    {}
+}
+
+/*!
+ * @brief string escaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to escape
+ * @return    escaped string
+ *
+ * Note the order of escaping "~" to "~0" and "/" to "~1" is important.
+ */
+template<typename StringType>
+inline StringType escape(StringType s)
+{
+    replace_substring(s, StringType{"~"}, StringType{"~0"});
+    replace_substring(s, StringType{"/"}, StringType{"~1"});
+    return s;
+}
+
+/*!
+ * @brief string unescaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to unescape
+ * @return    unescaped string
+ *
+ * Note the order of escaping "~1" to "/" and "~0" to "~" is important.
+ */
+template<typename StringType>
+static void unescape(StringType& s)
+{
+    replace_substring(s, StringType{"~1"}, StringType{"/"});
+    replace_substring(s, StringType{"~0"}, StringType{"~"});
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/position_t.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // size_t
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// struct to capture the start position of the current token
+struct position_t
+{
+    /// the total number of characters read
+    std::size_t chars_read_total = 0;
+    /// the number of characters read in the current line
+    std::size_t chars_read_current_line = 0;
+    /// the number of lines read
+    std::size_t lines_read = 0;
+
+    /// conversion to size_t to preserve SAX interface
+    constexpr operator size_t() const
+    {
+        return chars_read_total;
+    }
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2018 The Abseil Authors
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
+#include <utility> // index_sequence, make_index_sequence, index_sequence_for
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+#ifdef JSON_HAS_CPP_14
+
+// the following utilities are natively available in C++14
+using std::enable_if_t;
+using std::index_sequence;
+using std::make_index_sequence;
+using std::index_sequence_for;
+
+#else
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h
+// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.
+
+//// START OF CODE FROM GOOGLE ABSEIL
+
+// integer_sequence
+//
+// Class template representing a compile-time integer sequence. An instantiation
+// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
+// type through its template arguments (which is a common need when
+// working with C++11 variadic templates). `absl::integer_sequence` is designed
+// to be a drop-in replacement for C++14's `std::integer_sequence`.
+//
+// Example:
+//
+//   template< class T, T... Ints >
+//   void user_function(integer_sequence<T, Ints...>);
+//
+//   int main()
+//   {
+//     // user_function's `T` will be deduced to `int` and `Ints...`
+//     // will be deduced to `0, 1, 2, 3, 4`.
+//     user_function(make_integer_sequence<int, 5>());
+//   }
+template <typename T, T... Ints>
+struct integer_sequence
+{
+    using value_type = T;
+    static constexpr std::size_t size() noexcept
+    {
+        return sizeof...(Ints);
+    }
+};
+
+// index_sequence
+//
+// A helper template for an `integer_sequence` of `size_t`,
+// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
+// `std::index_sequence`.
+template <size_t... Ints>
+using index_sequence = integer_sequence<size_t, Ints...>;
+
+namespace utility_internal
+{
+
+template <typename Seq, size_t SeqSize, size_t Rem>
+struct Extend;
+
+// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 0>
+{
+    using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;
+};
+
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 1>
+{
+    using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;
+};
+
+// Recursion helper for 'make_integer_sequence<T, N>'.
+// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
+template <typename T, size_t N>
+struct Gen
+{
+    using type =
+        typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;
+};
+
+template <typename T>
+struct Gen<T, 0>
+{
+    using type = integer_sequence<T>;
+};
+
+}  // namespace utility_internal
+
+// Compile-time sequences of integers
+
+// make_integer_sequence
+//
+// This template alias is equivalent to
+// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
+// replacement for C++14's `std::make_integer_sequence`.
+template <typename T, T N>
+using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
+
+// make_index_sequence
+//
+// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
+// and is designed to be a drop-in replacement for C++14's
+// `std::make_index_sequence`.
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+
+// index_sequence_for
+//
+// Converts a typename pack into an index sequence of the same length, and
+// is designed to be a drop-in replacement for C++14's
+// `std::index_sequence_for()`
+template <typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+//// END OF CODE FROM GOOGLE ABSEIL
+
+#endif
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+    static JSON_INLINE_VARIABLE constexpr T value{};
+};
+
+#ifndef JSON_HAS_CPP_17
+    template<typename T>
+    constexpr T static_const<T>::value;
+#endif
+
+template<typename T, typename... Args>
+inline constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)
+{
+    return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <limits> // numeric_limits
+#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
+#include <utility> // declval
+#include <tuple> // tuple
+
+// #include <nlohmann/detail/iterators/iterator_traits.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <iterator> // random_access_iterator_tag
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/void_t.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename It, typename = void>
+struct iterator_types {};
+
+template<typename It>
+struct iterator_types <
+    It,
+    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
+    typename It::reference, typename It::iterator_category >>
+{
+    using difference_type = typename It::difference_type;
+    using value_type = typename It::value_type;
+    using pointer = typename It::pointer;
+    using reference = typename It::reference;
+    using iterator_category = typename It::iterator_category;
+};
+
+// This is required as some compilers implement std::iterator_traits in a way that
+// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
+template<typename T, typename = void>
+struct iterator_traits
+{
+};
+
+template<typename T>
+struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
+            : iterator_types<T>
+{
+};
+
+template<typename T>
+struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
+{
+    using iterator_category = std::random_access_iterator_tag;
+    using value_type = T;
+    using difference_type = ptrdiff_t;
+    using pointer = T*;
+    using reference = T&;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/call_std/begin.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/call_std/end.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+// #include <nlohmann/json_fwd.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
+    #define INCLUDE_NLOHMANN_JSON_FWD_HPP_
+
+    #include <cstdint> // int64_t, uint64_t
+    #include <map> // map
+    #include <memory> // allocator
+    #include <string> // string
+    #include <vector> // vector
+
+    // #include <nlohmann/detail/abi_macros.hpp>
+
+
+    /*!
+    @brief namespace for Niels Lohmann
+    @see https://github.com/nlohmann
+    @since version 1.0.0
+    */
+    NLOHMANN_JSON_NAMESPACE_BEGIN
+
+    /*!
+    @brief default JSONSerializer template argument
+
+    This serializer ignores the template arguments and uses ADL
+    ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
+    for serialization.
+    */
+    template<typename T = void, typename SFINAE = void>
+    struct adl_serializer;
+
+    /// a class to store JSON values
+    /// @sa https://json.nlohmann.me/api/basic_json/
+    template<template<typename U, typename V, typename... Args> class ObjectType =
+    std::map,
+    template<typename U, typename... Args> class ArrayType = std::vector,
+    class StringType = std::string, class BooleanType = bool,
+    class NumberIntegerType = std::int64_t,
+    class NumberUnsignedType = std::uint64_t,
+    class NumberFloatType = double,
+    template<typename U> class AllocatorType = std::allocator,
+    template<typename T, typename SFINAE = void> class JSONSerializer =
+    adl_serializer,
+    class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
+    class CustomBaseClass = void>
+    class basic_json;
+
+    /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+    /// @sa https://json.nlohmann.me/api/json_pointer/
+    template<typename RefStringType>
+    class json_pointer;
+
+    /*!
+    @brief default specialization
+    @sa https://json.nlohmann.me/api/json/
+    */
+    using json = basic_json<>;
+
+    /// @brief a minimal map-like container that preserves insertion order
+    /// @sa https://json.nlohmann.me/api/ordered_map/
+    template<class Key, class T, class IgnoredLess, class Allocator>
+    struct ordered_map;
+
+    /// @brief specialization that maintains the insertion order of object keys
+    /// @sa https://json.nlohmann.me/api/ordered_json/
+    using ordered_json = basic_json<nlohmann::ordered_map>;
+
+    NLOHMANN_JSON_NAMESPACE_END
+
+#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+/*!
+@brief detail namespace with internal helper functions
+
+This namespace collects functions that should not be exposed,
+implementations of some @ref basic_json methods, and meta-programming helpers.
+
+@since version 2.1.0
+*/
+namespace detail
+{
+
+/////////////
+// helpers //
+/////////////
+
+// Note to maintainers:
+//
+// Every trait in this file expects a non CV-qualified type.
+// The only exceptions are in the 'aliases for detected' section
+// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
+//
+// In this case, T has to be properly CV-qualified to constraint the function arguments
+// (e.g. to_json(BasicJsonType&, const T&))
+
+template<typename> struct is_basic_json : std::false_type {};
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
+
+// used by exceptions create() member functions
+// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t
+// false_type otherwise
+template<typename BasicJsonContext>
+struct is_basic_json_context :
+    std::integral_constant < bool,
+    is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value
+    || std::is_same<BasicJsonContext, std::nullptr_t>::value >
+{};
+
+//////////////////////
+// json_ref helpers //
+//////////////////////
+
+template<typename>
+class json_ref;
+
+template<typename>
+struct is_json_ref : std::false_type {};
+
+template<typename T>
+struct is_json_ref<json_ref<T>> : std::true_type {};
+
+//////////////////////////
+// aliases for detected //
+//////////////////////////
+
+template<typename T>
+using mapped_type_t = typename T::mapped_type;
+
+template<typename T>
+using key_type_t = typename T::key_type;
+
+template<typename T>
+using value_type_t = typename T::value_type;
+
+template<typename T>
+using difference_type_t = typename T::difference_type;
+
+template<typename T>
+using pointer_t = typename T::pointer;
+
+template<typename T>
+using reference_t = typename T::reference;
+
+template<typename T>
+using iterator_category_t = typename T::iterator_category;
+
+template<typename T, typename... Args>
+using to_json_function = decltype(T::to_json(std::declval<Args>()...));
+
+template<typename T, typename... Args>
+using from_json_function = decltype(T::from_json(std::declval<Args>()...));
+
+template<typename T, typename U>
+using get_template_function = decltype(std::declval<T>().template get<U>());
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T, typename = void>
+struct has_from_json : std::false_type {};
+
+// trait checking if j.get<T> is valid
+// use this trait instead of std::is_constructible or std::is_convertible,
+// both rely on, or make use of implicit conversions, and thus fail when T
+// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
+template <typename BasicJsonType, typename T>
+struct is_getable
+{
+    static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+    using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+    static constexpr bool value =
+        is_detected_exact<void, from_json_function, serializer,
+        const BasicJsonType&, T&>::value;
+};
+
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T, typename = void>
+struct has_non_default_from_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+    using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+    static constexpr bool value =
+        is_detected_exact<T, from_json_function, serializer,
+        const BasicJsonType&>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
+template<typename BasicJsonType, typename T, typename = void>
+struct has_to_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+    using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+    static constexpr bool value =
+        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
+        T>::value;
+};
+
+template<typename T>
+using detect_key_compare = typename T::key_compare;
+
+template<typename T>
+struct has_key_compare : std::integral_constant<bool, is_detected<detect_key_compare, T>::value> {};
+
+// obtains the actual object key comparator
+template<typename BasicJsonType>
+struct actual_object_comparator
+{
+    using object_t = typename BasicJsonType::object_t;
+    using object_comparator_t = typename BasicJsonType::default_object_comparator_t;
+    using type = typename std::conditional < has_key_compare<object_t>::value,
+          typename object_t::key_compare, object_comparator_t>::type;
+};
+
+template<typename BasicJsonType>
+using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
+
+///////////////////
+// is_ functions //
+///////////////////
+
+// https://en.cppreference.com/w/cpp/types/conjunction
+template<class...> struct conjunction : std::true_type { };
+template<class B> struct conjunction<B> : B { };
+template<class B, class... Bn>
+struct conjunction<B, Bn...>
+: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};
+
+// https://en.cppreference.com/w/cpp/types/negation
+template<class B> struct negation : std::integral_constant < bool, !B::value > { };
+
+// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
+// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
+// This causes compile errors in e.g. clang 3.5 or gcc 4.9.
+template <typename T>
+struct is_default_constructible : std::is_default_constructible<T> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<std::pair<T1, T2>>
+            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<const std::pair<T1, T2>>
+            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename... Ts>
+struct is_default_constructible<std::tuple<Ts...>>
+            : conjunction<is_default_constructible<Ts>...> {};
+
+template <typename... Ts>
+struct is_default_constructible<const std::tuple<Ts...>>
+            : conjunction<is_default_constructible<Ts>...> {};
+
+
+template <typename T, typename... Args>
+struct is_constructible : std::is_constructible<T, Args...> {};
+
+template <typename T1, typename T2>
+struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};
+
+template <typename T1, typename T2>
+struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};
+
+template <typename... Ts>
+struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};
+
+template <typename... Ts>
+struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};
+
+
+template<typename T, typename = void>
+struct is_iterator_traits : std::false_type {};
+
+template<typename T>
+struct is_iterator_traits<iterator_traits<T>>
+{
+  private:
+    using traits = iterator_traits<T>;
+
+  public:
+    static constexpr auto value =
+        is_detected<value_type_t, traits>::value &&
+        is_detected<difference_type_t, traits>::value &&
+        is_detected<pointer_t, traits>::value &&
+        is_detected<iterator_category_t, traits>::value &&
+        is_detected<reference_t, traits>::value;
+};
+
+template<typename T>
+struct is_range
+{
+  private:
+    using t_ref = typename std::add_lvalue_reference<T>::type;
+
+    using iterator = detected_t<result_of_begin, t_ref>;
+    using sentinel = detected_t<result_of_end, t_ref>;
+
+    // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator
+    // and https://en.cppreference.com/w/cpp/iterator/sentinel_for
+    // but reimplementing these would be too much work, as a lot of other concepts are used underneath
+    static constexpr auto is_iterator_begin =
+        is_iterator_traits<iterator_traits<iterator>>::value;
+
+  public:
+    static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;
+};
+
+template<typename R>
+using iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;
+
+template<typename T>
+using range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;
+
+// The following implementation of is_complete_type is taken from
+// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
+// and is written by Xiang Fan who agreed to using it in this library.
+
+template<typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template<typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+         typename = void>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type_impl <
+    BasicJsonType, CompatibleObjectType,
+    enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&
+    is_detected<key_type_t, CompatibleObjectType>::value >>
+{
+    using object_t = typename BasicJsonType::object_t;
+
+    // macOS's is_constructible does not play well with nonesuch...
+    static constexpr bool value =
+        is_constructible<typename object_t::key_type,
+        typename CompatibleObjectType::key_type>::value &&
+        is_constructible<typename object_t::mapped_type,
+        typename CompatibleObjectType::mapped_type>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type
+    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+         typename = void>
+struct is_constructible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type_impl <
+    BasicJsonType, ConstructibleObjectType,
+    enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&
+    is_detected<key_type_t, ConstructibleObjectType>::value >>
+{
+    using object_t = typename BasicJsonType::object_t;
+
+    static constexpr bool value =
+        (is_default_constructible<ConstructibleObjectType>::value &&
+         (std::is_move_assignable<ConstructibleObjectType>::value ||
+          std::is_copy_assignable<ConstructibleObjectType>::value) &&
+         (is_constructible<typename ConstructibleObjectType::key_type,
+          typename object_t::key_type>::value &&
+          std::is_same <
+          typename object_t::mapped_type,
+          typename ConstructibleObjectType::mapped_type >::value)) ||
+        (has_from_json<BasicJsonType,
+         typename ConstructibleObjectType::mapped_type>::value ||
+         has_non_default_from_json <
+         BasicJsonType,
+         typename ConstructibleObjectType::mapped_type >::value);
+};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type
+    : is_constructible_object_type_impl<BasicJsonType,
+      ConstructibleObjectType> {};
+
+template<typename BasicJsonType, typename CompatibleStringType>
+struct is_compatible_string_type
+{
+    static constexpr auto value =
+        is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
+};
+
+template<typename BasicJsonType, typename ConstructibleStringType>
+struct is_constructible_string_type
+{
+    // launder type through decltype() to fix compilation failure on ICPC
+#ifdef __INTEL_COMPILER
+    using laundered_type = decltype(std::declval<ConstructibleStringType>());
+#else
+    using laundered_type = ConstructibleStringType;
+#endif
+
+    static constexpr auto value =
+        conjunction <
+        is_constructible<laundered_type, typename BasicJsonType::string_t>,
+        is_detected_exact<typename BasicJsonType::string_t::value_type,
+        value_type_t, laundered_type >>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
+struct is_compatible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type_impl <
+    BasicJsonType, CompatibleArrayType,
+    enable_if_t <
+    is_detected<iterator_t, CompatibleArrayType>::value&&
+    is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+    !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
+{
+    static constexpr bool value =
+        is_constructible<BasicJsonType,
+        range_value_t<CompatibleArrayType>>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type
+    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
+struct is_constructible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+    BasicJsonType, ConstructibleArrayType,
+    enable_if_t<std::is_same<ConstructibleArrayType,
+    typename BasicJsonType::value_type>::value >>
+            : std::true_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+    BasicJsonType, ConstructibleArrayType,
+    enable_if_t < !std::is_same<ConstructibleArrayType,
+    typename BasicJsonType::value_type>::value&&
+    !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+    is_default_constructible<ConstructibleArrayType>::value&&
+(std::is_move_assignable<ConstructibleArrayType>::value ||
+ std::is_copy_assignable<ConstructibleArrayType>::value)&&
+is_detected<iterator_t, ConstructibleArrayType>::value&&
+is_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&
+is_detected<range_value_t, ConstructibleArrayType>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&
+        is_complete_type <
+        detected_t<range_value_t, ConstructibleArrayType >>::value >>
+{
+    using value_type = range_value_t<ConstructibleArrayType>;
+
+    static constexpr bool value =
+        std::is_same<value_type,
+        typename BasicJsonType::array_t::value_type>::value ||
+        has_from_json<BasicJsonType,
+        value_type>::value ||
+        has_non_default_from_json <
+        BasicJsonType,
+        value_type >::value;
+};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type
+    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType,
+         typename = void>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl <
+    RealIntegerType, CompatibleNumberIntegerType,
+    enable_if_t < std::is_integral<RealIntegerType>::value&&
+    std::is_integral<CompatibleNumberIntegerType>::value&&
+    !std::is_same<bool, CompatibleNumberIntegerType>::value >>
+{
+    // is there an assert somewhere on overflows?
+    using RealLimits = std::numeric_limits<RealIntegerType>;
+    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+    static constexpr auto value =
+        is_constructible<RealIntegerType,
+        CompatibleNumberIntegerType>::value &&
+        CompatibleLimits::is_integer &&
+        RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+    : is_compatible_integer_type_impl<RealIntegerType,
+      CompatibleNumberIntegerType> {};
+
+template<typename BasicJsonType, typename CompatibleType, typename = void>
+struct is_compatible_type_impl: std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type_impl <
+    BasicJsonType, CompatibleType,
+    enable_if_t<is_complete_type<CompatibleType>::value >>
+{
+    static constexpr bool value =
+        has_to_json<BasicJsonType, CompatibleType>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type
+    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
+
+template<typename T1, typename T2>
+struct is_constructible_tuple : std::false_type {};
+
+template<typename T1, typename... Args>
+struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};
+
+template<typename BasicJsonType, typename T>
+struct is_json_iterator_of : std::false_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::iterator> : std::true_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::const_iterator> : std::true_type
+{};
+
+// checks if a given type T is a template specialization of Primary
+template<template <typename...> class Primary, typename T>
+struct is_specialization_of : std::false_type {};
+
+template<template <typename...> class Primary, typename... Args>
+struct is_specialization_of<Primary, Primary<Args...>> : std::true_type {};
+
+template<typename T>
+using is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t<T>>;
+
+// checks if A and B are comparable using Compare functor
+template<typename Compare, typename A, typename B, typename = void>
+struct is_comparable : std::false_type {};
+
+template<typename Compare, typename A, typename B>
+struct is_comparable<Compare, A, B, void_t<
+decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())),
+decltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))
+>> : std::true_type {};
+
+template<typename T>
+using detect_is_transparent = typename T::is_transparent;
+
+// type trait to check if KeyType can be used as object key (without a BasicJsonType)
+// see is_usable_as_basic_json_key_type below
+template<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_key_type = typename std::conditional <
+                              is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value
+                              && !(ExcludeObjectKeyType && std::is_same<KeyType,
+                                   ObjectKeyType>::value)
+                              && (!RequireTransparentComparator
+                                  || is_detected <detect_is_transparent, Comparator>::value)
+                              && !is_json_pointer<KeyType>::value,
+                              std::true_type,
+                              std::false_type >::type;
+
+// type trait to check if KeyType can be used as object key
+// true if:
+//   - KeyType is comparable with BasicJsonType::object_t::key_type
+//   - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type
+//   - the comparator is transparent or RequireTransparentComparator is false
+//   - KeyType is not a JSON iterator or json_pointer
+template<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_basic_json_key_type = typename std::conditional <
+        is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
+        typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
+        RequireTransparentComparator, ExcludeObjectKeyType>::value
+        && !is_json_iterator_of<BasicJsonType, KeyType>::value,
+        std::true_type,
+        std::false_type >::type;
+
+template<typename ObjectType, typename KeyType>
+using detect_erase_with_key_type = decltype(std::declval<ObjectType&>().erase(std::declval<KeyType>()));
+
+// type trait to check if object_t has an erase() member functions accepting KeyType
+template<typename BasicJsonType, typename KeyType>
+using has_erase_with_key_type = typename std::conditional <
+                                is_detected <
+                                detect_erase_with_key_type,
+                                typename BasicJsonType::object_t, KeyType >::value,
+                                std::true_type,
+                                std::false_type >::type;
+
+// a naive helper to check if a type is an ordered_map (exploits the fact that
+// ordered_map inherits capacity() from std::vector)
+template <typename T>
+struct is_ordered_map
+{
+    using one = char;
+
+    struct two
+    {
+        char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+    };
+
+    template <typename C> static one test( decltype(&C::capacity) ) ;
+    template <typename C> static two test(...);
+
+    enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+};
+
+// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
+template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
+T conditional_static_cast(U value)
+{
+    return static_cast<T>(value);
+}
+
+template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
+T conditional_static_cast(U value)
+{
+    return value;
+}
+
+template<typename... Types>
+using all_integral = conjunction<std::is_integral<Types>...>;
+
+template<typename... Types>
+using all_signed = conjunction<std::is_signed<Types>...>;
+
+template<typename... Types>
+using all_unsigned = conjunction<std::is_unsigned<Types>...>;
+
+// there's a disjunction trait in another PR; replace when merged
+template<typename... Types>
+using same_sign = std::integral_constant < bool,
+      all_signed<Types...>::value || all_unsigned<Types...>::value >;
+
+template<typename OfType, typename T>
+using never_out_of_range = std::integral_constant < bool,
+      (std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType)))
+      || (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T)) >;
+
+template<typename OfType, typename T,
+         bool OfTypeSigned = std::is_signed<OfType>::value,
+         bool TSigned = std::is_signed<T>::value>
+struct value_in_range_of_impl2;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, false>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, false>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, true>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+    }
+};
+
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, true>
+{
+    static constexpr bool test(T val)
+    {
+        using CommonType = typename std::common_type<OfType, T>::type;
+        return static_cast<CommonType>(val) >= static_cast<CommonType>((std::numeric_limits<OfType>::min)())
+               && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+    }
+};
+
+template<typename OfType, typename T,
+         bool NeverOutOfRange = never_out_of_range<OfType, T>::value,
+         typename = detail::enable_if_t<all_integral<OfType, T>::value>>
+struct value_in_range_of_impl1;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, false>
+{
+    static constexpr bool test(T val)
+    {
+        return value_in_range_of_impl2<OfType, T>::test(val);
+    }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, true>
+{
+    static constexpr bool test(T /*val*/)
+    {
+        return true;
+    }
+};
+
+template<typename OfType, typename T>
+inline constexpr bool value_in_range_of(T val)
+{
+    return value_in_range_of_impl1<OfType, T>::test(val);
+}
+
+template<bool Value>
+using bool_constant = std::integral_constant<bool, Value>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_c_string
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+inline constexpr bool is_c_string()
+{
+    using TUnExt = typename std::remove_extent<T>::type;
+    using TUnCVExt = typename std::remove_cv<TUnExt>::type;
+    using TUnPtr = typename std::remove_pointer<T>::type;
+    using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;
+    return
+        (std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)
+        || (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);
+}
+
+}  // namespace impl
+
+// checks whether T is a [cv] char */[cv] char[] C string
+template<typename T>
+struct is_c_string : bool_constant<impl::is_c_string<T>()> {};
+
+template<typename T>
+using is_c_string_uncvref = is_c_string<uncvref_t<T>>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_transparent
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+inline constexpr bool is_transparent()
+{
+    return is_detected<detect_is_transparent, T>::value;
+}
+
+}  // namespace impl
+
+// checks whether T has a member named is_transparent
+template<typename T>
+struct is_transparent : bool_constant<impl::is_transparent<T>()> {};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_concat.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstring> // strlen
+#include <string> // string
+#include <utility> // forward
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+inline std::size_t concat_length()
+{
+    return 0;
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, const Args& ... rest);
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, const Args& ... rest);
+
+template<typename... Args>
+inline std::size_t concat_length(const char /*c*/, const Args& ... rest)
+{
+    return 1 + concat_length(rest...);
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, const Args& ... rest)
+{
+    // cppcheck-suppress ignoredReturnValue
+    return ::strlen(cstr) + concat_length(rest...);
+}
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, const Args& ... rest)
+{
+    return str.size() + concat_length(rest...);
+}
+
+template<typename OutStringType>
+inline void concat_into(OutStringType& /*out*/)
+{}
+
+template<typename StringType, typename Arg>
+using string_can_append = decltype(std::declval<StringType&>().append(std::declval < Arg && > ()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append = is_detected<string_can_append, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_op = decltype(std::declval<StringType&>() += std::declval < Arg && > ());
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_op = is_detected<string_can_append_op, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_iter = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().begin(), std::declval<const Arg&>().end()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_iter = is_detected<string_can_append_iter, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_data = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().data(), std::declval<const Arg&>().size()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_data = is_detected<string_can_append_data, StringType, Arg>;
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && detect_string_can_append_op<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && detect_string_can_append_iter<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && !detect_string_can_append_iter<OutStringType, Arg>::value
+                         && detect_string_can_append_data<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template<typename OutStringType, typename Arg, typename... Args,
+         enable_if_t<detect_string_can_append<OutStringType, Arg>::value, int> = 0>
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest)
+{
+    out.append(std::forward<Arg>(arg));
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && detect_string_can_append_op<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest)
+{
+    out += std::forward<Arg>(arg);
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && detect_string_can_append_iter<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+    out.append(arg.begin(), arg.end());
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+                         && !detect_string_can_append_op<OutStringType, Arg>::value
+                         && !detect_string_can_append_iter<OutStringType, Arg>::value
+                         && detect_string_can_append_data<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+    out.append(arg.data(), arg.size());
+    concat_into(out, std::forward<Args>(rest)...);
+}
+
+template<typename OutStringType = std::string, typename... Args>
+inline OutStringType concat(Args && ... args)
+{
+    OutStringType str;
+    str.reserve(concat_length(args...));
+    concat_into(str, std::forward<Args>(args)...);
+    return str;
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+////////////////
+// exceptions //
+////////////////
+
+/// @brief general exception of the @ref basic_json class
+/// @sa https://json.nlohmann.me/api/basic_json/exception/
+class exception : public std::exception
+{
+  public:
+    /// returns the explanatory string
+    const char* what() const noexcept override
+    {
+        return m.what();
+    }
+
+    /// the id of the exception
+    const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
+
+  protected:
+    JSON_HEDLEY_NON_NULL(3)
+    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
+
+    static std::string name(const std::string& ename, int id_)
+    {
+        return concat("[json.exception.", ename, '.', std::to_string(id_), "] ");
+    }
+
+    static std::string diagnostics(std::nullptr_t /*leaf_element*/)
+    {
+        return "";
+    }
+
+    template<typename BasicJsonType>
+    static std::string diagnostics(const BasicJsonType* leaf_element)
+    {
+#if JSON_DIAGNOSTICS
+        std::vector<std::string> tokens;
+        for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)
+        {
+            switch (current->m_parent->type())
+            {
+                case value_t::array:
+                {
+                    for (std::size_t i = 0; i < current->m_parent->m_data.m_value.array->size(); ++i)
+                    {
+                        if (&current->m_parent->m_data.m_value.array->operator[](i) == current)
+                        {
+                            tokens.emplace_back(std::to_string(i));
+                            break;
+                        }
+                    }
+                    break;
+                }
+
+                case value_t::object:
+                {
+                    for (const auto& element : *current->m_parent->m_data.m_value.object)
+                    {
+                        if (&element.second == current)
+                        {
+                            tokens.emplace_back(element.first.c_str());
+                            break;
+                        }
+                    }
+                    break;
+                }
+
+                case value_t::null: // LCOV_EXCL_LINE
+                case value_t::string: // LCOV_EXCL_LINE
+                case value_t::boolean: // LCOV_EXCL_LINE
+                case value_t::number_integer: // LCOV_EXCL_LINE
+                case value_t::number_unsigned: // LCOV_EXCL_LINE
+                case value_t::number_float: // LCOV_EXCL_LINE
+                case value_t::binary: // LCOV_EXCL_LINE
+                case value_t::discarded: // LCOV_EXCL_LINE
+                default:   // LCOV_EXCL_LINE
+                    break; // LCOV_EXCL_LINE
+            }
+        }
+
+        if (tokens.empty())
+        {
+            return "";
+        }
+
+        auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
+                                   [](const std::string & a, const std::string & b)
+        {
+            return concat(a, '/', detail::escape(b));
+        });
+        return concat('(', str, ") ");
+#else
+        static_cast<void>(leaf_element);
+        return "";
+#endif
+    }
+
+  private:
+    /// an exception object as storage for error messages
+    std::runtime_error m;
+};
+
+/// @brief exception indicating a parse error
+/// @sa https://json.nlohmann.me/api/basic_json/parse_error/
+class parse_error : public exception
+{
+  public:
+    /*!
+    @brief create a parse error exception
+    @param[in] id_       the id of the exception
+    @param[in] pos       the position where the error occurred (or with
+                         chars_read_total=0 if the position cannot be
+                         determined)
+    @param[in] what_arg  the explanatory string
+    @return parse_error object
+    */
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
+    {
+        const std::string w = concat(exception::name("parse_error", id_), "parse error",
+                                     position_string(pos), ": ", exception::diagnostics(context), what_arg);
+        return {id_, pos.chars_read_total, w.c_str()};
+    }
+
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
+    {
+        const std::string w = concat(exception::name("parse_error", id_), "parse error",
+                                     (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""),
+                                     ": ", exception::diagnostics(context), what_arg);
+        return {id_, byte_, w.c_str()};
+    }
+
+    /*!
+    @brief byte index of the parse error
+
+    The byte index of the last read character in the input file.
+
+    @note For an input with n bytes, 1 is the index of the first character and
+          n+1 is the index of the terminating null byte or the end of file.
+          This also holds true when reading a byte vector (CBOR or MessagePack).
+    */
+    const std::size_t byte;
+
+  private:
+    parse_error(int id_, std::size_t byte_, const char* what_arg)
+        : exception(id_, what_arg), byte(byte_) {}
+
+    static std::string position_string(const position_t& pos)
+    {
+        return concat(" at line ", std::to_string(pos.lines_read + 1),
+                      ", column ", std::to_string(pos.chars_read_current_line));
+    }
+};
+
+/// @brief exception indicating errors with iterators
+/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/
+class invalid_iterator : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        const std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    invalid_iterator(int id_, const char* what_arg)
+        : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating executing a member function with a wrong type
+/// @sa https://json.nlohmann.me/api/basic_json/type_error/
+class type_error : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        const std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating access out of the defined range
+/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/
+class out_of_range : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        const std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating other library errors
+/// @sa https://json.nlohmann.me/api/basic_json/other_error/
+class other_error : public exception
+{
+  public:
+    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+    static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+    {
+        const std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg);
+        return {id_, w.c_str()};
+    }
+
+  private:
+    JSON_HEDLEY_NON_NULL(3)
+    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// dispatching helper struct
+template <class T> struct identity_tag {};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/std_fs.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
+#include <experimental/filesystem>
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::experimental::filesystem;
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+#elif JSON_HAS_FILESYSTEM
+#include <filesystem>
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::filesystem;
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+#endif
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
+    }
+    n = nullptr;
+}
+
+// overloads for basic_json template parameters
+template < typename BasicJsonType, typename ArithmeticType,
+           enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
+                         !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+                         int > = 0 >
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+
+        case value_t::null:
+        case value_t::object:
+        case value_t::array:
+        case value_t::string:
+        case value_t::boolean:
+        case value_t::binary:
+        case value_t::discarded:
+        default:
+            JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+    }
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
+    }
+    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+    }
+    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template <
+    typename BasicJsonType, typename StringType,
+    enable_if_t <
+        std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
+        && is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value
+        && !std::is_same<typename BasicJsonType::string_t, StringType>::value
+        && !is_json_ref<StringType>::value, int > = 0 >
+inline void from_json(const BasicJsonType& j, StringType& s)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+    }
+
+    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+{
+    get_arithmetic_value(j, val);
+}
+
+#if !JSON_DISABLE_ENUM_SERIALIZATION
+template<typename BasicJsonType, typename EnumType,
+         enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, EnumType& e)
+{
+    typename std::underlying_type<EnumType>::type val;
+    get_arithmetic_value(j, val);
+    e = static_cast<EnumType>(val);
+}
+#endif  // JSON_DISABLE_ENUM_SERIALIZATION
+
+// forward_list doesn't have an insert method
+template<typename BasicJsonType, typename T, typename Allocator,
+         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    l.clear();
+    std::transform(j.rbegin(), j.rend(),
+                   std::front_inserter(l), [](const BasicJsonType & i)
+    {
+        return i.template get<T>();
+    });
+}
+
+// valarray doesn't have an insert method
+template<typename BasicJsonType, typename T,
+         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    l.resize(j.size());
+    std::transform(j.begin(), j.end(), std::begin(l),
+                   [](const BasicJsonType & elem)
+    {
+        return elem.template get<T>();
+    });
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json(const BasicJsonType& j, T (&arr)[N])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+-> decltype(j.template get<T>(), void())
+{
+    for (std::size_t i = 0; i < N; ++i)
+    {
+        arr[i] = j.at(i).template get<T>();
+    }
+}
+
+template<typename BasicJsonType>
+inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
+{
+    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
+                          priority_tag<2> /*unused*/)
+-> decltype(j.template get<T>(), void())
+{
+    for (std::size_t i = 0; i < N; ++i)
+    {
+        arr[i] = j.at(i).template get<T>();
+    }
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+         enable_if_t<
+             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+             int> = 0>
+auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
+-> decltype(
+    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
+    j.template get<typename ConstructibleArrayType::value_type>(),
+    void())
+{
+    using std::end;
+
+    ConstructibleArrayType ret;
+    ret.reserve(j.size());
+    std::transform(j.begin(), j.end(),
+                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename ConstructibleArrayType::value_type>();
+    });
+    arr = std::move(ret);
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+         enable_if_t<
+             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+             int> = 0>
+inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
+                                 priority_tag<0> /*unused*/)
+{
+    using std::end;
+
+    ConstructibleArrayType ret;
+    std::transform(
+        j.begin(), j.end(), std::inserter(ret, end(ret)),
+        [](const BasicJsonType & i)
+    {
+        // get<BasicJsonType>() returns *this, this won't call a from_json
+        // method when value_type is BasicJsonType
+        return i.template get<typename ConstructibleArrayType::value_type>();
+    });
+    arr = std::move(ret);
+}
+
+template < typename BasicJsonType, typename ConstructibleArrayType,
+           enable_if_t <
+               is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
+               !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
+               !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+               !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
+               !is_basic_json<ConstructibleArrayType>::value,
+               int > = 0 >
+auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
+-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
+j.template get<typename ConstructibleArrayType::value_type>(),
+void())
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+
+    from_json_array_impl(j, arr, priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename T, std::size_t... Idx >
+std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
+        identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
+{
+    return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
+}
+
+template < typename BasicJsonType, typename T, std::size_t N >
+auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
+-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+
+    return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
+    }
+
+    bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
+}
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
+    }
+
+    ConstructibleObjectType ret;
+    const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+    using value_type = typename ConstructibleObjectType::value_type;
+    std::transform(
+        inner_object->begin(), inner_object->end(),
+        std::inserter(ret, ret.begin()),
+        [](typename BasicJsonType::object_t::value_type const & p)
+    {
+        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
+    });
+    obj = std::move(ret);
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template < typename BasicJsonType, typename ArithmeticType,
+           enable_if_t <
+               std::is_arithmetic<ArithmeticType>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
+               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+               int > = 0 >
+inline void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+    switch (static_cast<value_t>(j))
+    {
+        case value_t::number_unsigned:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+            break;
+        }
+        case value_t::number_integer:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+            break;
+        }
+        case value_t::number_float:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+            break;
+        }
+        case value_t::boolean:
+        {
+            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+            break;
+        }
+
+        case value_t::null:
+        case value_t::object:
+        case value_t::array:
+        case value_t::string:
+        case value_t::binary:
+        case value_t::discarded:
+        default:
+            JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+    }
+}
+
+template<typename BasicJsonType, typename... Args, std::size_t... Idx>
+std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
+{
+    return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
+}
+
+template < typename BasicJsonType, class A1, class A2 >
+std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
+{
+    return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
+            std::forward<BasicJsonType>(j).at(1).template get<A2>()};
+}
+
+template<typename BasicJsonType, typename A1, typename A2>
+inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
+{
+    p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
+{
+    return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
+{
+    t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
+}
+
+template<typename BasicJsonType, typename TupleRelated>
+auto from_json(BasicJsonType&& j, TupleRelated&& t)
+-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+
+    return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
+           typename = enable_if_t < !std::is_constructible <
+                                        typename BasicJsonType::string_t, Key >::value >>
+inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    m.clear();
+    for (const auto& p : j)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+        {
+            JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+        }
+        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+    }
+}
+
+template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
+           typename = enable_if_t < !std::is_constructible <
+                                        typename BasicJsonType::string_t, Key >::value >>
+inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+    }
+    m.clear();
+    for (const auto& p : j)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+        {
+            JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+        }
+        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+    }
+}
+
+#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, std_fs::path& p)
+{
+    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+    {
+        JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+    }
+    p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+#endif
+
+struct from_json_fn
+{
+    template<typename BasicJsonType, typename T>
+    auto operator()(const BasicJsonType& j, T&& val) const
+    noexcept(noexcept(from_json(j, std::forward<T>(val))))
+    -> decltype(from_json(j, std::forward<T>(val)))
+    {
+        return from_json(j, std::forward<T>(val));
+    }
+};
+
+}  // namespace detail
+
+#ifndef JSON_HAS_CPP_17
+/// namespace to hold default `from_json` function
+/// to see why this is required:
+/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
+    detail::static_const<detail::from_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
+}  // namespace
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // copy
+#include <iterator> // begin, end
+#include <string> // string
+#include <tuple> // tuple, get
+#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
+#include <utility> // move, forward, declval, pair
+#include <valarray> // valarray
+#include <vector> // vector
+
+// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // size_t
+#include <iterator> // input_iterator_tag
+#include <string> // string, to_string
+#include <tuple> // tuple_size, get, tuple_element
+#include <utility> // move
+
+#if JSON_HAS_RANGES
+    #include <ranges> // enable_borrowed_range
+#endif
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename string_type>
+void int_to_string( string_type& target, std::size_t value )
+{
+    // For ADL
+    using std::to_string;
+    target = to_string(value);
+}
+template<typename IteratorType> class iteration_proxy_value
+{
+  public:
+    using difference_type = std::ptrdiff_t;
+    using value_type = iteration_proxy_value;
+    using pointer = value_type *;
+    using reference = value_type &;
+    using iterator_category = std::input_iterator_tag;
+    using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
+
+  private:
+    /// the iterator
+    IteratorType anchor{};
+    /// an index for arrays (used to create key names)
+    std::size_t array_index = 0;
+    /// last stringified array index
+    mutable std::size_t array_index_last = 0;
+    /// a string representation of the array index
+    mutable string_type array_index_str = "0";
+    /// an empty string (to return a reference for primitive values)
+    string_type empty_str{};
+
+  public:
+    explicit iteration_proxy_value() = default;
+    explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
+    noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+             && std::is_nothrow_default_constructible<string_type>::value)
+        : anchor(std::move(it))
+        , array_index(array_index_)
+    {}
+
+    iteration_proxy_value(iteration_proxy_value const&) = default;
+    iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
+    // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
+    iteration_proxy_value(iteration_proxy_value&&)
+    noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+             && std::is_nothrow_move_constructible<string_type>::value) = default;
+    iteration_proxy_value& operator=(iteration_proxy_value&&)
+    noexcept(std::is_nothrow_move_assignable<IteratorType>::value
+             && std::is_nothrow_move_assignable<string_type>::value) = default;
+    ~iteration_proxy_value() = default;
+
+    /// dereference operator (needed for range-based for)
+    const iteration_proxy_value& operator*() const
+    {
+        return *this;
+    }
+
+    /// increment operator (needed for range-based for)
+    iteration_proxy_value& operator++()
+    {
+        ++anchor;
+        ++array_index;
+
+        return *this;
+    }
+
+    iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto tmp = iteration_proxy_value(anchor, array_index);
+        ++anchor;
+        ++array_index;
+        return tmp;
+    }
+
+    /// equality operator (needed for InputIterator)
+    bool operator==(const iteration_proxy_value& o) const
+    {
+        return anchor == o.anchor;
+    }
+
+    /// inequality operator (needed for range-based for)
+    bool operator!=(const iteration_proxy_value& o) const
+    {
+        return anchor != o.anchor;
+    }
+
+    /// return key of the iterator
+    const string_type& key() const
+    {
+        JSON_ASSERT(anchor.m_object != nullptr);
+
+        switch (anchor.m_object->type())
+        {
+            // use integer array index as key
+            case value_t::array:
+            {
+                if (array_index != array_index_last)
+                {
+                    int_to_string( array_index_str, array_index );
+                    array_index_last = array_index;
+                }
+                return array_index_str;
+            }
+
+            // use key from the object
+            case value_t::object:
+                return anchor.key();
+
+            // use an empty key for all primitive types
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return empty_str;
+        }
+    }
+
+    /// return value of the iterator
+    typename IteratorType::reference value() const
+    {
+        return anchor.value();
+    }
+};
+
+/// proxy class for the items() function
+template<typename IteratorType> class iteration_proxy
+{
+  private:
+    /// the container to iterate
+    typename IteratorType::pointer container = nullptr;
+
+  public:
+    explicit iteration_proxy() = default;
+
+    /// construct iteration proxy from a container
+    explicit iteration_proxy(typename IteratorType::reference cont) noexcept
+        : container(&cont) {}
+
+    iteration_proxy(iteration_proxy const&) = default;
+    iteration_proxy& operator=(iteration_proxy const&) = default;
+    iteration_proxy(iteration_proxy&&) noexcept = default;
+    iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
+    ~iteration_proxy() = default;
+
+    /// return iterator begin (needed for range-based for)
+    iteration_proxy_value<IteratorType> begin() const noexcept
+    {
+        return iteration_proxy_value<IteratorType>(container->begin());
+    }
+
+    /// return iterator end (needed for range-based for)
+    iteration_proxy_value<IteratorType> end() const noexcept
+    {
+        return iteration_proxy_value<IteratorType>(container->end());
+    }
+};
+
+// Structured Bindings Support
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
+auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
+{
+    return i.key();
+}
+// Structured Bindings Support
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
+auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
+{
+    return i.value();
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// The Addition to the STD Namespace is required to add
+// Structured Bindings Support to the iteration_proxy_value class
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+namespace std
+{
+
+#if defined(__clang__)
+    // Fix: https://github.com/nlohmann/json/issues/1401
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+template<typename IteratorType>
+class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> // NOLINT(cert-dcl58-cpp)
+            : public std::integral_constant<std::size_t, 2> {};
+
+template<std::size_t N, typename IteratorType>
+class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> // NOLINT(cert-dcl58-cpp)
+{
+  public:
+    using type = decltype(
+                     get<N>(std::declval <
+                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
+};
+#if defined(__clang__)
+    #pragma clang diagnostic pop
+#endif
+
+}  // namespace std
+
+#if JSON_HAS_RANGES
+    template <typename IteratorType>
+    inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
+#endif
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/std_fs.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////
+// constructors //
+//////////////////
+
+/*
+ * Note all external_constructor<>::construct functions need to call
+ * j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
+ * allocated value (e.g., a string). See bug issue
+ * https://github.com/nlohmann/json/issues/2865 for more information.
+ */
+
+template<value_t> struct external_constructor;
+
+template<>
+struct external_constructor<value_t::boolean>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::boolean;
+        j.m_data.m_value = b;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::string>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::string;
+        j.m_data.m_value = s;
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::string;
+        j.m_data.m_value = std::move(s);
+        j.assert_invariant();
+    }
+
+    template < typename BasicJsonType, typename CompatibleStringType,
+               enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
+                             int > = 0 >
+    static void construct(BasicJsonType& j, const CompatibleStringType& str)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::string;
+        j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::binary>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::binary;
+        j.m_data.m_value = typename BasicJsonType::binary_t(b);
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::binary;
+        j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_float>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::number_float;
+        j.m_data.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_unsigned>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::number_unsigned;
+        j.m_data.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::number_integer>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::number_integer;
+        j.m_data.m_value = val;
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::array>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::array;
+        j.m_data.m_value = arr;
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::array;
+        j.m_data.m_value = std::move(arr);
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template < typename BasicJsonType, typename CompatibleArrayType,
+               enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
+                             int > = 0 >
+    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
+    {
+        using std::begin;
+        using std::end;
+
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::array;
+        j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const std::vector<bool>& arr)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::array;
+        j.m_data.m_value = value_t::array;
+        j.m_data.m_value.array->reserve(arr.size());
+        for (const bool x : arr)
+        {
+            j.m_data.m_value.array->push_back(x);
+            j.set_parent(j.m_data.m_value.array->back());
+        }
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType, typename T,
+             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+    static void construct(BasicJsonType& j, const std::valarray<T>& arr)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::array;
+        j.m_data.m_value = value_t::array;
+        j.m_data.m_value.array->resize(arr.size());
+        if (arr.size() > 0)
+        {
+            std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
+        }
+        j.set_parents();
+        j.assert_invariant();
+    }
+};
+
+template<>
+struct external_constructor<value_t::object>
+{
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::object;
+        j.m_data.m_value = obj;
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template<typename BasicJsonType>
+    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+    {
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::object;
+        j.m_data.m_value = std::move(obj);
+        j.set_parents();
+        j.assert_invariant();
+    }
+
+    template < typename BasicJsonType, typename CompatibleObjectType,
+               enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
+    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
+    {
+        using std::begin;
+        using std::end;
+
+        j.m_data.m_value.destroy(j.m_data.m_type);
+        j.m_data.m_type = value_t::object;
+        j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+        j.set_parents();
+        j.assert_invariant();
+    }
+};
+
+/////////////
+// to_json //
+/////////////
+
+template<typename BasicJsonType, typename T,
+         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
+inline void to_json(BasicJsonType& j, T b) noexcept
+{
+    external_constructor<value_t::boolean>::construct(j, b);
+}
+
+template < typename BasicJsonType, typename BoolRef,
+           enable_if_t <
+               ((std::is_same<std::vector<bool>::reference, BoolRef>::value
+                 && !std::is_same <std::vector<bool>::reference, typename BasicJsonType::boolean_t&>::value)
+                || (std::is_same<std::vector<bool>::const_reference, BoolRef>::value
+                    && !std::is_same <detail::uncvref_t<std::vector<bool>::const_reference>,
+                                      typename BasicJsonType::boolean_t >::value))
+               && std::is_convertible<const BoolRef&, typename BasicJsonType::boolean_t>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const BoolRef& b) noexcept
+{
+    external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));
+}
+
+template<typename BasicJsonType, typename CompatibleString,
+         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const CompatibleString& s)
+{
+    external_constructor<value_t::string>::construct(j, s);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+{
+    external_constructor<value_t::string>::construct(j, std::move(s));
+}
+
+template<typename BasicJsonType, typename FloatType,
+         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, FloatType val) noexcept
+{
+    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
+         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+{
+    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberIntegerType,
+         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+{
+    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
+}
+
+#if !JSON_DISABLE_ENUM_SERIALIZATION
+template<typename BasicJsonType, typename EnumType,
+         enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, EnumType e) noexcept
+{
+    using underlying_type = typename std::underlying_type<EnumType>::type;
+    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
+}
+#endif  // JSON_DISABLE_ENUM_SERIALIZATION
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
+{
+    external_constructor<value_t::array>::construct(j, e);
+}
+
+template < typename BasicJsonType, typename CompatibleArrayType,
+           enable_if_t < is_compatible_array_type<BasicJsonType,
+                         CompatibleArrayType>::value&&
+                         !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
+                         !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
+                         !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
+                         !is_basic_json<CompatibleArrayType>::value,
+                         int > = 0 >
+inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
+{
+    external_constructor<value_t::array>::construct(j, arr);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
+{
+    external_constructor<value_t::binary>::construct(j, bin);
+}
+
+template<typename BasicJsonType, typename T,
+         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
+{
+    external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+{
+    external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template < typename BasicJsonType, typename CompatibleObjectType,
+           enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
+{
+    external_constructor<value_t::object>::construct(j, obj);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+{
+    external_constructor<value_t::object>::construct(j, std::move(obj));
+}
+
+template <
+    typename BasicJsonType, typename T, std::size_t N,
+    enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
+                  const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+                  int > = 0 >
+inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+{
+    external_constructor<value_t::array>::construct(j, arr);
+}
+
+template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
+{
+    j = { p.first, p.second };
+}
+
+// for https://github.com/nlohmann/json/pull/1134
+template<typename BasicJsonType, typename T,
+         enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const T& b)
+{
+    j = { {b.key(), b.value()} };
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
+{
+    j = { std::get<Idx>(t)... };
+}
+
+template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
+inline void to_json(BasicJsonType& j, const T& t)
+{
+    to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
+}
+
+#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const std_fs::path& p)
+{
+    j = p.string();
+}
+#endif
+
+struct to_json_fn
+{
+    template<typename BasicJsonType, typename T>
+    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
+    -> decltype(to_json(j, std::forward<T>(val)), void())
+    {
+        return to_json(j, std::forward<T>(val));
+    }
+};
+}  // namespace detail
+
+#ifndef JSON_HAS_CPP_17
+/// namespace to hold default `to_json` function
+/// to see why this is required:
+/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
+    detail::static_const<detail::to_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
+}  // namespace
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @sa https://json.nlohmann.me/api/adl_serializer/
+template<typename ValueType, typename>
+struct adl_serializer
+{
+    /// @brief convert a JSON value to any value type
+    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+    template<typename BasicJsonType, typename TargetType = ValueType>
+    static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
+        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
+    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
+    {
+        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+    }
+
+    /// @brief convert a JSON value to any value type
+    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+    template<typename BasicJsonType, typename TargetType = ValueType>
+    static auto from_json(BasicJsonType && j) noexcept(
+    noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
+    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
+    {
+        return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
+    }
+
+    /// @brief convert any value type to a JSON value
+    /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/
+    template<typename BasicJsonType, typename TargetType = ValueType>
+    static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
+        noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))
+    -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
+    {
+        ::nlohmann::to_json(j, std::forward<TargetType>(val));
+    }
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/byte_container_with_subtype.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // uint8_t, uint64_t
+#include <tuple> // tie
+#include <utility> // move
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @brief an internal type for a backed binary type
+/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/
+template<typename BinaryType>
+class byte_container_with_subtype : public BinaryType
+{
+  public:
+    using container_type = BinaryType;
+    using subtype_type = std::uint64_t;
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype() noexcept(noexcept(container_type()))
+        : container_type()
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))
+        : container_type(b)
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))
+        : container_type(std::move(b))
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))
+        : container_type(b)
+        , m_subtype(subtype_)
+        , m_has_subtype(true)
+    {}
+
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+    byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))
+        : container_type(std::move(b))
+        , m_subtype(subtype_)
+        , m_has_subtype(true)
+    {}
+
+    bool operator==(const byte_container_with_subtype& rhs) const
+    {
+        return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
+               std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
+    }
+
+    bool operator!=(const byte_container_with_subtype& rhs) const
+    {
+        return !(rhs == *this);
+    }
+
+    /// @brief sets the binary subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/
+    void set_subtype(subtype_type subtype_) noexcept
+    {
+        m_subtype = subtype_;
+        m_has_subtype = true;
+    }
+
+    /// @brief return the binary subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/
+    constexpr subtype_type subtype() const noexcept
+    {
+        return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1);
+    }
+
+    /// @brief return whether the value has a subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/
+    constexpr bool has_subtype() const noexcept
+    {
+        return m_has_subtype;
+    }
+
+    /// @brief clears the binary subtype
+    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/
+    void clear_subtype() noexcept
+    {
+        m_subtype = 0;
+        m_has_subtype = false;
+    }
+
+  private:
+    subtype_type m_subtype = 0;
+    bool m_has_subtype = false;
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/conversions/from_json.hpp>
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/hash.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // uint8_t
+#include <cstddef> // size_t
+#include <functional> // hash
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// boost::hash_combine
+inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
+{
+    seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);
+    return seed;
+}
+
+/*!
+@brief hash a JSON value
+
+The hash function tries to rely on std::hash where possible. Furthermore, the
+type of the JSON value is taken into account to have different hash values for
+null, 0, 0U, and false, etc.
+
+@tparam BasicJsonType basic_json specialization
+@param j JSON value to hash
+@return hash value of j
+*/
+template<typename BasicJsonType>
+std::size_t hash(const BasicJsonType& j)
+{
+    using string_t = typename BasicJsonType::string_t;
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+
+    const auto type = static_cast<std::size_t>(j.type());
+    switch (j.type())
+    {
+        case BasicJsonType::value_t::null:
+        case BasicJsonType::value_t::discarded:
+        {
+            return combine(type, 0);
+        }
+
+        case BasicJsonType::value_t::object:
+        {
+            auto seed = combine(type, j.size());
+            for (const auto& element : j.items())
+            {
+                const auto h = std::hash<string_t> {}(element.key());
+                seed = combine(seed, h);
+                seed = combine(seed, hash(element.value()));
+            }
+            return seed;
+        }
+
+        case BasicJsonType::value_t::array:
+        {
+            auto seed = combine(type, j.size());
+            for (const auto& element : j)
+            {
+                seed = combine(seed, hash(element));
+            }
+            return seed;
+        }
+
+        case BasicJsonType::value_t::string:
+        {
+            const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::boolean:
+        {
+            const auto h = std::hash<bool> {}(j.template get<bool>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::number_integer:
+        {
+            const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::number_unsigned:
+        {
+            const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::number_float:
+        {
+            const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
+            return combine(type, h);
+        }
+
+        case BasicJsonType::value_t::binary:
+        {
+            auto seed = combine(type, j.get_binary().size());
+            const auto h = std::hash<bool> {}(j.get_binary().has_subtype());
+            seed = combine(seed, h);
+            seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));
+            for (const auto byte : j.get_binary())
+            {
+                seed = combine(seed, std::hash<std::uint8_t> {}(byte));
+            }
+            return seed;
+        }
+
+        default:                   // LCOV_EXCL_LINE
+            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+            return 0;              // LCOV_EXCL_LINE
+    }
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // generate_n
+#include <array> // array
+#include <cmath> // ldexp
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstdio> // snprintf
+#include <cstring> // memcpy
+#include <iterator> // back_inserter
+#include <limits> // numeric_limits
+#include <string> // char_traits, string
+#include <utility> // make_pair, move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstring> // strlen
+#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
+#include <memory> // shared_ptr, make_shared, addressof
+#include <numeric> // accumulate
+#include <string> // string, char_traits
+#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
+#include <utility> // pair, declval
+
+#ifndef JSON_NO_IO
+    #include <cstdio>   // FILE *
+    #include <istream>  // istream
+#endif                  // JSON_NO_IO
+
+// #include <nlohmann/detail/iterators/iterator_traits.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// the supported input formats
+enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
+
+////////////////////
+// input adapters //
+////////////////////
+
+#ifndef JSON_NO_IO
+/*!
+Input adapter for stdio file access. This adapter read only 1 byte and do not use any
+ buffer. This adapter is a very low level adapter.
+*/
+class file_input_adapter
+{
+  public:
+    using char_type = char;
+
+    JSON_HEDLEY_NON_NULL(2)
+    explicit file_input_adapter(std::FILE* f) noexcept
+        : m_file(f)
+    {
+        JSON_ASSERT(m_file != nullptr);
+    }
+
+    // make class move-only
+    file_input_adapter(const file_input_adapter&) = delete;
+    file_input_adapter(file_input_adapter&&) noexcept = default;
+    file_input_adapter& operator=(const file_input_adapter&) = delete;
+    file_input_adapter& operator=(file_input_adapter&&) = delete;
+    ~file_input_adapter() = default;
+
+    std::char_traits<char>::int_type get_character() noexcept
+    {
+        return std::fgetc(m_file);
+    }
+
+  private:
+    /// the file pointer to read from
+    std::FILE* m_file;
+};
+
+
+/*!
+Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
+beginning of input. Does not support changing the underlying std::streambuf
+in mid-input. Maintains underlying std::istream and std::streambuf to support
+subsequent use of standard std::istream operations to process any input
+characters following those used in parsing the JSON input.  Clears the
+std::istream flags; any input errors (e.g., EOF) will be detected by the first
+subsequent call for input from the std::istream.
+*/
+class input_stream_adapter
+{
+  public:
+    using char_type = char;
+
+    ~input_stream_adapter()
+    {
+        // clear stream flags; we use underlying streambuf I/O, do not
+        // maintain ifstream flags, except eof
+        if (is != nullptr)
+        {
+            is->clear(is->rdstate() & std::ios::eofbit);
+        }
+    }
+
+    explicit input_stream_adapter(std::istream& i)
+        : is(&i), sb(i.rdbuf())
+    {}
+
+    // delete because of pointer members
+    input_stream_adapter(const input_stream_adapter&) = delete;
+    input_stream_adapter& operator=(input_stream_adapter&) = delete;
+    input_stream_adapter& operator=(input_stream_adapter&&) = delete;
+
+    input_stream_adapter(input_stream_adapter&& rhs) noexcept
+        : is(rhs.is), sb(rhs.sb)
+    {
+        rhs.is = nullptr;
+        rhs.sb = nullptr;
+    }
+
+    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
+    // ensure that std::char_traits<char>::eof() and the character 0xFF do not
+    // end up as the same value, e.g. 0xFFFFFFFF.
+    std::char_traits<char>::int_type get_character()
+    {
+        auto res = sb->sbumpc();
+        // set eof manually, as we don't use the istream interface.
+        if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
+        {
+            is->clear(is->rdstate() | std::ios::eofbit);
+        }
+        return res;
+    }
+
+  private:
+    /// the associated input stream
+    std::istream* is = nullptr;
+    std::streambuf* sb = nullptr;
+};
+#endif  // JSON_NO_IO
+
+// General-purpose iterator-based adapter. It might not be as fast as
+// theoretically possible for some containers, but it is extremely versatile.
+template<typename IteratorType>
+class iterator_input_adapter
+{
+  public:
+    using char_type = typename std::iterator_traits<IteratorType>::value_type;
+
+    iterator_input_adapter(IteratorType first, IteratorType last)
+        : current(std::move(first)), end(std::move(last))
+    {}
+
+    typename std::char_traits<char_type>::int_type get_character()
+    {
+        if (JSON_HEDLEY_LIKELY(current != end))
+        {
+            auto result = std::char_traits<char_type>::to_int_type(*current);
+            std::advance(current, 1);
+            return result;
+        }
+
+        return std::char_traits<char_type>::eof();
+    }
+
+  private:
+    IteratorType current;
+    IteratorType end;
+
+    template<typename BaseInputAdapter, size_t T>
+    friend struct wide_string_input_helper;
+
+    bool empty() const
+    {
+        return current == end;
+    }
+};
+
+
+template<typename BaseInputAdapter, size_t T>
+struct wide_string_input_helper;
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 4>
+{
+    // UTF-32
+    static void fill_buffer(BaseInputAdapter& input,
+                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+                            size_t& utf8_bytes_index,
+                            size_t& utf8_bytes_filled)
+    {
+        utf8_bytes_index = 0;
+
+        if (JSON_HEDLEY_UNLIKELY(input.empty()))
+        {
+            utf8_bytes[0] = std::char_traits<char>::eof();
+            utf8_bytes_filled = 1;
+        }
+        else
+        {
+            // get the current character
+            const auto wc = input.get_character();
+
+            // UTF-32 to UTF-8 encoding
+            if (wc < 0x80)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                utf8_bytes_filled = 1;
+            }
+            else if (wc <= 0x7FF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 2;
+            }
+            else if (wc <= 0xFFFF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 3;
+            }
+            else if (wc <= 0x10FFFF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
+                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 4;
+            }
+            else
+            {
+                // unknown character
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                utf8_bytes_filled = 1;
+            }
+        }
+    }
+};
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 2>
+{
+    // UTF-16
+    static void fill_buffer(BaseInputAdapter& input,
+                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+                            size_t& utf8_bytes_index,
+                            size_t& utf8_bytes_filled)
+    {
+        utf8_bytes_index = 0;
+
+        if (JSON_HEDLEY_UNLIKELY(input.empty()))
+        {
+            utf8_bytes[0] = std::char_traits<char>::eof();
+            utf8_bytes_filled = 1;
+        }
+        else
+        {
+            // get the current character
+            const auto wc = input.get_character();
+
+            // UTF-16 to UTF-8 encoding
+            if (wc < 0x80)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                utf8_bytes_filled = 1;
+            }
+            else if (wc <= 0x7FF)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 2;
+            }
+            else if (0xD800 > wc || wc >= 0xE000)
+            {
+                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
+                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+                utf8_bytes_filled = 3;
+            }
+            else
+            {
+                if (JSON_HEDLEY_UNLIKELY(!input.empty()))
+                {
+                    const auto wc2 = static_cast<unsigned int>(input.get_character());
+                    const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
+                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
+                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
+                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
+                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
+                    utf8_bytes_filled = 4;
+                }
+                else
+                {
+                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+                    utf8_bytes_filled = 1;
+                }
+            }
+        }
+    }
+};
+
+// Wraps another input apdater to convert wide character types into individual bytes.
+template<typename BaseInputAdapter, typename WideCharType>
+class wide_string_input_adapter
+{
+  public:
+    using char_type = char;
+
+    wide_string_input_adapter(BaseInputAdapter base)
+        : base_adapter(base) {}
+
+    typename std::char_traits<char>::int_type get_character() noexcept
+    {
+        // check if buffer needs to be filled
+        if (utf8_bytes_index == utf8_bytes_filled)
+        {
+            fill_buffer<sizeof(WideCharType)>();
+
+            JSON_ASSERT(utf8_bytes_filled > 0);
+            JSON_ASSERT(utf8_bytes_index == 0);
+        }
+
+        // use buffer
+        JSON_ASSERT(utf8_bytes_filled > 0);
+        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
+        return utf8_bytes[utf8_bytes_index++];
+    }
+
+  private:
+    BaseInputAdapter base_adapter;
+
+    template<size_t T>
+    void fill_buffer()
+    {
+        wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
+    }
+
+    /// a buffer for UTF-8 bytes
+    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
+
+    /// index to the utf8_codes array for the next valid byte
+    std::size_t utf8_bytes_index = 0;
+    /// number of valid bytes in the utf8_codes array
+    std::size_t utf8_bytes_filled = 0;
+};
+
+
+template<typename IteratorType, typename Enable = void>
+struct iterator_input_adapter_factory
+{
+    using iterator_type = IteratorType;
+    using char_type = typename std::iterator_traits<iterator_type>::value_type;
+    using adapter_type = iterator_input_adapter<iterator_type>;
+
+    static adapter_type create(IteratorType first, IteratorType last)
+    {
+        return adapter_type(std::move(first), std::move(last));
+    }
+};
+
+template<typename T>
+struct is_iterator_of_multibyte
+{
+    using value_type = typename std::iterator_traits<T>::value_type;
+    enum
+    {
+        value = sizeof(value_type) > 1
+    };
+};
+
+template<typename IteratorType>
+struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
+{
+    using iterator_type = IteratorType;
+    using char_type = typename std::iterator_traits<iterator_type>::value_type;
+    using base_adapter_type = iterator_input_adapter<iterator_type>;
+    using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
+
+    static adapter_type create(IteratorType first, IteratorType last)
+    {
+        return adapter_type(base_adapter_type(std::move(first), std::move(last)));
+    }
+};
+
+// General purpose iterator-based input
+template<typename IteratorType>
+typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
+{
+    using factory_type = iterator_input_adapter_factory<IteratorType>;
+    return factory_type::create(first, last);
+}
+
+// Convenience shorthand from container to iterator
+// Enables ADL on begin(container) and end(container)
+// Encloses the using declarations in namespace for not to leak them to outside scope
+
+namespace container_input_adapter_factory_impl
+{
+
+using std::begin;
+using std::end;
+
+template<typename ContainerType, typename Enable = void>
+struct container_input_adapter_factory {};
+
+template<typename ContainerType>
+struct container_input_adapter_factory< ContainerType,
+       void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>
+       {
+           using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
+
+           static adapter_type create(const ContainerType& container)
+{
+    return input_adapter(begin(container), end(container));
+}
+       };
+
+}  // namespace container_input_adapter_factory_impl
+
+template<typename ContainerType>
+typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
+{
+    return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
+}
+
+#ifndef JSON_NO_IO
+// Special cases with fast paths
+inline file_input_adapter input_adapter(std::FILE* file)
+{
+    return file_input_adapter(file);
+}
+
+inline input_stream_adapter input_adapter(std::istream& stream)
+{
+    return input_stream_adapter(stream);
+}
+
+inline input_stream_adapter input_adapter(std::istream&& stream)
+{
+    return input_stream_adapter(stream);
+}
+#endif  // JSON_NO_IO
+
+using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
+
+// Null-delimited strings, and the like.
+template < typename CharT,
+           typename std::enable_if <
+               std::is_pointer<CharT>::value&&
+               !std::is_array<CharT>::value&&
+               std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+               sizeof(typename std::remove_pointer<CharT>::type) == 1,
+               int >::type = 0 >
+contiguous_bytes_input_adapter input_adapter(CharT b)
+{
+    auto length = std::strlen(reinterpret_cast<const char*>(b));
+    const auto* ptr = reinterpret_cast<const char*>(b);
+    return input_adapter(ptr, ptr + length);
+}
+
+template<typename T, std::size_t N>
+auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+{
+    return input_adapter(array, array + N);
+}
+
+// This class only handles inputs of input_buffer_adapter type.
+// It's required so that expressions like {ptr, len} can be implicitly cast
+// to the correct adapter.
+class span_input_adapter
+{
+  public:
+    template < typename CharT,
+               typename std::enable_if <
+                   std::is_pointer<CharT>::value&&
+                   std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+                   sizeof(typename std::remove_pointer<CharT>::type) == 1,
+                   int >::type = 0 >
+    span_input_adapter(CharT b, std::size_t l)
+        : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
+
+    template<class IteratorType,
+             typename std::enable_if<
+                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
+                 int>::type = 0>
+    span_input_adapter(IteratorType first, IteratorType last)
+        : ia(input_adapter(first, last)) {}
+
+    contiguous_bytes_input_adapter&& get()
+    {
+        return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
+    }
+
+  private:
+    contiguous_bytes_input_adapter ia;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/json_sax.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef>
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief SAX interface
+
+This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
+Each function is called in different situations while the input is parsed. The
+boolean return value informs the parser whether to continue processing the
+input.
+*/
+template<typename BasicJsonType>
+struct json_sax
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+
+    /*!
+    @brief a null value was read
+    @return whether parsing should proceed
+    */
+    virtual bool null() = 0;
+
+    /*!
+    @brief a boolean value was read
+    @param[in] val  boolean value
+    @return whether parsing should proceed
+    */
+    virtual bool boolean(bool val) = 0;
+
+    /*!
+    @brief an integer number was read
+    @param[in] val  integer value
+    @return whether parsing should proceed
+    */
+    virtual bool number_integer(number_integer_t val) = 0;
+
+    /*!
+    @brief an unsigned integer number was read
+    @param[in] val  unsigned integer value
+    @return whether parsing should proceed
+    */
+    virtual bool number_unsigned(number_unsigned_t val) = 0;
+
+    /*!
+    @brief a floating-point number was read
+    @param[in] val  floating-point value
+    @param[in] s    raw token value
+    @return whether parsing should proceed
+    */
+    virtual bool number_float(number_float_t val, const string_t& s) = 0;
+
+    /*!
+    @brief a string value was read
+    @param[in] val  string value
+    @return whether parsing should proceed
+    @note It is safe to move the passed string value.
+    */
+    virtual bool string(string_t& val) = 0;
+
+    /*!
+    @brief a binary value was read
+    @param[in] val  binary value
+    @return whether parsing should proceed
+    @note It is safe to move the passed binary value.
+    */
+    virtual bool binary(binary_t& val) = 0;
+
+    /*!
+    @brief the beginning of an object was read
+    @param[in] elements  number of object elements or -1 if unknown
+    @return whether parsing should proceed
+    @note binary formats may report the number of elements
+    */
+    virtual bool start_object(std::size_t elements) = 0;
+
+    /*!
+    @brief an object key was read
+    @param[in] val  object key
+    @return whether parsing should proceed
+    @note It is safe to move the passed string.
+    */
+    virtual bool key(string_t& val) = 0;
+
+    /*!
+    @brief the end of an object was read
+    @return whether parsing should proceed
+    */
+    virtual bool end_object() = 0;
+
+    /*!
+    @brief the beginning of an array was read
+    @param[in] elements  number of array elements or -1 if unknown
+    @return whether parsing should proceed
+    @note binary formats may report the number of elements
+    */
+    virtual bool start_array(std::size_t elements) = 0;
+
+    /*!
+    @brief the end of an array was read
+    @return whether parsing should proceed
+    */
+    virtual bool end_array() = 0;
+
+    /*!
+    @brief a parse error occurred
+    @param[in] position    the position in the input where the error occurs
+    @param[in] last_token  the last read token
+    @param[in] ex          an exception object describing the error
+    @return whether parsing should proceed (must return false)
+    */
+    virtual bool parse_error(std::size_t position,
+                             const std::string& last_token,
+                             const detail::exception& ex) = 0;
+
+    json_sax() = default;
+    json_sax(const json_sax&) = default;
+    json_sax(json_sax&&) noexcept = default;
+    json_sax& operator=(const json_sax&) = default;
+    json_sax& operator=(json_sax&&) noexcept = default;
+    virtual ~json_sax() = default;
+};
+
+
+namespace detail
+{
+/*!
+@brief SAX implementation to create a JSON value from SAX events
+
+This class implements the @ref json_sax interface and processes the SAX events
+to create a JSON value which makes it basically a DOM parser. The structure or
+hierarchy of the JSON value is managed by the stack `ref_stack` which contains
+a pointer to the respective array or object for each recursion depth.
+
+After successful parsing, the value that is passed by reference to the
+constructor contains the parsed value.
+
+@tparam BasicJsonType  the JSON type
+*/
+template<typename BasicJsonType>
+class json_sax_dom_parser
+{
+  public:
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+
+    /*!
+    @param[in,out] r  reference to a JSON value that is manipulated while
+                       parsing
+    @param[in] allow_exceptions_  whether parse errors yield exceptions
+    */
+    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
+        : root(r), allow_exceptions(allow_exceptions_)
+    {}
+
+    // make class move-only
+    json_sax_dom_parser(const json_sax_dom_parser&) = delete;
+    json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;
+    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~json_sax_dom_parser() = default;
+
+    bool null()
+    {
+        handle_value(nullptr);
+        return true;
+    }
+
+    bool boolean(bool val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_integer(number_integer_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_float(number_float_t val, const string_t& /*unused*/)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool string(string_t& val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool binary(binary_t& val)
+    {
+        handle_value(std::move(val));
+        return true;
+    }
+
+    bool start_object(std::size_t len)
+    {
+        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
+
+        if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool key(string_t& val)
+    {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_object());
+
+        // add null at given key and store the reference for later
+        object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val));
+        return true;
+    }
+
+    bool end_object()
+    {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_object());
+
+        ref_stack.back()->set_parents();
+        ref_stack.pop_back();
+        return true;
+    }
+
+    bool start_array(std::size_t len)
+    {
+        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
+
+        if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool end_array()
+    {
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(ref_stack.back()->is_array());
+
+        ref_stack.back()->set_parents();
+        ref_stack.pop_back();
+        return true;
+    }
+
+    template<class Exception>
+    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+                     const Exception& ex)
+    {
+        errored = true;
+        static_cast<void>(ex);
+        if (allow_exceptions)
+        {
+            JSON_THROW(ex);
+        }
+        return false;
+    }
+
+    constexpr bool is_errored() const
+    {
+        return errored;
+    }
+
+  private:
+    /*!
+    @invariant If the ref stack is empty, then the passed value will be the new
+               root.
+    @invariant If the ref stack contains a value, then it is an array or an
+               object to which we can add elements
+    */
+    template<typename Value>
+    JSON_HEDLEY_RETURNS_NON_NULL
+    BasicJsonType* handle_value(Value&& v)
+    {
+        if (ref_stack.empty())
+        {
+            root = BasicJsonType(std::forward<Value>(v));
+            return &root;
+        }
+
+        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+        if (ref_stack.back()->is_array())
+        {
+            ref_stack.back()->m_data.m_value.array->emplace_back(std::forward<Value>(v));
+            return &(ref_stack.back()->m_data.m_value.array->back());
+        }
+
+        JSON_ASSERT(ref_stack.back()->is_object());
+        JSON_ASSERT(object_element);
+        *object_element = BasicJsonType(std::forward<Value>(v));
+        return object_element;
+    }
+
+    /// the parsed JSON value
+    BasicJsonType& root;
+    /// stack to model hierarchy of values
+    std::vector<BasicJsonType*> ref_stack {};
+    /// helper to hold the reference for the next object element
+    BasicJsonType* object_element = nullptr;
+    /// whether a syntax error occurred
+    bool errored = false;
+    /// whether to throw exceptions in case of errors
+    const bool allow_exceptions = true;
+};
+
+template<typename BasicJsonType>
+class json_sax_dom_callback_parser
+{
+  public:
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using parser_callback_t = typename BasicJsonType::parser_callback_t;
+    using parse_event_t = typename BasicJsonType::parse_event_t;
+
+    json_sax_dom_callback_parser(BasicJsonType& r,
+                                 const parser_callback_t cb,
+                                 const bool allow_exceptions_ = true)
+        : root(r), callback(cb), allow_exceptions(allow_exceptions_)
+    {
+        keep_stack.push_back(true);
+    }
+
+    // make class move-only
+    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
+    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;
+    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~json_sax_dom_callback_parser() = default;
+
+    bool null()
+    {
+        handle_value(nullptr);
+        return true;
+    }
+
+    bool boolean(bool val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_integer(number_integer_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool number_float(number_float_t val, const string_t& /*unused*/)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool string(string_t& val)
+    {
+        handle_value(val);
+        return true;
+    }
+
+    bool binary(binary_t& val)
+    {
+        handle_value(std::move(val));
+        return true;
+    }
+
+    bool start_object(std::size_t len)
+    {
+        // check callback for object start
+        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
+        keep_stack.push_back(keep);
+
+        auto val = handle_value(BasicJsonType::value_t::object, true);
+        ref_stack.push_back(val.second);
+
+        // check object limit
+        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool key(string_t& val)
+    {
+        BasicJsonType k = BasicJsonType(val);
+
+        // check callback for key
+        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
+        key_keep_stack.push_back(keep);
+
+        // add discarded value at given key and store the reference for later
+        if (keep && ref_stack.back())
+        {
+            object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val) = discarded);
+        }
+
+        return true;
+    }
+
+    bool end_object()
+    {
+        if (ref_stack.back())
+        {
+            if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
+            {
+                // discard object
+                *ref_stack.back() = discarded;
+            }
+            else
+            {
+                ref_stack.back()->set_parents();
+            }
+        }
+
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(!keep_stack.empty());
+        ref_stack.pop_back();
+        keep_stack.pop_back();
+
+        if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
+        {
+            // remove discarded value
+            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
+            {
+                if (it->is_discarded())
+                {
+                    ref_stack.back()->erase(it);
+                    break;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    bool start_array(std::size_t len)
+    {
+        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
+        keep_stack.push_back(keep);
+
+        auto val = handle_value(BasicJsonType::value_t::array, true);
+        ref_stack.push_back(val.second);
+
+        // check array limit
+        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+        {
+            JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+        }
+
+        return true;
+    }
+
+    bool end_array()
+    {
+        bool keep = true;
+
+        if (ref_stack.back())
+        {
+            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
+            if (keep)
+            {
+                ref_stack.back()->set_parents();
+            }
+            else
+            {
+                // discard array
+                *ref_stack.back() = discarded;
+            }
+        }
+
+        JSON_ASSERT(!ref_stack.empty());
+        JSON_ASSERT(!keep_stack.empty());
+        ref_stack.pop_back();
+        keep_stack.pop_back();
+
+        // remove discarded value
+        if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
+        {
+            ref_stack.back()->m_data.m_value.array->pop_back();
+        }
+
+        return true;
+    }
+
+    template<class Exception>
+    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+                     const Exception& ex)
+    {
+        errored = true;
+        static_cast<void>(ex);
+        if (allow_exceptions)
+        {
+            JSON_THROW(ex);
+        }
+        return false;
+    }
+
+    constexpr bool is_errored() const
+    {
+        return errored;
+    }
+
+  private:
+    /*!
+    @param[in] v  value to add to the JSON value we build during parsing
+    @param[in] skip_callback  whether we should skip calling the callback
+               function; this is required after start_array() and
+               start_object() SAX events, because otherwise we would call the
+               callback function with an empty array or object, respectively.
+
+    @invariant If the ref stack is empty, then the passed value will be the new
+               root.
+    @invariant If the ref stack contains a value, then it is an array or an
+               object to which we can add elements
+
+    @return pair of boolean (whether value should be kept) and pointer (to the
+            passed value in the ref_stack hierarchy; nullptr if not kept)
+    */
+    template<typename Value>
+    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
+    {
+        JSON_ASSERT(!keep_stack.empty());
+
+        // do not handle this value if we know it would be added to a discarded
+        // container
+        if (!keep_stack.back())
+        {
+            return {false, nullptr};
+        }
+
+        // create value
+        auto value = BasicJsonType(std::forward<Value>(v));
+
+        // check callback
+        const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
+
+        // do not handle this value if we just learnt it shall be discarded
+        if (!keep)
+        {
+            return {false, nullptr};
+        }
+
+        if (ref_stack.empty())
+        {
+            root = std::move(value);
+            return {true, &root};
+        }
+
+        // skip this value if we already decided to skip the parent
+        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
+        if (!ref_stack.back())
+        {
+            return {false, nullptr};
+        }
+
+        // we now only expect arrays and objects
+        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+        // array
+        if (ref_stack.back()->is_array())
+        {
+            ref_stack.back()->m_data.m_value.array->emplace_back(std::move(value));
+            return {true, &(ref_stack.back()->m_data.m_value.array->back())};
+        }
+
+        // object
+        JSON_ASSERT(ref_stack.back()->is_object());
+        // check if we should store an element for the current key
+        JSON_ASSERT(!key_keep_stack.empty());
+        const bool store_element = key_keep_stack.back();
+        key_keep_stack.pop_back();
+
+        if (!store_element)
+        {
+            return {false, nullptr};
+        }
+
+        JSON_ASSERT(object_element);
+        *object_element = std::move(value);
+        return {true, object_element};
+    }
+
+    /// the parsed JSON value
+    BasicJsonType& root;
+    /// stack to model hierarchy of values
+    std::vector<BasicJsonType*> ref_stack {};
+    /// stack to manage which values to keep
+    std::vector<bool> keep_stack {};
+    /// stack to manage which object keys to keep
+    std::vector<bool> key_keep_stack {};
+    /// helper to hold the reference for the next object element
+    BasicJsonType* object_element = nullptr;
+    /// whether a syntax error occurred
+    bool errored = false;
+    /// callback function
+    const parser_callback_t callback = nullptr;
+    /// whether to throw exceptions in case of errors
+    const bool allow_exceptions = true;
+    /// a discarded value for the callback
+    BasicJsonType discarded = BasicJsonType::value_t::discarded;
+};
+
+template<typename BasicJsonType>
+class json_sax_acceptor
+{
+  public:
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+
+    bool null()
+    {
+        return true;
+    }
+
+    bool boolean(bool /*unused*/)
+    {
+        return true;
+    }
+
+    bool number_integer(number_integer_t /*unused*/)
+    {
+        return true;
+    }
+
+    bool number_unsigned(number_unsigned_t /*unused*/)
+    {
+        return true;
+    }
+
+    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool string(string_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool binary(binary_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool start_object(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
+    {
+        return true;
+    }
+
+    bool key(string_t& /*unused*/)
+    {
+        return true;
+    }
+
+    bool end_object()
+    {
+        return true;
+    }
+
+    bool start_array(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
+    {
+        return true;
+    }
+
+    bool end_array()
+    {
+        return true;
+    }
+
+    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
+    {
+        return false;
+    }
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/lexer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <clocale> // localeconv
+#include <cstddef> // size_t
+#include <cstdio> // snprintf
+#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
+#include <initializer_list> // initializer_list
+#include <string> // char_traits, string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/position_t.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////
+// lexer //
+///////////
+
+template<typename BasicJsonType>
+class lexer_base
+{
+  public:
+    /// token types for the parser
+    enum class token_type
+    {
+        uninitialized,    ///< indicating the scanner is uninitialized
+        literal_true,     ///< the `true` literal
+        literal_false,    ///< the `false` literal
+        literal_null,     ///< the `null` literal
+        value_string,     ///< a string -- use get_string() for actual value
+        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value
+        value_integer,    ///< a signed integer -- use get_number_integer() for actual value
+        value_float,      ///< an floating point number -- use get_number_float() for actual value
+        begin_array,      ///< the character for array begin `[`
+        begin_object,     ///< the character for object begin `{`
+        end_array,        ///< the character for array end `]`
+        end_object,       ///< the character for object end `}`
+        name_separator,   ///< the name separator `:`
+        value_separator,  ///< the value separator `,`
+        parse_error,      ///< indicating a parse error
+        end_of_input,     ///< indicating the end of the input buffer
+        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)
+    };
+
+    /// return name of values of type token_type (only used for errors)
+    JSON_HEDLEY_RETURNS_NON_NULL
+    JSON_HEDLEY_CONST
+    static const char* token_type_name(const token_type t) noexcept
+    {
+        switch (t)
+        {
+            case token_type::uninitialized:
+                return "<uninitialized>";
+            case token_type::literal_true:
+                return "true literal";
+            case token_type::literal_false:
+                return "false literal";
+            case token_type::literal_null:
+                return "null literal";
+            case token_type::value_string:
+                return "string literal";
+            case token_type::value_unsigned:
+            case token_type::value_integer:
+            case token_type::value_float:
+                return "number literal";
+            case token_type::begin_array:
+                return "'['";
+            case token_type::begin_object:
+                return "'{'";
+            case token_type::end_array:
+                return "']'";
+            case token_type::end_object:
+                return "'}'";
+            case token_type::name_separator:
+                return "':'";
+            case token_type::value_separator:
+                return "','";
+            case token_type::parse_error:
+                return "<parse error>";
+            case token_type::end_of_input:
+                return "end of input";
+            case token_type::literal_or_value:
+                return "'[', '{', or a literal";
+            // LCOV_EXCL_START
+            default: // catch non-enum values
+                return "unknown token";
+                // LCOV_EXCL_STOP
+        }
+    }
+};
+/*!
+@brief lexical analysis
+
+This class organizes the lexical analysis during JSON deserialization.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class lexer : public lexer_base<BasicJsonType>
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using char_type = typename InputAdapterType::char_type;
+    using char_int_type = typename std::char_traits<char_type>::int_type;
+
+  public:
+    using token_type = typename lexer_base<BasicJsonType>::token_type;
+
+    explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept
+        : ia(std::move(adapter))
+        , ignore_comments(ignore_comments_)
+        , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
+    {}
+
+    // delete because of pointer members
+    lexer(const lexer&) = delete;
+    lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    lexer& operator=(lexer&) = delete;
+    lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~lexer() = default;
+
+  private:
+    /////////////////////
+    // locales
+    /////////////////////
+
+    /// return the locale-dependent decimal point
+    JSON_HEDLEY_PURE
+    static char get_decimal_point() noexcept
+    {
+        const auto* loc = localeconv();
+        JSON_ASSERT(loc != nullptr);
+        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
+    }
+
+    /////////////////////
+    // scan functions
+    /////////////////////
+
+    /*!
+    @brief get codepoint from 4 hex characters following `\u`
+
+    For input "\u c1 c2 c3 c4" the codepoint is:
+      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
+    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
+
+    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
+    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
+    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
+    between the ASCII value of the character and the desired integer value.
+
+    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
+            non-hex character)
+    */
+    int get_codepoint()
+    {
+        // this function only makes sense after reading `\u`
+        JSON_ASSERT(current == 'u');
+        int codepoint = 0;
+
+        const auto factors = { 12u, 8u, 4u, 0u };
+        for (const auto factor : factors)
+        {
+            get();
+
+            if (current >= '0' && current <= '9')
+            {
+                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);
+            }
+            else if (current >= 'A' && current <= 'F')
+            {
+                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);
+            }
+            else if (current >= 'a' && current <= 'f')
+            {
+                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);
+            }
+            else
+            {
+                return -1;
+            }
+        }
+
+        JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);
+        return codepoint;
+    }
+
+    /*!
+    @brief check if the next byte(s) are inside a given range
+
+    Adds the current byte and, for each passed range, reads a new byte and
+    checks if it is inside the range. If a violation was detected, set up an
+    error message and return false. Otherwise, return true.
+
+    @param[in] ranges  list of integers; interpreted as list of pairs of
+                       inclusive lower and upper bound, respectively
+
+    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
+         1, 2, or 3 pairs. This precondition is enforced by an assertion.
+
+    @return true if and only if no range violation was detected
+    */
+    bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
+    {
+        JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
+        add(current);
+
+        for (auto range = ranges.begin(); range != ranges.end(); ++range)
+        {
+            get();
+            if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range)))
+            {
+                add(current);
+            }
+            else
+            {
+                error_message = "invalid string: ill-formed UTF-8 byte";
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /*!
+    @brief scan a string literal
+
+    This function scans a string according to Sect. 7 of RFC 8259. While
+    scanning, bytes are escaped and copied into buffer token_buffer. Then the
+    function returns successfully, token_buffer is *not* null-terminated (as it
+    may contain \0 bytes), and token_buffer.size() is the number of bytes in the
+    string.
+
+    @return token_type::value_string if string could be successfully scanned,
+            token_type::parse_error otherwise
+
+    @note In case of errors, variable error_message contains a textual
+          description.
+    */
+    token_type scan_string()
+    {
+        // reset token_buffer (ignore opening quote)
+        reset();
+
+        // we entered the function by reading an open quote
+        JSON_ASSERT(current == '\"');
+
+        while (true)
+        {
+            // get next character
+            switch (get())
+            {
+                // end of file while parsing string
+                case std::char_traits<char_type>::eof():
+                {
+                    error_message = "invalid string: missing closing quote";
+                    return token_type::parse_error;
+                }
+
+                // closing quote
+                case '\"':
+                {
+                    return token_type::value_string;
+                }
+
+                // escapes
+                case '\\':
+                {
+                    switch (get())
+                    {
+                        // quotation mark
+                        case '\"':
+                            add('\"');
+                            break;
+                        // reverse solidus
+                        case '\\':
+                            add('\\');
+                            break;
+                        // solidus
+                        case '/':
+                            add('/');
+                            break;
+                        // backspace
+                        case 'b':
+                            add('\b');
+                            break;
+                        // form feed
+                        case 'f':
+                            add('\f');
+                            break;
+                        // line feed
+                        case 'n':
+                            add('\n');
+                            break;
+                        // carriage return
+                        case 'r':
+                            add('\r');
+                            break;
+                        // tab
+                        case 't':
+                            add('\t');
+                            break;
+
+                        // unicode escapes
+                        case 'u':
+                        {
+                            const int codepoint1 = get_codepoint();
+                            int codepoint = codepoint1; // start with codepoint1
+
+                            if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))
+                            {
+                                error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+                                return token_type::parse_error;
+                            }
+
+                            // check if code point is a high surrogate
+                            if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)
+                            {
+                                // expect next \uxxxx entry
+                                if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u'))
+                                {
+                                    const int codepoint2 = get_codepoint();
+
+                                    if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
+                                    {
+                                        error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+                                        return token_type::parse_error;
+                                    }
+
+                                    // check if codepoint2 is a low surrogate
+                                    if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))
+                                    {
+                                        // overwrite codepoint
+                                        codepoint = static_cast<int>(
+                                                        // high surrogate occupies the most significant 22 bits
+                                                        (static_cast<unsigned int>(codepoint1) << 10u)
+                                                        // low surrogate occupies the least significant 15 bits
+                                                        + static_cast<unsigned int>(codepoint2)
+                                                        // there is still the 0xD800, 0xDC00 and 0x10000 noise
+                                                        // in the result, so we have to subtract with:
+                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
+                                                        - 0x35FDC00u);
+                                    }
+                                    else
+                                    {
+                                        error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+                                        return token_type::parse_error;
+                                    }
+                                }
+                                else
+                                {
+                                    error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+                                    return token_type::parse_error;
+                                }
+                            }
+                            else
+                            {
+                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))
+                                {
+                                    error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
+                                    return token_type::parse_error;
+                                }
+                            }
+
+                            // result of the above calculation yields a proper codepoint
+                            JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
+
+                            // translate codepoint into bytes
+                            if (codepoint < 0x80)
+                            {
+                                // 1-byte characters: 0xxxxxxx (ASCII)
+                                add(static_cast<char_int_type>(codepoint));
+                            }
+                            else if (codepoint <= 0x7FF)
+                            {
+                                // 2-byte characters: 110xxxxx 10xxxxxx
+                                add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));
+                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+                            }
+                            else if (codepoint <= 0xFFFF)
+                            {
+                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+                                add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));
+                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+                            }
+                            else
+                            {
+                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+                                add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));
+                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));
+                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+                            }
+
+                            break;
+                        }
+
+                        // other characters after escape
+                        default:
+                            error_message = "invalid string: forbidden character after backslash";
+                            return token_type::parse_error;
+                    }
+
+                    break;
+                }
+
+                // invalid control characters
+                case 0x00:
+                {
+                    error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
+                    return token_type::parse_error;
+                }
+
+                case 0x01:
+                {
+                    error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
+                    return token_type::parse_error;
+                }
+
+                case 0x02:
+                {
+                    error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
+                    return token_type::parse_error;
+                }
+
+                case 0x03:
+                {
+                    error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
+                    return token_type::parse_error;
+                }
+
+                case 0x04:
+                {
+                    error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
+                    return token_type::parse_error;
+                }
+
+                case 0x05:
+                {
+                    error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
+                    return token_type::parse_error;
+                }
+
+                case 0x06:
+                {
+                    error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
+                    return token_type::parse_error;
+                }
+
+                case 0x07:
+                {
+                    error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
+                    return token_type::parse_error;
+                }
+
+                case 0x08:
+                {
+                    error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
+                    return token_type::parse_error;
+                }
+
+                case 0x09:
+                {
+                    error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
+                    return token_type::parse_error;
+                }
+
+                case 0x0A:
+                {
+                    error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
+                    return token_type::parse_error;
+                }
+
+                case 0x0B:
+                {
+                    error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
+                    return token_type::parse_error;
+                }
+
+                case 0x0C:
+                {
+                    error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
+                    return token_type::parse_error;
+                }
+
+                case 0x0D:
+                {
+                    error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
+                    return token_type::parse_error;
+                }
+
+                case 0x0E:
+                {
+                    error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
+                    return token_type::parse_error;
+                }
+
+                case 0x0F:
+                {
+                    error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
+                    return token_type::parse_error;
+                }
+
+                case 0x10:
+                {
+                    error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
+                    return token_type::parse_error;
+                }
+
+                case 0x11:
+                {
+                    error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
+                    return token_type::parse_error;
+                }
+
+                case 0x12:
+                {
+                    error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
+                    return token_type::parse_error;
+                }
+
+                case 0x13:
+                {
+                    error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
+                    return token_type::parse_error;
+                }
+
+                case 0x14:
+                {
+                    error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
+                    return token_type::parse_error;
+                }
+
+                case 0x15:
+                {
+                    error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
+                    return token_type::parse_error;
+                }
+
+                case 0x16:
+                {
+                    error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
+                    return token_type::parse_error;
+                }
+
+                case 0x17:
+                {
+                    error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
+                    return token_type::parse_error;
+                }
+
+                case 0x18:
+                {
+                    error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
+                    return token_type::parse_error;
+                }
+
+                case 0x19:
+                {
+                    error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
+                    return token_type::parse_error;
+                }
+
+                case 0x1A:
+                {
+                    error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
+                    return token_type::parse_error;
+                }
+
+                case 0x1B:
+                {
+                    error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
+                    return token_type::parse_error;
+                }
+
+                case 0x1C:
+                {
+                    error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
+                    return token_type::parse_error;
+                }
+
+                case 0x1D:
+                {
+                    error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
+                    return token_type::parse_error;
+                }
+
+                case 0x1E:
+                {
+                    error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
+                    return token_type::parse_error;
+                }
+
+                case 0x1F:
+                {
+                    error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
+                    return token_type::parse_error;
+                }
+
+                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
+                case 0x20:
+                case 0x21:
+                case 0x23:
+                case 0x24:
+                case 0x25:
+                case 0x26:
+                case 0x27:
+                case 0x28:
+                case 0x29:
+                case 0x2A:
+                case 0x2B:
+                case 0x2C:
+                case 0x2D:
+                case 0x2E:
+                case 0x2F:
+                case 0x30:
+                case 0x31:
+                case 0x32:
+                case 0x33:
+                case 0x34:
+                case 0x35:
+                case 0x36:
+                case 0x37:
+                case 0x38:
+                case 0x39:
+                case 0x3A:
+                case 0x3B:
+                case 0x3C:
+                case 0x3D:
+                case 0x3E:
+                case 0x3F:
+                case 0x40:
+                case 0x41:
+                case 0x42:
+                case 0x43:
+                case 0x44:
+                case 0x45:
+                case 0x46:
+                case 0x47:
+                case 0x48:
+                case 0x49:
+                case 0x4A:
+                case 0x4B:
+                case 0x4C:
+                case 0x4D:
+                case 0x4E:
+                case 0x4F:
+                case 0x50:
+                case 0x51:
+                case 0x52:
+                case 0x53:
+                case 0x54:
+                case 0x55:
+                case 0x56:
+                case 0x57:
+                case 0x58:
+                case 0x59:
+                case 0x5A:
+                case 0x5B:
+                case 0x5D:
+                case 0x5E:
+                case 0x5F:
+                case 0x60:
+                case 0x61:
+                case 0x62:
+                case 0x63:
+                case 0x64:
+                case 0x65:
+                case 0x66:
+                case 0x67:
+                case 0x68:
+                case 0x69:
+                case 0x6A:
+                case 0x6B:
+                case 0x6C:
+                case 0x6D:
+                case 0x6E:
+                case 0x6F:
+                case 0x70:
+                case 0x71:
+                case 0x72:
+                case 0x73:
+                case 0x74:
+                case 0x75:
+                case 0x76:
+                case 0x77:
+                case 0x78:
+                case 0x79:
+                case 0x7A:
+                case 0x7B:
+                case 0x7C:
+                case 0x7D:
+                case 0x7E:
+                case 0x7F:
+                {
+                    add(current);
+                    break;
+                }
+
+                // U+0080..U+07FF: bytes C2..DF 80..BF
+                case 0xC2:
+                case 0xC3:
+                case 0xC4:
+                case 0xC5:
+                case 0xC6:
+                case 0xC7:
+                case 0xC8:
+                case 0xC9:
+                case 0xCA:
+                case 0xCB:
+                case 0xCC:
+                case 0xCD:
+                case 0xCE:
+                case 0xCF:
+                case 0xD0:
+                case 0xD1:
+                case 0xD2:
+                case 0xD3:
+                case 0xD4:
+                case 0xD5:
+                case 0xD6:
+                case 0xD7:
+                case 0xD8:
+                case 0xD9:
+                case 0xDA:
+                case 0xDB:
+                case 0xDC:
+                case 0xDD:
+                case 0xDE:
+                case 0xDF:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
+                case 0xE0:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
+                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
+                case 0xE1:
+                case 0xE2:
+                case 0xE3:
+                case 0xE4:
+                case 0xE5:
+                case 0xE6:
+                case 0xE7:
+                case 0xE8:
+                case 0xE9:
+                case 0xEA:
+                case 0xEB:
+                case 0xEC:
+                case 0xEE:
+                case 0xEF:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+D000..U+D7FF: bytes ED 80..9F 80..BF
+                case 0xED:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
+                case 0xF0:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
+                case 0xF1:
+                case 0xF2:
+                case 0xF3:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
+                case 0xF4:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
+                    {
+                        return token_type::parse_error;
+                    }
+                    break;
+                }
+
+                // remaining bytes (80..C1 and F5..FF) are ill-formed
+                default:
+                {
+                    error_message = "invalid string: ill-formed UTF-8 byte";
+                    return token_type::parse_error;
+                }
+            }
+        }
+    }
+
+    /*!
+     * @brief scan a comment
+     * @return whether comment could be scanned successfully
+     */
+    bool scan_comment()
+    {
+        switch (get())
+        {
+            // single-line comments skip input until a newline or EOF is read
+            case '/':
+            {
+                while (true)
+                {
+                    switch (get())
+                    {
+                        case '\n':
+                        case '\r':
+                        case std::char_traits<char_type>::eof():
+                        case '\0':
+                            return true;
+
+                        default:
+                            break;
+                    }
+                }
+            }
+
+            // multi-line comments skip input until */ is read
+            case '*':
+            {
+                while (true)
+                {
+                    switch (get())
+                    {
+                        case std::char_traits<char_type>::eof():
+                        case '\0':
+                        {
+                            error_message = "invalid comment; missing closing '*/'";
+                            return false;
+                        }
+
+                        case '*':
+                        {
+                            switch (get())
+                            {
+                                case '/':
+                                    return true;
+
+                                default:
+                                {
+                                    unget();
+                                    continue;
+                                }
+                            }
+                        }
+
+                        default:
+                            continue;
+                    }
+                }
+            }
+
+            // unexpected character after reading '/'
+            default:
+            {
+                error_message = "invalid comment; expecting '/' or '*' after '/'";
+                return false;
+            }
+        }
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    static void strtof(float& f, const char* str, char** endptr) noexcept
+    {
+        f = std::strtof(str, endptr);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    static void strtof(double& f, const char* str, char** endptr) noexcept
+    {
+        f = std::strtod(str, endptr);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    static void strtof(long double& f, const char* str, char** endptr) noexcept
+    {
+        f = std::strtold(str, endptr);
+    }
+
+    /*!
+    @brief scan a number literal
+
+    This function scans a string according to Sect. 6 of RFC 8259.
+
+    The function is realized with a deterministic finite state machine derived
+    from the grammar described in RFC 8259. Starting in state "init", the
+    input is read and used to determined the next state. Only state "done"
+    accepts the number. State "error" is a trap state to model errors. In the
+    table below, "anything" means any character but the ones listed before.
+
+    state    | 0        | 1-9      | e E      | +       | -       | .        | anything
+    ---------|----------|----------|----------|---------|---------|----------|-----------
+    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]
+    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]
+    zero     | done     | done     | exponent | done    | done    | decimal1 | done
+    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done
+    decimal1 | decimal2 | decimal2 | [error]  | [error] | [error] | [error]  | [error]
+    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done
+    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]
+    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]
+    any2     | any2     | any2     | done     | done    | done    | done     | done
+
+    The state machine is realized with one label per state (prefixed with
+    "scan_number_") and `goto` statements between them. The state machine
+    contains cycles, but any cycle can be left when EOF is read. Therefore,
+    the function is guaranteed to terminate.
+
+    During scanning, the read bytes are stored in token_buffer. This string is
+    then converted to a signed integer, an unsigned integer, or a
+    floating-point number.
+
+    @return token_type::value_unsigned, token_type::value_integer, or
+            token_type::value_float if number could be successfully scanned,
+            token_type::parse_error otherwise
+
+    @note The scanner is independent of the current locale. Internally, the
+          locale's decimal point is used instead of `.` to work with the
+          locale-dependent converters.
+    */
+    token_type scan_number()  // lgtm [cpp/use-of-goto]
+    {
+        // reset token_buffer to store the number's bytes
+        reset();
+
+        // the type of the parsed number; initially set to unsigned; will be
+        // changed if minus sign, decimal point or exponent is read
+        token_type number_type = token_type::value_unsigned;
+
+        // state (init): we just found out we need to scan a number
+        switch (current)
+        {
+            case '-':
+            {
+                add(current);
+                goto scan_number_minus;
+            }
+
+            case '0':
+            {
+                add(current);
+                goto scan_number_zero;
+            }
+
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any1;
+            }
+
+            // all other characters are rejected outside scan_number()
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+
+scan_number_minus:
+        // state: we just parsed a leading minus sign
+        number_type = token_type::value_integer;
+        switch (get())
+        {
+            case '0':
+            {
+                add(current);
+                goto scan_number_zero;
+            }
+
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any1;
+            }
+
+            default:
+            {
+                error_message = "invalid number; expected digit after '-'";
+                return token_type::parse_error;
+            }
+        }
+
+scan_number_zero:
+        // state: we just parse a zero (maybe with a leading minus sign)
+        switch (get())
+        {
+            case '.':
+            {
+                add(decimal_point_char);
+                goto scan_number_decimal1;
+            }
+
+            case 'e':
+            case 'E':
+            {
+                add(current);
+                goto scan_number_exponent;
+            }
+
+            default:
+                goto scan_number_done;
+        }
+
+scan_number_any1:
+        // state: we just parsed a number 0-9 (maybe with a leading minus sign)
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any1;
+            }
+
+            case '.':
+            {
+                add(decimal_point_char);
+                goto scan_number_decimal1;
+            }
+
+            case 'e':
+            case 'E':
+            {
+                add(current);
+                goto scan_number_exponent;
+            }
+
+            default:
+                goto scan_number_done;
+        }
+
+scan_number_decimal1:
+        // state: we just parsed a decimal point
+        number_type = token_type::value_float;
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_decimal2;
+            }
+
+            default:
+            {
+                error_message = "invalid number; expected digit after '.'";
+                return token_type::parse_error;
+            }
+        }
+
+scan_number_decimal2:
+        // we just parsed at least one number after a decimal point
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_decimal2;
+            }
+
+            case 'e':
+            case 'E':
+            {
+                add(current);
+                goto scan_number_exponent;
+            }
+
+            default:
+                goto scan_number_done;
+        }
+
+scan_number_exponent:
+        // we just parsed an exponent
+        number_type = token_type::value_float;
+        switch (get())
+        {
+            case '+':
+            case '-':
+            {
+                add(current);
+                goto scan_number_sign;
+            }
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any2;
+            }
+
+            default:
+            {
+                error_message =
+                    "invalid number; expected '+', '-', or digit after exponent";
+                return token_type::parse_error;
+            }
+        }
+
+scan_number_sign:
+        // we just parsed an exponent sign
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any2;
+            }
+
+            default:
+            {
+                error_message = "invalid number; expected digit after exponent sign";
+                return token_type::parse_error;
+            }
+        }
+
+scan_number_any2:
+        // we just parsed a number after the exponent or exponent sign
+        switch (get())
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            {
+                add(current);
+                goto scan_number_any2;
+            }
+
+            default:
+                goto scan_number_done;
+        }
+
+scan_number_done:
+        // unget the character after the number (we only read it to know that
+        // we are done scanning a number)
+        unget();
+
+        char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+        errno = 0;
+
+        // try to parse integers first and fall back to floats
+        if (number_type == token_type::value_unsigned)
+        {
+            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
+
+            // we checked the number format before
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+            if (errno == 0)
+            {
+                value_unsigned = static_cast<number_unsigned_t>(x);
+                if (value_unsigned == x)
+                {
+                    return token_type::value_unsigned;
+                }
+            }
+        }
+        else if (number_type == token_type::value_integer)
+        {
+            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
+
+            // we checked the number format before
+            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+            if (errno == 0)
+            {
+                value_integer = static_cast<number_integer_t>(x);
+                if (value_integer == x)
+                {
+                    return token_type::value_integer;
+                }
+            }
+        }
+
+        // this code is reached if we parse a floating-point number or if an
+        // integer conversion above failed
+        strtof(value_float, token_buffer.data(), &endptr);
+
+        // we checked the number format before
+        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+        return token_type::value_float;
+    }
+
+    /*!
+    @param[in] literal_text  the literal text to expect
+    @param[in] length        the length of the passed literal text
+    @param[in] return_type   the token type to return on success
+    */
+    JSON_HEDLEY_NON_NULL(2)
+    token_type scan_literal(const char_type* literal_text, const std::size_t length,
+                            token_type return_type)
+    {
+        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
+        for (std::size_t i = 1; i < length; ++i)
+        {
+            if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))
+            {
+                error_message = "invalid literal";
+                return token_type::parse_error;
+            }
+        }
+        return return_type;
+    }
+
+    /////////////////////
+    // input management
+    /////////////////////
+
+    /// reset token_buffer; current character is beginning of token
+    void reset() noexcept
+    {
+        token_buffer.clear();
+        token_string.clear();
+        token_string.push_back(std::char_traits<char_type>::to_char_type(current));
+    }
+
+    /*
+    @brief get next character from the input
+
+    This function provides the interface to the used input adapter. It does
+    not throw in case the input reached EOF, but returns a
+    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters
+    for use in error messages.
+
+    @return character read from the input
+    */
+    char_int_type get()
+    {
+        ++position.chars_read_total;
+        ++position.chars_read_current_line;
+
+        if (next_unget)
+        {
+            // just reset the next_unget variable and work with current
+            next_unget = false;
+        }
+        else
+        {
+            current = ia.get_character();
+        }
+
+        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+        {
+            token_string.push_back(std::char_traits<char_type>::to_char_type(current));
+        }
+
+        if (current == '\n')
+        {
+            ++position.lines_read;
+            position.chars_read_current_line = 0;
+        }
+
+        return current;
+    }
+
+    /*!
+    @brief unget current character (read it again on next get)
+
+    We implement unget by setting variable next_unget to true. The input is not
+    changed - we just simulate ungetting by modifying chars_read_total,
+    chars_read_current_line, and token_string. The next call to get() will
+    behave as if the unget character is read again.
+    */
+    void unget()
+    {
+        next_unget = true;
+
+        --position.chars_read_total;
+
+        // in case we "unget" a newline, we have to also decrement the lines_read
+        if (position.chars_read_current_line == 0)
+        {
+            if (position.lines_read > 0)
+            {
+                --position.lines_read;
+            }
+        }
+        else
+        {
+            --position.chars_read_current_line;
+        }
+
+        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+        {
+            JSON_ASSERT(!token_string.empty());
+            token_string.pop_back();
+        }
+    }
+
+    /// add a character to token_buffer
+    void add(char_int_type c)
+    {
+        token_buffer.push_back(static_cast<typename string_t::value_type>(c));
+    }
+
+  public:
+    /////////////////////
+    // value getters
+    /////////////////////
+
+    /// return integer value
+    constexpr number_integer_t get_number_integer() const noexcept
+    {
+        return value_integer;
+    }
+
+    /// return unsigned integer value
+    constexpr number_unsigned_t get_number_unsigned() const noexcept
+    {
+        return value_unsigned;
+    }
+
+    /// return floating-point value
+    constexpr number_float_t get_number_float() const noexcept
+    {
+        return value_float;
+    }
+
+    /// return current string value (implicitly resets the token; useful only once)
+    string_t& get_string()
+    {
+        return token_buffer;
+    }
+
+    /////////////////////
+    // diagnostics
+    /////////////////////
+
+    /// return position of last read token
+    constexpr position_t get_position() const noexcept
+    {
+        return position;
+    }
+
+    /// return the last read token (for errors only).  Will never contain EOF
+    /// (an arbitrary value that is not a valid char value, often -1), because
+    /// 255 may legitimately occur.  May contain NUL, which should be escaped.
+    std::string get_token_string() const
+    {
+        // escape control characters
+        std::string result;
+        for (const auto c : token_string)
+        {
+            if (static_cast<unsigned char>(c) <= '\x1F')
+            {
+                // escape control characters
+                std::array<char, 9> cs{{}};
+                static_cast<void>((std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                result += cs.data();
+            }
+            else
+            {
+                // add character as is
+                result.push_back(static_cast<std::string::value_type>(c));
+            }
+        }
+
+        return result;
+    }
+
+    /// return syntax error message
+    JSON_HEDLEY_RETURNS_NON_NULL
+    constexpr const char* get_error_message() const noexcept
+    {
+        return error_message;
+    }
+
+    /////////////////////
+    // actual scanner
+    /////////////////////
+
+    /*!
+    @brief skip the UTF-8 byte order mark
+    @return true iff there is no BOM or the correct BOM has been skipped
+    */
+    bool skip_bom()
+    {
+        if (get() == 0xEF)
+        {
+            // check if we completely parse the BOM
+            return get() == 0xBB && get() == 0xBF;
+        }
+
+        // the first character is not the beginning of the BOM; unget it to
+        // process is later
+        unget();
+        return true;
+    }
+
+    void skip_whitespace()
+    {
+        do
+        {
+            get();
+        }
+        while (current == ' ' || current == '\t' || current == '\n' || current == '\r');
+    }
+
+    token_type scan()
+    {
+        // initially, skip the BOM
+        if (position.chars_read_total == 0 && !skip_bom())
+        {
+            error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given";
+            return token_type::parse_error;
+        }
+
+        // read next character and ignore whitespace
+        skip_whitespace();
+
+        // ignore comments
+        while (ignore_comments && current == '/')
+        {
+            if (!scan_comment())
+            {
+                return token_type::parse_error;
+            }
+
+            // skip following whitespace
+            skip_whitespace();
+        }
+
+        switch (current)
+        {
+            // structural characters
+            case '[':
+                return token_type::begin_array;
+            case ']':
+                return token_type::end_array;
+            case '{':
+                return token_type::begin_object;
+            case '}':
+                return token_type::end_object;
+            case ':':
+                return token_type::name_separator;
+            case ',':
+                return token_type::value_separator;
+
+            // literals
+            case 't':
+            {
+                std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}};
+                return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);
+            }
+            case 'f':
+            {
+                std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}};
+                return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);
+            }
+            case 'n':
+            {
+                std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}};
+                return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);
+            }
+
+            // string
+            case '\"':
+                return scan_string();
+
+            // number
+            case '-':
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                return scan_number();
+
+            // end of input (the null byte is needed when parsing from
+            // string literals)
+            case '\0':
+            case std::char_traits<char_type>::eof():
+                return token_type::end_of_input;
+
+            // error
+            default:
+                error_message = "invalid literal";
+                return token_type::parse_error;
+        }
+    }
+
+  private:
+    /// input adapter
+    InputAdapterType ia;
+
+    /// whether comments should be ignored (true) or signaled as errors (false)
+    const bool ignore_comments = false;
+
+    /// the current character
+    char_int_type current = std::char_traits<char_type>::eof();
+
+    /// whether the next get() call should just return current
+    bool next_unget = false;
+
+    /// the start position of the current token
+    position_t position {};
+
+    /// raw input token string (for error messages)
+    std::vector<char_type> token_string {};
+
+    /// buffer for variable-length tokens (numbers, strings)
+    string_t token_buffer {};
+
+    /// a description of occurred lexer errors
+    const char* error_message = "";
+
+    // number values
+    number_integer_t value_integer = 0;
+    number_unsigned_t value_unsigned = 0;
+    number_float_t value_float = 0;
+
+    /// the decimal point
+    const char_int_type decimal_point_char = '.';
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/is_sax.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // size_t
+#include <utility> // declval
+#include <string> // string
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using null_function_t = decltype(std::declval<T&>().null());
+
+template<typename T>
+using boolean_function_t =
+    decltype(std::declval<T&>().boolean(std::declval<bool>()));
+
+template<typename T, typename Integer>
+using number_integer_function_t =
+    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
+
+template<typename T, typename Unsigned>
+using number_unsigned_function_t =
+    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));
+
+template<typename T, typename Float, typename String>
+using number_float_function_t = decltype(std::declval<T&>().number_float(
+                                    std::declval<Float>(), std::declval<const String&>()));
+
+template<typename T, typename String>
+using string_function_t =
+    decltype(std::declval<T&>().string(std::declval<String&>()));
+
+template<typename T, typename Binary>
+using binary_function_t =
+    decltype(std::declval<T&>().binary(std::declval<Binary&>()));
+
+template<typename T>
+using start_object_function_t =
+    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));
+
+template<typename T, typename String>
+using key_function_t =
+    decltype(std::declval<T&>().key(std::declval<String&>()));
+
+template<typename T>
+using end_object_function_t = decltype(std::declval<T&>().end_object());
+
+template<typename T>
+using start_array_function_t =
+    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
+
+template<typename T>
+using end_array_function_t = decltype(std::declval<T&>().end_array());
+
+template<typename T, typename Exception>
+using parse_error_function_t = decltype(std::declval<T&>().parse_error(
+        std::declval<std::size_t>(), std::declval<const std::string&>(),
+        std::declval<const Exception&>()));
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax
+{
+  private:
+    static_assert(is_basic_json<BasicJsonType>::value,
+                  "BasicJsonType must be of type basic_json<...>");
+
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using exception_t = typename BasicJsonType::exception;
+
+  public:
+    static constexpr bool value =
+        is_detected_exact<bool, null_function_t, SAX>::value &&
+        is_detected_exact<bool, boolean_function_t, SAX>::value &&
+        is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&
+        is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&
+        is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&
+        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
+        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&
+        is_detected_exact<bool, start_object_function_t, SAX>::value &&
+        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
+        is_detected_exact<bool, end_object_function_t, SAX>::value &&
+        is_detected_exact<bool, start_array_function_t, SAX>::value &&
+        is_detected_exact<bool, end_array_function_t, SAX>::value &&
+        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
+};
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax_static_asserts
+{
+  private:
+    static_assert(is_basic_json<BasicJsonType>::value,
+                  "BasicJsonType must be of type basic_json<...>");
+
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using exception_t = typename BasicJsonType::exception;
+
+  public:
+    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,
+                  "Missing/invalid function: bool null()");
+    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+                  "Missing/invalid function: bool boolean(bool)");
+    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+                  "Missing/invalid function: bool boolean(bool)");
+    static_assert(
+        is_detected_exact<bool, number_integer_function_t, SAX,
+        number_integer_t>::value,
+        "Missing/invalid function: bool number_integer(number_integer_t)");
+    static_assert(
+        is_detected_exact<bool, number_unsigned_function_t, SAX,
+        number_unsigned_t>::value,
+        "Missing/invalid function: bool number_unsigned(number_unsigned_t)");
+    static_assert(is_detected_exact<bool, number_float_function_t, SAX,
+                  number_float_t, string_t>::value,
+                  "Missing/invalid function: bool number_float(number_float_t, const string_t&)");
+    static_assert(
+        is_detected_exact<bool, string_function_t, SAX, string_t>::value,
+        "Missing/invalid function: bool string(string_t&)");
+    static_assert(
+        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,
+        "Missing/invalid function: bool binary(binary_t&)");
+    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,
+                  "Missing/invalid function: bool start_object(std::size_t)");
+    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,
+                  "Missing/invalid function: bool key(string_t&)");
+    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,
+                  "Missing/invalid function: bool end_object()");
+    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,
+                  "Missing/invalid function: bool start_array(std::size_t)");
+    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,
+                  "Missing/invalid function: bool end_array()");
+    static_assert(
+        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,
+        "Missing/invalid function: bool parse_error(std::size_t, const "
+        "std::string&, const exception&)");
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// how to treat CBOR tags
+enum class cbor_tag_handler_t
+{
+    error,   ///< throw a parse_error exception in case of a tag
+    ignore,  ///< ignore tags
+    store    ///< store tags as binary type
+};
+
+/*!
+@brief determine system byte order
+
+@return true if and only if system's byte order is little endian
+
+@note from https://stackoverflow.com/a/1001328/266378
+*/
+static inline bool little_endianness(int num = 1) noexcept
+{
+    return *reinterpret_cast<char*>(&num) == 1;
+}
+
+
+///////////////////
+// binary reader //
+///////////////////
+
+/*!
+@brief deserialization of CBOR, MessagePack, and UBJSON values
+*/
+template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>>
+class binary_reader
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using json_sax_t = SAX;
+    using char_type = typename InputAdapterType::char_type;
+    using char_int_type = typename std::char_traits<char_type>::int_type;
+
+  public:
+    /*!
+    @brief create a binary reader
+
+    @param[in] adapter  input adapter to read from
+    */
+    explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format)
+    {
+        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+    }
+
+    // make class move-only
+    binary_reader(const binary_reader&) = delete;
+    binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    binary_reader& operator=(const binary_reader&) = delete;
+    binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+    ~binary_reader() = default;
+
+    /*!
+    @param[in] format  the binary format to parse
+    @param[in] sax_    a SAX event processor
+    @param[in] strict  whether to expect the input to be consumed completed
+    @param[in] tag_handler  how to treat CBOR tags
+
+    @return whether parsing was successful
+    */
+    JSON_HEDLEY_NON_NULL(3)
+    bool sax_parse(const input_format_t format,
+                   json_sax_t* sax_,
+                   const bool strict = true,
+                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        sax = sax_;
+        bool result = false;
+
+        switch (format)
+        {
+            case input_format_t::bson:
+                result = parse_bson_internal();
+                break;
+
+            case input_format_t::cbor:
+                result = parse_cbor_internal(true, tag_handler);
+                break;
+
+            case input_format_t::msgpack:
+                result = parse_msgpack_internal();
+                break;
+
+            case input_format_t::ubjson:
+            case input_format_t::bjdata:
+                result = parse_ubjson_internal();
+                break;
+
+            case input_format_t::json: // LCOV_EXCL_LINE
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+
+        // strict mode: next byte must be EOF
+        if (result && strict)
+        {
+            if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata)
+            {
+                get_ignore_noop();
+            }
+            else
+            {
+                get();
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))
+            {
+                return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,
+                                        exception_message(input_format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr));
+            }
+        }
+
+        return result;
+    }
+
+  private:
+    //////////
+    // BSON //
+    //////////
+
+    /*!
+    @brief Reads in a BSON-object and passes it to the SAX-parser.
+    @return whether a valid BSON-value was passed to the SAX parser
+    */
+    bool parse_bson_internal()
+    {
+        std::int32_t document_size{};
+        get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+        {
+            return false;
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))
+        {
+            return false;
+        }
+
+        return sax->end_object();
+    }
+
+    /*!
+    @brief Parses a C-style string from the BSON input.
+    @param[in,out] result  A reference to the string variable where the read
+                            string is to be stored.
+    @return `true` if the \x00-byte indicating the end of the string was
+             encountered before the EOF; false` indicates an unexpected EOF.
+    */
+    bool get_bson_cstr(string_t& result)
+    {
+        auto out = std::back_inserter(result);
+        while (true)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring")))
+            {
+                return false;
+            }
+            if (current == 0x00)
+            {
+                return true;
+            }
+            *out++ = static_cast<typename string_t::value_type>(current);
+        }
+    }
+
+    /*!
+    @brief Parses a zero-terminated string of length @a len from the BSON
+           input.
+    @param[in] len  The length (including the zero-byte at the end) of the
+                    string to be read.
+    @param[in,out] result  A reference to the string variable where the read
+                            string is to be stored.
+    @tparam NumberType The type of the length @a len
+    @pre len >= 1
+    @return `true` if the string was successfully parsed
+    */
+    template<typename NumberType>
+    bool get_bson_string(const NumberType len, string_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(len < 1))
+        {
+            auto last_token = get_token_string();
+            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                    exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
+        }
+
+        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();
+    }
+
+    /*!
+    @brief Parses a byte array input of length @a len from the BSON input.
+    @param[in] len  The length of the byte array to be read.
+    @param[in,out] result  A reference to the binary variable where the read
+                            array is to be stored.
+    @tparam NumberType The type of the length @a len
+    @pre len >= 0
+    @return `true` if the byte array was successfully parsed
+    */
+    template<typename NumberType>
+    bool get_bson_binary(const NumberType len, binary_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(len < 0))
+        {
+            auto last_token = get_token_string();
+            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                    exception_message(input_format_t::bson, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr));
+        }
+
+        // All BSON binary values have a subtype
+        std::uint8_t subtype{};
+        get_number<std::uint8_t>(input_format_t::bson, subtype);
+        result.set_subtype(subtype);
+
+        return get_binary(input_format_t::bson, len, result);
+    }
+
+    /*!
+    @brief Read a BSON document element of the given @a element_type.
+    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html
+    @param[in] element_type_parse_position The position in the input stream,
+               where the `element_type` was read.
+    @warning Not all BSON element types are supported yet. An unsupported
+             @a element_type will give rise to a parse_error.114:
+             Unsupported BSON record type 0x...
+    @return whether a valid BSON-object/array was passed to the SAX parser
+    */
+    bool parse_bson_element_internal(const char_int_type element_type,
+                                     const std::size_t element_type_parse_position)
+    {
+        switch (element_type)
+        {
+            case 0x01: // double
+            {
+                double number{};
+                return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0x02: // string
+            {
+                std::int32_t len{};
+                string_t value;
+                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);
+            }
+
+            case 0x03: // object
+            {
+                return parse_bson_internal();
+            }
+
+            case 0x04: // array
+            {
+                return parse_bson_array();
+            }
+
+            case 0x05: // binary
+            {
+                std::int32_t len{};
+                binary_t value;
+                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);
+            }
+
+            case 0x08: // boolean
+            {
+                return sax->boolean(get() != 0);
+            }
+
+            case 0x0A: // null
+            {
+                return sax->null();
+            }
+
+            case 0x10: // int32
+            {
+                std::int32_t value{};
+                return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+            }
+
+            case 0x12: // int64
+            {
+                std::int64_t value{};
+                return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+            }
+
+            default: // anything else not supported (yet)
+            {
+                std::array<char, 3> cr{{}};
+                static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                const std::string cr_str{cr.data()};
+                return sax->parse_error(element_type_parse_position, cr_str,
+                                        parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief Read a BSON element list (as specified in the BSON-spec)
+
+    The same binary layout is used for objects and arrays, hence it must be
+    indicated with the argument @a is_array which one is expected
+    (true --> array, false --> object).
+
+    @param[in] is_array Determines if the element list being read is to be
+                        treated as an object (@a is_array == false), or as an
+                        array (@a is_array == true).
+    @return whether a valid BSON-object/array was passed to the SAX parser
+    */
+    bool parse_bson_element_list(const bool is_array)
+    {
+        string_t key;
+
+        while (auto element_type = get())
+        {
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list")))
+            {
+                return false;
+            }
+
+            const std::size_t element_type_parse_position = chars_read;
+            if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
+            {
+                return false;
+            }
+
+            if (!is_array && !sax->key(key))
+            {
+                return false;
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))
+            {
+                return false;
+            }
+
+            // get_bson_cstr only appends
+            key.clear();
+        }
+
+        return true;
+    }
+
+    /*!
+    @brief Reads an array from the BSON input and passes it to the SAX-parser.
+    @return whether a valid BSON-array was passed to the SAX parser
+    */
+    bool parse_bson_array()
+    {
+        std::int32_t document_size{};
+        get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+        {
+            return false;
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))
+        {
+            return false;
+        }
+
+        return sax->end_array();
+    }
+
+    //////////
+    // CBOR //
+    //////////
+
+    /*!
+    @param[in] get_char  whether a new character should be retrieved from the
+                         input (true) or whether the last read character should
+                         be considered instead (false)
+    @param[in] tag_handler how CBOR tags should be treated
+
+    @return whether a valid CBOR value was passed to the SAX parser
+    */
+    bool parse_cbor_internal(const bool get_char,
+                             const cbor_tag_handler_t tag_handler)
+    {
+        switch (get_char ? get() : current)
+        {
+            // EOF
+            case std::char_traits<char_type>::eof():
+                return unexpect_eof(input_format_t::cbor, "value");
+
+            // Integer 0x00..0x17 (0..23)
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07:
+            case 0x08:
+            case 0x09:
+            case 0x0A:
+            case 0x0B:
+            case 0x0C:
+            case 0x0D:
+            case 0x0E:
+            case 0x0F:
+            case 0x10:
+            case 0x11:
+            case 0x12:
+            case 0x13:
+            case 0x14:
+            case 0x15:
+            case 0x16:
+            case 0x17:
+                return sax->number_unsigned(static_cast<number_unsigned_t>(current));
+
+            case 0x18: // Unsigned integer (one-byte uint8_t follows)
+            {
+                std::uint8_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            case 0x19: // Unsigned integer (two-byte uint16_t follows)
+            {
+                std::uint16_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            case 0x1A: // Unsigned integer (four-byte uint32_t follows)
+            {
+                std::uint32_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
+            {
+                std::uint64_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+            }
+
+            // Negative integer -1-0x00..-1-0x17 (-1..-24)
+            case 0x20:
+            case 0x21:
+            case 0x22:
+            case 0x23:
+            case 0x24:
+            case 0x25:
+            case 0x26:
+            case 0x27:
+            case 0x28:
+            case 0x29:
+            case 0x2A:
+            case 0x2B:
+            case 0x2C:
+            case 0x2D:
+            case 0x2E:
+            case 0x2F:
+            case 0x30:
+            case 0x31:
+            case 0x32:
+            case 0x33:
+            case 0x34:
+            case 0x35:
+            case 0x36:
+            case 0x37:
+                return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));
+
+            case 0x38: // Negative integer (one-byte uint8_t follows)
+            {
+                std::uint8_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+            }
+
+            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
+            {
+                std::uint16_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+            }
+
+            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
+            {
+                std::uint32_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+            }
+
+            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
+            {
+                std::uint64_t number{};
+                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)
+                        - static_cast<number_integer_t>(number));
+            }
+
+            // Binary data (0x00..0x17 bytes follow)
+            case 0x40:
+            case 0x41:
+            case 0x42:
+            case 0x43:
+            case 0x44:
+            case 0x45:
+            case 0x46:
+            case 0x47:
+            case 0x48:
+            case 0x49:
+            case 0x4A:
+            case 0x4B:
+            case 0x4C:
+            case 0x4D:
+            case 0x4E:
+            case 0x4F:
+            case 0x50:
+            case 0x51:
+            case 0x52:
+            case 0x53:
+            case 0x54:
+            case 0x55:
+            case 0x56:
+            case 0x57:
+            case 0x58: // Binary data (one-byte uint8_t for n follows)
+            case 0x59: // Binary data (two-byte uint16_t for n follow)
+            case 0x5A: // Binary data (four-byte uint32_t for n follow)
+            case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+            case 0x5F: // Binary data (indefinite length)
+            {
+                binary_t b;
+                return get_cbor_binary(b) && sax->binary(b);
+            }
+
+            // UTF-8 string (0x00..0x17 bytes follow)
+            case 0x60:
+            case 0x61:
+            case 0x62:
+            case 0x63:
+            case 0x64:
+            case 0x65:
+            case 0x66:
+            case 0x67:
+            case 0x68:
+            case 0x69:
+            case 0x6A:
+            case 0x6B:
+            case 0x6C:
+            case 0x6D:
+            case 0x6E:
+            case 0x6F:
+            case 0x70:
+            case 0x71:
+            case 0x72:
+            case 0x73:
+            case 0x74:
+            case 0x75:
+            case 0x76:
+            case 0x77:
+            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+            case 0x7F: // UTF-8 string (indefinite length)
+            {
+                string_t s;
+                return get_cbor_string(s) && sax->string(s);
+            }
+
+            // array (0x00..0x17 data items follow)
+            case 0x80:
+            case 0x81:
+            case 0x82:
+            case 0x83:
+            case 0x84:
+            case 0x85:
+            case 0x86:
+            case 0x87:
+            case 0x88:
+            case 0x89:
+            case 0x8A:
+            case 0x8B:
+            case 0x8C:
+            case 0x8D:
+            case 0x8E:
+            case 0x8F:
+            case 0x90:
+            case 0x91:
+            case 0x92:
+            case 0x93:
+            case 0x94:
+            case 0x95:
+            case 0x96:
+            case 0x97:
+                return get_cbor_array(
+                           conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+
+            case 0x98: // array (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x99: // array (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x9A: // array (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x9B: // array (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0x9F: // array (indefinite length)
+                return get_cbor_array(static_cast<std::size_t>(-1), tag_handler);
+
+            // map (0x00..0x17 pairs of data items follow)
+            case 0xA0:
+            case 0xA1:
+            case 0xA2:
+            case 0xA3:
+            case 0xA4:
+            case 0xA5:
+            case 0xA6:
+            case 0xA7:
+            case 0xA8:
+            case 0xA9:
+            case 0xAA:
+            case 0xAB:
+            case 0xAC:
+            case 0xAD:
+            case 0xAE:
+            case 0xAF:
+            case 0xB0:
+            case 0xB1:
+            case 0xB2:
+            case 0xB3:
+            case 0xB4:
+            case 0xB5:
+            case 0xB6:
+            case 0xB7:
+                return get_cbor_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+
+            case 0xB8: // map (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xB9: // map (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xBA: // map (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xBB: // map (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
+            }
+
+            case 0xBF: // map (indefinite length)
+                return get_cbor_object(static_cast<std::size_t>(-1), tag_handler);
+
+            case 0xC6: // tagged item
+            case 0xC7:
+            case 0xC8:
+            case 0xC9:
+            case 0xCA:
+            case 0xCB:
+            case 0xCC:
+            case 0xCD:
+            case 0xCE:
+            case 0xCF:
+            case 0xD0:
+            case 0xD1:
+            case 0xD2:
+            case 0xD3:
+            case 0xD4:
+            case 0xD8: // tagged item (1 bytes follow)
+            case 0xD9: // tagged item (2 bytes follow)
+            case 0xDA: // tagged item (4 bytes follow)
+            case 0xDB: // tagged item (8 bytes follow)
+            {
+                switch (tag_handler)
+                {
+                    case cbor_tag_handler_t::error:
+                    {
+                        auto last_token = get_token_string();
+                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                                exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+                    }
+
+                    case cbor_tag_handler_t::ignore:
+                    {
+                        // ignore binary subtype
+                        switch (current)
+                        {
+                            case 0xD8:
+                            {
+                                std::uint8_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            case 0xD9:
+                            {
+                                std::uint16_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            case 0xDA:
+                            {
+                                std::uint32_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            case 0xDB:
+                            {
+                                std::uint64_t subtype_to_ignore{};
+                                get_number(input_format_t::cbor, subtype_to_ignore);
+                                break;
+                            }
+                            default:
+                                break;
+                        }
+                        return parse_cbor_internal(true, tag_handler);
+                    }
+
+                    case cbor_tag_handler_t::store:
+                    {
+                        binary_t b;
+                        // use binary subtype and store in binary container
+                        switch (current)
+                        {
+                            case 0xD8:
+                            {
+                                std::uint8_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            case 0xD9:
+                            {
+                                std::uint16_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            case 0xDA:
+                            {
+                                std::uint32_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            case 0xDB:
+                            {
+                                std::uint64_t subtype{};
+                                get_number(input_format_t::cbor, subtype);
+                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+                                break;
+                            }
+                            default:
+                                return parse_cbor_internal(true, tag_handler);
+                        }
+                        get();
+                        return get_cbor_binary(b) && sax->binary(b);
+                    }
+
+                    default:                 // LCOV_EXCL_LINE
+                        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+                        return false;        // LCOV_EXCL_LINE
+                }
+            }
+
+            case 0xF4: // false
+                return sax->boolean(false);
+
+            case 0xF5: // true
+                return sax->boolean(true);
+
+            case 0xF6: // null
+                return sax->null();
+
+            case 0xF9: // Half-Precision Float (two-byte IEEE 754)
+            {
+                const auto byte1_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+                {
+                    return false;
+                }
+                const auto byte2_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+                {
+                    return false;
+                }
+
+                const auto byte1 = static_cast<unsigned char>(byte1_raw);
+                const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+                // code from RFC 7049, Appendix D, Figure 3:
+                // As half-precision floating-point numbers were only added
+                // to IEEE 754 in 2008, today's programming platforms often
+                // still only have limited support for them. It is very
+                // easy to include at least decoding support for them even
+                // without such support. An example of a small decoder for
+                // half-precision floating-point numbers in the C language
+                // is shown in Fig. 3.
+                const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);
+                const double val = [&half]
+                {
+                    const int exp = (half >> 10u) & 0x1Fu;
+                    const unsigned int mant = half & 0x3FFu;
+                    JSON_ASSERT(0 <= exp&& exp <= 32);
+                    JSON_ASSERT(mant <= 1024);
+                    switch (exp)
+                    {
+                        case 0:
+                            return std::ldexp(mant, -24);
+                        case 31:
+                            return (mant == 0)
+                            ? std::numeric_limits<double>::infinity()
+                            : std::numeric_limits<double>::quiet_NaN();
+                        default:
+                            return std::ldexp(mant + 1024, exp - 25);
+                    }
+                }();
+                return sax->number_float((half & 0x8000u) != 0
+                                         ? static_cast<number_float_t>(-val)
+                                         : static_cast<number_float_t>(val), "");
+            }
+
+            case 0xFA: // Single-Precision Float (four-byte IEEE 754)
+            {
+                float number{};
+                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
+            {
+                double number{};
+                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            default: // anything else (0xFF is handled inside the other types)
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a CBOR string
+
+    This function first reads starting bytes to determine the expected
+    string length and then copies this number of bytes into a string.
+    Additionally, CBOR's strings with indefinite lengths are supported.
+
+    @param[out] result  created string
+
+    @return whether string creation completed
+    */
+    bool get_cbor_string(string_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            // UTF-8 string (0x00..0x17 bytes follow)
+            case 0x60:
+            case 0x61:
+            case 0x62:
+            case 0x63:
+            case 0x64:
+            case 0x65:
+            case 0x66:
+            case 0x67:
+            case 0x68:
+            case 0x69:
+            case 0x6A:
+            case 0x6B:
+            case 0x6C:
+            case 0x6D:
+            case 0x6E:
+            case 0x6F:
+            case 0x70:
+            case 0x71:
+            case 0x72:
+            case 0x73:
+            case 0x74:
+            case 0x75:
+            case 0x76:
+            case 0x77:
+            {
+                return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+            }
+
+            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+            }
+
+            case 0x7F: // UTF-8 string (indefinite length)
+            {
+                while (get() != 0xFF)
+                {
+                    string_t chunk;
+                    if (!get_cbor_string(chunk))
+                    {
+                        return false;
+                    }
+                    result.append(chunk);
+                }
+                return true;
+            }
+
+            default:
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                        exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a CBOR byte array
+
+    This function first reads starting bytes to determine the expected
+    byte array length and then copies this number of bytes into the byte array.
+    Additionally, CBOR's byte arrays with indefinite lengths are supported.
+
+    @param[out] result  created byte array
+
+    @return whether byte array creation completed
+    */
+    bool get_cbor_binary(binary_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            // Binary data (0x00..0x17 bytes follow)
+            case 0x40:
+            case 0x41:
+            case 0x42:
+            case 0x43:
+            case 0x44:
+            case 0x45:
+            case 0x46:
+            case 0x47:
+            case 0x48:
+            case 0x49:
+            case 0x4A:
+            case 0x4B:
+            case 0x4C:
+            case 0x4D:
+            case 0x4E:
+            case 0x4F:
+            case 0x50:
+            case 0x51:
+            case 0x52:
+            case 0x53:
+            case 0x54:
+            case 0x55:
+            case 0x56:
+            case 0x57:
+            {
+                return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+            }
+
+            case 0x58: // Binary data (one-byte uint8_t for n follows)
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x59: // Binary data (two-byte uint16_t for n follow)
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x5A: // Binary data (four-byte uint32_t for n follow)
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+            {
+                std::uint64_t len{};
+                return get_number(input_format_t::cbor, len) &&
+                       get_binary(input_format_t::cbor, len, result);
+            }
+
+            case 0x5F: // Binary data (indefinite length)
+            {
+                while (get() != 0xFF)
+                {
+                    binary_t chunk;
+                    if (!get_cbor_binary(chunk))
+                    {
+                        return false;
+                    }
+                    result.insert(result.end(), chunk.begin(), chunk.end());
+                }
+                return true;
+            }
+
+            default:
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                        exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @param[in] len  the length of the array or static_cast<std::size_t>(-1) for an
+                    array of indefinite size
+    @param[in] tag_handler how CBOR tags should be treated
+    @return whether array creation completed
+    */
+    bool get_cbor_array(const std::size_t len,
+                        const cbor_tag_handler_t tag_handler)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+        {
+            return false;
+        }
+
+        if (len != static_cast<std::size_t>(-1))
+        {
+            for (std::size_t i = 0; i < len; ++i)
+            {
+                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+                {
+                    return false;
+                }
+            }
+        }
+        else
+        {
+            while (get() != 0xFF)
+            {
+                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
+                {
+                    return false;
+                }
+            }
+        }
+
+        return sax->end_array();
+    }
+
+    /*!
+    @param[in] len  the length of the object or static_cast<std::size_t>(-1) for an
+                    object of indefinite size
+    @param[in] tag_handler how CBOR tags should be treated
+    @return whether object creation completed
+    */
+    bool get_cbor_object(const std::size_t len,
+                         const cbor_tag_handler_t tag_handler)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+        {
+            return false;
+        }
+
+        if (len != 0)
+        {
+            string_t key;
+            if (len != static_cast<std::size_t>(-1))
+            {
+                for (std::size_t i = 0; i < len; ++i)
+                {
+                    get();
+                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+
+                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+            else
+            {
+                while (get() != 0xFF)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+
+                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+        }
+
+        return sax->end_object();
+    }
+
+    /////////////
+    // MsgPack //
+    /////////////
+
+    /*!
+    @return whether a valid MessagePack value was passed to the SAX parser
+    */
+    bool parse_msgpack_internal()
+    {
+        switch (get())
+        {
+            // EOF
+            case std::char_traits<char_type>::eof():
+                return unexpect_eof(input_format_t::msgpack, "value");
+
+            // positive fixint
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07:
+            case 0x08:
+            case 0x09:
+            case 0x0A:
+            case 0x0B:
+            case 0x0C:
+            case 0x0D:
+            case 0x0E:
+            case 0x0F:
+            case 0x10:
+            case 0x11:
+            case 0x12:
+            case 0x13:
+            case 0x14:
+            case 0x15:
+            case 0x16:
+            case 0x17:
+            case 0x18:
+            case 0x19:
+            case 0x1A:
+            case 0x1B:
+            case 0x1C:
+            case 0x1D:
+            case 0x1E:
+            case 0x1F:
+            case 0x20:
+            case 0x21:
+            case 0x22:
+            case 0x23:
+            case 0x24:
+            case 0x25:
+            case 0x26:
+            case 0x27:
+            case 0x28:
+            case 0x29:
+            case 0x2A:
+            case 0x2B:
+            case 0x2C:
+            case 0x2D:
+            case 0x2E:
+            case 0x2F:
+            case 0x30:
+            case 0x31:
+            case 0x32:
+            case 0x33:
+            case 0x34:
+            case 0x35:
+            case 0x36:
+            case 0x37:
+            case 0x38:
+            case 0x39:
+            case 0x3A:
+            case 0x3B:
+            case 0x3C:
+            case 0x3D:
+            case 0x3E:
+            case 0x3F:
+            case 0x40:
+            case 0x41:
+            case 0x42:
+            case 0x43:
+            case 0x44:
+            case 0x45:
+            case 0x46:
+            case 0x47:
+            case 0x48:
+            case 0x49:
+            case 0x4A:
+            case 0x4B:
+            case 0x4C:
+            case 0x4D:
+            case 0x4E:
+            case 0x4F:
+            case 0x50:
+            case 0x51:
+            case 0x52:
+            case 0x53:
+            case 0x54:
+            case 0x55:
+            case 0x56:
+            case 0x57:
+            case 0x58:
+            case 0x59:
+            case 0x5A:
+            case 0x5B:
+            case 0x5C:
+            case 0x5D:
+            case 0x5E:
+            case 0x5F:
+            case 0x60:
+            case 0x61:
+            case 0x62:
+            case 0x63:
+            case 0x64:
+            case 0x65:
+            case 0x66:
+            case 0x67:
+            case 0x68:
+            case 0x69:
+            case 0x6A:
+            case 0x6B:
+            case 0x6C:
+            case 0x6D:
+            case 0x6E:
+            case 0x6F:
+            case 0x70:
+            case 0x71:
+            case 0x72:
+            case 0x73:
+            case 0x74:
+            case 0x75:
+            case 0x76:
+            case 0x77:
+            case 0x78:
+            case 0x79:
+            case 0x7A:
+            case 0x7B:
+            case 0x7C:
+            case 0x7D:
+            case 0x7E:
+            case 0x7F:
+                return sax->number_unsigned(static_cast<number_unsigned_t>(current));
+
+            // fixmap
+            case 0x80:
+            case 0x81:
+            case 0x82:
+            case 0x83:
+            case 0x84:
+            case 0x85:
+            case 0x86:
+            case 0x87:
+            case 0x88:
+            case 0x89:
+            case 0x8A:
+            case 0x8B:
+            case 0x8C:
+            case 0x8D:
+            case 0x8E:
+            case 0x8F:
+                return get_msgpack_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+            // fixarray
+            case 0x90:
+            case 0x91:
+            case 0x92:
+            case 0x93:
+            case 0x94:
+            case 0x95:
+            case 0x96:
+            case 0x97:
+            case 0x98:
+            case 0x99:
+            case 0x9A:
+            case 0x9B:
+            case 0x9C:
+            case 0x9D:
+            case 0x9E:
+            case 0x9F:
+                return get_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+            // fixstr
+            case 0xA0:
+            case 0xA1:
+            case 0xA2:
+            case 0xA3:
+            case 0xA4:
+            case 0xA5:
+            case 0xA6:
+            case 0xA7:
+            case 0xA8:
+            case 0xA9:
+            case 0xAA:
+            case 0xAB:
+            case 0xAC:
+            case 0xAD:
+            case 0xAE:
+            case 0xAF:
+            case 0xB0:
+            case 0xB1:
+            case 0xB2:
+            case 0xB3:
+            case 0xB4:
+            case 0xB5:
+            case 0xB6:
+            case 0xB7:
+            case 0xB8:
+            case 0xB9:
+            case 0xBA:
+            case 0xBB:
+            case 0xBC:
+            case 0xBD:
+            case 0xBE:
+            case 0xBF:
+            case 0xD9: // str 8
+            case 0xDA: // str 16
+            case 0xDB: // str 32
+            {
+                string_t s;
+                return get_msgpack_string(s) && sax->string(s);
+            }
+
+            case 0xC0: // nil
+                return sax->null();
+
+            case 0xC2: // false
+                return sax->boolean(false);
+
+            case 0xC3: // true
+                return sax->boolean(true);
+
+            case 0xC4: // bin 8
+            case 0xC5: // bin 16
+            case 0xC6: // bin 32
+            case 0xC7: // ext 8
+            case 0xC8: // ext 16
+            case 0xC9: // ext 32
+            case 0xD4: // fixext 1
+            case 0xD5: // fixext 2
+            case 0xD6: // fixext 4
+            case 0xD7: // fixext 8
+            case 0xD8: // fixext 16
+            {
+                binary_t b;
+                return get_msgpack_binary(b) && sax->binary(b);
+            }
+
+            case 0xCA: // float 32
+            {
+                float number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0xCB: // float 64
+            {
+                double number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 0xCC: // uint 8
+            {
+                std::uint8_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xCD: // uint 16
+            {
+                std::uint16_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xCE: // uint 32
+            {
+                std::uint32_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xCF: // uint 64
+            {
+                std::uint64_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+            }
+
+            case 0xD0: // int 8
+            {
+                std::int8_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xD1: // int 16
+            {
+                std::int16_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xD2: // int 32
+            {
+                std::int32_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xD3: // int 64
+            {
+                std::int64_t number{};
+                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+            }
+
+            case 0xDC: // array 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));
+            }
+
+            case 0xDD: // array 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast<std::size_t>(len));
+            }
+
+            case 0xDE: // map 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));
+            }
+
+            case 0xDF: // map 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast<std::size_t>(len));
+            }
+
+            // negative fixint
+            case 0xE0:
+            case 0xE1:
+            case 0xE2:
+            case 0xE3:
+            case 0xE4:
+            case 0xE5:
+            case 0xE6:
+            case 0xE7:
+            case 0xE8:
+            case 0xE9:
+            case 0xEA:
+            case 0xEB:
+            case 0xEC:
+            case 0xED:
+            case 0xEE:
+            case 0xEF:
+            case 0xF0:
+            case 0xF1:
+            case 0xF2:
+            case 0xF3:
+            case 0xF4:
+            case 0xF5:
+            case 0xF6:
+            case 0xF7:
+            case 0xF8:
+            case 0xF9:
+            case 0xFA:
+            case 0xFB:
+            case 0xFC:
+            case 0xFD:
+            case 0xFE:
+            case 0xFF:
+                return sax->number_integer(static_cast<std::int8_t>(current));
+
+            default: // anything else
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a MessagePack string
+
+    This function first reads starting bytes to determine the expected
+    string length and then copies this number of bytes into a string.
+
+    @param[out] result  created string
+
+    @return whether string creation completed
+    */
+    bool get_msgpack_string(string_t& result)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            // fixstr
+            case 0xA0:
+            case 0xA1:
+            case 0xA2:
+            case 0xA3:
+            case 0xA4:
+            case 0xA5:
+            case 0xA6:
+            case 0xA7:
+            case 0xA8:
+            case 0xA9:
+            case 0xAA:
+            case 0xAB:
+            case 0xAC:
+            case 0xAD:
+            case 0xAE:
+            case 0xAF:
+            case 0xB0:
+            case 0xB1:
+            case 0xB2:
+            case 0xB3:
+            case 0xB4:
+            case 0xB5:
+            case 0xB6:
+            case 0xB7:
+            case 0xB8:
+            case 0xB9:
+            case 0xBA:
+            case 0xBB:
+            case 0xBC:
+            case 0xBD:
+            case 0xBE:
+            case 0xBF:
+            {
+                return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);
+            }
+
+            case 0xD9: // str 8
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+            }
+
+            case 0xDA: // str 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+            }
+
+            case 0xDB: // str 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+            }
+
+            default:
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                        exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr));
+            }
+        }
+    }
+
+    /*!
+    @brief reads a MessagePack byte array
+
+    This function first reads starting bytes to determine the expected
+    byte array length and then copies this number of bytes into a byte array.
+
+    @param[out] result  created byte array
+
+    @return whether byte array creation completed
+    */
+    bool get_msgpack_binary(binary_t& result)
+    {
+        // helper function to set the subtype
+        auto assign_and_return_true = [&result](std::int8_t subtype)
+        {
+            result.set_subtype(static_cast<std::uint8_t>(subtype));
+            return true;
+        };
+
+        switch (current)
+        {
+            case 0xC4: // bin 8
+            {
+                std::uint8_t len{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_binary(input_format_t::msgpack, len, result);
+            }
+
+            case 0xC5: // bin 16
+            {
+                std::uint16_t len{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_binary(input_format_t::msgpack, len, result);
+            }
+
+            case 0xC6: // bin 32
+            {
+                std::uint32_t len{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_binary(input_format_t::msgpack, len, result);
+            }
+
+            case 0xC7: // ext 8
+            {
+                std::uint8_t len{};
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, len, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xC8: // ext 16
+            {
+                std::uint16_t len{};
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, len, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xC9: // ext 32
+            {
+                std::uint32_t len{};
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, len) &&
+                       get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, len, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD4: // fixext 1
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 1, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD5: // fixext 2
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 2, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD6: // fixext 4
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 4, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD7: // fixext 8
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 8, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            case 0xD8: // fixext 16
+            {
+                std::int8_t subtype{};
+                return get_number(input_format_t::msgpack, subtype) &&
+                       get_binary(input_format_t::msgpack, 16, result) &&
+                       assign_and_return_true(subtype);
+            }
+
+            default:           // LCOV_EXCL_LINE
+                return false;  // LCOV_EXCL_LINE
+        }
+    }
+
+    /*!
+    @param[in] len  the length of the array
+    @return whether array creation completed
+    */
+    bool get_msgpack_array(const std::size_t len)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+        {
+            return false;
+        }
+
+        for (std::size_t i = 0; i < len; ++i)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+            {
+                return false;
+            }
+        }
+
+        return sax->end_array();
+    }
+
+    /*!
+    @param[in] len  the length of the object
+    @return whether object creation completed
+    */
+    bool get_msgpack_object(const std::size_t len)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+        {
+            return false;
+        }
+
+        string_t key;
+        for (std::size_t i = 0; i < len; ++i)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))
+            {
+                return false;
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+            {
+                return false;
+            }
+            key.clear();
+        }
+
+        return sax->end_object();
+    }
+
+    ////////////
+    // UBJSON //
+    ////////////
+
+    /*!
+    @param[in] get_char  whether a new character should be retrieved from the
+                         input (true, default) or whether the last read
+                         character should be considered instead
+
+    @return whether a valid UBJSON value was passed to the SAX parser
+    */
+    bool parse_ubjson_internal(const bool get_char = true)
+    {
+        return get_ubjson_value(get_char ? get_ignore_noop() : current);
+    }
+
+    /*!
+    @brief reads a UBJSON string
+
+    This function is either called after reading the 'S' byte explicitly
+    indicating a string, or in case of an object key where the 'S' byte can be
+    left out.
+
+    @param[out] result   created string
+    @param[in] get_char  whether a new character should be retrieved from the
+                         input (true, default) or whether the last read
+                         character should be considered instead
+
+    @return whether string creation completed
+    */
+    bool get_ubjson_string(string_t& result, const bool get_char = true)
+    {
+        if (get_char)
+        {
+            get();  // TODO(niels): may we ignore N here?
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+        {
+            return false;
+        }
+
+        switch (current)
+        {
+            case 'U':
+            {
+                std::uint8_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'i':
+            {
+                std::int8_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'I':
+            {
+                std::int16_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'l':
+            {
+                std::int32_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'L':
+            {
+                std::int64_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'u':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint16_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'm':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint32_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            case 'M':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint64_t len{};
+                return get_number(input_format, len) && get_string(input_format, len, result);
+            }
+
+            default:
+                break;
+        }
+        auto last_token = get_token_string();
+        std::string message;
+
+        if (input_format != input_format_t::bjdata)
+        {
+            message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
+        }
+        else
+        {
+            message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
+        }
+        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
+    }
+
+    /*!
+    @param[out] dim  an integer vector storing the ND array dimensions
+    @return whether reading ND array size vector is successful
+    */
+    bool get_ubjson_ndarray_size(std::vector<size_t>& dim)
+    {
+        std::pair<std::size_t, char_int_type> size_and_type;
+        size_t dimlen = 0;
+        bool no_ndarray = true;
+
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray)))
+        {
+            return false;
+        }
+
+        if (size_and_type.first != npos)
+        {
+            if (size_and_type.second != 0)
+            {
+                if (size_and_type.second != 'N')
+                {
+                    for (std::size_t i = 0; i < size_and_type.first; ++i)
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))
+                        {
+                            return false;
+                        }
+                        dim.push_back(dimlen);
+                    }
+                }
+            }
+            else
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray)))
+                    {
+                        return false;
+                    }
+                    dim.push_back(dimlen);
+                }
+            }
+        }
+        else
+        {
+            while (current != ']')
+            {
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current)))
+                {
+                    return false;
+                }
+                dim.push_back(dimlen);
+                get_ignore_noop();
+            }
+        }
+        return true;
+    }
+
+    /*!
+    @param[out] result  determined size
+    @param[in,out] is_ndarray  for input, `true` means already inside an ndarray vector
+                               or ndarray dimension is not allowed; `false` means ndarray
+                               is allowed; for output, `true` means an ndarray is found;
+                               is_ndarray can only return `true` when its initial value
+                               is `false`
+    @param[in] prefix  type marker if already read, otherwise set to 0
+
+    @return whether size determination completed
+    */
+    bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
+    {
+        if (prefix == 0)
+        {
+            prefix = get_ignore_noop();
+        }
+
+        switch (prefix)
+        {
+            case 'U':
+            {
+                std::uint8_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'i':
+            {
+                std::int8_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
+                return true;
+            }
+
+            case 'I':
+            {
+                std::int16_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'l':
+            {
+                std::int32_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'L':
+            {
+                std::int64_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (number < 0)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+                                            exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+                }
+                if (!value_in_range_of<std::size_t>(number))
+                {
+                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+                                            exception_message(input_format, "integer value overflow", "size"), nullptr));
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'u':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint16_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                result = static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'm':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint32_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                result = conditional_static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case 'M':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint64_t number{};
+                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+                {
+                    return false;
+                }
+                if (!value_in_range_of<std::size_t>(number))
+                {
+                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+                                            exception_message(input_format, "integer value overflow", "size"), nullptr));
+                }
+                result = detail::conditional_static_cast<std::size_t>(number);
+                return true;
+            }
+
+            case '[':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimensional vector is not allowed", "size"), nullptr));
+                }
+                std::vector<size_t> dim;
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
+                {
+                    return false;
+                }
+                if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector
+                {
+                    result = dim.at(dim.size() - 1);
+                    return true;
+                }
+                if (!dim.empty())  // if ndarray, convert to an object in JData annotated array format
+                {
+                    for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container
+                    {
+                        if ( i == 0 )
+                        {
+                            result = 0;
+                            return true;
+                        }
+                    }
+
+                    string_t key = "_ArraySize_";
+                    if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
+                    {
+                        return false;
+                    }
+                    result = 1;
+                    for (auto i : dim)
+                    {
+                        result *= i;
+                        if (result == 0 || result == npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be npos as it is used to initialize size in get_ubjson_size_type()
+                        {
+                            return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
+                        }
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
+                        {
+                            return false;
+                        }
+                    }
+                    is_ndarray = true;
+                    return sax->end_array();
+                }
+                result = 0;
+                return true;
+            }
+
+            default:
+                break;
+        }
+        auto last_token = get_token_string();
+        std::string message;
+
+        if (input_format != input_format_t::bjdata)
+        {
+            message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
+        }
+        else
+        {
+            message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
+        }
+        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
+    }
+
+    /*!
+    @brief determine the type and size for a container
+
+    In the optimized UBJSON format, a type and a size can be provided to allow
+    for a more compact representation.
+
+    @param[out] result  pair of the size and the type
+    @param[in] inside_ndarray  whether the parser is parsing an ND array dimensional vector
+
+    @return whether pair creation completed
+    */
+    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result, bool inside_ndarray = false)
+    {
+        result.first = npos; // size
+        result.second = 0; // type
+        bool is_ndarray = false;
+
+        get_ignore_noop();
+
+        if (current == '$')
+        {
+            result.second = get();  // must not ignore 'N', because 'N' maybe the type
+            if (input_format == input_format_t::bjdata
+                    && JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second)))
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format, concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr));
+            }
+
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type")))
+            {
+                return false;
+            }
+
+            get_ignore_noop();
+            if (JSON_HEDLEY_UNLIKELY(current != '#'))
+            {
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+                {
+                    return false;
+                }
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
+            }
+
+            const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+            if (input_format == input_format_t::bjdata && is_ndarray)
+            {
+                if (inside_ndarray)
+                {
+                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+                                            exception_message(input_format, "ndarray can not be recursive", "size"), nullptr));
+                }
+                result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
+            }
+            return is_error;
+        }
+
+        if (current == '#')
+        {
+            const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+            if (input_format == input_format_t::bjdata && is_ndarray)
+            {
+                return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+                                        exception_message(input_format, "ndarray requires both type and size", "size"), nullptr));
+            }
+            return is_error;
+        }
+
+        return true;
+    }
+
+    /*!
+    @param prefix  the previously read or set type prefix
+    @return whether value creation completed
+    */
+    bool get_ubjson_value(const char_int_type prefix)
+    {
+        switch (prefix)
+        {
+            case std::char_traits<char_type>::eof():  // EOF
+                return unexpect_eof(input_format, "value");
+
+            case 'T':  // true
+                return sax->boolean(true);
+            case 'F':  // false
+                return sax->boolean(false);
+
+            case 'Z':  // null
+                return sax->null();
+
+            case 'U':
+            {
+                std::uint8_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'i':
+            {
+                std::int8_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'I':
+            {
+                std::int16_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'l':
+            {
+                std::int32_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'L':
+            {
+                std::int64_t number{};
+                return get_number(input_format, number) && sax->number_integer(number);
+            }
+
+            case 'u':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint16_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'm':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint32_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'M':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                std::uint64_t number{};
+                return get_number(input_format, number) && sax->number_unsigned(number);
+            }
+
+            case 'h':
+            {
+                if (input_format != input_format_t::bjdata)
+                {
+                    break;
+                }
+                const auto byte1_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+                {
+                    return false;
+                }
+                const auto byte2_raw = get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+                {
+                    return false;
+                }
+
+                const auto byte1 = static_cast<unsigned char>(byte1_raw);
+                const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+                // code from RFC 7049, Appendix D, Figure 3:
+                // As half-precision floating-point numbers were only added
+                // to IEEE 754 in 2008, today's programming platforms often
+                // still only have limited support for them. It is very
+                // easy to include at least decoding support for them even
+                // without such support. An example of a small decoder for
+                // half-precision floating-point numbers in the C language
+                // is shown in Fig. 3.
+                const auto half = static_cast<unsigned int>((byte2 << 8u) + byte1);
+                const double val = [&half]
+                {
+                    const int exp = (half >> 10u) & 0x1Fu;
+                    const unsigned int mant = half & 0x3FFu;
+                    JSON_ASSERT(0 <= exp&& exp <= 32);
+                    JSON_ASSERT(mant <= 1024);
+                    switch (exp)
+                    {
+                        case 0:
+                            return std::ldexp(mant, -24);
+                        case 31:
+                            return (mant == 0)
+                            ? std::numeric_limits<double>::infinity()
+                            : std::numeric_limits<double>::quiet_NaN();
+                        default:
+                            return std::ldexp(mant + 1024, exp - 25);
+                    }
+                }();
+                return sax->number_float((half & 0x8000u) != 0
+                                         ? static_cast<number_float_t>(-val)
+                                         : static_cast<number_float_t>(val), "");
+            }
+
+            case 'd':
+            {
+                float number{};
+                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 'D':
+            {
+                double number{};
+                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+            }
+
+            case 'H':
+            {
+                return get_ubjson_high_precision_number();
+            }
+
+            case 'C':  // char
+            {
+                get();
+                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "char")))
+                {
+                    return false;
+                }
+                if (JSON_HEDLEY_UNLIKELY(current > 127))
+                {
+                    auto last_token = get_token_string();
+                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+                                            exception_message(input_format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
+                }
+                string_t s(1, static_cast<typename string_t::value_type>(current));
+                return sax->string(s);
+            }
+
+            case 'S':  // string
+            {
+                string_t s;
+                return get_ubjson_string(s) && sax->string(s);
+            }
+
+            case '[':  // array
+                return get_ubjson_array();
+
+            case '{':  // object
+                return get_ubjson_object();
+
+            default: // anything else
+                break;
+        }
+        auto last_token = get_token_string();
+        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, "invalid byte: 0x" + last_token, "value"), nullptr));
+    }
+
+    /*!
+    @return whether array creation completed
+    */
+    bool get_ubjson_array()
+    {
+        std::pair<std::size_t, char_int_type> size_and_type;
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+        {
+            return false;
+        }
+
+        // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
+        // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
+
+        if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
+        {
+            size_and_type.second &= ~(static_cast<char_int_type>(1) << 8);  // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
+            auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t)
+            {
+                return p.first < t;
+            });
+            string_t key = "_ArrayType_";
+            if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second))
+            {
+                auto last_token = get_token_string();
+                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                        exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
+            }
+
+            string_t type = it->second; // sax->string() takes a reference
+            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
+            {
+                return false;
+            }
+
+            if (size_and_type.second == 'C')
+            {
+                size_and_type.second = 'U';
+            }
+
+            key = "_ArrayData_";
+            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
+            {
+                return false;
+            }
+
+            for (std::size_t i = 0; i < size_and_type.first; ++i)
+            {
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+                {
+                    return false;
+                }
+            }
+
+            return (sax->end_array() && sax->end_object());
+        }
+
+        if (size_and_type.first != npos)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))
+            {
+                return false;
+            }
+
+            if (size_and_type.second != 0)
+            {
+                if (size_and_type.second != 'N')
+                {
+                    for (std::size_t i = 0; i < size_and_type.first; ++i)
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+                        {
+                            return false;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+                    {
+                        return false;
+                    }
+                }
+            }
+        }
+        else
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+            {
+                return false;
+            }
+
+            while (current != ']')
+            {
+                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))
+                {
+                    return false;
+                }
+                get_ignore_noop();
+            }
+        }
+
+        return sax->end_array();
+    }
+
+    /*!
+    @return whether object creation completed
+    */
+    bool get_ubjson_object()
+    {
+        std::pair<std::size_t, char_int_type> size_and_type;
+        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+        {
+            return false;
+        }
+
+        // do not accept ND-array size in objects in BJData
+        if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
+        {
+            auto last_token = get_token_string();
+            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+                                    exception_message(input_format, "BJData object does not support ND-array size in optimized format", "object"), nullptr));
+        }
+
+        string_t key;
+        if (size_and_type.first != npos)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))
+            {
+                return false;
+            }
+
+            if (size_and_type.second != 0)
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+            else
+            {
+                for (std::size_t i = 0; i < size_and_type.first; ++i)
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+                    {
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+                    {
+                        return false;
+                    }
+                    key.clear();
+                }
+            }
+        }
+        else
+        {
+            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+            {
+                return false;
+            }
+
+            while (current != '}')
+            {
+                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))
+                {
+                    return false;
+                }
+                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+                {
+                    return false;
+                }
+                get_ignore_noop();
+                key.clear();
+            }
+        }
+
+        return sax->end_object();
+    }
+
+    // Note, no reader for UBJSON binary types is implemented because they do
+    // not exist
+
+    bool get_ubjson_high_precision_number()
+    {
+        // get size of following number string
+        std::size_t size{};
+        bool no_ndarray = true;
+        auto res = get_ubjson_size_value(size, no_ndarray);
+        if (JSON_HEDLEY_UNLIKELY(!res))
+        {
+            return res;
+        }
+
+        // get number string
+        std::vector<char> number_vector;
+        for (std::size_t i = 0; i < size; ++i)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+            {
+                return false;
+            }
+            number_vector.push_back(static_cast<char>(current));
+        }
+
+        // parse number string
+        using ia_type = decltype(detail::input_adapter(number_vector));
+        auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);
+        const auto result_number = number_lexer.scan();
+        const auto number_string = number_lexer.get_token_string();
+        const auto result_remainder = number_lexer.scan();
+
+        using token_type = typename detail::lexer_base<BasicJsonType>::token_type;
+
+        if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
+        {
+            return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+                                    exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+        }
+
+        switch (result_number)
+        {
+            case token_type::value_integer:
+                return sax->number_integer(number_lexer.get_number_integer());
+            case token_type::value_unsigned:
+                return sax->number_unsigned(number_lexer.get_number_unsigned());
+            case token_type::value_float:
+                return sax->number_float(number_lexer.get_number_float(), std::move(number_string));
+            case token_type::uninitialized:
+            case token_type::literal_true:
+            case token_type::literal_false:
+            case token_type::literal_null:
+            case token_type::value_string:
+            case token_type::begin_array:
+            case token_type::begin_object:
+            case token_type::end_array:
+            case token_type::end_object:
+            case token_type::name_separator:
+            case token_type::value_separator:
+            case token_type::parse_error:
+            case token_type::end_of_input:
+            case token_type::literal_or_value:
+            default:
+                return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+                                        exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+        }
+    }
+
+    ///////////////////////
+    // Utility functions //
+    ///////////////////////
+
+    /*!
+    @brief get next character from the input
+
+    This function provides the interface to the used input adapter. It does
+    not throw in case the input reached EOF, but returns a -'ve valued
+    `std::char_traits<char_type>::eof()` in that case.
+
+    @return character read from the input
+    */
+    char_int_type get()
+    {
+        ++chars_read;
+        return current = ia.get_character();
+    }
+
+    /*!
+    @return character read from the input after ignoring all 'N' entries
+    */
+    char_int_type get_ignore_noop()
+    {
+        do
+        {
+            get();
+        }
+        while (current == 'N');
+
+        return current;
+    }
+
+    /*
+    @brief read a number from the input
+
+    @tparam NumberType the type of the number
+    @param[in] format   the current format (for diagnostics)
+    @param[out] result  number of type @a NumberType
+
+    @return whether conversion completed
+
+    @note This function needs to respect the system's endianness, because
+          bytes in CBOR, MessagePack, and UBJSON are stored in network order
+          (big endian) and therefore need reordering on little endian systems.
+          On the other hand, BSON and BJData use little endian and should reorder
+          on big endian systems.
+    */
+    template<typename NumberType, bool InputIsLittleEndian = false>
+    bool get_number(const input_format_t format, NumberType& result)
+    {
+        // step 1: read input into array with system's byte order
+        std::array<std::uint8_t, sizeof(NumberType)> vec{};
+        for (std::size_t i = 0; i < sizeof(NumberType); ++i)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
+            {
+                return false;
+            }
+
+            // reverse byte order prior to conversion if necessary
+            if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata))
+            {
+                vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);
+            }
+            else
+            {
+                vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE
+            }
+        }
+
+        // step 2: convert array into number of type T and return
+        std::memcpy(&result, vec.data(), sizeof(NumberType));
+        return true;
+    }
+
+    /*!
+    @brief create a string by reading characters from the input
+
+    @tparam NumberType the type of the number
+    @param[in] format the current format (for diagnostics)
+    @param[in] len number of characters to read
+    @param[out] result string created by reading @a len bytes
+
+    @return whether string creation completed
+
+    @note We can not reserve @a len bytes for the result, because @a len
+          may be too large. Usually, @ref unexpect_eof() detects the end of
+          the input before we run out of string memory.
+    */
+    template<typename NumberType>
+    bool get_string(const input_format_t format,
+                    const NumberType len,
+                    string_t& result)
+    {
+        bool success = true;
+        for (NumberType i = 0; i < len; i++)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string")))
+            {
+                success = false;
+                break;
+            }
+            result.push_back(static_cast<typename string_t::value_type>(current));
+        }
+        return success;
+    }
+
+    /*!
+    @brief create a byte array by reading bytes from the input
+
+    @tparam NumberType the type of the number
+    @param[in] format the current format (for diagnostics)
+    @param[in] len number of bytes to read
+    @param[out] result byte array created by reading @a len bytes
+
+    @return whether byte array creation completed
+
+    @note We can not reserve @a len bytes for the result, because @a len
+          may be too large. Usually, @ref unexpect_eof() detects the end of
+          the input before we run out of memory.
+    */
+    template<typename NumberType>
+    bool get_binary(const input_format_t format,
+                    const NumberType len,
+                    binary_t& result)
+    {
+        bool success = true;
+        for (NumberType i = 0; i < len; i++)
+        {
+            get();
+            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary")))
+            {
+                success = false;
+                break;
+            }
+            result.push_back(static_cast<std::uint8_t>(current));
+        }
+        return success;
+    }
+
+    /*!
+    @param[in] format   the current format (for diagnostics)
+    @param[in] context  further context information (for diagnostics)
+    @return whether the last read character is not EOF
+    */
+    JSON_HEDLEY_NON_NULL(3)
+    bool unexpect_eof(const input_format_t format, const char* context) const
+    {
+        if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))
+        {
+            return sax->parse_error(chars_read, "<end of file>",
+                                    parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr));
+        }
+        return true;
+    }
+
+    /*!
+    @return a string representation of the last read byte
+    */
+    std::string get_token_string() const
+    {
+        std::array<char, 3> cr{{}};
+        static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+        return std::string{cr.data()};
+    }
+
+    /*!
+    @param[in] format   the current format
+    @param[in] detail   a detailed error message
+    @param[in] context  further context information
+    @return a message string to use in the parse_error exceptions
+    */
+    std::string exception_message(const input_format_t format,
+                                  const std::string& detail,
+                                  const std::string& context) const
+    {
+        std::string error_msg = "syntax error while parsing ";
+
+        switch (format)
+        {
+            case input_format_t::cbor:
+                error_msg += "CBOR";
+                break;
+
+            case input_format_t::msgpack:
+                error_msg += "MessagePack";
+                break;
+
+            case input_format_t::ubjson:
+                error_msg += "UBJSON";
+                break;
+
+            case input_format_t::bson:
+                error_msg += "BSON";
+                break;
+
+            case input_format_t::bjdata:
+                error_msg += "BJData";
+                break;
+
+            case input_format_t::json: // LCOV_EXCL_LINE
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+
+        return concat(error_msg, ' ', context, ": ", detail);
+    }
+
+  private:
+    static JSON_INLINE_VARIABLE constexpr std::size_t npos = static_cast<std::size_t>(-1);
+
+    /// input adapter
+    InputAdapterType ia;
+
+    /// the current character
+    char_int_type current = std::char_traits<char_type>::eof();
+
+    /// the number of characters read
+    std::size_t chars_read = 0;
+
+    /// whether we can assume little endianness
+    const bool is_little_endian = little_endianness();
+
+    /// input format
+    const input_format_t input_format = input_format_t::json;
+
+    /// the SAX parser
+    json_sax_t* sax = nullptr;
+
+    // excluded markers in bjdata optimized type
+#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
+    make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
+
+#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \
+    make_array<bjd_type>(                      \
+    bjd_type{'C', "char"},                     \
+    bjd_type{'D', "double"},                   \
+    bjd_type{'I', "int16"},                    \
+    bjd_type{'L', "int64"},                    \
+    bjd_type{'M', "uint64"},                   \
+    bjd_type{'U', "uint8"},                    \
+    bjd_type{'d', "single"},                   \
+    bjd_type{'i', "int8"},                     \
+    bjd_type{'l', "int32"},                    \
+    bjd_type{'m', "uint32"},                   \
+    bjd_type{'u', "uint16"})
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    // lookup tables
+    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+    const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers =
+        JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_;
+
+    using bjd_type = std::pair<char_int_type, string_t>;
+    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+    const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map =
+        JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_;
+
+#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_
+#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_
+};
+
+#ifndef JSON_HAS_CPP_17
+    template<typename BasicJsonType, typename InputAdapterType, typename SAX>
+    constexpr std::size_t binary_reader<BasicJsonType, InputAdapterType, SAX>::npos;
+#endif
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/input/parser.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cmath> // isfinite
+#include <cstdint> // uint8_t
+#include <functional> // function
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/json_sax.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/is_sax.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+////////////
+// parser //
+////////////
+
+enum class parse_event_t : std::uint8_t
+{
+    /// the parser read `{` and started to process a JSON object
+    object_start,
+    /// the parser read `}` and finished processing a JSON object
+    object_end,
+    /// the parser read `[` and started to process a JSON array
+    array_start,
+    /// the parser read `]` and finished processing a JSON array
+    array_end,
+    /// the parser read a key of a value in an object
+    key,
+    /// the parser finished reading a JSON value
+    value
+};
+
+template<typename BasicJsonType>
+using parser_callback_t =
+    std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
+
+/*!
+@brief syntax analysis
+
+This class implements a recursive descent parser.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class parser
+{
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using string_t = typename BasicJsonType::string_t;
+    using lexer_t = lexer<BasicJsonType, InputAdapterType>;
+    using token_type = typename lexer_t::token_type;
+
+  public:
+    /// a parser reading from an input adapter
+    explicit parser(InputAdapterType&& adapter,
+                    const parser_callback_t<BasicJsonType> cb = nullptr,
+                    const bool allow_exceptions_ = true,
+                    const bool skip_comments = false)
+        : callback(cb)
+        , m_lexer(std::move(adapter), skip_comments)
+        , allow_exceptions(allow_exceptions_)
+    {
+        // read first token
+        get_token();
+    }
+
+    /*!
+    @brief public parser interface
+
+    @param[in] strict      whether to expect the last token to be EOF
+    @param[in,out] result  parsed JSON value
+
+    @throw parse_error.101 in case of an unexpected token
+    @throw parse_error.102 if to_unicode fails or surrogate error
+    @throw parse_error.103 if to_unicode fails
+    */
+    void parse(const bool strict, BasicJsonType& result)
+    {
+        if (callback)
+        {
+            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
+            sax_parse_internal(&sdp);
+
+            // in strict mode, input must be completely read
+            if (strict && (get_token() != token_type::end_of_input))
+            {
+                sdp.parse_error(m_lexer.get_position(),
+                                m_lexer.get_token_string(),
+                                parse_error::create(101, m_lexer.get_position(),
+                                                    exception_message(token_type::end_of_input, "value"), nullptr));
+            }
+
+            // in case of an error, return discarded value
+            if (sdp.is_errored())
+            {
+                result = value_t::discarded;
+                return;
+            }
+
+            // set top-level value to null if it was discarded by the callback
+            // function
+            if (result.is_discarded())
+            {
+                result = nullptr;
+            }
+        }
+        else
+        {
+            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
+            sax_parse_internal(&sdp);
+
+            // in strict mode, input must be completely read
+            if (strict && (get_token() != token_type::end_of_input))
+            {
+                sdp.parse_error(m_lexer.get_position(),
+                                m_lexer.get_token_string(),
+                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+            }
+
+            // in case of an error, return discarded value
+            if (sdp.is_errored())
+            {
+                result = value_t::discarded;
+                return;
+            }
+        }
+
+        result.assert_invariant();
+    }
+
+    /*!
+    @brief public accept interface
+
+    @param[in] strict  whether to expect the last token to be EOF
+    @return whether the input is a proper JSON text
+    */
+    bool accept(const bool strict = true)
+    {
+        json_sax_acceptor<BasicJsonType> sax_acceptor;
+        return sax_parse(&sax_acceptor, strict);
+    }
+
+    template<typename SAX>
+    JSON_HEDLEY_NON_NULL(2)
+    bool sax_parse(SAX* sax, const bool strict = true)
+    {
+        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+        const bool result = sax_parse_internal(sax);
+
+        // strict mode: next byte must be EOF
+        if (result && strict && (get_token() != token_type::end_of_input))
+        {
+            return sax->parse_error(m_lexer.get_position(),
+                                    m_lexer.get_token_string(),
+                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+        }
+
+        return result;
+    }
+
+  private:
+    template<typename SAX>
+    JSON_HEDLEY_NON_NULL(2)
+    bool sax_parse_internal(SAX* sax)
+    {
+        // stack to remember the hierarchy of structured values we are parsing
+        // true = array; false = object
+        std::vector<bool> states;
+        // value to avoid a goto (see comment where set to true)
+        bool skip_to_state_evaluation = false;
+
+        while (true)
+        {
+            if (!skip_to_state_evaluation)
+            {
+                // invariant: get_token() was called before each iteration
+                switch (last_token)
+                {
+                    case token_type::begin_object:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+                        {
+                            return false;
+                        }
+
+                        // closing } -> we are done
+                        if (get_token() == token_type::end_object)
+                        {
+                            if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+                            {
+                                return false;
+                            }
+                            break;
+                        }
+
+                        // parse key
+                        if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
+                        {
+                            return sax->parse_error(m_lexer.get_position(),
+                                                    m_lexer.get_token_string(),
+                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+                        }
+                        if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+                        {
+                            return false;
+                        }
+
+                        // parse separator (:)
+                        if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+                        {
+                            return sax->parse_error(m_lexer.get_position(),
+                                                    m_lexer.get_token_string(),
+                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+                        }
+
+                        // remember we are now inside an object
+                        states.push_back(false);
+
+                        // parse values
+                        get_token();
+                        continue;
+                    }
+
+                    case token_type::begin_array:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+                        {
+                            return false;
+                        }
+
+                        // closing ] -> we are done
+                        if (get_token() == token_type::end_array)
+                        {
+                            if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+                            {
+                                return false;
+                            }
+                            break;
+                        }
+
+                        // remember we are now inside an array
+                        states.push_back(true);
+
+                        // parse values (no need to call get_token)
+                        continue;
+                    }
+
+                    case token_type::value_float:
+                    {
+                        const auto res = m_lexer.get_number_float();
+
+                        if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
+                        {
+                            return sax->parse_error(m_lexer.get_position(),
+                                                    m_lexer.get_token_string(),
+                                                    out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
+                        }
+
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
+                        {
+                            return false;
+                        }
+
+                        break;
+                    }
+
+                    case token_type::literal_false:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::literal_null:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->null()))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::literal_true:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::value_integer:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::value_string:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::value_unsigned:
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
+                        {
+                            return false;
+                        }
+                        break;
+                    }
+
+                    case token_type::parse_error:
+                    {
+                        // using "uninitialized" to avoid "expected" message
+                        return sax->parse_error(m_lexer.get_position(),
+                                                m_lexer.get_token_string(),
+                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
+                    }
+
+                    case token_type::uninitialized:
+                    case token_type::end_array:
+                    case token_type::end_object:
+                    case token_type::name_separator:
+                    case token_type::value_separator:
+                    case token_type::end_of_input:
+                    case token_type::literal_or_value:
+                    default: // the last token was unexpected
+                    {
+                        return sax->parse_error(m_lexer.get_position(),
+                                                m_lexer.get_token_string(),
+                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
+                    }
+                }
+            }
+            else
+            {
+                skip_to_state_evaluation = false;
+            }
+
+            // we reached this line after we successfully parsed a value
+            if (states.empty())
+            {
+                // empty stack: we reached the end of the hierarchy: done
+                return true;
+            }
+
+            if (states.back())  // array
+            {
+                // comma -> next value
+                if (get_token() == token_type::value_separator)
+                {
+                    // parse a new value
+                    get_token();
+                    continue;
+                }
+
+                // closing ]
+                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
+                {
+                    if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+                    {
+                        return false;
+                    }
+
+                    // We are done with this array. Before we can parse a
+                    // new value, we need to evaluate the new state first.
+                    // By setting skip_to_state_evaluation to false, we
+                    // are effectively jumping to the beginning of this if.
+                    JSON_ASSERT(!states.empty());
+                    states.pop_back();
+                    skip_to_state_evaluation = true;
+                    continue;
+                }
+
+                return sax->parse_error(m_lexer.get_position(),
+                                        m_lexer.get_token_string(),
+                                        parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
+            }
+
+            // states.back() is false -> object
+
+            // comma -> next value
+            if (get_token() == token_type::value_separator)
+            {
+                // parse key
+                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
+                {
+                    return sax->parse_error(m_lexer.get_position(),
+                                            m_lexer.get_token_string(),
+                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+                }
+
+                if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+                {
+                    return false;
+                }
+
+                // parse separator (:)
+                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+                {
+                    return sax->parse_error(m_lexer.get_position(),
+                                            m_lexer.get_token_string(),
+                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+                }
+
+                // parse values
+                get_token();
+                continue;
+            }
+
+            // closing }
+            if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
+            {
+                if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+                {
+                    return false;
+                }
+
+                // We are done with this object. Before we can parse a
+                // new value, we need to evaluate the new state first.
+                // By setting skip_to_state_evaluation to false, we
+                // are effectively jumping to the beginning of this if.
+                JSON_ASSERT(!states.empty());
+                states.pop_back();
+                skip_to_state_evaluation = true;
+                continue;
+            }
+
+            return sax->parse_error(m_lexer.get_position(),
+                                    m_lexer.get_token_string(),
+                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
+        }
+    }
+
+    /// get next token from lexer
+    token_type get_token()
+    {
+        return last_token = m_lexer.scan();
+    }
+
+    std::string exception_message(const token_type expected, const std::string& context)
+    {
+        std::string error_msg = "syntax error ";
+
+        if (!context.empty())
+        {
+            error_msg += concat("while parsing ", context, ' ');
+        }
+
+        error_msg += "- ";
+
+        if (last_token == token_type::parse_error)
+        {
+            error_msg += concat(m_lexer.get_error_message(), "; last read: '",
+                                m_lexer.get_token_string(), '\'');
+        }
+        else
+        {
+            error_msg += concat("unexpected ", lexer_t::token_type_name(last_token));
+        }
+
+        if (expected != token_type::uninitialized)
+        {
+            error_msg += concat("; expected ", lexer_t::token_type_name(expected));
+        }
+
+        return error_msg;
+    }
+
+  private:
+    /// callback function
+    const parser_callback_t<BasicJsonType> callback = nullptr;
+    /// the type of the last read token
+    token_type last_token = token_type::uninitialized;
+    /// the lexer
+    lexer_t m_lexer;
+    /// whether to throw exceptions in case of errors
+    const bool allow_exceptions = true;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // ptrdiff_t
+#include <limits>  // numeric_limits
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*
+@brief an iterator for primitive JSON types
+
+This class models an iterator for primitive JSON types (boolean, number,
+string). It's only purpose is to allow the iterator/const_iterator classes
+to "iterate" over primitive values. Internally, the iterator is modeled by
+a `difference_type` variable. Value begin_value (`0`) models the begin,
+end_value (`1`) models past the end.
+*/
+class primitive_iterator_t
+{
+  private:
+    using difference_type = std::ptrdiff_t;
+    static constexpr difference_type begin_value = 0;
+    static constexpr difference_type end_value = begin_value + 1;
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /// iterator as signed integer type
+    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
+
+  public:
+    constexpr difference_type get_value() const noexcept
+    {
+        return m_it;
+    }
+
+    /// set iterator to a defined beginning
+    void set_begin() noexcept
+    {
+        m_it = begin_value;
+    }
+
+    /// set iterator to a defined past the end
+    void set_end() noexcept
+    {
+        m_it = end_value;
+    }
+
+    /// return whether the iterator can be dereferenced
+    constexpr bool is_begin() const noexcept
+    {
+        return m_it == begin_value;
+    }
+
+    /// return whether the iterator is at end
+    constexpr bool is_end() const noexcept
+    {
+        return m_it == end_value;
+    }
+
+    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+    {
+        return lhs.m_it == rhs.m_it;
+    }
+
+    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+    {
+        return lhs.m_it < rhs.m_it;
+    }
+
+    primitive_iterator_t operator+(difference_type n) noexcept
+    {
+        auto result = *this;
+        result += n;
+        return result;
+    }
+
+    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+    {
+        return lhs.m_it - rhs.m_it;
+    }
+
+    primitive_iterator_t& operator++() noexcept
+    {
+        ++m_it;
+        return *this;
+    }
+
+    primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        ++m_it;
+        return result;
+    }
+
+    primitive_iterator_t& operator--() noexcept
+    {
+        --m_it;
+        return *this;
+    }
+
+    primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        --m_it;
+        return result;
+    }
+
+    primitive_iterator_t& operator+=(difference_type n) noexcept
+    {
+        m_it += n;
+        return *this;
+    }
+
+    primitive_iterator_t& operator-=(difference_type n) noexcept
+    {
+        m_it -= n;
+        return *this;
+    }
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief an iterator value
+
+@note This structure could easily be a union, but MSVC currently does not allow
+unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
+*/
+template<typename BasicJsonType> struct internal_iterator
+{
+    /// iterator for JSON objects
+    typename BasicJsonType::object_t::iterator object_iterator {};
+    /// iterator for JSON arrays
+    typename BasicJsonType::array_t::iterator array_iterator {};
+    /// generic iterator for all other types
+    primitive_iterator_t primitive_iterator {};
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/iter_impl.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
+#include <type_traits> // conditional, is_const, remove_const
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// forward declare, to be able to friend it later on
+template<typename IteratorType> class iteration_proxy;
+template<typename IteratorType> class iteration_proxy_value;
+
+/*!
+@brief a template for a bidirectional iterator for the @ref basic_json class
+This class implements a both iterators (iterator and const_iterator) for the
+@ref basic_json class.
+@note An iterator is called *initialized* when a pointer to a JSON value has
+      been set (e.g., by a constructor or a copy assignment). If the iterator is
+      default-constructed, it is *uninitialized* and most methods are undefined.
+      **The library uses assertions to detect calls on uninitialized iterators.**
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+  The iterator that can be moved can be moved in both directions (i.e.
+  incremented and decremented).
+@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
+       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
+*/
+template<typename BasicJsonType>
+class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
+{
+    /// the iterator with BasicJsonType of different const-ness
+    using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
+    /// allow basic_json to access private members
+    friend other_iter_impl;
+    friend BasicJsonType;
+    friend iteration_proxy<iter_impl>;
+    friend iteration_proxy_value<iter_impl>;
+
+    using object_t = typename BasicJsonType::object_t;
+    using array_t = typename BasicJsonType::array_t;
+    // make sure BasicJsonType is basic_json or const basic_json
+    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
+                  "iter_impl only accepts (const) basic_json");
+    // superficial check for the LegacyBidirectionalIterator named requirement
+    static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
+                  &&  std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,
+                  "basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
+
+  public:
+    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
+    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
+    /// A user-defined iterator should provide publicly accessible typedefs named
+    /// iterator_category, value_type, difference_type, pointer, and reference.
+    /// Note that value_type is required to be non-const, even for constant iterators.
+    using iterator_category = std::bidirectional_iterator_tag;
+
+    /// the type of the values when the iterator is dereferenced
+    using value_type = typename BasicJsonType::value_type;
+    /// a type to represent differences between iterators
+    using difference_type = typename BasicJsonType::difference_type;
+    /// defines a pointer to the type iterated over (value_type)
+    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
+          typename BasicJsonType::const_pointer,
+          typename BasicJsonType::pointer>::type;
+    /// defines a reference to the type iterated over (value_type)
+    using reference =
+        typename std::conditional<std::is_const<BasicJsonType>::value,
+        typename BasicJsonType::const_reference,
+        typename BasicJsonType::reference>::type;
+
+    iter_impl() = default;
+    ~iter_impl() = default;
+    iter_impl(iter_impl&&) noexcept = default;
+    iter_impl& operator=(iter_impl&&) noexcept = default;
+
+    /*!
+    @brief constructor for a given JSON instance
+    @param[in] object  pointer to a JSON object for this iterator
+    @pre object != nullptr
+    @post The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    explicit iter_impl(pointer object) noexcept : m_object(object)
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+            {
+                m_it.object_iterator = typename object_t::iterator();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_it.array_iterator = typename array_t::iterator();
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator = primitive_iterator_t();
+                break;
+            }
+        }
+    }
+
+    /*!
+    @note The conventional copy constructor and copy assignment are implicitly
+          defined. Combined with the following converting constructor and
+          assignment, they support: (1) copy from iterator to iterator, (2)
+          copy from const iterator to const iterator, and (3) conversion from
+          iterator to const iterator. However conversion from const iterator
+          to iterator is not defined.
+    */
+
+    /*!
+    @brief const copy constructor
+    @param[in] other const iterator to copy from
+    @note This copy constructor had to be defined explicitly to circumvent a bug
+          occurring on msvc v19.0 compiler (VS 2015) debug build. For more
+          information refer to: https://github.com/nlohmann/json/issues/1608
+    */
+    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
+        : m_object(other.m_object), m_it(other.m_it)
+    {}
+
+    /*!
+    @brief converting assignment
+    @param[in] other const iterator to copy from
+    @return const/non-const iterator
+    @note It is not checked whether @a other is initialized.
+    */
+    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
+    {
+        if (&other != this)
+        {
+            m_object = other.m_object;
+            m_it = other.m_it;
+        }
+        return *this;
+    }
+
+    /*!
+    @brief converting constructor
+    @param[in] other  non-const iterator to copy from
+    @note It is not checked whether @a other is initialized.
+    */
+    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
+        : m_object(other.m_object), m_it(other.m_it)
+    {}
+
+    /*!
+    @brief converting assignment
+    @param[in] other  non-const iterator to copy from
+    @return const/non-const iterator
+    @note It is not checked whether @a other is initialized.
+    */
+    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)
+    {
+        m_object = other.m_object;
+        m_it = other.m_it;
+        return *this;
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /*!
+    @brief set the iterator to the first value
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    void set_begin() noexcept
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+            {
+                m_it.object_iterator = m_object->m_data.m_value.object->begin();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_it.array_iterator = m_object->m_data.m_value.array->begin();
+                break;
+            }
+
+            case value_t::null:
+            {
+                // set to end so begin()==end() is true: null is empty
+                m_it.primitive_iterator.set_end();
+                break;
+            }
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator.set_begin();
+                break;
+            }
+        }
+    }
+
+    /*!
+    @brief set the iterator past the last value
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    void set_end() noexcept
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+            {
+                m_it.object_iterator = m_object->m_data.m_value.object->end();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_it.array_iterator = m_object->m_data.m_value.array->end();
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator.set_end();
+                break;
+            }
+        }
+    }
+
+  public:
+    /*!
+    @brief return a reference to the value pointed to by the iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    reference operator*() const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+            {
+                JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
+                return m_it.object_iterator->second;
+            }
+
+            case value_t::array:
+            {
+                JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
+                return *m_it.array_iterator;
+            }
+
+            case value_t::null:
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+                {
+                    return *m_object;
+                }
+
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+            }
+        }
+    }
+
+    /*!
+    @brief dereference the iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    pointer operator->() const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+            {
+                JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
+                return &(m_it.object_iterator->second);
+            }
+
+            case value_t::array:
+            {
+                JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
+                return &*m_it.array_iterator;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+                {
+                    return m_object;
+                }
+
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+            }
+        }
+    }
+
+    /*!
+    @brief post-increment (it++)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        ++(*this);
+        return result;
+    }
+
+    /*!
+    @brief pre-increment (++it)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator++()
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+            {
+                std::advance(m_it.object_iterator, 1);
+                break;
+            }
+
+            case value_t::array:
+            {
+                std::advance(m_it.array_iterator, 1);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                ++m_it.primitive_iterator;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    /*!
+    @brief post-decrement (it--)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        auto result = *this;
+        --(*this);
+        return result;
+    }
+
+    /*!
+    @brief pre-decrement (--it)
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator--()
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+            {
+                std::advance(m_it.object_iterator, -1);
+                break;
+            }
+
+            case value_t::array:
+            {
+                std::advance(m_it.array_iterator, -1);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                --m_it.primitive_iterator;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    /*!
+    @brief comparison: equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+    bool operator==(const IterImpl& other) const
+    {
+        // if objects are not the same, the comparison is undefined
+        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+        }
+
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+                return (m_it.object_iterator == other.m_it.object_iterator);
+
+            case value_t::array:
+                return (m_it.array_iterator == other.m_it.array_iterator);
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return (m_it.primitive_iterator == other.m_it.primitive_iterator);
+        }
+    }
+
+    /*!
+    @brief comparison: not equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+    bool operator!=(const IterImpl& other) const
+    {
+        return !operator==(other);
+    }
+
+    /*!
+    @brief comparison: smaller
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator<(const iter_impl& other) const
+    {
+        // if objects are not the same, the comparison is undefined
+        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+        }
+
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
+
+            case value_t::array:
+                return (m_it.array_iterator < other.m_it.array_iterator);
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return (m_it.primitive_iterator < other.m_it.primitive_iterator);
+        }
+    }
+
+    /*!
+    @brief comparison: less than or equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator<=(const iter_impl& other) const
+    {
+        return !other.operator < (*this);
+    }
+
+    /*!
+    @brief comparison: greater than
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator>(const iter_impl& other) const
+    {
+        return !operator<=(other);
+    }
+
+    /*!
+    @brief comparison: greater than or equal
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    bool operator>=(const iter_impl& other) const
+    {
+        return !operator<(other);
+    }
+
+    /*!
+    @brief add to iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator+=(difference_type i)
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+            case value_t::array:
+            {
+                std::advance(m_it.array_iterator, i);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                m_it.primitive_iterator += i;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    /*!
+    @brief subtract from iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl& operator-=(difference_type i)
+    {
+        return operator+=(-i);
+    }
+
+    /*!
+    @brief add to iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator+(difference_type i) const
+    {
+        auto result = *this;
+        result += i;
+        return result;
+    }
+
+    /*!
+    @brief addition of distance and iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    friend iter_impl operator+(difference_type i, const iter_impl& it)
+    {
+        auto result = it;
+        result += i;
+        return result;
+    }
+
+    /*!
+    @brief subtract from iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    iter_impl operator-(difference_type i) const
+    {
+        auto result = *this;
+        result -= i;
+        return result;
+    }
+
+    /*!
+    @brief return difference
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    difference_type operator-(const iter_impl& other) const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+            case value_t::array:
+                return m_it.array_iterator - other.m_it.array_iterator;
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                return m_it.primitive_iterator - other.m_it.primitive_iterator;
+        }
+    }
+
+    /*!
+    @brief access to successor
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    reference operator[](difference_type n) const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        switch (m_object->m_data.m_type)
+        {
+            case value_t::object:
+                JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
+
+            case value_t::array:
+                return *std::next(m_it.array_iterator, n);
+
+            case value_t::null:
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))
+                {
+                    return *m_object;
+                }
+
+                JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+            }
+        }
+    }
+
+    /*!
+    @brief return the key of an object iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    const typename object_t::key_type& key() const
+    {
+        JSON_ASSERT(m_object != nullptr);
+
+        if (JSON_HEDLEY_LIKELY(m_object->is_object()))
+        {
+            return m_it.object_iterator->first;
+        }
+
+        JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
+    }
+
+    /*!
+    @brief return the value of an iterator
+    @pre The iterator is initialized; i.e. `m_object != nullptr`.
+    */
+    reference value() const
+    {
+        return operator*();
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /// associated JSON instance
+    pointer m_object = nullptr;
+    /// the actual iterator of the associated instance
+    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
+
+// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // ptrdiff_t
+#include <iterator> // reverse_iterator
+#include <utility> // declval
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////////
+// reverse_iterator //
+//////////////////////
+
+/*!
+@brief a template for a reverse iterator class
+
+@tparam Base the base iterator type to reverse. Valid types are @ref
+iterator (to create @ref reverse_iterator) and @ref const_iterator (to
+create @ref const_reverse_iterator).
+
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+  The iterator that can be moved can be moved in both directions (i.e.
+  incremented and decremented).
+- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
+  It is possible to write to the pointed-to element (only if @a Base is
+  @ref iterator).
+
+@since version 1.0.0
+*/
+template<typename Base>
+class json_reverse_iterator : public std::reverse_iterator<Base>
+{
+  public:
+    using difference_type = std::ptrdiff_t;
+    /// shortcut to the reverse iterator adapter
+    using base_iterator = std::reverse_iterator<Base>;
+    /// the reference type for the pointed-to element
+    using reference = typename Base::reference;
+
+    /// create reverse iterator from iterator
+    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
+        : base_iterator(it) {}
+
+    /// create reverse iterator from base class
+    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
+
+    /// post-increment (it++)
+    json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
+    }
+
+    /// pre-increment (++it)
+    json_reverse_iterator& operator++()
+    {
+        return static_cast<json_reverse_iterator&>(base_iterator::operator++());
+    }
+
+    /// post-decrement (it--)
+    json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp)
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
+    }
+
+    /// pre-decrement (--it)
+    json_reverse_iterator& operator--()
+    {
+        return static_cast<json_reverse_iterator&>(base_iterator::operator--());
+    }
+
+    /// add to iterator
+    json_reverse_iterator& operator+=(difference_type i)
+    {
+        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
+    }
+
+    /// add to iterator
+    json_reverse_iterator operator+(difference_type i) const
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
+    }
+
+    /// subtract from iterator
+    json_reverse_iterator operator-(difference_type i) const
+    {
+        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
+    }
+
+    /// return difference
+    difference_type operator-(const json_reverse_iterator& other) const
+    {
+        return base_iterator(*this) - base_iterator(other);
+    }
+
+    /// access to successor
+    reference operator[](difference_type n) const
+    {
+        return *(this->operator+(n));
+    }
+
+    /// return the key of an object iterator
+    auto key() const -> decltype(std::declval<Base>().key())
+    {
+        auto it = --this->base();
+        return it.key();
+    }
+
+    /// return the value of an iterator
+    reference value() const
+    {
+        auto it = --this->base();
+        return it.operator * ();
+    }
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+
+// #include <nlohmann/detail/json_custom_base_class.hpp>
+
+
+#include <type_traits> // conditional, is_same
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief Default base class of the @ref basic_json class.
+
+So that the correct implementations of the copy / move ctors / assign operators
+of @ref basic_json do not require complex case distinctions
+(no base class / custom base class used as customization point),
+@ref basic_json always has a base class.
+By default, this class is used because it is empty and thus has no effect
+on the behavior of @ref basic_json.
+*/
+struct json_default_base {};
+
+template<class T>
+using json_base_class = typename std::conditional <
+                        std::is_same<T, void>::value,
+                        json_default_base,
+                        T
+                        >::type;
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/json_pointer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // all_of
+#include <cctype> // isdigit
+#include <cerrno> // errno, ERANGE
+#include <cstdlib> // strtoull
+#ifndef JSON_NO_IO
+    #include <iosfwd> // ostream
+#endif  // JSON_NO_IO
+#include <limits> // max
+#include <numeric> // accumulate
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+/// @sa https://json.nlohmann.me/api/json_pointer/
+template<typename RefStringType>
+class json_pointer
+{
+    // allow basic_json to access private members
+    NLOHMANN_BASIC_JSON_TPL_DECLARATION
+    friend class basic_json;
+
+    template<typename>
+    friend class json_pointer;
+
+    template<typename T>
+    struct string_t_helper
+    {
+        using type = T;
+    };
+
+    NLOHMANN_BASIC_JSON_TPL_DECLARATION
+    struct string_t_helper<NLOHMANN_BASIC_JSON_TPL>
+    {
+        using type = StringType;
+    };
+
+  public:
+    // for backwards compatibility accept BasicJsonType
+    using string_t = typename string_t_helper<RefStringType>::type;
+
+    /// @brief create JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
+    explicit json_pointer(const string_t& s = "")
+        : reference_tokens(split(s))
+    {}
+
+    /// @brief return a string representation of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/to_string/
+    string_t to_string() const
+    {
+        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
+                               string_t{},
+                               [](const string_t& a, const string_t& b)
+        {
+            return detail::concat(a, '/', detail::escape(b));
+        });
+    }
+
+    /// @brief return a string representation of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
+    operator string_t() const
+    {
+        return to_string();
+    }
+
+#ifndef JSON_NO_IO
+    /// @brief write string representation of the JSON pointer to stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+    friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr)
+    {
+        o << ptr.to_string();
+        return o;
+    }
+#endif
+
+    /// @brief append another JSON pointer at the end of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+    json_pointer& operator/=(const json_pointer& ptr)
+    {
+        reference_tokens.insert(reference_tokens.end(),
+                                ptr.reference_tokens.begin(),
+                                ptr.reference_tokens.end());
+        return *this;
+    }
+
+    /// @brief append an unescaped reference token at the end of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+    json_pointer& operator/=(string_t token)
+    {
+        push_back(std::move(token));
+        return *this;
+    }
+
+    /// @brief append an array index at the end of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+    json_pointer& operator/=(std::size_t array_idx)
+    {
+        return *this /= std::to_string(array_idx);
+    }
+
+    /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+    friend json_pointer operator/(const json_pointer& lhs,
+                                  const json_pointer& rhs)
+    {
+        return json_pointer(lhs) /= rhs;
+    }
+
+    /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+    friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
+    {
+        return json_pointer(lhs) /= std::move(token);
+    }
+
+    /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+    friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)
+    {
+        return json_pointer(lhs) /= array_idx;
+    }
+
+    /// @brief returns the parent of this JSON pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/
+    json_pointer parent_pointer() const
+    {
+        if (empty())
+        {
+            return *this;
+        }
+
+        json_pointer res = *this;
+        res.pop_back();
+        return res;
+    }
+
+    /// @brief remove last reference token
+    /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/
+    void pop_back()
+    {
+        if (JSON_HEDLEY_UNLIKELY(empty()))
+        {
+            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+        }
+
+        reference_tokens.pop_back();
+    }
+
+    /// @brief return last reference token
+    /// @sa https://json.nlohmann.me/api/json_pointer/back/
+    const string_t& back() const
+    {
+        if (JSON_HEDLEY_UNLIKELY(empty()))
+        {
+            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+        }
+
+        return reference_tokens.back();
+    }
+
+    /// @brief append an unescaped token at the end of the reference pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+    void push_back(const string_t& token)
+    {
+        reference_tokens.push_back(token);
+    }
+
+    /// @brief append an unescaped token at the end of the reference pointer
+    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+    void push_back(string_t&& token)
+    {
+        reference_tokens.push_back(std::move(token));
+    }
+
+    /// @brief return whether pointer points to the root document
+    /// @sa https://json.nlohmann.me/api/json_pointer/empty/
+    bool empty() const noexcept
+    {
+        return reference_tokens.empty();
+    }
+
+  private:
+    /*!
+    @param[in] s  reference token to be converted into an array index
+
+    @return integer representation of @a s
+
+    @throw parse_error.106  if an array index begins with '0'
+    @throw parse_error.109  if an array index begins not with a digit
+    @throw out_of_range.404 if string @a s could not be converted to an integer
+    @throw out_of_range.410 if an array index exceeds size_type
+    */
+    template<typename BasicJsonType>
+    static typename BasicJsonType::size_type array_index(const string_t& s)
+    {
+        using size_type = typename BasicJsonType::size_type;
+
+        // error condition (cf. RFC 6901, Sect. 4)
+        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
+        {
+            JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
+        }
+
+        // error condition (cf. RFC 6901, Sect. 4)
+        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
+        {
+            JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
+        }
+
+        const char* p = s.c_str();
+        char* p_end = nullptr;
+        errno = 0; // strtoull doesn't reset errno
+        const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
+        if (p == p_end // invalid input or empty string
+                || errno == ERANGE // out of range
+                || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
+        {
+            JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
+        }
+
+        // only triggered on special platforms (like 32bit), see also
+        // https://github.com/nlohmann/json/pull/2203
+        if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))  // NOLINT(runtime/int)
+        {
+            JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr));   // LCOV_EXCL_LINE
+        }
+
+        return static_cast<size_type>(res);
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    json_pointer top() const
+    {
+        if (JSON_HEDLEY_UNLIKELY(empty()))
+        {
+            JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+        }
+
+        json_pointer result = *this;
+        result.reference_tokens = {reference_tokens[0]};
+        return result;
+    }
+
+  private:
+    /*!
+    @brief create and return a reference to the pointed to value
+
+    @complexity Linear in the number of reference tokens.
+
+    @throw parse_error.109 if array index is not a number
+    @throw type_error.313 if value cannot be unflattened
+    */
+    template<typename BasicJsonType>
+    BasicJsonType& get_and_create(BasicJsonType& j) const
+    {
+        auto* result = &j;
+
+        // in case no reference tokens exist, return a reference to the JSON value
+        // j which will be overwritten by a primitive value
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (result->type())
+            {
+                case detail::value_t::null:
+                {
+                    if (reference_token == "0")
+                    {
+                        // start a new array if reference token is 0
+                        result = &result->operator[](0);
+                    }
+                    else
+                    {
+                        // start a new object otherwise
+                        result = &result->operator[](reference_token);
+                    }
+                    break;
+                }
+
+                case detail::value_t::object:
+                {
+                    // create an entry in the object
+                    result = &result->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    // create an entry in the array
+                    result = &result->operator[](array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                /*
+                The following code is only reached if there exists a reference
+                token _and_ the current value is primitive. In this case, we have
+                an error situation, because primitive values may only occur as
+                single value; that is, with an empty list of reference tokens.
+                */
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
+            }
+        }
+
+        return *result;
+    }
+
+    /*!
+    @brief return a reference to the pointed to value
+
+    @note This version does not throw if a value is not present, but tries to
+          create nested values instead. For instance, calling this function
+          with pointer `"/this/that"` on a null value is equivalent to calling
+          `operator[]("this").operator[]("that")` on that value, effectively
+          changing the null value to an object.
+
+    @param[in] ptr  a JSON value
+
+    @return reference to the JSON value pointed to by the JSON pointer
+
+    @complexity Linear in the length of the JSON pointer.
+
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    BasicJsonType& get_unchecked(BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            // convert null values to arrays or objects before continuing
+            if (ptr->is_null())
+            {
+                // check if reference token is a number
+                const bool nums =
+                    std::all_of(reference_token.begin(), reference_token.end(),
+                                [](const unsigned char x)
+                {
+                    return std::isdigit(x);
+                });
+
+                // change value to array for numbers or "-" or to object otherwise
+                *ptr = (nums || reference_token == "-")
+                       ? detail::value_t::array
+                       : detail::value_t::object;
+            }
+
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // use unchecked object access
+                    ptr = &ptr->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (reference_token == "-")
+                    {
+                        // explicitly treat "-" as index beyond the end
+                        ptr = &ptr->operator[](ptr->m_data.m_value.array->size());
+                    }
+                    else
+                    {
+                        // convert array index to number; unchecked access
+                        ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+                    }
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.402  if the array index '-' is used
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    BasicJsonType& get_checked(BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // note: at performs range check
+                    ptr = &ptr->at(reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" always fails the range check
+                        JSON_THROW(detail::out_of_range::create(402, detail::concat(
+                                "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
+                                ") is out of range"), ptr));
+                    }
+
+                    // note: at performs range check
+                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @brief return a const reference to the pointed to value
+
+    @param[in] ptr  a JSON value
+
+    @return const reference to the JSON value pointed to by the JSON
+    pointer
+
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.402  if the array index '-' is used
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // use unchecked object access
+                    ptr = &ptr->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" cannot be used for const access
+                        JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
+                    }
+
+                    // use unchecked array access
+                    ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    @throw out_of_range.402  if the array index '-' is used
+    @throw out_of_range.404  if the JSON pointer can not be resolved
+    */
+    template<typename BasicJsonType>
+    const BasicJsonType& get_checked(const BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    // note: at performs range check
+                    ptr = &ptr->at(reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" always fails the range check
+                        JSON_THROW(detail::out_of_range::create(402, detail::concat(
+                                "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
+                                ") is out of range"), ptr));
+                    }
+
+                    // note: at performs range check
+                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                    JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+            }
+        }
+
+        return *ptr;
+    }
+
+    /*!
+    @throw parse_error.106   if an array index begins with '0'
+    @throw parse_error.109   if an array index was not a number
+    */
+    template<typename BasicJsonType>
+    bool contains(const BasicJsonType* ptr) const
+    {
+        for (const auto& reference_token : reference_tokens)
+        {
+            switch (ptr->type())
+            {
+                case detail::value_t::object:
+                {
+                    if (!ptr->contains(reference_token))
+                    {
+                        // we did not find the key in the object
+                        return false;
+                    }
+
+                    ptr = &ptr->operator[](reference_token);
+                    break;
+                }
+
+                case detail::value_t::array:
+                {
+                    if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+                    {
+                        // "-" always fails the range check
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
+                    {
+                        // invalid char
+                        return false;
+                    }
+                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
+                    {
+                        if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
+                        {
+                            // first char should be between '1' and '9'
+                            return false;
+                        }
+                        for (std::size_t i = 1; i < reference_token.size(); i++)
+                        {
+                            if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
+                            {
+                                // other char should be between '0' and '9'
+                                return false;
+                            }
+                        }
+                    }
+
+                    const auto idx = array_index<BasicJsonType>(reference_token);
+                    if (idx >= ptr->size())
+                    {
+                        // index out of range
+                        return false;
+                    }
+
+                    ptr = &ptr->operator[](idx);
+                    break;
+                }
+
+                case detail::value_t::null:
+                case detail::value_t::string:
+                case detail::value_t::boolean:
+                case detail::value_t::number_integer:
+                case detail::value_t::number_unsigned:
+                case detail::value_t::number_float:
+                case detail::value_t::binary:
+                case detail::value_t::discarded:
+                default:
+                {
+                    // we do not expect primitive values if there is still a
+                    // reference token to process
+                    return false;
+                }
+            }
+        }
+
+        // no reference token left means we found a primitive value
+        return true;
+    }
+
+    /*!
+    @brief split the string input to reference tokens
+
+    @note This function is only called by the json_pointer constructor.
+          All exceptions below are documented there.
+
+    @throw parse_error.107  if the pointer is not empty or begins with '/'
+    @throw parse_error.108  if character '~' is not followed by '0' or '1'
+    */
+    static std::vector<string_t> split(const string_t& reference_string)
+    {
+        std::vector<string_t> result;
+
+        // special case: empty reference string -> no reference tokens
+        if (reference_string.empty())
+        {
+            return result;
+        }
+
+        // check if nonempty reference string begins with slash
+        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
+        {
+            JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
+        }
+
+        // extract the reference tokens:
+        // - slash: position of the last read slash (or end of string)
+        // - start: position after the previous slash
+        for (
+            // search for the first slash after the first character
+            std::size_t slash = reference_string.find_first_of('/', 1),
+            // set the beginning of the first reference token
+            start = 1;
+            // we can stop if start == 0 (if slash == string_t::npos)
+            start != 0;
+            // set the beginning of the next reference token
+            // (will eventually be 0 if slash == string_t::npos)
+            start = (slash == string_t::npos) ? 0 : slash + 1,
+            // find next slash
+            slash = reference_string.find_first_of('/', start))
+        {
+            // use the text between the beginning of the reference token
+            // (start) and the last slash (slash).
+            auto reference_token = reference_string.substr(start, slash - start);
+
+            // check reference tokens are properly escaped
+            for (std::size_t pos = reference_token.find_first_of('~');
+                    pos != string_t::npos;
+                    pos = reference_token.find_first_of('~', pos + 1))
+            {
+                JSON_ASSERT(reference_token[pos] == '~');
+
+                // ~ must be followed by 0 or 1
+                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
+                                         (reference_token[pos + 1] != '0' &&
+                                          reference_token[pos + 1] != '1')))
+                {
+                    JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
+                }
+            }
+
+            // finally, store the reference token
+            detail::unescape(reference_token);
+            result.push_back(reference_token);
+        }
+
+        return result;
+    }
+
+  private:
+    /*!
+    @param[in] reference_string  the reference string to the current value
+    @param[in] value             the value to consider
+    @param[in,out] result        the result object to insert values to
+
+    @note Empty objects or arrays are flattened to `null`.
+    */
+    template<typename BasicJsonType>
+    static void flatten(const string_t& reference_string,
+                        const BasicJsonType& value,
+                        BasicJsonType& result)
+    {
+        switch (value.type())
+        {
+            case detail::value_t::array:
+            {
+                if (value.m_data.m_value.array->empty())
+                {
+                    // flatten empty array as null
+                    result[reference_string] = nullptr;
+                }
+                else
+                {
+                    // iterate array and use index as reference string
+                    for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)
+                    {
+                        flatten(detail::concat(reference_string, '/', std::to_string(i)),
+                                value.m_data.m_value.array->operator[](i), result);
+                    }
+                }
+                break;
+            }
+
+            case detail::value_t::object:
+            {
+                if (value.m_data.m_value.object->empty())
+                {
+                    // flatten empty object as null
+                    result[reference_string] = nullptr;
+                }
+                else
+                {
+                    // iterate object and use keys as reference string
+                    for (const auto& element : *value.m_data.m_value.object)
+                    {
+                        flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result);
+                    }
+                }
+                break;
+            }
+
+            case detail::value_t::null:
+            case detail::value_t::string:
+            case detail::value_t::boolean:
+            case detail::value_t::number_integer:
+            case detail::value_t::number_unsigned:
+            case detail::value_t::number_float:
+            case detail::value_t::binary:
+            case detail::value_t::discarded:
+            default:
+            {
+                // add primitive value with its reference string
+                result[reference_string] = value;
+                break;
+            }
+        }
+    }
+
+    /*!
+    @param[in] value  flattened JSON
+
+    @return unflattened JSON
+
+    @throw parse_error.109 if array index is not a number
+    @throw type_error.314  if value is not an object
+    @throw type_error.315  if object values are not primitive
+    @throw type_error.313  if value cannot be unflattened
+    */
+    template<typename BasicJsonType>
+    static BasicJsonType
+    unflatten(const BasicJsonType& value)
+    {
+        if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
+        {
+            JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
+        }
+
+        BasicJsonType result;
+
+        // iterate the JSON object values
+        for (const auto& element : *value.m_data.m_value.object)
+        {
+            if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
+            {
+                JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
+            }
+
+            // assign value to reference pointed to by JSON pointer; Note that if
+            // the JSON pointer is "" (i.e., points to the whole value), function
+            // get_and_create returns a reference to result itself. An assignment
+            // will then create a primitive value.
+            json_pointer(element.first).get_and_create(result) = element.second;
+        }
+
+        return result;
+    }
+
+    // can't use conversion operator because of ambiguity
+    json_pointer<string_t> convert() const&
+    {
+        json_pointer<string_t> result;
+        result.reference_tokens = reference_tokens;
+        return result;
+    }
+
+    json_pointer<string_t> convert()&&
+    {
+        json_pointer<string_t> result;
+        result.reference_tokens = std::move(reference_tokens);
+        return result;
+    }
+
+  public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+    /// @brief compares two JSON pointers for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeRhs>
+    bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept
+    {
+        return reference_tokens == rhs.reference_tokens;
+    }
+
+    /// @brief compares JSON pointer and string for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))
+    bool operator==(const string_t& rhs) const
+    {
+        return *this == json_pointer(rhs);
+    }
+
+    /// @brief 3-way compares two JSON pointers
+    template<typename RefStringTypeRhs>
+    std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD*
+    {
+        return  reference_tokens <=> rhs.reference_tokens; // *NOPAD*
+    }
+#else
+    /// @brief compares two JSON pointers for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+    /// @brief compares JSON pointer and string for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeLhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                           const StringType& rhs);
+
+    /// @brief compares string and JSON pointer for equality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+    template<typename RefStringTypeRhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator==(const StringType& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs);
+
+    /// @brief compares two JSON pointers for inequality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+    template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+    /// @brief compares JSON pointer and string for inequality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+    template<typename RefStringTypeLhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                           const StringType& rhs);
+
+    /// @brief compares string and JSON pointer for inequality
+    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+    template<typename RefStringTypeRhs, typename StringType>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator!=(const StringType& lhs,
+                           const json_pointer<RefStringTypeRhs>& rhs);
+
+    /// @brief compares two JSON pointer for less-than
+    template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+    // NOLINTNEXTLINE(readability-redundant-declaration)
+    friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+                          const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+#endif
+
+  private:
+    /// the reference tokens
+    std::vector<string_t> reference_tokens;
+};
+
+#if !JSON_HAS_THREE_WAY_COMPARISON
+// functions cannot be defined inside class due to ODR violations
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+    return lhs.reference_tokens == rhs.reference_tokens;
+}
+
+template<typename RefStringTypeLhs,
+         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+                       const StringType& rhs)
+{
+    return lhs == json_pointer<RefStringTypeLhs>(rhs);
+}
+
+template<typename RefStringTypeRhs,
+         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const StringType& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs)
+{
+    return json_pointer<RefStringTypeRhs>(lhs) == rhs;
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+    return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs,
+         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+                       const StringType& rhs)
+{
+    return !(lhs == rhs);
+}
+
+template<typename RefStringTypeRhs,
+         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const StringType& lhs,
+                       const json_pointer<RefStringTypeRhs>& rhs)
+{
+    return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+                      const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+    return lhs.reference_tokens < rhs.reference_tokens;
+}
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/json_ref.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <initializer_list>
+#include <utility>
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+class json_ref
+{
+  public:
+    using value_type = BasicJsonType;
+
+    json_ref(value_type&& value)
+        : owned_value(std::move(value))
+    {}
+
+    json_ref(const value_type& value)
+        : value_ref(&value)
+    {}
+
+    json_ref(std::initializer_list<json_ref> init)
+        : owned_value(init)
+    {}
+
+    template <
+        class... Args,
+        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >
+    json_ref(Args && ... args)
+        : owned_value(std::forward<Args>(args)...)
+    {}
+
+    // class should be movable only
+    json_ref(json_ref&&) noexcept = default;
+    json_ref(const json_ref&) = delete;
+    json_ref& operator=(const json_ref&) = delete;
+    json_ref& operator=(json_ref&&) = delete;
+    ~json_ref() = default;
+
+    value_type moved_or_copied() const
+    {
+        if (value_ref == nullptr)
+        {
+            return std::move(owned_value);
+        }
+        return *value_ref;
+    }
+
+    value_type const& operator*() const
+    {
+        return value_ref ? *value_ref : owned_value;
+    }
+
+    value_type const* operator->() const
+    {
+        return &** this;
+    }
+
+  private:
+    mutable value_type owned_value = nullptr;
+    value_type const* value_ref = nullptr;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/output/binary_writer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // reverse
+#include <array> // array
+#include <map> // map
+#include <cmath> // isnan, isinf
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstring> // memcpy
+#include <limits> // numeric_limits
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // copy
+#include <cstddef> // size_t
+#include <iterator> // back_inserter
+#include <memory> // shared_ptr, make_shared
+#include <string> // basic_string
+#include <vector> // vector
+
+#ifndef JSON_NO_IO
+    #include <ios>      // streamsize
+    #include <ostream>  // basic_ostream
+#endif  // JSON_NO_IO
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// abstract output adapter interface
+template<typename CharType> struct output_adapter_protocol
+{
+    virtual void write_character(CharType c) = 0;
+    virtual void write_characters(const CharType* s, std::size_t length) = 0;
+    virtual ~output_adapter_protocol() = default;
+
+    output_adapter_protocol() = default;
+    output_adapter_protocol(const output_adapter_protocol&) = default;
+    output_adapter_protocol(output_adapter_protocol&&) noexcept = default;
+    output_adapter_protocol& operator=(const output_adapter_protocol&) = default;
+    output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;
+};
+
+/// a type to simplify interfaces
+template<typename CharType>
+using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
+
+/// output adapter for byte vectors
+template<typename CharType, typename AllocatorType = std::allocator<CharType>>
+class output_vector_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept
+        : v(vec)
+    {}
+
+    void write_character(CharType c) override
+    {
+        v.push_back(c);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override
+    {
+        v.insert(v.end(), s, s + length);
+    }
+
+  private:
+    std::vector<CharType, AllocatorType>& v;
+};
+
+#ifndef JSON_NO_IO
+/// output adapter for output streams
+template<typename CharType>
+class output_stream_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept
+        : stream(s)
+    {}
+
+    void write_character(CharType c) override
+    {
+        stream.put(c);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override
+    {
+        stream.write(s, static_cast<std::streamsize>(length));
+    }
+
+  private:
+    std::basic_ostream<CharType>& stream;
+};
+#endif  // JSON_NO_IO
+
+/// output adapter for basic_string
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_string_adapter : public output_adapter_protocol<CharType>
+{
+  public:
+    explicit output_string_adapter(StringType& s) noexcept
+        : str(s)
+    {}
+
+    void write_character(CharType c) override
+    {
+        str.push_back(c);
+    }
+
+    JSON_HEDLEY_NON_NULL(2)
+    void write_characters(const CharType* s, std::size_t length) override
+    {
+        str.append(s, length);
+    }
+
+  private:
+    StringType& str;
+};
+
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_adapter
+{
+  public:
+    template<typename AllocatorType = std::allocator<CharType>>
+    output_adapter(std::vector<CharType, AllocatorType>& vec)
+        : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}
+
+#ifndef JSON_NO_IO
+    output_adapter(std::basic_ostream<CharType>& s)
+        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
+#endif  // JSON_NO_IO
+
+    output_adapter(StringType& s)
+        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
+
+    operator output_adapter_t<CharType>()
+    {
+        return oa;
+    }
+
+  private:
+    output_adapter_t<CharType> oa = nullptr;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////
+// binary writer //
+///////////////////
+
+/*!
+@brief serialization to CBOR and MessagePack values
+*/
+template<typename BasicJsonType, typename CharType>
+class binary_writer
+{
+    using string_t = typename BasicJsonType::string_t;
+    using binary_t = typename BasicJsonType::binary_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+
+  public:
+    /*!
+    @brief create a binary writer
+
+    @param[in] adapter  output adapter to write to
+    */
+    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
+    {
+        JSON_ASSERT(oa);
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    @pre       j.type() == value_t::object
+    */
+    void write_bson(const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::object:
+            {
+                write_bson_object(*j.m_data.m_value.object);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::array:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
+            }
+        }
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    */
+    void write_cbor(const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::null:
+            {
+                oa->write_character(to_char_type(0xF6));
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                oa->write_character(j.m_data.m_value.boolean
+                                    ? to_char_type(0xF5)
+                                    : to_char_type(0xF4));
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                if (j.m_data.m_value.number_integer >= 0)
+                {
+                    // CBOR does not differentiate between positive signed
+                    // integers and unsigned integers. Therefore, we used the
+                    // code from the value_t::number_unsigned case here.
+                    if (j.m_data.m_value.number_integer <= 0x17)
+                    {
+                        write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x18));
+                        write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x19));
+                        write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x1A));
+                        write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
+                    }
+                    else
+                    {
+                        oa->write_character(to_char_type(0x1B));
+                        write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
+                    }
+                }
+                else
+                {
+                    // The conversions below encode the sign in the first
+                    // byte, and the value is converted to a positive number.
+                    const auto positive_number = -1 - j.m_data.m_value.number_integer;
+                    if (j.m_data.m_value.number_integer >= -24)
+                    {
+                        write_number(static_cast<std::uint8_t>(0x20 + positive_number));
+                    }
+                    else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x38));
+                        write_number(static_cast<std::uint8_t>(positive_number));
+                    }
+                    else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x39));
+                        write_number(static_cast<std::uint16_t>(positive_number));
+                    }
+                    else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        oa->write_character(to_char_type(0x3A));
+                        write_number(static_cast<std::uint32_t>(positive_number));
+                    }
+                    else
+                    {
+                        oa->write_character(to_char_type(0x3B));
+                        write_number(static_cast<std::uint64_t>(positive_number));
+                    }
+                }
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                if (j.m_data.m_value.number_unsigned <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_unsigned));
+                }
+                else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x18));
+                    write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_unsigned));
+                }
+                else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x19));
+                    write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_unsigned));
+                }
+                else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x1A));
+                    write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_unsigned));
+                }
+                else
+                {
+                    oa->write_character(to_char_type(0x1B));
+                    write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
+                }
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                if (std::isnan(j.m_data.m_value.number_float))
+                {
+                    // NaN is 0xf97e00 in CBOR
+                    oa->write_character(to_char_type(0xF9));
+                    oa->write_character(to_char_type(0x7E));
+                    oa->write_character(to_char_type(0x00));
+                }
+                else if (std::isinf(j.m_data.m_value.number_float))
+                {
+                    // Infinity is 0xf97c00, -Infinity is 0xf9fc00
+                    oa->write_character(to_char_type(0xf9));
+                    oa->write_character(j.m_data.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
+                    oa->write_character(to_char_type(0x00));
+                }
+                else
+                {
+                    write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::cbor);
+                }
+                break;
+            }
+
+            case value_t::string:
+            {
+                // step 1: write control byte and the string length
+                const auto N = j.m_data.m_value.string->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0x60 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x78));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x79));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x7A));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x7B));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write the string
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
+                    j.m_data.m_value.string->size());
+                break;
+            }
+
+            case value_t::array:
+            {
+                // step 1: write control byte and the array size
+                const auto N = j.m_data.m_value.array->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0x80 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x98));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x99));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x9A));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x9B));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write each element
+                for (const auto& el : *j.m_data.m_value.array)
+                {
+                    write_cbor(el);
+                }
+                break;
+            }
+
+            case value_t::binary:
+            {
+                if (j.m_data.m_value.binary->has_subtype())
+                {
+                    if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xd8));
+                        write_number(static_cast<std::uint8_t>(j.m_data.m_value.binary->subtype()));
+                    }
+                    else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xd9));
+                        write_number(static_cast<std::uint16_t>(j.m_data.m_value.binary->subtype()));
+                    }
+                    else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xda));
+                        write_number(static_cast<std::uint32_t>(j.m_data.m_value.binary->subtype()));
+                    }
+                    else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)())
+                    {
+                        write_number(static_cast<std::uint8_t>(0xdb));
+                        write_number(static_cast<std::uint64_t>(j.m_data.m_value.binary->subtype()));
+                    }
+                }
+
+                // step 1: write control byte and the binary array size
+                const auto N = j.m_data.m_value.binary->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0x40 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x58));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x59));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x5A));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0x5B));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write each element
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
+                    N);
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                // step 1: write control byte and the object size
+                const auto N = j.m_data.m_value.object->size();
+                if (N <= 0x17)
+                {
+                    write_number(static_cast<std::uint8_t>(0xA0 + N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xB8));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xB9));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xBA));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+                // LCOV_EXCL_START
+                else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    oa->write_character(to_char_type(0xBB));
+                    write_number(static_cast<std::uint64_t>(N));
+                }
+                // LCOV_EXCL_STOP
+
+                // step 2: write each element
+                for (const auto& el : *j.m_data.m_value.object)
+                {
+                    write_cbor(el.first);
+                    write_cbor(el.second);
+                }
+                break;
+            }
+
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    */
+    void write_msgpack(const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::null: // nil
+            {
+                oa->write_character(to_char_type(0xC0));
+                break;
+            }
+
+            case value_t::boolean: // true and false
+            {
+                oa->write_character(j.m_data.m_value.boolean
+                                    ? to_char_type(0xC3)
+                                    : to_char_type(0xC2));
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                if (j.m_data.m_value.number_integer >= 0)
+                {
+                    // MessagePack does not differentiate between positive
+                    // signed integers and unsigned integers. Therefore, we used
+                    // the code from the value_t::number_unsigned case here.
+                    if (j.m_data.m_value.number_unsigned < 128)
+                    {
+                        // positive fixnum
+                        write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+                    {
+                        // uint 8
+                        oa->write_character(to_char_type(0xCC));
+                        write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+                    {
+                        // uint 16
+                        oa->write_character(to_char_type(0xCD));
+                        write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+                    {
+                        // uint 32
+                        oa->write_character(to_char_type(0xCE));
+                        write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+                    {
+                        // uint 64
+                        oa->write_character(to_char_type(0xCF));
+                        write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
+                    }
+                }
+                else
+                {
+                    if (j.m_data.m_value.number_integer >= -32)
+                    {
+                        // negative fixnum
+                        write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
+                             j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+                    {
+                        // int 8
+                        oa->write_character(to_char_type(0xD0));
+                        write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
+                             j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+                    {
+                        // int 16
+                        oa->write_character(to_char_type(0xD1));
+                        write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
+                             j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+                    {
+                        // int 32
+                        oa->write_character(to_char_type(0xD2));
+                        write_number(static_cast<std::int32_t>(j.m_data.m_value.number_integer));
+                    }
+                    else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&
+                             j.m_data.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+                    {
+                        // int 64
+                        oa->write_character(to_char_type(0xD3));
+                        write_number(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
+                    }
+                }
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                if (j.m_data.m_value.number_unsigned < 128)
+                {
+                    // positive fixnum
+                    write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+                }
+                else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    // uint 8
+                    oa->write_character(to_char_type(0xCC));
+                    write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+                }
+                else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // uint 16
+                    oa->write_character(to_char_type(0xCD));
+                    write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer));
+                }
+                else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // uint 32
+                    oa->write_character(to_char_type(0xCE));
+                    write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
+                }
+                else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    // uint 64
+                    oa->write_character(to_char_type(0xCF));
+                    write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
+                }
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::msgpack);
+                break;
+            }
+
+            case value_t::string:
+            {
+                // step 1: write control byte and the string length
+                const auto N = j.m_data.m_value.string->size();
+                if (N <= 31)
+                {
+                    // fixstr
+                    write_number(static_cast<std::uint8_t>(0xA0 | N));
+                }
+                else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    // str 8
+                    oa->write_character(to_char_type(0xD9));
+                    write_number(static_cast<std::uint8_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // str 16
+                    oa->write_character(to_char_type(0xDA));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // str 32
+                    oa->write_character(to_char_type(0xDB));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 2: write the string
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
+                    j.m_data.m_value.string->size());
+                break;
+            }
+
+            case value_t::array:
+            {
+                // step 1: write control byte and the array size
+                const auto N = j.m_data.m_value.array->size();
+                if (N <= 15)
+                {
+                    // fixarray
+                    write_number(static_cast<std::uint8_t>(0x90 | N));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // array 16
+                    oa->write_character(to_char_type(0xDC));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // array 32
+                    oa->write_character(to_char_type(0xDD));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 2: write each element
+                for (const auto& el : *j.m_data.m_value.array)
+                {
+                    write_msgpack(el);
+                }
+                break;
+            }
+
+            case value_t::binary:
+            {
+                // step 0: determine if the binary type has a set subtype to
+                // determine whether or not to use the ext or fixext types
+                const bool use_ext = j.m_data.m_value.binary->has_subtype();
+
+                // step 1: write control byte and the byte string length
+                const auto N = j.m_data.m_value.binary->size();
+                if (N <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    std::uint8_t output_type{};
+                    bool fixed = true;
+                    if (use_ext)
+                    {
+                        switch (N)
+                        {
+                            case 1:
+                                output_type = 0xD4; // fixext 1
+                                break;
+                            case 2:
+                                output_type = 0xD5; // fixext 2
+                                break;
+                            case 4:
+                                output_type = 0xD6; // fixext 4
+                                break;
+                            case 8:
+                                output_type = 0xD7; // fixext 8
+                                break;
+                            case 16:
+                                output_type = 0xD8; // fixext 16
+                                break;
+                            default:
+                                output_type = 0xC7; // ext 8
+                                fixed = false;
+                                break;
+                        }
+
+                    }
+                    else
+                    {
+                        output_type = 0xC4; // bin 8
+                        fixed = false;
+                    }
+
+                    oa->write_character(to_char_type(output_type));
+                    if (!fixed)
+                    {
+                        write_number(static_cast<std::uint8_t>(N));
+                    }
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    const std::uint8_t output_type = use_ext
+                                                     ? 0xC8 // ext 16
+                                                     : 0xC5; // bin 16
+
+                    oa->write_character(to_char_type(output_type));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    const std::uint8_t output_type = use_ext
+                                                     ? 0xC9 // ext 32
+                                                     : 0xC6; // bin 32
+
+                    oa->write_character(to_char_type(output_type));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 1.5: if this is an ext type, write the subtype
+                if (use_ext)
+                {
+                    write_number(static_cast<std::int8_t>(j.m_data.m_value.binary->subtype()));
+                }
+
+                // step 2: write the byte string
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
+                    N);
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                // step 1: write control byte and the object size
+                const auto N = j.m_data.m_value.object->size();
+                if (N <= 15)
+                {
+                    // fixmap
+                    write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));
+                }
+                else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+                {
+                    // map 16
+                    oa->write_character(to_char_type(0xDE));
+                    write_number(static_cast<std::uint16_t>(N));
+                }
+                else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+                {
+                    // map 32
+                    oa->write_character(to_char_type(0xDF));
+                    write_number(static_cast<std::uint32_t>(N));
+                }
+
+                // step 2: write each element
+                for (const auto& el : *j.m_data.m_value.object)
+                {
+                    write_msgpack(el.first);
+                    write_msgpack(el.second);
+                }
+                break;
+            }
+
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+    /*!
+    @param[in] j  JSON value to serialize
+    @param[in] use_count   whether to use '#' prefixes (optimized format)
+    @param[in] use_type    whether to use '$' prefixes (optimized format)
+    @param[in] add_prefix  whether prefixes need to be used for this value
+    @param[in] use_bjdata  whether write in BJData format, default is false
+    */
+    void write_ubjson(const BasicJsonType& j, const bool use_count,
+                      const bool use_type, const bool add_prefix = true,
+                      const bool use_bjdata = false)
+    {
+        switch (j.type())
+        {
+            case value_t::null:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('Z'));
+                }
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(j.m_data.m_value.boolean
+                                        ? to_char_type('T')
+                                        : to_char_type('F'));
+                }
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                write_number_with_ubjson_prefix(j.m_data.m_value.number_integer, add_prefix, use_bjdata);
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                write_number_with_ubjson_prefix(j.m_data.m_value.number_unsigned, add_prefix, use_bjdata);
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                write_number_with_ubjson_prefix(j.m_data.m_value.number_float, add_prefix, use_bjdata);
+                break;
+            }
+
+            case value_t::string:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('S'));
+                }
+                write_number_with_ubjson_prefix(j.m_data.m_value.string->size(), true, use_bjdata);
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
+                    j.m_data.m_value.string->size());
+                break;
+            }
+
+            case value_t::array:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('['));
+                }
+
+                bool prefix_required = true;
+                if (use_type && !j.m_data.m_value.array->empty())
+                {
+                    JSON_ASSERT(use_count);
+                    const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
+                                                         [this, first_prefix, use_bjdata](const BasicJsonType & v)
+                    {
+                        return ubjson_prefix(v, use_bjdata) == first_prefix;
+                    });
+
+                    std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+                    if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+                    {
+                        prefix_required = false;
+                        oa->write_character(to_char_type('$'));
+                        oa->write_character(first_prefix);
+                    }
+                }
+
+                if (use_count)
+                {
+                    oa->write_character(to_char_type('#'));
+                    write_number_with_ubjson_prefix(j.m_data.m_value.array->size(), true, use_bjdata);
+                }
+
+                for (const auto& el : *j.m_data.m_value.array)
+                {
+                    write_ubjson(el, use_count, use_type, prefix_required, use_bjdata);
+                }
+
+                if (!use_count)
+                {
+                    oa->write_character(to_char_type(']'));
+                }
+
+                break;
+            }
+
+            case value_t::binary:
+            {
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('['));
+                }
+
+                if (use_type && !j.m_data.m_value.binary->empty())
+                {
+                    JSON_ASSERT(use_count);
+                    oa->write_character(to_char_type('$'));
+                    oa->write_character('U');
+                }
+
+                if (use_count)
+                {
+                    oa->write_character(to_char_type('#'));
+                    write_number_with_ubjson_prefix(j.m_data.m_value.binary->size(), true, use_bjdata);
+                }
+
+                if (use_type)
+                {
+                    oa->write_characters(
+                        reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
+                        j.m_data.m_value.binary->size());
+                }
+                else
+                {
+                    for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i)
+                    {
+                        oa->write_character(to_char_type('U'));
+                        oa->write_character(j.m_data.m_value.binary->data()[i]);
+                    }
+                }
+
+                if (!use_count)
+                {
+                    oa->write_character(to_char_type(']'));
+                }
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                if (use_bjdata && j.m_data.m_value.object->size() == 3 && j.m_data.m_value.object->find("_ArrayType_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArraySize_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArrayData_") != j.m_data.m_value.object->end())
+                {
+                    if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type))  // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)
+                    {
+                        break;
+                    }
+                }
+
+                if (add_prefix)
+                {
+                    oa->write_character(to_char_type('{'));
+                }
+
+                bool prefix_required = true;
+                if (use_type && !j.m_data.m_value.object->empty())
+                {
+                    JSON_ASSERT(use_count);
+                    const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+                    const bool same_prefix = std::all_of(j.begin(), j.end(),
+                                                         [this, first_prefix, use_bjdata](const BasicJsonType & v)
+                    {
+                        return ubjson_prefix(v, use_bjdata) == first_prefix;
+                    });
+
+                    std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+                    if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+                    {
+                        prefix_required = false;
+                        oa->write_character(to_char_type('$'));
+                        oa->write_character(first_prefix);
+                    }
+                }
+
+                if (use_count)
+                {
+                    oa->write_character(to_char_type('#'));
+                    write_number_with_ubjson_prefix(j.m_data.m_value.object->size(), true, use_bjdata);
+                }
+
+                for (const auto& el : *j.m_data.m_value.object)
+                {
+                    write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
+                    oa->write_characters(
+                        reinterpret_cast<const CharType*>(el.first.c_str()),
+                        el.first.size());
+                    write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata);
+                }
+
+                if (!use_count)
+                {
+                    oa->write_character(to_char_type('}'));
+                }
+
+                break;
+            }
+
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+  private:
+    //////////
+    // BSON //
+    //////////
+
+    /*!
+    @return The size of a BSON document entry header, including the id marker
+            and the entry name size (and its null-terminator).
+    */
+    static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
+    {
+        const auto it = name.find(static_cast<typename string_t::value_type>(0));
+        if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
+        {
+            JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
+            static_cast<void>(j);
+        }
+
+        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
+    }
+
+    /*!
+    @brief Writes the given @a element_type and @a name to the output adapter
+    */
+    void write_bson_entry_header(const string_t& name,
+                                 const std::uint8_t element_type)
+    {
+        oa->write_character(to_char_type(element_type)); // boolean
+        oa->write_characters(
+            reinterpret_cast<const CharType*>(name.c_str()),
+            name.size() + 1u);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and boolean value @a value
+    */
+    void write_bson_boolean(const string_t& name,
+                            const bool value)
+    {
+        write_bson_entry_header(name, 0x08);
+        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and double value @a value
+    */
+    void write_bson_double(const string_t& name,
+                           const double value)
+    {
+        write_bson_entry_header(name, 0x01);
+        write_number<double>(value, true);
+    }
+
+    /*!
+    @return The size of the BSON-encoded string in @a value
+    */
+    static std::size_t calc_bson_string_size(const string_t& value)
+    {
+        return sizeof(std::int32_t) + value.size() + 1ul;
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and string value @a value
+    */
+    void write_bson_string(const string_t& name,
+                           const string_t& value)
+    {
+        write_bson_entry_header(name, 0x02);
+
+        write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);
+        oa->write_characters(
+            reinterpret_cast<const CharType*>(value.c_str()),
+            value.size() + 1);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and null value
+    */
+    void write_bson_null(const string_t& name)
+    {
+        write_bson_entry_header(name, 0x0A);
+    }
+
+    /*!
+    @return The size of the BSON-encoded integer @a value
+    */
+    static std::size_t calc_bson_integer_size(const std::int64_t value)
+    {
+        return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()
+               ? sizeof(std::int32_t)
+               : sizeof(std::int64_t);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and integer @a value
+    */
+    void write_bson_integer(const string_t& name,
+                            const std::int64_t value)
+    {
+        if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())
+        {
+            write_bson_entry_header(name, 0x10); // int32
+            write_number<std::int32_t>(static_cast<std::int32_t>(value), true);
+        }
+        else
+        {
+            write_bson_entry_header(name, 0x12); // int64
+            write_number<std::int64_t>(static_cast<std::int64_t>(value), true);
+        }
+    }
+
+    /*!
+    @return The size of the BSON-encoded unsigned integer in @a j
+    */
+    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
+    {
+        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+               ? sizeof(std::int32_t)
+               : sizeof(std::int64_t);
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and unsigned @a value
+    */
+    void write_bson_unsigned(const string_t& name,
+                             const BasicJsonType& j)
+    {
+        if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+        {
+            write_bson_entry_header(name, 0x10 /* int32 */);
+            write_number<std::int32_t>(static_cast<std::int32_t>(j.m_data.m_value.number_unsigned), true);
+        }
+        else if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+        {
+            write_bson_entry_header(name, 0x12 /* int64 */);
+            write_number<std::int64_t>(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned), true);
+        }
+        else
+        {
+            JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_data.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j));
+        }
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and object @a value
+    */
+    void write_bson_object_entry(const string_t& name,
+                                 const typename BasicJsonType::object_t& value)
+    {
+        write_bson_entry_header(name, 0x03); // object
+        write_bson_object(value);
+    }
+
+    /*!
+    @return The size of the BSON-encoded array @a value
+    */
+    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
+    {
+        std::size_t array_index = 0ul;
+
+        const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)
+        {
+            return result + calc_bson_element_size(std::to_string(array_index++), el);
+        });
+
+        return sizeof(std::int32_t) + embedded_document_size + 1ul;
+    }
+
+    /*!
+    @return The size of the BSON-encoded binary array @a value
+    */
+    static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)
+    {
+        return sizeof(std::int32_t) + value.size() + 1ul;
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and array @a value
+    */
+    void write_bson_array(const string_t& name,
+                          const typename BasicJsonType::array_t& value)
+    {
+        write_bson_entry_header(name, 0x04); // array
+        write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_array_size(value)), true);
+
+        std::size_t array_index = 0ul;
+
+        for (const auto& el : value)
+        {
+            write_bson_element(std::to_string(array_index++), el);
+        }
+
+        oa->write_character(to_char_type(0x00));
+    }
+
+    /*!
+    @brief Writes a BSON element with key @a name and binary value @a value
+    */
+    void write_bson_binary(const string_t& name,
+                           const binary_t& value)
+    {
+        write_bson_entry_header(name, 0x05);
+
+        write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true);
+        write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
+
+        oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
+    }
+
+    /*!
+    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name
+    @return The calculated size for the BSON document entry for @a j with the given @a name.
+    */
+    static std::size_t calc_bson_element_size(const string_t& name,
+            const BasicJsonType& j)
+    {
+        const auto header_size = calc_bson_entry_header_size(name, j);
+        switch (j.type())
+        {
+            case value_t::object:
+                return header_size + calc_bson_object_size(*j.m_data.m_value.object);
+
+            case value_t::array:
+                return header_size + calc_bson_array_size(*j.m_data.m_value.array);
+
+            case value_t::binary:
+                return header_size + calc_bson_binary_size(*j.m_data.m_value.binary);
+
+            case value_t::boolean:
+                return header_size + 1ul;
+
+            case value_t::number_float:
+                return header_size + 8ul;
+
+            case value_t::number_integer:
+                return header_size + calc_bson_integer_size(j.m_data.m_value.number_integer);
+
+            case value_t::number_unsigned:
+                return header_size + calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
+
+            case value_t::string:
+                return header_size + calc_bson_string_size(*j.m_data.m_value.string);
+
+            case value_t::null:
+                return header_size + 0ul;
+
+            // LCOV_EXCL_START
+            case value_t::discarded:
+            default:
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+                return 0ul;
+                // LCOV_EXCL_STOP
+        }
+    }
+
+    /*!
+    @brief Serializes the JSON value @a j to BSON and associates it with the
+           key @a name.
+    @param name The name to associate with the JSON entity @a j within the
+                current BSON document
+    */
+    void write_bson_element(const string_t& name,
+                            const BasicJsonType& j)
+    {
+        switch (j.type())
+        {
+            case value_t::object:
+                return write_bson_object_entry(name, *j.m_data.m_value.object);
+
+            case value_t::array:
+                return write_bson_array(name, *j.m_data.m_value.array);
+
+            case value_t::binary:
+                return write_bson_binary(name, *j.m_data.m_value.binary);
+
+            case value_t::boolean:
+                return write_bson_boolean(name, j.m_data.m_value.boolean);
+
+            case value_t::number_float:
+                return write_bson_double(name, j.m_data.m_value.number_float);
+
+            case value_t::number_integer:
+                return write_bson_integer(name, j.m_data.m_value.number_integer);
+
+            case value_t::number_unsigned:
+                return write_bson_unsigned(name, j);
+
+            case value_t::string:
+                return write_bson_string(name, *j.m_data.m_value.string);
+
+            case value_t::null:
+                return write_bson_null(name);
+
+            // LCOV_EXCL_START
+            case value_t::discarded:
+            default:
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+                return;
+                // LCOV_EXCL_STOP
+        }
+    }
+
+    /*!
+    @brief Calculates the size of the BSON serialization of the given
+           JSON-object @a j.
+    @param[in] value  JSON value to serialize
+    @pre       value.type() == value_t::object
+    */
+    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
+    {
+        const std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0),
+                                          [](size_t result, const typename BasicJsonType::object_t::value_type & el)
+        {
+            return result += calc_bson_element_size(el.first, el.second);
+        });
+
+        return sizeof(std::int32_t) + document_size + 1ul;
+    }
+
+    /*!
+    @param[in] value  JSON value to serialize
+    @pre       value.type() == value_t::object
+    */
+    void write_bson_object(const typename BasicJsonType::object_t& value)
+    {
+        write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_object_size(value)), true);
+
+        for (const auto& el : value)
+        {
+            write_bson_element(el.first, el.second);
+        }
+
+        oa->write_character(to_char_type(0x00));
+    }
+
+    //////////
+    // CBOR //
+    //////////
+
+    static constexpr CharType get_cbor_float_prefix(float /*unused*/)
+    {
+        return to_char_type(0xFA);  // Single-Precision Float
+    }
+
+    static constexpr CharType get_cbor_float_prefix(double /*unused*/)
+    {
+        return to_char_type(0xFB);  // Double-Precision Float
+    }
+
+    /////////////
+    // MsgPack //
+    /////////////
+
+    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
+    {
+        return to_char_type(0xCA);  // float 32
+    }
+
+    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
+    {
+        return to_char_type(0xCB);  // float 64
+    }
+
+    ////////////
+    // UBJSON //
+    ////////////
+
+    // UBJSON: write number (floating point)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_floating_point<NumberType>::value, int>::type = 0>
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix,
+                                         const bool use_bjdata)
+    {
+        if (add_prefix)
+        {
+            oa->write_character(get_ubjson_float_prefix(n));
+        }
+        write_number(n, use_bjdata);
+    }
+
+    // UBJSON: write number (unsigned integer)
+    template<typename NumberType, typename std::enable_if<
+                 std::is_unsigned<NumberType>::value, int>::type = 0>
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix,
+                                         const bool use_bjdata)
+    {
+        if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('i'));  // int8
+            }
+            write_number(static_cast<std::uint8_t>(n), use_bjdata);
+        }
+        else if (n <= (std::numeric_limits<std::uint8_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('U'));  // uint8
+            }
+            write_number(static_cast<std::uint8_t>(n), use_bjdata);
+        }
+        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('I'));  // int16
+            }
+            write_number(static_cast<std::int16_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint16_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('u'));  // uint16 - bjdata only
+            }
+            write_number(static_cast<std::uint16_t>(n), use_bjdata);
+        }
+        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('l'));  // int32
+            }
+            write_number(static_cast<std::int32_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('m'));  // uint32 - bjdata only
+            }
+            write_number(static_cast<std::uint32_t>(n), use_bjdata);
+        }
+        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('L'));  // int64
+            }
+            write_number(static_cast<std::int64_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && n <= (std::numeric_limits<uint64_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('M'));  // uint64 - bjdata only
+            }
+            write_number(static_cast<std::uint64_t>(n), use_bjdata);
+        }
+        else
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('H'));  // high-precision number
+            }
+
+            const auto number = BasicJsonType(n).dump();
+            write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+            for (std::size_t i = 0; i < number.size(); ++i)
+            {
+                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+            }
+        }
+    }
+
+    // UBJSON: write number (signed integer)
+    template < typename NumberType, typename std::enable_if <
+                   std::is_signed<NumberType>::value&&
+                   !std::is_floating_point<NumberType>::value, int >::type = 0 >
+    void write_number_with_ubjson_prefix(const NumberType n,
+                                         const bool add_prefix,
+                                         const bool use_bjdata)
+    {
+        if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('i'));  // int8
+            }
+            write_number(static_cast<std::int8_t>(n), use_bjdata);
+        }
+        else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('U'));  // uint8
+            }
+            write_number(static_cast<std::uint8_t>(n), use_bjdata);
+        }
+        else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('I'));  // int16
+            }
+            write_number(static_cast<std::int16_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::max)())))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('u'));  // uint16 - bjdata only
+            }
+            write_number(static_cast<uint16_t>(n), use_bjdata);
+        }
+        else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('l'));  // int32
+            }
+            write_number(static_cast<std::int32_t>(n), use_bjdata);
+        }
+        else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::max)())))
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('m'));  // uint32 - bjdata only
+            }
+            write_number(static_cast<uint32_t>(n), use_bjdata);
+        }
+        else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('L'));  // int64
+            }
+            write_number(static_cast<std::int64_t>(n), use_bjdata);
+        }
+        // LCOV_EXCL_START
+        else
+        {
+            if (add_prefix)
+            {
+                oa->write_character(to_char_type('H'));  // high-precision number
+            }
+
+            const auto number = BasicJsonType(n).dump();
+            write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+            for (std::size_t i = 0; i < number.size(); ++i)
+            {
+                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+            }
+        }
+        // LCOV_EXCL_STOP
+    }
+
+    /*!
+    @brief determine the type prefix of container values
+    */
+    CharType ubjson_prefix(const BasicJsonType& j, const bool use_bjdata) const noexcept
+    {
+        switch (j.type())
+        {
+            case value_t::null:
+                return 'Z';
+
+            case value_t::boolean:
+                return j.m_data.m_value.boolean ? 'T' : 'F';
+
+            case value_t::number_integer:
+            {
+                if ((std::numeric_limits<std::int8_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+                {
+                    return 'i';
+                }
+                if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+                {
+                    return 'U';
+                }
+                if ((std::numeric_limits<std::int16_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+                {
+                    return 'I';
+                }
+                if (use_bjdata && ((std::numeric_limits<std::uint16_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()))
+                {
+                    return 'u';
+                }
+                if ((std::numeric_limits<std::int32_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+                {
+                    return 'l';
+                }
+                if (use_bjdata && ((std::numeric_limits<std::uint32_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()))
+                {
+                    return 'm';
+                }
+                if ((std::numeric_limits<std::int64_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+                {
+                    return 'L';
+                }
+                // anything else is treated as high-precision number
+                return 'H'; // LCOV_EXCL_LINE
+            }
+
+            case value_t::number_unsigned:
+            {
+                if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+                {
+                    return 'i';
+                }
+                if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))
+                {
+                    return 'U';
+                }
+                if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+                {
+                    return 'I';
+                }
+                if (use_bjdata && j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)()))
+                {
+                    return 'u';
+                }
+                if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+                {
+                    return 'l';
+                }
+                if (use_bjdata && j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)()))
+                {
+                    return 'm';
+                }
+                if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+                {
+                    return 'L';
+                }
+                if (use_bjdata && j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+                {
+                    return 'M';
+                }
+                // anything else is treated as high-precision number
+                return 'H'; // LCOV_EXCL_LINE
+            }
+
+            case value_t::number_float:
+                return get_ubjson_float_prefix(j.m_data.m_value.number_float);
+
+            case value_t::string:
+                return 'S';
+
+            case value_t::array: // fallthrough
+            case value_t::binary:
+                return '[';
+
+            case value_t::object:
+                return '{';
+
+            case value_t::discarded:
+            default:  // discarded values
+                return 'N';
+        }
+    }
+
+    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
+    {
+        return 'd';  // float 32
+    }
+
+    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
+    {
+        return 'D';  // float 64
+    }
+
+    /*!
+    @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
+    */
+    bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type)
+    {
+        std::map<string_t, CharType> bjdtype = {{"uint8", 'U'},  {"int8", 'i'},  {"uint16", 'u'}, {"int16", 'I'},
+            {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'}
+        };
+
+        string_t key = "_ArrayType_";
+        auto it = bjdtype.find(static_cast<string_t>(value.at(key)));
+        if (it == bjdtype.end())
+        {
+            return true;
+        }
+        CharType dtype = it->second;
+
+        key = "_ArraySize_";
+        std::size_t len = (value.at(key).empty() ? 0 : 1);
+        for (const auto& el : value.at(key))
+        {
+            len *= static_cast<std::size_t>(el.m_data.m_value.number_unsigned);
+        }
+
+        key = "_ArrayData_";
+        if (value.at(key).size() != len)
+        {
+            return true;
+        }
+
+        oa->write_character('[');
+        oa->write_character('$');
+        oa->write_character(dtype);
+        oa->write_character('#');
+
+        key = "_ArraySize_";
+        write_ubjson(value.at(key), use_count, use_type, true,  true);
+
+        key = "_ArrayData_";
+        if (dtype == 'U' || dtype == 'C')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint8_t>(el.m_data.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'i')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int8_t>(el.m_data.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'u')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint16_t>(el.m_data.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'I')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int16_t>(el.m_data.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'm')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint32_t>(el.m_data.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'l')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int32_t>(el.m_data.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'M')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::uint64_t>(el.m_data.m_value.number_unsigned), true);
+            }
+        }
+        else if (dtype == 'L')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<std::int64_t>(el.m_data.m_value.number_integer), true);
+            }
+        }
+        else if (dtype == 'd')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<float>(el.m_data.m_value.number_float), true);
+            }
+        }
+        else if (dtype == 'D')
+        {
+            for (const auto& el : value.at(key))
+            {
+                write_number(static_cast<double>(el.m_data.m_value.number_float), true);
+            }
+        }
+        return false;
+    }
+
+    ///////////////////////
+    // Utility functions //
+    ///////////////////////
+
+    /*
+    @brief write a number to output input
+    @param[in] n number of type @a NumberType
+    @param[in] OutputIsLittleEndian Set to true if output data is
+                                 required to be little endian
+    @tparam NumberType the type of the number
+
+    @note This function needs to respect the system's endianness, because bytes
+          in CBOR, MessagePack, and UBJSON are stored in network order (big
+          endian) and therefore need reordering on little endian systems.
+          On the other hand, BSON and BJData use little endian and should reorder
+          on big endian systems.
+    */
+    template<typename NumberType>
+    void write_number(const NumberType n, const bool OutputIsLittleEndian = false)
+    {
+        // step 1: write number to array of length NumberType
+        std::array<CharType, sizeof(NumberType)> vec{};
+        std::memcpy(vec.data(), &n, sizeof(NumberType));
+
+        // step 2: write array to output (with possible reordering)
+        if (is_little_endian != OutputIsLittleEndian)
+        {
+            // reverse byte order prior to conversion if necessary
+            std::reverse(vec.begin(), vec.end());
+        }
+
+        oa->write_characters(vec.data(), sizeof(NumberType));
+    }
+
+    void write_compact_float(const number_float_t n, detail::input_format_t format)
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
+                static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
+                static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))
+        {
+            oa->write_character(format == detail::input_format_t::cbor
+                                ? get_cbor_float_prefix(static_cast<float>(n))
+                                : get_msgpack_float_prefix(static_cast<float>(n)));
+            write_number(static_cast<float>(n));
+        }
+        else
+        {
+            oa->write_character(format == detail::input_format_t::cbor
+                                ? get_cbor_float_prefix(n)
+                                : get_msgpack_float_prefix(n));
+            write_number(n);
+        }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+    }
+
+  public:
+    // The following to_char_type functions are implement the conversion
+    // between uint8_t and CharType. In case CharType is not unsigned,
+    // such a conversion is required to allow values greater than 128.
+    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.
+    template < typename C = CharType,
+               enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >
+    static constexpr CharType to_char_type(std::uint8_t x) noexcept
+    {
+        return *reinterpret_cast<char*>(&x);
+    }
+
+    template < typename C = CharType,
+               enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >
+    static CharType to_char_type(std::uint8_t x) noexcept
+    {
+        static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
+        static_assert(std::is_trivial<CharType>::value, "CharType must be trivial");
+        CharType result;
+        std::memcpy(&result, &x, sizeof(x));
+        return result;
+    }
+
+    template<typename C = CharType,
+             enable_if_t<std::is_unsigned<C>::value>* = nullptr>
+    static constexpr CharType to_char_type(std::uint8_t x) noexcept
+    {
+        return x;
+    }
+
+    template < typename InputCharType, typename C = CharType,
+               enable_if_t <
+                   std::is_signed<C>::value &&
+                   std::is_signed<char>::value &&
+                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value
+                   > * = nullptr >
+    static constexpr CharType to_char_type(InputCharType x) noexcept
+    {
+        return x;
+    }
+
+  private:
+    /// whether we can assume little endianness
+    const bool is_little_endian = little_endianness();
+
+    /// the output
+    output_adapter_t<CharType> oa = nullptr;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+// #include <nlohmann/detail/output/serializer.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2008-2009 Björn Hoehrmann <bjoern@hoehrmann.de>
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // reverse, remove, fill, find, none_of
+#include <array> // array
+#include <clocale> // localeconv, lconv
+#include <cmath> // labs, isfinite, isnan, signbit
+#include <cstddef> // size_t, ptrdiff_t
+#include <cstdint> // uint8_t
+#include <cstdio> // snprintf
+#include <limits> // numeric_limits
+#include <string> // string, char_traits
+#include <iomanip> // setfill, setw
+#include <type_traits> // is_same
+#include <utility> // move
+
+// #include <nlohmann/detail/conversions/to_chars.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cmath>   // signbit, isfinite
+#include <cstdint> // intN_t, uintN_t
+#include <cstring> // memcpy, memmove
+#include <limits> // numeric_limits
+#include <type_traits> // conditional
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief implements the Grisu2 algorithm for binary to decimal floating-point
+conversion.
+
+This implementation is a slightly modified version of the reference
+implementation which may be obtained from
+http://florian.loitsch.com/publications (bench.tar.gz).
+
+The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
+
+For a detailed description of the algorithm see:
+
+[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
+    Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
+    Language Design and Implementation, PLDI 2010
+[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
+    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
+    Design and Implementation, PLDI 1996
+*/
+namespace dtoa_impl
+{
+
+template<typename Target, typename Source>
+Target reinterpret_bits(const Source source)
+{
+    static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
+
+    Target target;
+    std::memcpy(&target, &source, sizeof(Source));
+    return target;
+}
+
+struct diyfp // f * 2^e
+{
+    static constexpr int kPrecision = 64; // = q
+
+    std::uint64_t f = 0;
+    int e = 0;
+
+    constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
+
+    /*!
+    @brief returns x - y
+    @pre x.e == y.e and x.f >= y.f
+    */
+    static diyfp sub(const diyfp& x, const diyfp& y) noexcept
+    {
+        JSON_ASSERT(x.e == y.e);
+        JSON_ASSERT(x.f >= y.f);
+
+        return {x.f - y.f, x.e};
+    }
+
+    /*!
+    @brief returns x * y
+    @note The result is rounded. (Only the upper q bits are returned.)
+    */
+    static diyfp mul(const diyfp& x, const diyfp& y) noexcept
+    {
+        static_assert(kPrecision == 64, "internal error");
+
+        // Computes:
+        //  f = round((x.f * y.f) / 2^q)
+        //  e = x.e + y.e + q
+
+        // Emulate the 64-bit * 64-bit multiplication:
+        //
+        // p = u * v
+        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)
+        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )
+        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )
+        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )
+        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)
+        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )
+        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )
+        //
+        // (Since Q might be larger than 2^32 - 1)
+        //
+        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)
+        //
+        // (Q_hi + H does not overflow a 64-bit int)
+        //
+        //   = p_lo + 2^64 p_hi
+
+        const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;
+        const std::uint64_t u_hi = x.f >> 32u;
+        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;
+        const std::uint64_t v_hi = y.f >> 32u;
+
+        const std::uint64_t p0 = u_lo * v_lo;
+        const std::uint64_t p1 = u_lo * v_hi;
+        const std::uint64_t p2 = u_hi * v_lo;
+        const std::uint64_t p3 = u_hi * v_hi;
+
+        const std::uint64_t p0_hi = p0 >> 32u;
+        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;
+        const std::uint64_t p1_hi = p1 >> 32u;
+        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;
+        const std::uint64_t p2_hi = p2 >> 32u;
+
+        std::uint64_t Q = p0_hi + p1_lo + p2_lo;
+
+        // The full product might now be computed as
+        //
+        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)
+        // p_lo = p0_lo + (Q << 32)
+        //
+        // But in this particular case here, the full p_lo is not required.
+        // Effectively we only need to add the highest bit in p_lo to p_hi (and
+        // Q_hi + 1 does not overflow).
+
+        Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up
+
+        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);
+
+        return {h, x.e + y.e + 64};
+    }
+
+    /*!
+    @brief normalize x such that the significand is >= 2^(q-1)
+    @pre x.f != 0
+    */
+    static diyfp normalize(diyfp x) noexcept
+    {
+        JSON_ASSERT(x.f != 0);
+
+        while ((x.f >> 63u) == 0)
+        {
+            x.f <<= 1u;
+            x.e--;
+        }
+
+        return x;
+    }
+
+    /*!
+    @brief normalize x such that the result has the exponent E
+    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.
+    */
+    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
+    {
+        const int delta = x.e - target_exponent;
+
+        JSON_ASSERT(delta >= 0);
+        JSON_ASSERT(((x.f << delta) >> delta) == x.f);
+
+        return {x.f << delta, target_exponent};
+    }
+};
+
+struct boundaries
+{
+    diyfp w;
+    diyfp minus;
+    diyfp plus;
+};
+
+/*!
+Compute the (normalized) diyfp representing the input number 'value' and its
+boundaries.
+
+@pre value must be finite and positive
+*/
+template<typename FloatType>
+boundaries compute_boundaries(FloatType value)
+{
+    JSON_ASSERT(std::isfinite(value));
+    JSON_ASSERT(value > 0);
+
+    // Convert the IEEE representation into a diyfp.
+    //
+    // If v is denormal:
+    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))
+    // If v is normalized:
+    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))
+
+    static_assert(std::numeric_limits<FloatType>::is_iec559,
+                  "internal error: dtoa_short requires an IEEE-754 floating-point implementation");
+
+    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)
+    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);
+    constexpr int      kMinExp    = 1 - kBias;
+    constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)
+
+    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;
+
+    const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));
+    const std::uint64_t E = bits >> (kPrecision - 1);
+    const std::uint64_t F = bits & (kHiddenBit - 1);
+
+    const bool is_denormal = E == 0;
+    const diyfp v = is_denormal
+                    ? diyfp(F, kMinExp)
+                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
+
+    // Compute the boundaries m- and m+ of the floating-point value
+    // v = f * 2^e.
+    //
+    // Determine v- and v+, the floating-point predecessor and successor if v,
+    // respectively.
+    //
+    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)
+    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)
+    //
+    //      v+ = v + 2^e
+    //
+    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_
+    // between m- and m+ round to v, regardless of how the input rounding
+    // algorithm breaks ties.
+    //
+    //      ---+-------------+-------------+-------------+-------------+---  (A)
+    //         v-            m-            v             m+            v+
+    //
+    //      -----------------+------+------+-------------+-------------+---  (B)
+    //                       v-     m-     v             m+            v+
+
+    const bool lower_boundary_is_closer = F == 0 && E > 1;
+    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
+    const diyfp m_minus = lower_boundary_is_closer
+                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)
+                          : diyfp(2 * v.f - 1, v.e - 1); // (A)
+
+    // Determine the normalized w+ = m+.
+    const diyfp w_plus = diyfp::normalize(m_plus);
+
+    // Determine w- = m- such that e_(w-) = e_(w+).
+    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);
+
+    return {diyfp::normalize(v), w_minus, w_plus};
+}
+
+// Given normalized diyfp w, Grisu needs to find a (normalized) cached
+// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies
+// within a certain range [alpha, gamma] (Definition 3.2 from [1])
+//
+//      alpha <= e = e_c + e_w + q <= gamma
+//
+// or
+//
+//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q
+//                          <= f_c * f_w * 2^gamma
+//
+// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies
+//
+//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma
+//
+// or
+//
+//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)
+//
+// The choice of (alpha,gamma) determines the size of the table and the form of
+// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well
+// in practice:
+//
+// The idea is to cut the number c * w = f * 2^e into two parts, which can be
+// processed independently: An integral part p1, and a fractional part p2:
+//
+//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e
+//              = (f div 2^-e) + (f mod 2^-e) * 2^e
+//              = p1 + p2 * 2^e
+//
+// The conversion of p1 into decimal form requires a series of divisions and
+// modulos by (a power of) 10. These operations are faster for 32-bit than for
+// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be
+// achieved by choosing
+//
+//      -e >= 32   or   e <= -32 := gamma
+//
+// In order to convert the fractional part
+//
+//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...
+//
+// into decimal form, the fraction is repeatedly multiplied by 10 and the digits
+// d[-i] are extracted in order:
+//
+//      (10 * p2) div 2^-e = d[-1]
+//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...
+//
+// The multiplication by 10 must not overflow. It is sufficient to choose
+//
+//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.
+//
+// Since p2 = f mod 2^-e < 2^-e,
+//
+//      -e <= 60   or   e >= -60 := alpha
+
+constexpr int kAlpha = -60;
+constexpr int kGamma = -32;
+
+struct cached_power // c = f * 2^e ~= 10^k
+{
+    std::uint64_t f;
+    int e;
+    int k;
+};
+
+/*!
+For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
+power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
+satisfies (Definition 3.2 from [1])
+
+     alpha <= e_c + e + q <= gamma.
+*/
+inline cached_power get_cached_power_for_binary_exponent(int e)
+{
+    // Now
+    //
+    //      alpha <= e_c + e + q <= gamma                                    (1)
+    //      ==> f_c * 2^alpha <= c * 2^e * 2^q
+    //
+    // and since the c's are normalized, 2^(q-1) <= f_c,
+    //
+    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)
+    //      ==> 2^(alpha - e - 1) <= c
+    //
+    // If c were an exact power of ten, i.e. c = 10^k, one may determine k as
+    //
+    //      k = ceil( log_10( 2^(alpha - e - 1) ) )
+    //        = ceil( (alpha - e - 1) * log_10(2) )
+    //
+    // From the paper:
+    // "In theory the result of the procedure could be wrong since c is rounded,
+    //  and the computation itself is approximated [...]. In practice, however,
+    //  this simple function is sufficient."
+    //
+    // For IEEE double precision floating-point numbers converted into
+    // normalized diyfp's w = f * 2^e, with q = 64,
+    //
+    //      e >= -1022      (min IEEE exponent)
+    //           -52        (p - 1)
+    //           -52        (p - 1, possibly normalize denormal IEEE numbers)
+    //           -11        (normalize the diyfp)
+    //         = -1137
+    //
+    // and
+    //
+    //      e <= +1023      (max IEEE exponent)
+    //           -52        (p - 1)
+    //           -11        (normalize the diyfp)
+    //         = 960
+    //
+    // This binary exponent range [-1137,960] results in a decimal exponent
+    // range [-307,324]. One does not need to store a cached power for each
+    // k in this range. For each such k it suffices to find a cached power
+    // such that the exponent of the product lies in [alpha,gamma].
+    // This implies that the difference of the decimal exponents of adjacent
+    // table entries must be less than or equal to
+    //
+    //      floor( (gamma - alpha) * log_10(2) ) = 8.
+    //
+    // (A smaller distance gamma-alpha would require a larger table.)
+
+    // NB:
+    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.
+
+    constexpr int kCachedPowersMinDecExp = -300;
+    constexpr int kCachedPowersDecStep = 8;
+
+    static constexpr std::array<cached_power, 79> kCachedPowers =
+    {
+        {
+            { 0xAB70FE17C79AC6CA, -1060, -300 },
+            { 0xFF77B1FCBEBCDC4F, -1034, -292 },
+            { 0xBE5691EF416BD60C, -1007, -284 },
+            { 0x8DD01FAD907FFC3C,  -980, -276 },
+            { 0xD3515C2831559A83,  -954, -268 },
+            { 0x9D71AC8FADA6C9B5,  -927, -260 },
+            { 0xEA9C227723EE8BCB,  -901, -252 },
+            { 0xAECC49914078536D,  -874, -244 },
+            { 0x823C12795DB6CE57,  -847, -236 },
+            { 0xC21094364DFB5637,  -821, -228 },
+            { 0x9096EA6F3848984F,  -794, -220 },
+            { 0xD77485CB25823AC7,  -768, -212 },
+            { 0xA086CFCD97BF97F4,  -741, -204 },
+            { 0xEF340A98172AACE5,  -715, -196 },
+            { 0xB23867FB2A35B28E,  -688, -188 },
+            { 0x84C8D4DFD2C63F3B,  -661, -180 },
+            { 0xC5DD44271AD3CDBA,  -635, -172 },
+            { 0x936B9FCEBB25C996,  -608, -164 },
+            { 0xDBAC6C247D62A584,  -582, -156 },
+            { 0xA3AB66580D5FDAF6,  -555, -148 },
+            { 0xF3E2F893DEC3F126,  -529, -140 },
+            { 0xB5B5ADA8AAFF80B8,  -502, -132 },
+            { 0x87625F056C7C4A8B,  -475, -124 },
+            { 0xC9BCFF6034C13053,  -449, -116 },
+            { 0x964E858C91BA2655,  -422, -108 },
+            { 0xDFF9772470297EBD,  -396, -100 },
+            { 0xA6DFBD9FB8E5B88F,  -369,  -92 },
+            { 0xF8A95FCF88747D94,  -343,  -84 },
+            { 0xB94470938FA89BCF,  -316,  -76 },
+            { 0x8A08F0F8BF0F156B,  -289,  -68 },
+            { 0xCDB02555653131B6,  -263,  -60 },
+            { 0x993FE2C6D07B7FAC,  -236,  -52 },
+            { 0xE45C10C42A2B3B06,  -210,  -44 },
+            { 0xAA242499697392D3,  -183,  -36 },
+            { 0xFD87B5F28300CA0E,  -157,  -28 },
+            { 0xBCE5086492111AEB,  -130,  -20 },
+            { 0x8CBCCC096F5088CC,  -103,  -12 },
+            { 0xD1B71758E219652C,   -77,   -4 },
+            { 0x9C40000000000000,   -50,    4 },
+            { 0xE8D4A51000000000,   -24,   12 },
+            { 0xAD78EBC5AC620000,     3,   20 },
+            { 0x813F3978F8940984,    30,   28 },
+            { 0xC097CE7BC90715B3,    56,   36 },
+            { 0x8F7E32CE7BEA5C70,    83,   44 },
+            { 0xD5D238A4ABE98068,   109,   52 },
+            { 0x9F4F2726179A2245,   136,   60 },
+            { 0xED63A231D4C4FB27,   162,   68 },
+            { 0xB0DE65388CC8ADA8,   189,   76 },
+            { 0x83C7088E1AAB65DB,   216,   84 },
+            { 0xC45D1DF942711D9A,   242,   92 },
+            { 0x924D692CA61BE758,   269,  100 },
+            { 0xDA01EE641A708DEA,   295,  108 },
+            { 0xA26DA3999AEF774A,   322,  116 },
+            { 0xF209787BB47D6B85,   348,  124 },
+            { 0xB454E4A179DD1877,   375,  132 },
+            { 0x865B86925B9BC5C2,   402,  140 },
+            { 0xC83553C5C8965D3D,   428,  148 },
+            { 0x952AB45CFA97A0B3,   455,  156 },
+            { 0xDE469FBD99A05FE3,   481,  164 },
+            { 0xA59BC234DB398C25,   508,  172 },
+            { 0xF6C69A72A3989F5C,   534,  180 },
+            { 0xB7DCBF5354E9BECE,   561,  188 },
+            { 0x88FCF317F22241E2,   588,  196 },
+            { 0xCC20CE9BD35C78A5,   614,  204 },
+            { 0x98165AF37B2153DF,   641,  212 },
+            { 0xE2A0B5DC971F303A,   667,  220 },
+            { 0xA8D9D1535CE3B396,   694,  228 },
+            { 0xFB9B7CD9A4A7443C,   720,  236 },
+            { 0xBB764C4CA7A44410,   747,  244 },
+            { 0x8BAB8EEFB6409C1A,   774,  252 },
+            { 0xD01FEF10A657842C,   800,  260 },
+            { 0x9B10A4E5E9913129,   827,  268 },
+            { 0xE7109BFBA19C0C9D,   853,  276 },
+            { 0xAC2820D9623BF429,   880,  284 },
+            { 0x80444B5E7AA7CF85,   907,  292 },
+            { 0xBF21E44003ACDD2D,   933,  300 },
+            { 0x8E679C2F5E44FF8F,   960,  308 },
+            { 0xD433179D9C8CB841,   986,  316 },
+            { 0x9E19DB92B4E31BA9,  1013,  324 },
+        }
+    };
+
+    // This computation gives exactly the same results for k as
+    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)
+    // for |e| <= 1500, but doesn't require floating-point operations.
+    // NB: log_10(2) ~= 78913 / 2^18
+    JSON_ASSERT(e >= -1500);
+    JSON_ASSERT(e <=  1500);
+    const int f = kAlpha - e - 1;
+    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);
+
+    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
+    JSON_ASSERT(index >= 0);
+    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());
+
+    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];
+    JSON_ASSERT(kAlpha <= cached.e + e + 64);
+    JSON_ASSERT(kGamma >= cached.e + e + 64);
+
+    return cached;
+}
+
+/*!
+For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
+For n == 0, returns 1 and sets pow10 := 1.
+*/
+inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)
+{
+    // LCOV_EXCL_START
+    if (n >= 1000000000)
+    {
+        pow10 = 1000000000;
+        return 10;
+    }
+    // LCOV_EXCL_STOP
+    if (n >= 100000000)
+    {
+        pow10 = 100000000;
+        return  9;
+    }
+    if (n >= 10000000)
+    {
+        pow10 = 10000000;
+        return  8;
+    }
+    if (n >= 1000000)
+    {
+        pow10 = 1000000;
+        return  7;
+    }
+    if (n >= 100000)
+    {
+        pow10 = 100000;
+        return  6;
+    }
+    if (n >= 10000)
+    {
+        pow10 = 10000;
+        return  5;
+    }
+    if (n >= 1000)
+    {
+        pow10 = 1000;
+        return  4;
+    }
+    if (n >= 100)
+    {
+        pow10 = 100;
+        return  3;
+    }
+    if (n >= 10)
+    {
+        pow10 = 10;
+        return  2;
+    }
+
+    pow10 = 1;
+    return 1;
+}
+
+inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,
+                         std::uint64_t rest, std::uint64_t ten_k)
+{
+    JSON_ASSERT(len >= 1);
+    JSON_ASSERT(dist <= delta);
+    JSON_ASSERT(rest <= delta);
+    JSON_ASSERT(ten_k > 0);
+
+    //               <--------------------------- delta ---->
+    //                                  <---- dist --------->
+    // --------------[------------------+-------------------]--------------
+    //               M-                 w                   M+
+    //
+    //                                  ten_k
+    //                                <------>
+    //                                       <---- rest ---->
+    // --------------[------------------+----+--------------]--------------
+    //                                  w    V
+    //                                       = buf * 10^k
+    //
+    // ten_k represents a unit-in-the-last-place in the decimal representation
+    // stored in buf.
+    // Decrement buf by ten_k while this takes buf closer to w.
+
+    // The tests are written in this order to avoid overflow in unsigned
+    // integer arithmetic.
+
+    while (rest < dist
+            && delta - rest >= ten_k
+            && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))
+    {
+        JSON_ASSERT(buf[len - 1] != '0');
+        buf[len - 1]--;
+        rest += ten_k;
+    }
+}
+
+/*!
+Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
+M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
+*/
+inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
+                             diyfp M_minus, diyfp w, diyfp M_plus)
+{
+    static_assert(kAlpha >= -60, "internal error");
+    static_assert(kGamma <= -32, "internal error");
+
+    // Generates the digits (and the exponent) of a decimal floating-point
+    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's
+    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.
+    //
+    //               <--------------------------- delta ---->
+    //                                  <---- dist --------->
+    // --------------[------------------+-------------------]--------------
+    //               M-                 w                   M+
+    //
+    // Grisu2 generates the digits of M+ from left to right and stops as soon as
+    // V is in [M-,M+].
+
+    JSON_ASSERT(M_plus.e >= kAlpha);
+    JSON_ASSERT(M_plus.e <= kGamma);
+
+    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
+    std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)
+
+    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):
+    //
+    //      M+ = f * 2^e
+    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e
+    //         = ((p1        ) * 2^-e + (p2        )) * 2^e
+    //         = p1 + p2 * 2^e
+
+    const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);
+
+    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
+    std::uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e
+
+    // 1)
+    //
+    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
+
+    JSON_ASSERT(p1 > 0);
+
+    std::uint32_t pow10{};
+    const int k = find_largest_pow10(p1, pow10);
+
+    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
+    //
+    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))
+    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))
+    //
+    //      M+ = p1                                             + p2 * 2^e
+    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e
+    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e
+    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e
+    //
+    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)
+    //
+    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]
+    //
+    // but stop as soon as
+    //
+    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e
+
+    int n = k;
+    while (n > 0)
+    {
+        // Invariants:
+        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)
+        //      pow10 = 10^(n-1) <= p1 < 10^n
+        //
+        const std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)
+        const std::uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)
+        //
+        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
+        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
+        //
+        JSON_ASSERT(d <= 9);
+        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+        //
+        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
+        //
+        p1 = r;
+        n--;
+        //
+        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)
+        //      pow10 = 10^n
+        //
+
+        // Now check if enough digits have been generated.
+        // Compute
+        //
+        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e
+        //
+        // Note:
+        // Since rest and delta share the same exponent e, it suffices to
+        // compare the significands.
+        const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;
+        if (rest <= delta)
+        {
+            // V = buffer * 10^n, with M- <= V <= M+.
+
+            decimal_exponent += n;
+
+            // We may now just stop. But instead look if the buffer could be
+            // decremented to bring V closer to w.
+            //
+            // pow10 = 10^n is now 1 ulp in the decimal representation V.
+            // The rounding procedure works with diyfp's with an implicit
+            // exponent of e.
+            //
+            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e
+            //
+            const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;
+            grisu2_round(buffer, length, dist, delta, rest, ten_n);
+
+            return;
+        }
+
+        pow10 /= 10;
+        //
+        //      pow10 = 10^(n-1) <= p1 < 10^n
+        // Invariants restored.
+    }
+
+    // 2)
+    //
+    // The digits of the integral part have been generated:
+    //
+    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e
+    //         = buffer            + p2 * 2^e
+    //
+    // Now generate the digits of the fractional part p2 * 2^e.
+    //
+    // Note:
+    // No decimal point is generated: the exponent is adjusted instead.
+    //
+    // p2 actually represents the fraction
+    //
+    //      p2 * 2^e
+    //          = p2 / 2^-e
+    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...
+    //
+    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)
+    //
+    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m
+    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)
+    //
+    // using
+    //
+    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)
+    //                = (                   d) * 2^-e + (                   r)
+    //
+    // or
+    //      10^m * p2 * 2^e = d + r * 2^e
+    //
+    // i.e.
+    //
+    //      M+ = buffer + p2 * 2^e
+    //         = buffer + 10^-m * (d + r * 2^e)
+    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e
+    //
+    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
+
+    JSON_ASSERT(p2 > delta);
+
+    int m = 0;
+    for (;;)
+    {
+        // Invariant:
+        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e
+        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e
+        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e
+        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
+        //
+        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
+        p2 *= 10;
+        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e
+        const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
+        //
+        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e
+        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
+        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
+        //
+        JSON_ASSERT(d <= 9);
+        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+        //
+        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
+        //
+        p2 = r;
+        m++;
+        //
+        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e
+        // Invariant restored.
+
+        // Check if enough digits have been generated.
+        //
+        //      10^-m * p2 * 2^e <= delta * 2^e
+        //              p2 * 2^e <= 10^m * delta * 2^e
+        //                    p2 <= 10^m * delta
+        delta *= 10;
+        dist  *= 10;
+        if (p2 <= delta)
+        {
+            break;
+        }
+    }
+
+    // V = buffer * 10^-m, with M- <= V <= M+.
+
+    decimal_exponent -= m;
+
+    // 1 ulp in the decimal representation is now 10^-m.
+    // Since delta and dist are now scaled by 10^m, we need to do the
+    // same with ulp in order to keep the units in sync.
+    //
+    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e
+    //
+    const std::uint64_t ten_m = one.f;
+    grisu2_round(buffer, length, dist, delta, p2, ten_m);
+
+    // By construction this algorithm generates the shortest possible decimal
+    // number (Loitsch, Theorem 6.2) which rounds back to w.
+    // For an input number of precision p, at least
+    //
+    //      N = 1 + ceil(p * log_10(2))
+    //
+    // decimal digits are sufficient to identify all binary floating-point
+    // numbers (Matula, "In-and-Out conversions").
+    // This implies that the algorithm does not produce more than N decimal
+    // digits.
+    //
+    //      N = 17 for p = 53 (IEEE double precision)
+    //      N = 9  for p = 24 (IEEE single precision)
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+JSON_HEDLEY_NON_NULL(1)
+inline void grisu2(char* buf, int& len, int& decimal_exponent,
+                   diyfp m_minus, diyfp v, diyfp m_plus)
+{
+    JSON_ASSERT(m_plus.e == m_minus.e);
+    JSON_ASSERT(m_plus.e == v.e);
+
+    //  --------(-----------------------+-----------------------)--------    (A)
+    //          m-                      v                       m+
+    //
+    //  --------------------(-----------+-----------------------)--------    (B)
+    //                      m-          v                       m+
+    //
+    // First scale v (and m- and m+) such that the exponent is in the range
+    // [alpha, gamma].
+
+    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);
+
+    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k
+
+    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]
+    const diyfp w       = diyfp::mul(v,       c_minus_k);
+    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);
+    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);
+
+    //  ----(---+---)---------------(---+---)---------------(---+---)----
+    //          w-                      w                       w+
+    //          = c*m-                  = c*v                   = c*m+
+    //
+    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and
+    // w+ are now off by a small amount.
+    // In fact:
+    //
+    //      w - v * 10^k < 1 ulp
+    //
+    // To account for this inaccuracy, add resp. subtract 1 ulp.
+    //
+    //  --------+---[---------------(---+---)---------------]---+--------
+    //          w-  M-                  w                   M+  w+
+    //
+    // Now any number in [M-, M+] (bounds included) will round to w when input,
+    // regardless of how the input rounding algorithm breaks ties.
+    //
+    // And digit_gen generates the shortest possible such number in [M-, M+].
+    // Note that this does not mean that Grisu2 always generates the shortest
+    // possible number in the interval (m-, m+).
+    const diyfp M_minus(w_minus.f + 1, w_minus.e);
+    const diyfp M_plus (w_plus.f  - 1, w_plus.e );
+
+    decimal_exponent = -cached.k; // = -(-k) = k
+
+    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+template<typename FloatType>
+JSON_HEDLEY_NON_NULL(1)
+void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
+{
+    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
+                  "internal error: not enough precision");
+
+    JSON_ASSERT(std::isfinite(value));
+    JSON_ASSERT(value > 0);
+
+    // If the neighbors (and boundaries) of 'value' are always computed for double-precision
+    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
+    // decimal representations are not exactly "short".
+    //
+    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)
+    // says "value is converted to a string as if by std::sprintf in the default ("C") locale"
+    // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars'
+    // does.
+    // On the other hand, the documentation for 'std::to_chars' requires that "parsing the
+    // representation using the corresponding std::from_chars function recovers value exactly". That
+    // indicates that single precision floating-point numbers should be recovered using
+    // 'std::strtof'.
+    //
+    // NB: If the neighbors are computed for single-precision numbers, there is a single float
+    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision
+    //     value is off by 1 ulp.
+#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if)
+    const boundaries w = compute_boundaries(static_cast<double>(value));
+#else
+    const boundaries w = compute_boundaries(value);
+#endif
+
+    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
+}
+
+/*!
+@brief appends a decimal representation of e to buf
+@return a pointer to the element following the exponent.
+@pre -1000 < e < 1000
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* append_exponent(char* buf, int e)
+{
+    JSON_ASSERT(e > -1000);
+    JSON_ASSERT(e <  1000);
+
+    if (e < 0)
+    {
+        e = -e;
+        *buf++ = '-';
+    }
+    else
+    {
+        *buf++ = '+';
+    }
+
+    auto k = static_cast<std::uint32_t>(e);
+    if (k < 10)
+    {
+        // Always print at least two digits in the exponent.
+        // This is for compatibility with printf("%g").
+        *buf++ = '0';
+        *buf++ = static_cast<char>('0' + k);
+    }
+    else if (k < 100)
+    {
+        *buf++ = static_cast<char>('0' + k / 10);
+        k %= 10;
+        *buf++ = static_cast<char>('0' + k);
+    }
+    else
+    {
+        *buf++ = static_cast<char>('0' + k / 100);
+        k %= 100;
+        *buf++ = static_cast<char>('0' + k / 10);
+        k %= 10;
+        *buf++ = static_cast<char>('0' + k);
+    }
+
+    return buf;
+}
+
+/*!
+@brief prettify v = buf * 10^decimal_exponent
+
+If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
+notation. Otherwise it will be printed in exponential notation.
+
+@pre min_exp < 0
+@pre max_exp > 0
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* format_buffer(char* buf, int len, int decimal_exponent,
+                           int min_exp, int max_exp)
+{
+    JSON_ASSERT(min_exp < 0);
+    JSON_ASSERT(max_exp > 0);
+
+    const int k = len;
+    const int n = len + decimal_exponent;
+
+    // v = buf * 10^(n-k)
+    // k is the length of the buffer (number of decimal digits)
+    // n is the position of the decimal point relative to the start of the buffer.
+
+    if (k <= n && n <= max_exp)
+    {
+        // digits[000]
+        // len <= max_exp + 2
+
+        std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));
+        // Make it look like a floating-point number (#362, #378)
+        buf[n + 0] = '.';
+        buf[n + 1] = '0';
+        return buf + (static_cast<size_t>(n) + 2);
+    }
+
+    if (0 < n && n <= max_exp)
+    {
+        // dig.its
+        // len <= max_digits10 + 1
+
+        JSON_ASSERT(k > n);
+
+        std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));
+        buf[n] = '.';
+        return buf + (static_cast<size_t>(k) + 1U);
+    }
+
+    if (min_exp < n && n <= 0)
+    {
+        // 0.[000]digits
+        // len <= 2 + (-min_exp - 1) + max_digits10
+
+        std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));
+        buf[0] = '0';
+        buf[1] = '.';
+        std::memset(buf + 2, '0', static_cast<size_t>(-n));
+        return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));
+    }
+
+    if (k == 1)
+    {
+        // dE+123
+        // len <= 1 + 5
+
+        buf += 1;
+    }
+    else
+    {
+        // d.igitsE+123
+        // len <= max_digits10 + 1 + 5
+
+        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);
+        buf[1] = '.';
+        buf += 1 + static_cast<size_t>(k);
+    }
+
+    *buf++ = 'e';
+    return append_exponent(buf, n - 1);
+}
+
+}  // namespace dtoa_impl
+
+/*!
+@brief generates a decimal representation of the floating-point number value in [first, last).
+
+The format of the resulting decimal representation is similar to printf's %g
+format. Returns an iterator pointing past-the-end of the decimal representation.
+
+@note The input number must be finite, i.e. NaN's and Inf's are not supported.
+@note The buffer must be large enough.
+@note The result is NOT null-terminated.
+*/
+template<typename FloatType>
+JSON_HEDLEY_NON_NULL(1, 2)
+JSON_HEDLEY_RETURNS_NON_NULL
+char* to_chars(char* first, const char* last, FloatType value)
+{
+    static_cast<void>(last); // maybe unused - fix warning
+    JSON_ASSERT(std::isfinite(value));
+
+    // Use signbit(value) instead of (value < 0) since signbit works for -0.
+    if (std::signbit(value))
+    {
+        value = -value;
+        *first++ = '-';
+    }
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+    if (value == 0) // +-0
+    {
+        *first++ = '0';
+        // Make it look like a floating-point number (#362, #378)
+        *first++ = '.';
+        *first++ = '0';
+        return first;
+    }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
+
+    // Compute v = buffer * 10^decimal_exponent.
+    // The decimal digits are stored in the buffer, which needs to be interpreted
+    // as an unsigned decimal integer.
+    // len is the length of the buffer, i.e. the number of decimal digits.
+    int len = 0;
+    int decimal_exponent = 0;
+    dtoa_impl::grisu2(first, len, decimal_exponent, value);
+
+    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
+
+    // Format the buffer like printf("%.*g", prec, value)
+    constexpr int kMinExp = -4;
+    // Use digits10 here to increase compatibility with version 2.
+    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
+
+    JSON_ASSERT(last - first >= kMaxExp + 2);
+    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+
+    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
+}
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/output/binary_writer.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////
+// serialization //
+///////////////////
+
+/// how to treat decoding errors
+enum class error_handler_t
+{
+    strict,  ///< throw a type_error exception in case of invalid UTF-8
+    replace, ///< replace invalid UTF-8 sequences with U+FFFD
+    ignore   ///< ignore invalid UTF-8 sequences
+};
+
+template<typename BasicJsonType>
+class serializer
+{
+    using string_t = typename BasicJsonType::string_t;
+    using number_float_t = typename BasicJsonType::number_float_t;
+    using number_integer_t = typename BasicJsonType::number_integer_t;
+    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+    using binary_char_t = typename BasicJsonType::binary_t::value_type;
+    static constexpr std::uint8_t UTF8_ACCEPT = 0;
+    static constexpr std::uint8_t UTF8_REJECT = 1;
+
+  public:
+    /*!
+    @param[in] s  output stream to serialize to
+    @param[in] ichar  indentation character to use
+    @param[in] error_handler_  how to react on decoding errors
+    */
+    serializer(output_adapter_t<char> s, const char ichar,
+               error_handler_t error_handler_ = error_handler_t::strict)
+        : o(std::move(s))
+        , loc(std::localeconv())
+        , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
+        , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
+        , indent_char(ichar)
+        , indent_string(512, indent_char)
+        , error_handler(error_handler_)
+    {}
+
+    // delete because of pointer members
+    serializer(const serializer&) = delete;
+    serializer& operator=(const serializer&) = delete;
+    serializer(serializer&&) = delete;
+    serializer& operator=(serializer&&) = delete;
+    ~serializer() = default;
+
+    /*!
+    @brief internal implementation of the serialization function
+
+    This function is called by the public member function dump and organizes
+    the serialization internally. The indentation level is propagated as
+    additional parameter. In case of arrays and objects, the function is
+    called recursively.
+
+    - strings and object keys are escaped using `escape_string()`
+    - integer numbers are converted implicitly via `operator<<`
+    - floating-point numbers are converted to a string using `"%g"` format
+    - binary values are serialized as objects containing the subtype and the
+      byte array
+
+    @param[in] val               value to serialize
+    @param[in] pretty_print      whether the output shall be pretty-printed
+    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
+    in the output are escaped with `\uXXXX` sequences, and the result consists
+    of ASCII characters only.
+    @param[in] indent_step       the indent level
+    @param[in] current_indent    the current indent level (only used internally)
+    */
+    void dump(const BasicJsonType& val,
+              const bool pretty_print,
+              const bool ensure_ascii,
+              const unsigned int indent_step,
+              const unsigned int current_indent = 0)
+    {
+        switch (val.m_data.m_type)
+        {
+            case value_t::object:
+            {
+                if (val.m_data.m_value.object->empty())
+                {
+                    o->write_characters("{}", 2);
+                    return;
+                }
+
+                if (pretty_print)
+                {
+                    o->write_characters("{\n", 2);
+
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    // first n-1 elements
+                    auto i = val.m_data.m_value.object->cbegin();
+                    for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
+                    {
+                        o->write_characters(indent_string.c_str(), new_indent);
+                        o->write_character('\"');
+                        dump_escaped(i->first, ensure_ascii);
+                        o->write_characters("\": ", 3);
+                        dump(i->second, true, ensure_ascii, indent_step, new_indent);
+                        o->write_characters(",\n", 2);
+                    }
+
+                    // last element
+                    JSON_ASSERT(i != val.m_data.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
+                    o->write_characters(indent_string.c_str(), new_indent);
+                    o->write_character('\"');
+                    dump_escaped(i->first, ensure_ascii);
+                    o->write_characters("\": ", 3);
+                    dump(i->second, true, ensure_ascii, indent_step, new_indent);
+
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character('}');
+                }
+                else
+                {
+                    o->write_character('{');
+
+                    // first n-1 elements
+                    auto i = val.m_data.m_value.object->cbegin();
+                    for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
+                    {
+                        o->write_character('\"');
+                        dump_escaped(i->first, ensure_ascii);
+                        o->write_characters("\":", 2);
+                        dump(i->second, false, ensure_ascii, indent_step, current_indent);
+                        o->write_character(',');
+                    }
+
+                    // last element
+                    JSON_ASSERT(i != val.m_data.m_value.object->cend());
+                    JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
+                    o->write_character('\"');
+                    dump_escaped(i->first, ensure_ascii);
+                    o->write_characters("\":", 2);
+                    dump(i->second, false, ensure_ascii, indent_step, current_indent);
+
+                    o->write_character('}');
+                }
+
+                return;
+            }
+
+            case value_t::array:
+            {
+                if (val.m_data.m_value.array->empty())
+                {
+                    o->write_characters("[]", 2);
+                    return;
+                }
+
+                if (pretty_print)
+                {
+                    o->write_characters("[\n", 2);
+
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    // first n-1 elements
+                    for (auto i = val.m_data.m_value.array->cbegin();
+                            i != val.m_data.m_value.array->cend() - 1; ++i)
+                    {
+                        o->write_characters(indent_string.c_str(), new_indent);
+                        dump(*i, true, ensure_ascii, indent_step, new_indent);
+                        o->write_characters(",\n", 2);
+                    }
+
+                    // last element
+                    JSON_ASSERT(!val.m_data.m_value.array->empty());
+                    o->write_characters(indent_string.c_str(), new_indent);
+                    dump(val.m_data.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
+
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character(']');
+                }
+                else
+                {
+                    o->write_character('[');
+
+                    // first n-1 elements
+                    for (auto i = val.m_data.m_value.array->cbegin();
+                            i != val.m_data.m_value.array->cend() - 1; ++i)
+                    {
+                        dump(*i, false, ensure_ascii, indent_step, current_indent);
+                        o->write_character(',');
+                    }
+
+                    // last element
+                    JSON_ASSERT(!val.m_data.m_value.array->empty());
+                    dump(val.m_data.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
+
+                    o->write_character(']');
+                }
+
+                return;
+            }
+
+            case value_t::string:
+            {
+                o->write_character('\"');
+                dump_escaped(*val.m_data.m_value.string, ensure_ascii);
+                o->write_character('\"');
+                return;
+            }
+
+            case value_t::binary:
+            {
+                if (pretty_print)
+                {
+                    o->write_characters("{\n", 2);
+
+                    // variable to hold indentation for recursive calls
+                    const auto new_indent = current_indent + indent_step;
+                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+                    {
+                        indent_string.resize(indent_string.size() * 2, ' ');
+                    }
+
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"bytes\": [", 10);
+
+                    if (!val.m_data.m_value.binary->empty())
+                    {
+                        for (auto i = val.m_data.m_value.binary->cbegin();
+                                i != val.m_data.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_characters(", ", 2);
+                        }
+                        dump_integer(val.m_data.m_value.binary->back());
+                    }
+
+                    o->write_characters("],\n", 3);
+                    o->write_characters(indent_string.c_str(), new_indent);
+
+                    o->write_characters("\"subtype\": ", 11);
+                    if (val.m_data.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_data.m_value.binary->subtype());
+                    }
+                    else
+                    {
+                        o->write_characters("null", 4);
+                    }
+                    o->write_character('\n');
+                    o->write_characters(indent_string.c_str(), current_indent);
+                    o->write_character('}');
+                }
+                else
+                {
+                    o->write_characters("{\"bytes\":[", 10);
+
+                    if (!val.m_data.m_value.binary->empty())
+                    {
+                        for (auto i = val.m_data.m_value.binary->cbegin();
+                                i != val.m_data.m_value.binary->cend() - 1; ++i)
+                        {
+                            dump_integer(*i);
+                            o->write_character(',');
+                        }
+                        dump_integer(val.m_data.m_value.binary->back());
+                    }
+
+                    o->write_characters("],\"subtype\":", 12);
+                    if (val.m_data.m_value.binary->has_subtype())
+                    {
+                        dump_integer(val.m_data.m_value.binary->subtype());
+                        o->write_character('}');
+                    }
+                    else
+                    {
+                        o->write_characters("null}", 5);
+                    }
+                }
+                return;
+            }
+
+            case value_t::boolean:
+            {
+                if (val.m_data.m_value.boolean)
+                {
+                    o->write_characters("true", 4);
+                }
+                else
+                {
+                    o->write_characters("false", 5);
+                }
+                return;
+            }
+
+            case value_t::number_integer:
+            {
+                dump_integer(val.m_data.m_value.number_integer);
+                return;
+            }
+
+            case value_t::number_unsigned:
+            {
+                dump_integer(val.m_data.m_value.number_unsigned);
+                return;
+            }
+
+            case value_t::number_float:
+            {
+                dump_float(val.m_data.m_value.number_float);
+                return;
+            }
+
+            case value_t::discarded:
+            {
+                o->write_characters("<discarded>", 11);
+                return;
+            }
+
+            case value_t::null:
+            {
+                o->write_characters("null", 4);
+                return;
+            }
+
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+    }
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /*!
+    @brief dump escaped string
+
+    Escape a string by replacing certain special characters by a sequence of an
+    escape character (backslash) and another character and other control
+    characters by a sequence of "\u" followed by a four-digit hex
+    representation. The escaped string is written to output stream @a o.
+
+    @param[in] s  the string to escape
+    @param[in] ensure_ascii  whether to escape non-ASCII characters with
+                             \uXXXX sequences
+
+    @complexity Linear in the length of string @a s.
+    */
+    void dump_escaped(const string_t& s, const bool ensure_ascii)
+    {
+        std::uint32_t codepoint{};
+        std::uint8_t state = UTF8_ACCEPT;
+        std::size_t bytes = 0;  // number of bytes written to string_buffer
+
+        // number of bytes written at the point of the last valid byte
+        std::size_t bytes_after_last_accept = 0;
+        std::size_t undumped_chars = 0;
+
+        for (std::size_t i = 0; i < s.size(); ++i)
+        {
+            const auto byte = static_cast<std::uint8_t>(s[i]);
+
+            switch (decode(state, codepoint, byte))
+            {
+                case UTF8_ACCEPT:  // decode found a new code point
+                {
+                    switch (codepoint)
+                    {
+                        case 0x08: // backspace
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'b';
+                            break;
+                        }
+
+                        case 0x09: // horizontal tab
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 't';
+                            break;
+                        }
+
+                        case 0x0A: // newline
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'n';
+                            break;
+                        }
+
+                        case 0x0C: // formfeed
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'f';
+                            break;
+                        }
+
+                        case 0x0D: // carriage return
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = 'r';
+                            break;
+                        }
+
+                        case 0x22: // quotation mark
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = '\"';
+                            break;
+                        }
+
+                        case 0x5C: // reverse solidus
+                        {
+                            string_buffer[bytes++] = '\\';
+                            string_buffer[bytes++] = '\\';
+                            break;
+                        }
+
+                        default:
+                        {
+                            // escape control characters (0x00..0x1F) or, if
+                            // ensure_ascii parameter is used, non-ASCII characters
+                            if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
+                            {
+                                if (codepoint <= 0xFFFF)
+                                {
+                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
+                                                                      static_cast<std::uint16_t>(codepoint)));
+                                    bytes += 6;
+                                }
+                                else
+                                {
+                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
+                                                                      static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
+                                                                      static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
+                                    bytes += 12;
+                                }
+                            }
+                            else
+                            {
+                                // copy byte to buffer (all previous bytes
+                                // been copied have in default case above)
+                                string_buffer[bytes++] = s[i];
+                            }
+                            break;
+                        }
+                    }
+
+                    // write buffer and reset index; there must be 13 bytes
+                    // left, as this is the maximal number of bytes to be
+                    // written ("\uxxxx\uxxxx\0") for one code point
+                    if (string_buffer.size() - bytes < 13)
+                    {
+                        o->write_characters(string_buffer.data(), bytes);
+                        bytes = 0;
+                    }
+
+                    // remember the byte position of this accept
+                    bytes_after_last_accept = bytes;
+                    undumped_chars = 0;
+                    break;
+                }
+
+                case UTF8_REJECT:  // decode found invalid UTF-8 byte
+                {
+                    switch (error_handler)
+                    {
+                        case error_handler_t::strict:
+                        {
+                            JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
+                        }
+
+                        case error_handler_t::ignore:
+                        case error_handler_t::replace:
+                        {
+                            // in case we saw this character the first time, we
+                            // would like to read it again, because the byte
+                            // may be OK for itself, but just not OK for the
+                            // previous sequence
+                            if (undumped_chars > 0)
+                            {
+                                --i;
+                            }
+
+                            // reset length buffer to the last accepted index;
+                            // thus removing/ignoring the invalid characters
+                            bytes = bytes_after_last_accept;
+
+                            if (error_handler == error_handler_t::replace)
+                            {
+                                // add a replacement character
+                                if (ensure_ascii)
+                                {
+                                    string_buffer[bytes++] = '\\';
+                                    string_buffer[bytes++] = 'u';
+                                    string_buffer[bytes++] = 'f';
+                                    string_buffer[bytes++] = 'f';
+                                    string_buffer[bytes++] = 'f';
+                                    string_buffer[bytes++] = 'd';
+                                }
+                                else
+                                {
+                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
+                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
+                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
+                                }
+
+                                // write buffer and reset index; there must be 13 bytes
+                                // left, as this is the maximal number of bytes to be
+                                // written ("\uxxxx\uxxxx\0") for one code point
+                                if (string_buffer.size() - bytes < 13)
+                                {
+                                    o->write_characters(string_buffer.data(), bytes);
+                                    bytes = 0;
+                                }
+
+                                bytes_after_last_accept = bytes;
+                            }
+
+                            undumped_chars = 0;
+
+                            // continue processing the string
+                            state = UTF8_ACCEPT;
+                            break;
+                        }
+
+                        default:            // LCOV_EXCL_LINE
+                            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+                    }
+                    break;
+                }
+
+                default:  // decode found yet incomplete multi-byte code point
+                {
+                    if (!ensure_ascii)
+                    {
+                        // code point will not be escaped - copy byte to buffer
+                        string_buffer[bytes++] = s[i];
+                    }
+                    ++undumped_chars;
+                    break;
+                }
+            }
+        }
+
+        // we finished processing the string
+        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
+        {
+            // write buffer
+            if (bytes > 0)
+            {
+                o->write_characters(string_buffer.data(), bytes);
+            }
+        }
+        else
+        {
+            // we finish reading, but do not accept: string was incomplete
+            switch (error_handler)
+            {
+                case error_handler_t::strict:
+                {
+                    JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s.back() | 0))), nullptr));
+                }
+
+                case error_handler_t::ignore:
+                {
+                    // write all accepted bytes
+                    o->write_characters(string_buffer.data(), bytes_after_last_accept);
+                    break;
+                }
+
+                case error_handler_t::replace:
+                {
+                    // write all accepted bytes
+                    o->write_characters(string_buffer.data(), bytes_after_last_accept);
+                    // add a replacement character
+                    if (ensure_ascii)
+                    {
+                        o->write_characters("\\ufffd", 6);
+                    }
+                    else
+                    {
+                        o->write_characters("\xEF\xBF\xBD", 3);
+                    }
+                    break;
+                }
+
+                default:            // LCOV_EXCL_LINE
+                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+            }
+        }
+    }
+
+  private:
+    /*!
+    @brief count digits
+
+    Count the number of decimal (base 10) digits for an input unsigned integer.
+
+    @param[in] x  unsigned integer number to count its digits
+    @return    number of decimal digits
+    */
+    inline unsigned int count_digits(number_unsigned_t x) noexcept
+    {
+        unsigned int n_digits = 1;
+        for (;;)
+        {
+            if (x < 10)
+            {
+                return n_digits;
+            }
+            if (x < 100)
+            {
+                return n_digits + 1;
+            }
+            if (x < 1000)
+            {
+                return n_digits + 2;
+            }
+            if (x < 10000)
+            {
+                return n_digits + 3;
+            }
+            x = x / 10000u;
+            n_digits += 4;
+        }
+    }
+
+    /*!
+     * @brief convert a byte to a uppercase hex representation
+     * @param[in] byte byte to represent
+     * @return representation ("00".."FF")
+     */
+    static std::string hex_bytes(std::uint8_t byte)
+    {
+        std::string result = "FF";
+        constexpr const char* nibble_to_hex = "0123456789ABCDEF";
+        result[0] = nibble_to_hex[byte / 16];
+        result[1] = nibble_to_hex[byte % 16];
+        return result;
+    }
+
+    // templates to avoid warnings about useless casts
+    template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
+    bool is_negative_number(NumberType x)
+    {
+        return x < 0;
+    }
+
+    template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >
+    bool is_negative_number(NumberType /*unused*/)
+    {
+        return false;
+    }
+
+    /*!
+    @brief dump an integer
+
+    Dump a given integer to output stream @a o. Works internally with
+    @a number_buffer.
+
+    @param[in] x  integer number (signed or unsigned) to dump
+    @tparam NumberType either @a number_integer_t or @a number_unsigned_t
+    */
+    template < typename NumberType, detail::enable_if_t <
+                   std::is_integral<NumberType>::value ||
+                   std::is_same<NumberType, number_unsigned_t>::value ||
+                   std::is_same<NumberType, number_integer_t>::value ||
+                   std::is_same<NumberType, binary_char_t>::value,
+                   int > = 0 >
+    void dump_integer(NumberType x)
+    {
+        static constexpr std::array<std::array<char, 2>, 100> digits_to_99
+        {
+            {
+                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},
+                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},
+                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},
+                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},
+                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},
+                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},
+                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},
+                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},
+                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},
+                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},
+            }
+        };
+
+        // special case for "0"
+        if (x == 0)
+        {
+            o->write_character('0');
+            return;
+        }
+
+        // use a pointer to fill the buffer
+        auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+
+        number_unsigned_t abs_value;
+
+        unsigned int n_chars{};
+
+        if (is_negative_number(x))
+        {
+            *buffer_ptr = '-';
+            abs_value = remove_sign(static_cast<number_integer_t>(x));
+
+            // account one more byte for the minus sign
+            n_chars = 1 + count_digits(abs_value);
+        }
+        else
+        {
+            abs_value = static_cast<number_unsigned_t>(x);
+            n_chars = count_digits(abs_value);
+        }
+
+        // spare 1 byte for '\0'
+        JSON_ASSERT(n_chars < number_buffer.size() - 1);
+
+        // jump to the end to generate the string from backward,
+        // so we later avoid reversing the result
+        buffer_ptr += n_chars;
+
+        // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
+        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg
+        while (abs_value >= 100)
+        {
+            const auto digits_index = static_cast<unsigned>((abs_value % 100));
+            abs_value /= 100;
+            *(--buffer_ptr) = digits_to_99[digits_index][1];
+            *(--buffer_ptr) = digits_to_99[digits_index][0];
+        }
+
+        if (abs_value >= 10)
+        {
+            const auto digits_index = static_cast<unsigned>(abs_value);
+            *(--buffer_ptr) = digits_to_99[digits_index][1];
+            *(--buffer_ptr) = digits_to_99[digits_index][0];
+        }
+        else
+        {
+            *(--buffer_ptr) = static_cast<char>('0' + abs_value);
+        }
+
+        o->write_characters(number_buffer.data(), n_chars);
+    }
+
+    /*!
+    @brief dump a floating-point number
+
+    Dump a given floating-point number to output stream @a o. Works internally
+    with @a number_buffer.
+
+    @param[in] x  floating-point number to dump
+    */
+    void dump_float(number_float_t x)
+    {
+        // NaN / inf
+        if (!std::isfinite(x))
+        {
+            o->write_characters("null", 4);
+            return;
+        }
+
+        // If number_float_t is an IEEE-754 single or double precision number,
+        // use the Grisu2 algorithm to produce short numbers which are
+        // guaranteed to round-trip, using strtof and strtod, resp.
+        //
+        // NB: The test below works if <long double> == <double>.
+        static constexpr bool is_ieee_single_or_double
+            = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||
+              (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);
+
+        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
+    }
+
+    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
+    {
+        auto* begin = number_buffer.data();
+        auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
+
+        o->write_characters(begin, static_cast<size_t>(end - begin));
+    }
+
+    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
+    {
+        // get number of digits for a float -> text -> float round-trip
+        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
+
+        // the actual conversion
+        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
+
+        // negative value indicates an error
+        JSON_ASSERT(len > 0);
+        // check if buffer was large enough
+        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
+
+        // erase thousands separator
+        if (thousands_sep != '\0')
+        {
+            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
+            const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
+            std::fill(end, number_buffer.end(), '\0');
+            JSON_ASSERT((end - number_buffer.begin()) <= len);
+            len = (end - number_buffer.begin());
+        }
+
+        // convert decimal point to '.'
+        if (decimal_point != '\0' && decimal_point != '.')
+        {
+            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
+            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
+            if (dec_pos != number_buffer.end())
+            {
+                *dec_pos = '.';
+            }
+        }
+
+        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
+
+        // determine if we need to append ".0"
+        const bool value_is_int_like =
+            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
+                         [](char c)
+        {
+            return c == '.' || c == 'e';
+        });
+
+        if (value_is_int_like)
+        {
+            o->write_characters(".0", 2);
+        }
+    }
+
+    /*!
+    @brief check whether a string is UTF-8 encoded
+
+    The function checks each byte of a string whether it is UTF-8 encoded. The
+    result of the check is stored in the @a state parameter. The function must
+    be called initially with state 0 (accept). State 1 means the string must
+    be rejected, because the current byte is not allowed. If the string is
+    completely processed, but the state is non-zero, the string ended
+    prematurely; that is, the last byte indicated more bytes should have
+    followed.
+
+    @param[in,out] state  the state of the decoding
+    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)
+    @param[in] byte       next byte to decode
+    @return               new state
+
+    @note The function has been edited: a std::array is used.
+
+    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+    */
+    static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
+    {
+        static const std::array<std::uint8_t, 400> utf8d =
+        {
+            {
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
+                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
+                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
+                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
+                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
+                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
+                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
+                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
+                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
+                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
+                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
+                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
+            }
+        };
+
+        JSON_ASSERT(byte < utf8d.size());
+        const std::uint8_t type = utf8d[byte];
+
+        codep = (state != UTF8_ACCEPT)
+                ? (byte & 0x3fu) | (codep << 6u)
+                : (0xFFu >> type) & (byte);
+
+        const std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
+        JSON_ASSERT(index < utf8d.size());
+        state = utf8d[index];
+        return state;
+    }
+
+    /*
+     * Overload to make the compiler happy while it is instantiating
+     * dump_integer for number_unsigned_t.
+     * Must never be called.
+     */
+    number_unsigned_t remove_sign(number_unsigned_t x)
+    {
+        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        return x; // LCOV_EXCL_LINE
+    }
+
+    /*
+     * Helper function for dump_integer
+     *
+     * This function takes a negative signed integer and returns its absolute
+     * value as unsigned integer. The plus/minus shuffling is necessary as we can
+     * not directly remove the sign of an arbitrary signed integer as the
+     * absolute values of INT_MIN and INT_MAX are usually not the same. See
+     * #1708 for details.
+     */
+    inline number_unsigned_t remove_sign(number_integer_t x) noexcept
+    {
+        JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
+        return static_cast<number_unsigned_t>(-(x + 1)) + 1;
+    }
+
+  private:
+    /// the output of the serializer
+    output_adapter_t<char> o = nullptr;
+
+    /// a (hopefully) large enough character buffer
+    std::array<char, 64> number_buffer{{}};
+
+    /// the locale
+    const std::lconv* loc = nullptr;
+    /// the locale's thousand separator character
+    const char thousands_sep = '\0';
+    /// the locale's decimal point character
+    const char decimal_point = '\0';
+
+    /// string buffer
+    std::array<char, 512> string_buffer{{}};
+
+    /// the indentation character
+    const char indent_char;
+    /// the indentation string
+    string_t indent_string;
+
+    /// error_handler how to react on decoding errors
+    const error_handler_t error_handler;
+};
+
+}  // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/value_t.hpp>
+
+// #include <nlohmann/json_fwd.hpp>
+
+// #include <nlohmann/ordered_map.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <functional> // equal_to, less
+#include <initializer_list> // initializer_list
+#include <iterator> // input_iterator_tag, iterator_traits
+#include <memory> // allocator
+#include <stdexcept> // for out_of_range
+#include <type_traits> // enable_if, is_convertible
+#include <utility> // pair
+#include <vector> // vector
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// ordered_map: a minimal map-like container that preserves insertion order
+/// for use within nlohmann::basic_json<ordered_map>
+template <class Key, class T, class IgnoredLess = std::less<Key>,
+          class Allocator = std::allocator<std::pair<const Key, T>>>
+                  struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>
+{
+    using key_type = Key;
+    using mapped_type = T;
+    using Container = std::vector<std::pair<const Key, T>, Allocator>;
+    using iterator = typename Container::iterator;
+    using const_iterator = typename Container::const_iterator;
+    using size_type = typename Container::size_type;
+    using value_type = typename Container::value_type;
+#ifdef JSON_HAS_CPP_14
+    using key_compare = std::equal_to<>;
+#else
+    using key_compare = std::equal_to<Key>;
+#endif
+
+    // Explicit constructors instead of `using Container::Container`
+    // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
+    ordered_map() noexcept(noexcept(Container())) : Container{} {}
+    explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
+    template <class It>
+    ordered_map(It first, It last, const Allocator& alloc = Allocator())
+        : Container{first, last, alloc} {}
+    ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
+        : Container{init, alloc} {}
+
+    std::pair<iterator, bool> emplace(const key_type& key, T&& t)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return {it, false};
+            }
+        }
+        Container::emplace_back(key, std::forward<T>(t));
+        return {std::prev(this->end()), true};
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    std::pair<iterator, bool> emplace(KeyType && key, T && t)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return {it, false};
+            }
+        }
+        Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));
+        return {std::prev(this->end()), true};
+    }
+
+    T& operator[](const key_type& key)
+    {
+        return emplace(key, T{}).first->second;
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    T & operator[](KeyType && key)
+    {
+        return emplace(std::forward<KeyType>(key), T{}).first->second;
+    }
+
+    const T& operator[](const key_type& key) const
+    {
+        return at(key);
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    const T & operator[](KeyType && key) const
+    {
+        return at(std::forward<KeyType>(key));
+    }
+
+    T& at(const key_type& key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    T & at(KeyType && key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    const T& at(const key_type& key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    const T & at(KeyType && key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it->second;
+            }
+        }
+
+        JSON_THROW(std::out_of_range("key not found"));
+    }
+
+    size_type erase(const key_type& key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                // Since we cannot move const Keys, re-construct them in place
+                for (auto next = it; ++next != this->end(); ++it)
+                {
+                    it->~value_type(); // Destroy but keep allocation
+                    new (&*it) value_type{std::move(*next)};
+                }
+                Container::pop_back();
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    size_type erase(KeyType && key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                // Since we cannot move const Keys, re-construct them in place
+                for (auto next = it; ++next != this->end(); ++it)
+                {
+                    it->~value_type(); // Destroy but keep allocation
+                    new (&*it) value_type{std::move(*next)};
+                }
+                Container::pop_back();
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    iterator erase(iterator pos)
+    {
+        return erase(pos, std::next(pos));
+    }
+
+    iterator erase(iterator first, iterator last)
+    {
+        if (first == last)
+        {
+            return first;
+        }
+
+        const auto elements_affected = std::distance(first, last);
+        const auto offset = std::distance(Container::begin(), first);
+
+        // This is the start situation. We need to delete elements_affected
+        // elements (3 in this example: e, f, g), and need to return an
+        // iterator past the last deleted element (h in this example).
+        // Note that offset is the distance from the start of the vector
+        // to first. We will need this later.
+
+        // [ a, b, c, d, e, f, g, h, i, j ]
+        //               ^        ^
+        //             first    last
+
+        // Since we cannot move const Keys, we re-construct them in place.
+        // We start at first and re-construct (viz. copy) the elements from
+        // the back of the vector. Example for first iteration:
+
+        //               ,--------.
+        //               v        |   destroy e and re-construct with h
+        // [ a, b, c, d, e, f, g, h, i, j ]
+        //               ^        ^
+        //               it       it + elements_affected
+
+        for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it)
+        {
+            it->~value_type(); // destroy but keep allocation
+            new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it
+        }
+
+        // [ a, b, c, d, h, i, j, h, i, j ]
+        //               ^        ^
+        //             first    last
+
+        // remove the unneeded elements at the end of the vector
+        Container::resize(this->size() - static_cast<size_type>(elements_affected));
+
+        // [ a, b, c, d, h, i, j ]
+        //               ^        ^
+        //             first    last
+
+        // first is now pointing past the last deleted element, but we cannot
+        // use this iterator, because it may have been invalidated by the
+        // resize call. Instead, we can return begin() + offset.
+        return Container::begin() + offset;
+    }
+
+    size_type count(const key_type& key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    size_type count(KeyType && key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    iterator find(const key_type& key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it;
+            }
+        }
+        return Container::end();
+    }
+
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+    iterator find(KeyType && key)
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it;
+            }
+        }
+        return Container::end();
+    }
+
+    const_iterator find(const key_type& key) const
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, key))
+            {
+                return it;
+            }
+        }
+        return Container::end();
+    }
+
+    std::pair<iterator, bool> insert( value_type&& value )
+    {
+        return emplace(value.first, std::move(value.second));
+    }
+
+    std::pair<iterator, bool> insert( const value_type& value )
+    {
+        for (auto it = this->begin(); it != this->end(); ++it)
+        {
+            if (m_compare(it->first, value.first))
+            {
+                return {it, false};
+            }
+        }
+        Container::push_back(value);
+        return {--this->end(), true};
+    }
+
+    template<typename InputIt>
+    using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
+            std::input_iterator_tag>::value>::type;
+
+    template<typename InputIt, typename = require_input_iter<InputIt>>
+    void insert(InputIt first, InputIt last)
+    {
+        for (auto it = first; it != last; ++it)
+        {
+            insert(*it);
+        }
+    }
+
+private:
+    JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare();
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+
+#if defined(JSON_HAS_CPP_17)
+    #include <any>
+    #include <string_view>
+#endif
+
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+@since version 1.0.0
+*/
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief a class to store JSON values
+
+@internal
+@invariant The member variables @a m_value and @a m_type have the following
+relationship:
+- If `m_type == value_t::object`, then `m_value.object != nullptr`.
+- If `m_type == value_t::array`, then `m_value.array != nullptr`.
+- If `m_type == value_t::string`, then `m_value.string != nullptr`.
+The invariants are checked by member function assert_invariant().
+
+@note ObjectType trick from https://stackoverflow.com/a/9860911
+@endinternal
+
+@since version 1.0.0
+
+@nosubgrouping
+*/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
+    : public ::nlohmann::detail::json_base_class<CustomBaseClass>
+{
+  private:
+    template<detail::value_t> friend struct detail::external_constructor;
+
+    template<typename>
+    friend class ::nlohmann::json_pointer;
+    // can be restored when json_pointer backwards compatibility is removed
+    // friend ::nlohmann::json_pointer<StringType>;
+
+    template<typename BasicJsonType, typename InputType>
+    friend class ::nlohmann::detail::parser;
+    friend ::nlohmann::detail::serializer<basic_json>;
+    template<typename BasicJsonType>
+    friend class ::nlohmann::detail::iter_impl;
+    template<typename BasicJsonType, typename CharType>
+    friend class ::nlohmann::detail::binary_writer;
+    template<typename BasicJsonType, typename InputType, typename SAX>
+    friend class ::nlohmann::detail::binary_reader;
+    template<typename BasicJsonType>
+    friend class ::nlohmann::detail::json_sax_dom_parser;
+    template<typename BasicJsonType>
+    friend class ::nlohmann::detail::json_sax_dom_callback_parser;
+    friend class ::nlohmann::detail::exception;
+
+    /// workaround type for MSVC
+    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
+    using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    // convenience aliases for types residing in namespace detail;
+    using lexer = ::nlohmann::detail::lexer_base<basic_json>;
+
+    template<typename InputAdapterType>
+    static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(
+        InputAdapterType adapter,
+        detail::parser_callback_t<basic_json>cb = nullptr,
+        const bool allow_exceptions = true,
+        const bool ignore_comments = false
+                                 )
+    {
+        return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
+                std::move(cb), allow_exceptions, ignore_comments);
+    }
+
+  private:
+    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
+    template<typename BasicJsonType>
+    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
+    template<typename BasicJsonType>
+    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;
+    template<typename Iterator>
+    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;
+    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;
+
+    template<typename CharType>
+    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;
+
+    template<typename InputType>
+    using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;
+    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    using serializer = ::nlohmann::detail::serializer<basic_json>;
+
+  public:
+    using value_t = detail::value_t;
+    /// JSON Pointer, see @ref nlohmann::json_pointer
+    using json_pointer = ::nlohmann::json_pointer<StringType>;
+    template<typename T, typename SFINAE>
+    using json_serializer = JSONSerializer<T, SFINAE>;
+    /// how to treat decoding errors
+    using error_handler_t = detail::error_handler_t;
+    /// how to treat CBOR tags
+    using cbor_tag_handler_t = detail::cbor_tag_handler_t;
+    /// helper type for initializer lists of basic_json values
+    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
+
+    using input_format_t = detail::input_format_t;
+    /// SAX interface type, see @ref nlohmann::json_sax
+    using json_sax_t = json_sax<basic_json>;
+
+    ////////////////
+    // exceptions //
+    ////////////////
+
+    /// @name exceptions
+    /// Classes to implement user-defined exceptions.
+    /// @{
+
+    using exception = detail::exception;
+    using parse_error = detail::parse_error;
+    using invalid_iterator = detail::invalid_iterator;
+    using type_error = detail::type_error;
+    using out_of_range = detail::out_of_range;
+    using other_error = detail::other_error;
+
+    /// @}
+
+
+    /////////////////////
+    // container types //
+    /////////////////////
+
+    /// @name container types
+    /// The canonic container types to use @ref basic_json like any other STL
+    /// container.
+    /// @{
+
+    /// the type of elements in a basic_json container
+    using value_type = basic_json;
+
+    /// the type of an element reference
+    using reference = value_type&;
+    /// the type of an element const reference
+    using const_reference = const value_type&;
+
+    /// a type to represent differences between iterators
+    using difference_type = std::ptrdiff_t;
+    /// a type to represent container sizes
+    using size_type = std::size_t;
+
+    /// the allocator type
+    using allocator_type = AllocatorType<basic_json>;
+
+    /// the type of an element pointer
+    using pointer = typename std::allocator_traits<allocator_type>::pointer;
+    /// the type of an element const pointer
+    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
+
+    /// an iterator for a basic_json container
+    using iterator = iter_impl<basic_json>;
+    /// a const iterator for a basic_json container
+    using const_iterator = iter_impl<const basic_json>;
+    /// a reverse iterator for a basic_json container
+    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;
+    /// a const reverse iterator for a basic_json container
+    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;
+
+    /// @}
+
+
+    /// @brief returns the allocator associated with the container
+    /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/
+    static allocator_type get_allocator()
+    {
+        return allocator_type();
+    }
+
+    /// @brief returns version information on the library
+    /// @sa https://json.nlohmann.me/api/basic_json/meta/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json meta()
+    {
+        basic_json result;
+
+        result["copyright"] = "(C) 2013-2022 Niels Lohmann";
+        result["name"] = "JSON for Modern C++";
+        result["url"] = "https://github.com/nlohmann/json";
+        result["version"]["string"] =
+            detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.',
+                           std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.',
+                           std::to_string(NLOHMANN_JSON_VERSION_PATCH));
+        result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
+        result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
+        result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
+
+#ifdef _WIN32
+        result["platform"] = "win32";
+#elif defined __linux__
+        result["platform"] = "linux";
+#elif defined __APPLE__
+        result["platform"] = "apple";
+#elif defined __unix__
+        result["platform"] = "unix";
+#else
+        result["platform"] = "unknown";
+#endif
+
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+        result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__clang__)
+        result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+        result["compiler"] = {{"family", "gcc"}, {"version", detail::concat(
+                    std::to_string(__GNUC__), '.',
+                    std::to_string(__GNUC_MINOR__), '.',
+                    std::to_string(__GNUC_PATCHLEVEL__))
+            }
+        };
+#elif defined(__HP_cc) || defined(__HP_aCC)
+        result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+        result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+        result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+        result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+        result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+        result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+
+#if defined(_MSVC_LANG)
+        result["compiler"]["c++"] = std::to_string(_MSVC_LANG);
+#elif defined(__cplusplus)
+        result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+        result["compiler"]["c++"] = "unknown";
+#endif
+        return result;
+    }
+
+
+    ///////////////////////////
+    // JSON value data types //
+    ///////////////////////////
+
+    /// @name JSON value data types
+    /// The data types to store a JSON value. These types are derived from
+    /// the template arguments passed to class @ref basic_json.
+    /// @{
+
+    /// @brief default object key comparator type
+    /// The actual object key comparator type (@ref object_comparator_t) may be
+    /// different.
+    /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
+#if defined(JSON_HAS_CPP_14)
+    // use of transparent comparator avoids unnecessary repeated construction of temporaries
+    // in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
+    using default_object_comparator_t = std::less<>;
+#else
+    using default_object_comparator_t = std::less<StringType>;
+#endif
+
+    /// @brief a type for an object
+    /// @sa https://json.nlohmann.me/api/basic_json/object_t/
+    using object_t = ObjectType<StringType,
+          basic_json,
+          default_object_comparator_t,
+          AllocatorType<std::pair<const StringType,
+          basic_json>>>;
+
+    /// @brief a type for an array
+    /// @sa https://json.nlohmann.me/api/basic_json/array_t/
+    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
+
+    /// @brief a type for a string
+    /// @sa https://json.nlohmann.me/api/basic_json/string_t/
+    using string_t = StringType;
+
+    /// @brief a type for a boolean
+    /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/
+    using boolean_t = BooleanType;
+
+    /// @brief a type for a number (integer)
+    /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/
+    using number_integer_t = NumberIntegerType;
+
+    /// @brief a type for a number (unsigned)
+    /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/
+    using number_unsigned_t = NumberUnsignedType;
+
+    /// @brief a type for a number (floating-point)
+    /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/
+    using number_float_t = NumberFloatType;
+
+    /// @brief a type for a packed binary type
+    /// @sa https://json.nlohmann.me/api/basic_json/binary_t/
+    using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
+
+    /// @brief object key comparator type
+    /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/
+    using object_comparator_t = detail::actual_object_comparator_t<basic_json>;
+
+    /// @}
+
+  private:
+
+    /// helper for exception-safe object creation
+    template<typename T, typename... Args>
+    JSON_HEDLEY_RETURNS_NON_NULL
+    static T* create(Args&& ... args)
+    {
+        AllocatorType<T> alloc;
+        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;
+
+        auto deleter = [&](T * obj)
+        {
+            AllocatorTraits::deallocate(alloc, obj, 1);
+        };
+        std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);
+        AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);
+        JSON_ASSERT(obj != nullptr);
+        return obj.release();
+    }
+
+    ////////////////////////
+    // JSON value storage //
+    ////////////////////////
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    /*!
+    @brief a JSON value
+
+    The actual storage for a JSON value of the @ref basic_json class. This
+    union combines the different storage types for the JSON value types
+    defined in @ref value_t.
+
+    JSON type | value_t type    | used type
+    --------- | --------------- | ------------------------
+    object    | object          | pointer to @ref object_t
+    array     | array           | pointer to @ref array_t
+    string    | string          | pointer to @ref string_t
+    boolean   | boolean         | @ref boolean_t
+    number    | number_integer  | @ref number_integer_t
+    number    | number_unsigned | @ref number_unsigned_t
+    number    | number_float    | @ref number_float_t
+    binary    | binary          | pointer to @ref binary_t
+    null      | null            | *no value is stored*
+
+    @note Variable-length types (objects, arrays, and strings) are stored as
+    pointers. The size of the union should not exceed 64 bits if the default
+    value types are used.
+
+    @since version 1.0.0
+    */
+    union json_value
+    {
+        /// object (stored with pointer to save storage)
+        object_t* object;
+        /// array (stored with pointer to save storage)
+        array_t* array;
+        /// string (stored with pointer to save storage)
+        string_t* string;
+        /// binary (stored with pointer to save storage)
+        binary_t* binary;
+        /// boolean
+        boolean_t boolean;
+        /// number (integer)
+        number_integer_t number_integer;
+        /// number (unsigned integer)
+        number_unsigned_t number_unsigned;
+        /// number (floating-point)
+        number_float_t number_float;
+
+        /// default constructor (for null values)
+        json_value() = default;
+        /// constructor for booleans
+        json_value(boolean_t v) noexcept : boolean(v) {}
+        /// constructor for numbers (integer)
+        json_value(number_integer_t v) noexcept : number_integer(v) {}
+        /// constructor for numbers (unsigned)
+        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}
+        /// constructor for numbers (floating-point)
+        json_value(number_float_t v) noexcept : number_float(v) {}
+        /// constructor for empty values of a given type
+        json_value(value_t t)
+        {
+            switch (t)
+            {
+                case value_t::object:
+                {
+                    object = create<object_t>();
+                    break;
+                }
+
+                case value_t::array:
+                {
+                    array = create<array_t>();
+                    break;
+                }
+
+                case value_t::string:
+                {
+                    string = create<string_t>("");
+                    break;
+                }
+
+                case value_t::binary:
+                {
+                    binary = create<binary_t>();
+                    break;
+                }
+
+                case value_t::boolean:
+                {
+                    boolean = static_cast<boolean_t>(false);
+                    break;
+                }
+
+                case value_t::number_integer:
+                {
+                    number_integer = static_cast<number_integer_t>(0);
+                    break;
+                }
+
+                case value_t::number_unsigned:
+                {
+                    number_unsigned = static_cast<number_unsigned_t>(0);
+                    break;
+                }
+
+                case value_t::number_float:
+                {
+                    number_float = static_cast<number_float_t>(0.0);
+                    break;
+                }
+
+                case value_t::null:
+                {
+                    object = nullptr;  // silence warning, see #821
+                    break;
+                }
+
+                case value_t::discarded:
+                default:
+                {
+                    object = nullptr;  // silence warning, see #821
+                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
+                    {
+                        JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.11.2", nullptr)); // LCOV_EXCL_LINE
+                    }
+                    break;
+                }
+            }
+        }
+
+        /// constructor for strings
+        json_value(const string_t& value) : string(create<string_t>(value)) {}
+
+        /// constructor for rvalue strings
+        json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}
+
+        /// constructor for objects
+        json_value(const object_t& value) : object(create<object_t>(value)) {}
+
+        /// constructor for rvalue objects
+        json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}
+
+        /// constructor for arrays
+        json_value(const array_t& value) : array(create<array_t>(value)) {}
+
+        /// constructor for rvalue arrays
+        json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}
+
+        /// constructor for binary arrays
+        json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}
+
+        /// constructor for rvalue binary arrays
+        json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}
+
+        /// constructor for binary arrays (internal type)
+        json_value(const binary_t& value) : binary(create<binary_t>(value)) {}
+
+        /// constructor for rvalue binary arrays (internal type)
+        json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}
+
+        void destroy(value_t t)
+        {
+            if (
+                (t == value_t::object && object == nullptr) ||
+                (t == value_t::array && array == nullptr) ||
+                (t == value_t::string && string == nullptr) ||
+                (t == value_t::binary && binary == nullptr)
+            )
+            {
+                //not initialized (e.g. due to exception in the ctor)
+                return;
+            }
+            if (t == value_t::array || t == value_t::object)
+            {
+                // flatten the current json_value to a heap-allocated stack
+                std::vector<basic_json> stack;
+
+                // move the top-level items to stack
+                if (t == value_t::array)
+                {
+                    stack.reserve(array->size());
+                    std::move(array->begin(), array->end(), std::back_inserter(stack));
+                }
+                else
+                {
+                    stack.reserve(object->size());
+                    for (auto&& it : *object)
+                    {
+                        stack.push_back(std::move(it.second));
+                    }
+                }
+
+                while (!stack.empty())
+                {
+                    // move the last item to local variable to be processed
+                    basic_json current_item(std::move(stack.back()));
+                    stack.pop_back();
+
+                    // if current_item is array/object, move
+                    // its children to the stack to be processed later
+                    if (current_item.is_array())
+                    {
+                        std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack));
+
+                        current_item.m_data.m_value.array->clear();
+                    }
+                    else if (current_item.is_object())
+                    {
+                        for (auto&& it : *current_item.m_data.m_value.object)
+                        {
+                            stack.push_back(std::move(it.second));
+                        }
+
+                        current_item.m_data.m_value.object->clear();
+                    }
+
+                    // it's now safe that current_item get destructed
+                    // since it doesn't have any children
+                }
+            }
+
+            switch (t)
+            {
+                case value_t::object:
+                {
+                    AllocatorType<object_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
+                    break;
+                }
+
+                case value_t::array:
+                {
+                    AllocatorType<array_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
+                    break;
+                }
+
+                case value_t::string:
+                {
+                    AllocatorType<string_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
+                    break;
+                }
+
+                case value_t::binary:
+                {
+                    AllocatorType<binary_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);
+                    break;
+                }
+
+                case value_t::null:
+                case value_t::boolean:
+                case value_t::number_integer:
+                case value_t::number_unsigned:
+                case value_t::number_float:
+                case value_t::discarded:
+                default:
+                {
+                    break;
+                }
+            }
+        }
+    };
+
+  private:
+    /*!
+    @brief checks the class invariants
+
+    This function asserts the class invariants. It needs to be called at the
+    end of every constructor to make sure that created objects respect the
+    invariant. Furthermore, it has to be called each time the type of a JSON
+    value is changed, because the invariant expresses a relationship between
+    @a m_type and @a m_value.
+
+    Furthermore, the parent relation is checked for arrays and objects: If
+    @a check_parents true and the value is an array or object, then the
+    container's elements must have the current value as parent.
+
+    @param[in] check_parents  whether the parent relation should be checked.
+               The value is true by default and should only be set to false
+               during destruction of objects when the invariant does not
+               need to hold.
+    */
+    void assert_invariant(bool check_parents = true) const noexcept
+    {
+        JSON_ASSERT(m_data.m_type != value_t::object || m_data.m_value.object != nullptr);
+        JSON_ASSERT(m_data.m_type != value_t::array || m_data.m_value.array != nullptr);
+        JSON_ASSERT(m_data.m_type != value_t::string || m_data.m_value.string != nullptr);
+        JSON_ASSERT(m_data.m_type != value_t::binary || m_data.m_value.binary != nullptr);
+
+#if JSON_DIAGNOSTICS
+        JSON_TRY
+        {
+            // cppcheck-suppress assertWithSideEffect
+            JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)
+            {
+                return j.m_parent == this;
+            }));
+        }
+        JSON_CATCH(...) {} // LCOV_EXCL_LINE
+#endif
+        static_cast<void>(check_parents);
+    }
+
+    void set_parents()
+    {
+#if JSON_DIAGNOSTICS
+        switch (m_data.m_type)
+        {
+            case value_t::array:
+            {
+                for (auto& element : *m_data.m_value.array)
+                {
+                    element.m_parent = this;
+                }
+                break;
+            }
+
+            case value_t::object:
+            {
+                for (auto& element : *m_data.m_value.object)
+                {
+                    element.second.m_parent = this;
+                }
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                break;
+        }
+#endif
+    }
+
+    iterator set_parents(iterator it, typename iterator::difference_type count_set_parents)
+    {
+#if JSON_DIAGNOSTICS
+        for (typename iterator::difference_type i = 0; i < count_set_parents; ++i)
+        {
+            (it + i)->m_parent = this;
+        }
+#else
+        static_cast<void>(count_set_parents);
+#endif
+        return it;
+    }
+
+    reference set_parent(reference j, std::size_t old_capacity = static_cast<std::size_t>(-1))
+    {
+#if JSON_DIAGNOSTICS
+        if (old_capacity != static_cast<std::size_t>(-1))
+        {
+            // see https://github.com/nlohmann/json/issues/2838
+            JSON_ASSERT(type() == value_t::array);
+            if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity))
+            {
+                // capacity has changed: update all parents
+                set_parents();
+                return j;
+            }
+        }
+
+        // ordered_json uses a vector internally, so pointers could have
+        // been invalidated; see https://github.com/nlohmann/json/issues/2962
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning(push )
+#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr
+#endif
+        if (detail::is_ordered_map<object_t>::value)
+        {
+            set_parents();
+            return j;
+        }
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning( pop )
+#endif
+
+        j.m_parent = this;
+#else
+        static_cast<void>(j);
+        static_cast<void>(old_capacity);
+#endif
+        return j;
+    }
+
+  public:
+    //////////////////////////
+    // JSON parser callback //
+    //////////////////////////
+
+    /// @brief parser event types
+    /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/
+    using parse_event_t = detail::parse_event_t;
+
+    /// @brief per-element parser callback type
+    /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/
+    using parser_callback_t = detail::parser_callback_t<basic_json>;
+
+    //////////////////
+    // constructors //
+    //////////////////
+
+    /// @name constructors and destructors
+    /// Constructors of class @ref basic_json, copy/move constructor, copy
+    /// assignment, static functions creating objects, and the destructor.
+    /// @{
+
+    /// @brief create an empty value with a given type
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(const value_t v)
+        : m_data(v)
+    {
+        assert_invariant();
+    }
+
+    /// @brief create a null object
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape)
+        : basic_json(value_t::null)
+    {
+        assert_invariant();
+    }
+
+    /// @brief create a JSON value from compatible types
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    template < typename CompatibleType,
+               typename U = detail::uncvref_t<CompatibleType>,
+               detail::enable_if_t <
+                   !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >
+    basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
+                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
+                                           std::forward<CompatibleType>(val))))
+    {
+        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief create a JSON value from an existing one
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    template < typename BasicJsonType,
+               detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >
+    basic_json(const BasicJsonType& val)
+    {
+        using other_boolean_t = typename BasicJsonType::boolean_t;
+        using other_number_float_t = typename BasicJsonType::number_float_t;
+        using other_number_integer_t = typename BasicJsonType::number_integer_t;
+        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+        using other_string_t = typename BasicJsonType::string_t;
+        using other_object_t = typename BasicJsonType::object_t;
+        using other_array_t = typename BasicJsonType::array_t;
+        using other_binary_t = typename BasicJsonType::binary_t;
+
+        switch (val.type())
+        {
+            case value_t::boolean:
+                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
+                break;
+            case value_t::number_float:
+                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
+                break;
+            case value_t::number_integer:
+                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
+                break;
+            case value_t::number_unsigned:
+                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
+                break;
+            case value_t::string:
+                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
+                break;
+            case value_t::object:
+                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
+                break;
+            case value_t::array:
+                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
+                break;
+            case value_t::binary:
+                JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
+                break;
+            case value_t::null:
+                *this = nullptr;
+                break;
+            case value_t::discarded:
+                m_data.m_type = value_t::discarded;
+                break;
+            default:            // LCOV_EXCL_LINE
+                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+        }
+        JSON_ASSERT(m_data.m_type == val.type());
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief create a container (array or object) from an initializer list
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(initializer_list_t init,
+               bool type_deduction = true,
+               value_t manual_type = value_t::array)
+    {
+        // check if each element is an array with two elements whose first
+        // element is a string
+        bool is_an_object = std::all_of(init.begin(), init.end(),
+                                        [](const detail::json_ref<basic_json>& element_ref)
+        {
+            return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();
+        });
+
+        // adjust type if type deduction is not wanted
+        if (!type_deduction)
+        {
+            // if array is wanted, do not create an object though possible
+            if (manual_type == value_t::array)
+            {
+                is_an_object = false;
+            }
+
+            // if object is wanted but impossible, throw an exception
+            if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
+            {
+                JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr));
+            }
+        }
+
+        if (is_an_object)
+        {
+            // the initializer list is a list of pairs -> create object
+            m_data.m_type = value_t::object;
+            m_data.m_value = value_t::object;
+
+            for (auto& element_ref : init)
+            {
+                auto element = element_ref.moved_or_copied();
+                m_data.m_value.object->emplace(
+                    std::move(*((*element.m_data.m_value.array)[0].m_data.m_value.string)),
+                    std::move((*element.m_data.m_value.array)[1]));
+            }
+        }
+        else
+        {
+            // the initializer list describes an array -> create array
+            m_data.m_type = value_t::array;
+            m_data.m_value.array = create<array_t>(init.begin(), init.end());
+        }
+
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief explicitly create a binary array (without subtype)
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(const typename binary_t::container_type& init)
+    {
+        auto res = basic_json();
+        res.m_data.m_type = value_t::binary;
+        res.m_data.m_value = init;
+        return res;
+    }
+
+    /// @brief explicitly create a binary array (with subtype)
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
+    {
+        auto res = basic_json();
+        res.m_data.m_type = value_t::binary;
+        res.m_data.m_value = binary_t(init, subtype);
+        return res;
+    }
+
+    /// @brief explicitly create a binary array
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(typename binary_t::container_type&& init)
+    {
+        auto res = basic_json();
+        res.m_data.m_type = value_t::binary;
+        res.m_data.m_value = std::move(init);
+        return res;
+    }
+
+    /// @brief explicitly create a binary array (with subtype)
+    /// @sa https://json.nlohmann.me/api/basic_json/binary/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
+    {
+        auto res = basic_json();
+        res.m_data.m_type = value_t::binary;
+        res.m_data.m_value = binary_t(std::move(init), subtype);
+        return res;
+    }
+
+    /// @brief explicitly create an array from an initializer list
+    /// @sa https://json.nlohmann.me/api/basic_json/array/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json array(initializer_list_t init = {})
+    {
+        return basic_json(init, false, value_t::array);
+    }
+
+    /// @brief explicitly create an object from an initializer list
+    /// @sa https://json.nlohmann.me/api/basic_json/object/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json object(initializer_list_t init = {})
+    {
+        return basic_json(init, false, value_t::object);
+    }
+
+    /// @brief construct an array with count copies of given value
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(size_type cnt, const basic_json& val):
+        m_data{cnt, val}
+    {
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief construct a JSON container given an iterator range
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    template < class InputIT, typename std::enable_if <
+                   std::is_same<InputIT, typename basic_json_t::iterator>::value ||
+                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >
+    basic_json(InputIT first, InputIT last)
+    {
+        JSON_ASSERT(first.m_object != nullptr);
+        JSON_ASSERT(last.m_object != nullptr);
+
+        // make sure iterator fits the current value
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr));
+        }
+
+        // copy type from first iterator
+        m_data.m_type = first.m_object->m_data.m_type;
+
+        // check if iterator range is complete for primitive values
+        switch (m_data.m_type)
+        {
+            case value_t::boolean:
+            case value_t::number_float:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::string:
+            {
+                if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
+                                         || !last.m_it.primitive_iterator.is_end()))
+                {
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object));
+                }
+                break;
+            }
+
+            case value_t::null:
+            case value_t::object:
+            case value_t::array:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+                break;
+        }
+
+        switch (m_data.m_type)
+        {
+            case value_t::number_integer:
+            {
+                m_data.m_value.number_integer = first.m_object->m_data.m_value.number_integer;
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                m_data.m_value.number_unsigned = first.m_object->m_data.m_value.number_unsigned;
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                m_data.m_value.number_float = first.m_object->m_data.m_value.number_float;
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                m_data.m_value.boolean = first.m_object->m_data.m_value.boolean;
+                break;
+            }
+
+            case value_t::string:
+            {
+                m_data.m_value = *first.m_object->m_data.m_value.string;
+                break;
+            }
+
+            case value_t::object:
+            {
+                m_data.m_value.object = create<object_t>(first.m_it.object_iterator,
+                                        last.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_data.m_value.array = create<array_t>(first.m_it.array_iterator,
+                                                       last.m_it.array_iterator);
+                break;
+            }
+
+            case value_t::binary:
+            {
+                m_data.m_value = *first.m_object->m_data.m_value.binary;
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
+        }
+
+        set_parents();
+        assert_invariant();
+    }
+
+
+    ///////////////////////////////////////
+    // other constructors and destructor //
+    ///////////////////////////////////////
+
+    template<typename JsonRef,
+             detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
+                                 std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
+    basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}
+
+    /// @brief copy constructor
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(const basic_json& other)
+        : json_base_class_t(other)
+    {
+        m_data.m_type = other.m_data.m_type;
+        // check of passed value is valid
+        other.assert_invariant();
+
+        switch (m_data.m_type)
+        {
+            case value_t::object:
+            {
+                m_data.m_value = *other.m_data.m_value.object;
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_data.m_value = *other.m_data.m_value.array;
+                break;
+            }
+
+            case value_t::string:
+            {
+                m_data.m_value = *other.m_data.m_value.string;
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                m_data.m_value = other.m_data.m_value.boolean;
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                m_data.m_value = other.m_data.m_value.number_integer;
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                m_data.m_value = other.m_data.m_value.number_unsigned;
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                m_data.m_value = other.m_data.m_value.number_float;
+                break;
+            }
+
+            case value_t::binary:
+            {
+                m_data.m_value = *other.m_data.m_value.binary;
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                break;
+        }
+
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief move constructor
+    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+    basic_json(basic_json&& other) noexcept
+        : json_base_class_t(std::forward<json_base_class_t>(other)),
+          m_data(std::move(other.m_data))
+    {
+        // check that passed value is valid
+        other.assert_invariant(false);
+
+        // invalidate payload
+        other.m_data.m_type = value_t::null;
+        other.m_data.m_value = {};
+
+        set_parents();
+        assert_invariant();
+    }
+
+    /// @brief copy assignment
+    /// @sa https://json.nlohmann.me/api/basic_json/operator=/
+    basic_json& operator=(basic_json other) noexcept (
+        std::is_nothrow_move_constructible<value_t>::value&&
+        std::is_nothrow_move_assignable<value_t>::value&&
+        std::is_nothrow_move_constructible<json_value>::value&&
+        std::is_nothrow_move_assignable<json_value>::value&&
+        std::is_nothrow_move_assignable<json_base_class_t>::value
+    )
+    {
+        // check that passed value is valid
+        other.assert_invariant();
+
+        using std::swap;
+        swap(m_data.m_type, other.m_data.m_type);
+        swap(m_data.m_value, other.m_data.m_value);
+        json_base_class_t::operator=(std::move(other));
+
+        set_parents();
+        assert_invariant();
+        return *this;
+    }
+
+    /// @brief destructor
+    /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/
+    ~basic_json() noexcept
+    {
+        assert_invariant(false);
+    }
+
+    /// @}
+
+  public:
+    ///////////////////////
+    // object inspection //
+    ///////////////////////
+
+    /// @name object inspection
+    /// Functions to inspect the type of a JSON value.
+    /// @{
+
+    /// @brief serialization
+    /// @sa https://json.nlohmann.me/api/basic_json/dump/
+    string_t dump(const int indent = -1,
+                  const char indent_char = ' ',
+                  const bool ensure_ascii = false,
+                  const error_handler_t error_handler = error_handler_t::strict) const
+    {
+        string_t result;
+        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);
+
+        if (indent >= 0)
+        {
+            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
+        }
+        else
+        {
+            s.dump(*this, false, ensure_ascii, 0);
+        }
+
+        return result;
+    }
+
+    /// @brief return the type of the JSON value (explicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/type/
+    constexpr value_t type() const noexcept
+    {
+        return m_data.m_type;
+    }
+
+    /// @brief return whether type is primitive
+    /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/
+    constexpr bool is_primitive() const noexcept
+    {
+        return is_null() || is_string() || is_boolean() || is_number() || is_binary();
+    }
+
+    /// @brief return whether type is structured
+    /// @sa https://json.nlohmann.me/api/basic_json/is_structured/
+    constexpr bool is_structured() const noexcept
+    {
+        return is_array() || is_object();
+    }
+
+    /// @brief return whether value is null
+    /// @sa https://json.nlohmann.me/api/basic_json/is_null/
+    constexpr bool is_null() const noexcept
+    {
+        return m_data.m_type == value_t::null;
+    }
+
+    /// @brief return whether value is a boolean
+    /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/
+    constexpr bool is_boolean() const noexcept
+    {
+        return m_data.m_type == value_t::boolean;
+    }
+
+    /// @brief return whether value is a number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number/
+    constexpr bool is_number() const noexcept
+    {
+        return is_number_integer() || is_number_float();
+    }
+
+    /// @brief return whether value is an integer number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/
+    constexpr bool is_number_integer() const noexcept
+    {
+        return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned;
+    }
+
+    /// @brief return whether value is an unsigned integer number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/
+    constexpr bool is_number_unsigned() const noexcept
+    {
+        return m_data.m_type == value_t::number_unsigned;
+    }
+
+    /// @brief return whether value is a floating-point number
+    /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/
+    constexpr bool is_number_float() const noexcept
+    {
+        return m_data.m_type == value_t::number_float;
+    }
+
+    /// @brief return whether value is an object
+    /// @sa https://json.nlohmann.me/api/basic_json/is_object/
+    constexpr bool is_object() const noexcept
+    {
+        return m_data.m_type == value_t::object;
+    }
+
+    /// @brief return whether value is an array
+    /// @sa https://json.nlohmann.me/api/basic_json/is_array/
+    constexpr bool is_array() const noexcept
+    {
+        return m_data.m_type == value_t::array;
+    }
+
+    /// @brief return whether value is a string
+    /// @sa https://json.nlohmann.me/api/basic_json/is_string/
+    constexpr bool is_string() const noexcept
+    {
+        return m_data.m_type == value_t::string;
+    }
+
+    /// @brief return whether value is a binary array
+    /// @sa https://json.nlohmann.me/api/basic_json/is_binary/
+    constexpr bool is_binary() const noexcept
+    {
+        return m_data.m_type == value_t::binary;
+    }
+
+    /// @brief return whether value is discarded
+    /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/
+    constexpr bool is_discarded() const noexcept
+    {
+        return m_data.m_type == value_t::discarded;
+    }
+
+    /// @brief return the type of the JSON value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/
+    constexpr operator value_t() const noexcept
+    {
+        return m_data.m_type;
+    }
+
+    /// @}
+
+  private:
+    //////////////////
+    // value access //
+    //////////////////
+
+    /// get a boolean (explicit)
+    boolean_t get_impl(boolean_t* /*unused*/) const
+    {
+        if (JSON_HEDLEY_LIKELY(is_boolean()))
+        {
+            return m_data.m_value.boolean;
+        }
+
+        JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this));
+    }
+
+    /// get a pointer to the value (object)
+    object_t* get_impl_ptr(object_t* /*unused*/) noexcept
+    {
+        return is_object() ? m_data.m_value.object : nullptr;
+    }
+
+    /// get a pointer to the value (object)
+    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
+    {
+        return is_object() ? m_data.m_value.object : nullptr;
+    }
+
+    /// get a pointer to the value (array)
+    array_t* get_impl_ptr(array_t* /*unused*/) noexcept
+    {
+        return is_array() ? m_data.m_value.array : nullptr;
+    }
+
+    /// get a pointer to the value (array)
+    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
+    {
+        return is_array() ? m_data.m_value.array : nullptr;
+    }
+
+    /// get a pointer to the value (string)
+    string_t* get_impl_ptr(string_t* /*unused*/) noexcept
+    {
+        return is_string() ? m_data.m_value.string : nullptr;
+    }
+
+    /// get a pointer to the value (string)
+    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
+    {
+        return is_string() ? m_data.m_value.string : nullptr;
+    }
+
+    /// get a pointer to the value (boolean)
+    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
+    {
+        return is_boolean() ? &m_data.m_value.boolean : nullptr;
+    }
+
+    /// get a pointer to the value (boolean)
+    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
+    {
+        return is_boolean() ? &m_data.m_value.boolean : nullptr;
+    }
+
+    /// get a pointer to the value (integer number)
+    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
+    {
+        return is_number_integer() ? &m_data.m_value.number_integer : nullptr;
+    }
+
+    /// get a pointer to the value (integer number)
+    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
+    {
+        return is_number_integer() ? &m_data.m_value.number_integer : nullptr;
+    }
+
+    /// get a pointer to the value (unsigned number)
+    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
+    {
+        return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;
+    }
+
+    /// get a pointer to the value (unsigned number)
+    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
+    {
+        return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;
+    }
+
+    /// get a pointer to the value (floating-point number)
+    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
+    {
+        return is_number_float() ? &m_data.m_value.number_float : nullptr;
+    }
+
+    /// get a pointer to the value (floating-point number)
+    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
+    {
+        return is_number_float() ? &m_data.m_value.number_float : nullptr;
+    }
+
+    /// get a pointer to the value (binary)
+    binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept
+    {
+        return is_binary() ? m_data.m_value.binary : nullptr;
+    }
+
+    /// get a pointer to the value (binary)
+    constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept
+    {
+        return is_binary() ? m_data.m_value.binary : nullptr;
+    }
+
+    /*!
+    @brief helper function to implement get_ref()
+
+    This function helps to implement get_ref() without code duplication for
+    const and non-const overloads
+
+    @tparam ThisType will be deduced as `basic_json` or `const basic_json`
+
+    @throw type_error.303 if ReferenceType does not match underlying value
+    type of the current JSON
+    */
+    template<typename ReferenceType, typename ThisType>
+    static ReferenceType get_ref_impl(ThisType& obj)
+    {
+        // delegate the call to get_ptr<>()
+        auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
+
+        if (JSON_HEDLEY_LIKELY(ptr != nullptr))
+        {
+            return *ptr;
+        }
+
+        JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
+    }
+
+  public:
+    /// @name value access
+    /// Direct access to the stored value of a JSON value.
+    /// @{
+
+    /// @brief get a pointer value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+    template<typename PointerType, typename std::enable_if<
+                 std::is_pointer<PointerType>::value, int>::type = 0>
+    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+    {
+        // delegate the call to get_impl_ptr<>()
+        return get_impl_ptr(static_cast<PointerType>(nullptr));
+    }
+
+    /// @brief get a pointer value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+    template < typename PointerType, typename std::enable_if <
+                   std::is_pointer<PointerType>::value&&
+                   std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >
+    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+    {
+        // delegate the call to get_impl_ptr<>() const
+        return get_impl_ptr(static_cast<PointerType>(nullptr));
+    }
+
+  private:
+    /*!
+    @brief get a value (explicit)
+
+    Explicit type conversion between the JSON value and a compatible value
+    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
+    The value is converted by calling the @ref json_serializer<ValueType>
+    `from_json()` method.
+
+    The function is equivalent to executing
+    @code {.cpp}
+    ValueType ret;
+    JSONSerializer<ValueType>::from_json(*this, ret);
+    return ret;
+    @endcode
+
+    This overloads is chosen if:
+    - @a ValueType is not @ref basic_json,
+    - @ref json_serializer<ValueType> has a `from_json()` method of the form
+      `void from_json(const basic_json&, ValueType&)`, and
+    - @ref json_serializer<ValueType> does not have a `from_json()` method of
+      the form `ValueType from_json(const basic_json&)`
+
+    @tparam ValueType the returned value type
+
+    @return copy of the JSON value, converted to @a ValueType
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+    @liveexample{The example below shows several conversions from JSON values
+    to other types. There a few things to note: (1) Floating-point numbers can
+    be converted to integers\, (2) A JSON array can be converted to a standard
+    `std::vector<short>`\, (3) A JSON object can be converted to C++
+    associative containers such as `std::unordered_map<std::string\,
+    json>`.,get__ValueType_const}
+
+    @since version 2.1.0
+    */
+    template < typename ValueType,
+               detail::enable_if_t <
+                   detail::is_default_constructible<ValueType>::value&&
+                   detail::has_from_json<basic_json_t, ValueType>::value,
+                   int > = 0 >
+    ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(
+                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
+    {
+        auto ret = ValueType();
+        JSONSerializer<ValueType>::from_json(*this, ret);
+        return ret;
+    }
+
+    /*!
+    @brief get a value (explicit); special case
+
+    Explicit type conversion between the JSON value and a compatible value
+    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
+    The value is converted by calling the @ref json_serializer<ValueType>
+    `from_json()` method.
+
+    The function is equivalent to executing
+    @code {.cpp}
+    return JSONSerializer<ValueType>::from_json(*this);
+    @endcode
+
+    This overloads is chosen if:
+    - @a ValueType is not @ref basic_json and
+    - @ref json_serializer<ValueType> has a `from_json()` method of the form
+      `ValueType from_json(const basic_json&)`
+
+    @note If @ref json_serializer<ValueType> has both overloads of
+    `from_json()`, this one is chosen.
+
+    @tparam ValueType the returned value type
+
+    @return copy of the JSON value, converted to @a ValueType
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+    @since version 2.1.0
+    */
+    template < typename ValueType,
+               detail::enable_if_t <
+                   detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+                   int > = 0 >
+    ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(
+                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))
+    {
+        return JSONSerializer<ValueType>::from_json(*this);
+    }
+
+    /*!
+    @brief get special-case overload
+
+    This overloads converts the current @ref basic_json in a different
+    @ref basic_json type
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this, converted into @a BasicJsonType
+
+    @complexity Depending on the implementation of the called `from_json()`
+                method.
+
+    @since version 3.2.0
+    */
+    template < typename BasicJsonType,
+               detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value,
+                   int > = 0 >
+    BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
+    {
+        return *this;
+    }
+
+    /*!
+    @brief get special-case overload
+
+    This overloads avoids a lot of template boilerplate, it can be seen as the
+    identity method
+
+    @tparam BasicJsonType == @ref basic_json
+
+    @return a copy of *this
+
+    @complexity Constant.
+
+    @since version 2.1.0
+    */
+    template<typename BasicJsonType,
+             detail::enable_if_t<
+                 std::is_same<BasicJsonType, basic_json_t>::value,
+                 int> = 0>
+    basic_json get_impl(detail::priority_tag<3> /*unused*/) const
+    {
+        return *this;
+    }
+
+    /*!
+    @brief get a pointer value (explicit)
+    @copydoc get()
+    */
+    template<typename PointerType,
+             detail::enable_if_t<
+                 std::is_pointer<PointerType>::value,
+                 int> = 0>
+    constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept
+    -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
+    {
+        // delegate the call to get_ptr
+        return get_ptr<PointerType>();
+    }
+
+  public:
+    /*!
+    @brief get a (pointer) value (explicit)
+
+    Performs explicit type conversion between the JSON value and a compatible value if required.
+
+    - If the requested type is a pointer to the internally stored JSON value that pointer is returned.
+    No copies are made.
+
+    - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible
+    from the current @ref basic_json.
+
+    - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`
+    method.
+
+    @tparam ValueTypeCV the provided value type
+    @tparam ValueType the returned value type
+
+    @return copy of the JSON value, converted to @tparam ValueType if necessary
+
+    @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
+
+    @since version 2.1.0
+    */
+    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
+#if defined(JSON_HAS_CPP_14)
+    constexpr
+#endif
+    auto get() const noexcept(
+    noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))
+    -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))
+    {
+        // we cannot static_assert on ValueTypeCV being non-const, because
+        // there is support for get<const basic_json_t>(), which is why we
+        // still need the uncvref
+        static_assert(!std::is_reference<ValueTypeCV>::value,
+                      "get() cannot be used with reference types, you might want to use get_ref()");
+        return get_impl<ValueType>(detail::priority_tag<4> {});
+    }
+
+    /*!
+    @brief get a pointer value (explicit)
+
+    Explicit pointer access to the internally stored JSON value. No copies are
+    made.
+
+    @warning The pointer becomes invalid if the underlying JSON object
+    changes.
+
+    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
+    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
+    @ref number_unsigned_t, or @ref number_float_t.
+
+    @return pointer to the internally stored JSON value if the requested
+    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
+
+    @complexity Constant.
+
+    @liveexample{The example below shows how pointers to internal values of a
+    JSON value can be requested. Note that no type conversions are made and a
+    `nullptr` is returned if the value and the requested pointer type does not
+    match.,get__PointerType}
+
+    @sa see @ref get_ptr() for explicit pointer-member access
+
+    @since version 1.0.0
+    */
+    template<typename PointerType, typename std::enable_if<
+                 std::is_pointer<PointerType>::value, int>::type = 0>
+    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
+    {
+        // delegate the call to get_ptr
+        return get_ptr<PointerType>();
+    }
+
+    /// @brief get a value (explicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_to/
+    template < typename ValueType,
+               detail::enable_if_t <
+                   !detail::is_basic_json<ValueType>::value&&
+                   detail::has_from_json<basic_json_t, ValueType>::value,
+                   int > = 0 >
+    ValueType & get_to(ValueType& v) const noexcept(noexcept(
+                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
+    {
+        JSONSerializer<ValueType>::from_json(*this, v);
+        return v;
+    }
+
+    // specialization to allow calling get_to with a basic_json value
+    // see https://github.com/nlohmann/json/issues/2175
+    template<typename ValueType,
+             detail::enable_if_t <
+                 detail::is_basic_json<ValueType>::value,
+                 int> = 0>
+    ValueType & get_to(ValueType& v) const
+    {
+        v = *this;
+        return v;
+    }
+
+    template <
+        typename T, std::size_t N,
+        typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+        detail::enable_if_t <
+            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >
+    Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+    noexcept(noexcept(JSONSerializer<Array>::from_json(
+                          std::declval<const basic_json_t&>(), v)))
+    {
+        JSONSerializer<Array>::from_json(*this, v);
+        return v;
+    }
+
+    /// @brief get a reference value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
+    template<typename ReferenceType, typename std::enable_if<
+                 std::is_reference<ReferenceType>::value, int>::type = 0>
+    ReferenceType get_ref()
+    {
+        // delegate call to get_ref_impl
+        return get_ref_impl<ReferenceType>(*this);
+    }
+
+    /// @brief get a reference value (implicit)
+    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
+    template < typename ReferenceType, typename std::enable_if <
+                   std::is_reference<ReferenceType>::value&&
+                   std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >
+    ReferenceType get_ref() const
+    {
+        // delegate call to get_ref_impl
+        return get_ref_impl<ReferenceType>(*this);
+    }
+
+    /*!
+    @brief get a value (implicit)
+
+    Implicit type conversion between the JSON value and a compatible value.
+    The call is realized by calling @ref get() const.
+
+    @tparam ValueType non-pointer type compatible to the JSON value, for
+    instance `int` for JSON integer numbers, `bool` for JSON booleans, or
+    `std::vector` types for JSON arrays. The character type of @ref string_t
+    as well as an initializer list of this type is excluded to avoid
+    ambiguities as these types implicitly convert to `std::string`.
+
+    @return copy of the JSON value, converted to type @a ValueType
+
+    @throw type_error.302 in case passed type @a ValueType is incompatible
+    to the JSON value type (e.g., the JSON value is of type boolean, but a
+    string is requested); see example below
+
+    @complexity Linear in the size of the JSON value.
+
+    @liveexample{The example below shows several conversions from JSON values
+    to other types. There a few things to note: (1) Floating-point numbers can
+    be converted to integers\, (2) A JSON array can be converted to a standard
+    `std::vector<short>`\, (3) A JSON object can be converted to C++
+    associative containers such as `std::unordered_map<std::string\,
+    json>`.,operator__ValueType}
+
+    @since version 1.0.0
+    */
+    template < typename ValueType, typename std::enable_if <
+                   detail::conjunction <
+                       detail::negation<std::is_pointer<ValueType>>,
+                       detail::negation<std::is_same<ValueType, std::nullptr_t>>,
+                       detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>,
+                                        detail::negation<std::is_same<ValueType, typename string_t::value_type>>,
+                                        detail::negation<detail::is_basic_json<ValueType>>,
+                                        detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,
+#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
+                                                detail::negation<std::is_same<ValueType, std::string_view>>,
+#endif
+#if defined(JSON_HAS_CPP_17)
+                                                detail::negation<std::is_same<ValueType, std::any>>,
+#endif
+                                                detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType>
+                                                >::value, int >::type = 0 >
+                                        JSON_EXPLICIT operator ValueType() const
+    {
+        // delegate the call to get<>() const
+        return get<ValueType>();
+    }
+
+    /// @brief get a binary value
+    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+    binary_t& get_binary()
+    {
+        if (!is_binary())
+        {
+            JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+        }
+
+        return *get_ptr<binary_t*>();
+    }
+
+    /// @brief get a binary value
+    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+    const binary_t& get_binary() const
+    {
+        if (!is_binary())
+        {
+            JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+        }
+
+        return *get_ptr<const binary_t*>();
+    }
+
+    /// @}
+
+
+    ////////////////////
+    // element access //
+    ////////////////////
+
+    /// @name element access
+    /// Access to the JSON value.
+    /// @{
+
+    /// @brief access specified array element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    reference at(size_type idx)
+    {
+        // at only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            JSON_TRY
+            {
+                return set_parent(m_data.m_value.array->at(idx));
+            }
+            JSON_CATCH (std::out_of_range&)
+            {
+                // create better exception explanation
+                JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+            }
+        }
+        else
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+    }
+
+    /// @brief access specified array element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    const_reference at(size_type idx) const
+    {
+        // at only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            JSON_TRY
+            {
+                return m_data.m_value.array->at(idx);
+            }
+            JSON_CATCH (std::out_of_range&)
+            {
+                // create better exception explanation
+                JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+            }
+        }
+        else
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    reference at(const typename object_t::key_type& key)
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_data.m_value.object->find(key);
+        if (it == m_data.m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+        }
+        return set_parent(it->second);
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    reference at(KeyType && key)
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
+        if (it == m_data.m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+        }
+        return set_parent(it->second);
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    const_reference at(const typename object_t::key_type& key) const
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_data.m_value.object->find(key);
+        if (it == m_data.m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+        }
+        return it->second;
+    }
+
+    /// @brief access specified object element with bounds checking
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    const_reference at(KeyType && key) const
+    {
+        // at only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+        }
+
+        auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
+        if (it == m_data.m_value.object->end())
+        {
+            JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+        }
+        return it->second;
+    }
+
+    /// @brief access specified array element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    reference operator[](size_type idx)
+    {
+        // implicitly convert null value to an empty array
+        if (is_null())
+        {
+            m_data.m_type = value_t::array;
+            m_data.m_value.array = create<array_t>();
+            assert_invariant();
+        }
+
+        // operator[] only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            // fill up array with null values if given idx is outside range
+            if (idx >= m_data.m_value.array->size())
+            {
+#if JSON_DIAGNOSTICS
+                // remember array size & capacity before resizing
+                const auto old_size = m_data.m_value.array->size();
+                const auto old_capacity = m_data.m_value.array->capacity();
+#endif
+                m_data.m_value.array->resize(idx + 1);
+
+#if JSON_DIAGNOSTICS
+                if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity))
+                {
+                    // capacity has changed: update all parents
+                    set_parents();
+                }
+                else
+                {
+                    // set parent for values added above
+                    set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size));
+                }
+#endif
+                assert_invariant();
+            }
+
+            return m_data.m_value.array->operator[](idx);
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
+    }
+
+    /// @brief access specified array element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    const_reference operator[](size_type idx) const
+    {
+        // const operator[] only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            return m_data.m_value.array->operator[](idx);
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
+    }
+
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    reference operator[](typename object_t::key_type key)
+    {
+        // implicitly convert null value to an empty object
+        if (is_null())
+        {
+            m_data.m_type = value_t::object;
+            m_data.m_value.object = create<object_t>();
+            assert_invariant();
+        }
+
+        // operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto result = m_data.m_value.object->emplace(std::move(key), nullptr);
+            return set_parent(result.first->second);
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
+
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    const_reference operator[](const typename object_t::key_type& key) const
+    {
+        // const operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto it = m_data.m_value.object->find(key);
+            JSON_ASSERT(it != m_data.m_value.object->end());
+            return it->second;
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
+
+    // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
+    // (they seemingly cannot be constrained to resolve the ambiguity)
+    template<typename T>
+    reference operator[](T* key)
+    {
+        return operator[](typename object_t::key_type(key));
+    }
+
+    template<typename T>
+    const_reference operator[](T* key) const
+    {
+        return operator[](typename object_t::key_type(key));
+    }
+
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    reference operator[](KeyType && key)
+    {
+        // implicitly convert null value to an empty object
+        if (is_null())
+        {
+            m_data.m_type = value_t::object;
+            m_data.m_value.object = create<object_t>();
+            assert_invariant();
+        }
+
+        // operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto result = m_data.m_value.object->emplace(std::forward<KeyType>(key), nullptr);
+            return set_parent(result.first->second);
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
+
+    /// @brief access specified object element
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    const_reference operator[](KeyType && key) const
+    {
+        // const operator[] only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
+            JSON_ASSERT(it != m_data.m_value.object->end());
+            return it->second;
+        }
+
+        JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+    }
+
+  private:
+    template<typename KeyType>
+    using is_comparable_with_object_key = detail::is_comparable <
+        object_comparator_t, const typename object_t::key_type&, KeyType >;
+
+    template<typename ValueType>
+    using value_return_type = std::conditional <
+        detail::is_c_string_uncvref<ValueType>::value,
+        string_t, typename std::decay<ValueType>::type >;
+
+  public:
+    /// @brief access specified object element with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, detail::enable_if_t <
+                   !detail::is_transparent<object_comparator_t>::value
+                   && detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(key);
+            if (it != end())
+            {
+                return it->template get<ValueType>();
+            }
+
+            return default_value;
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   !detail::is_transparent<object_comparator_t>::value
+                   && detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(key);
+            if (it != end())
+            {
+                return it->template get<ReturnType>();
+            }
+
+            return std::forward<ValueType>(default_value);
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class KeyType, detail::enable_if_t <
+                   detail::is_transparent<object_comparator_t>::value
+                   && !detail::is_json_pointer<KeyType>::value
+                   && is_comparable_with_object_key<KeyType>::value
+                   && detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ValueType value(KeyType && key, const ValueType& default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(std::forward<KeyType>(key));
+            if (it != end())
+            {
+                return it->template get<ValueType>();
+            }
+
+            return default_value;
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element via JSON Pointer with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class KeyType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   detail::is_transparent<object_comparator_t>::value
+                   && !detail::is_json_pointer<KeyType>::value
+                   && is_comparable_with_object_key<KeyType>::value
+                   && detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ReturnType value(KeyType && key, ValueType && default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if key is found, return value and given default value otherwise
+            const auto it = find(std::forward<KeyType>(key));
+            if (it != end())
+            {
+                return it->template get<ReturnType>();
+            }
+
+            return std::forward<ValueType>(default_value);
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element via JSON Pointer with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, detail::enable_if_t <
+                   detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ValueType value(const json_pointer& ptr, const ValueType& default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if pointer resolves a value, return it or use default value
+            JSON_TRY
+            {
+                return ptr.get_checked(this).template get<ValueType>();
+            }
+            JSON_INTERNAL_CATCH (out_of_range&)
+            {
+                return default_value;
+            }
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    /// @brief access specified object element via JSON Pointer with default value
+    /// @sa https://json.nlohmann.me/api/basic_json/value/
+    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    ReturnType value(const json_pointer& ptr, ValueType && default_value) const
+    {
+        // value only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            // if pointer resolves a value, return it or use default value
+            JSON_TRY
+            {
+                return ptr.get_checked(this).template get<ReturnType>();
+            }
+            JSON_INTERNAL_CATCH (out_of_range&)
+            {
+                return std::forward<ValueType>(default_value);
+            }
+        }
+
+        JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+    }
+
+    template < class ValueType, class BasicJsonType, detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value
+                   && detail::is_getable<basic_json_t, ValueType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const
+    {
+        return value(ptr.convert(), default_value);
+    }
+
+    template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
+               detail::enable_if_t <
+                   detail::is_basic_json<BasicJsonType>::value
+                   && detail::is_getable<basic_json_t, ReturnType>::value
+                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    ReturnType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const
+    {
+        return value(ptr.convert(), std::forward<ValueType>(default_value));
+    }
+
+    /// @brief access the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/front/
+    reference front()
+    {
+        return *begin();
+    }
+
+    /// @brief access the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/front/
+    const_reference front() const
+    {
+        return *cbegin();
+    }
+
+    /// @brief access the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/back/
+    reference back()
+    {
+        auto tmp = end();
+        --tmp;
+        return *tmp;
+    }
+
+    /// @brief access the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/back/
+    const_reference back() const
+    {
+        auto tmp = cend();
+        --tmp;
+        return *tmp;
+    }
+
+    /// @brief remove element given an iterator
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    template < class IteratorType, detail::enable_if_t <
+                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+    IteratorType erase(IteratorType pos)
+    {
+        // make sure iterator fits the current value
+        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+        }
+
+        IteratorType result = end();
+
+        switch (m_data.m_type)
+        {
+            case value_t::boolean:
+            case value_t::number_float:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::string:
+            case value_t::binary:
+            {
+                if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
+                {
+                    JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
+                }
+
+                if (is_string())
+                {
+                    AllocatorType<string_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);
+                    m_data.m_value.string = nullptr;
+                }
+                else if (is_binary())
+                {
+                    AllocatorType<binary_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);
+                    m_data.m_value.binary = nullptr;
+                }
+
+                m_data.m_type = value_t::null;
+                assert_invariant();
+                break;
+            }
+
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_data.m_value.object->erase(pos.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                result.m_it.array_iterator = m_data.m_value.array->erase(pos.m_it.array_iterator);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+
+        return result;
+    }
+
+    /// @brief remove elements given an iterator range
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    template < class IteratorType, detail::enable_if_t <
+                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+    IteratorType erase(IteratorType first, IteratorType last)
+    {
+        // make sure iterator fits the current value
+        if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this));
+        }
+
+        IteratorType result = end();
+
+        switch (m_data.m_type)
+        {
+            case value_t::boolean:
+            case value_t::number_float:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::string:
+            case value_t::binary:
+            {
+                if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
+                                       || !last.m_it.primitive_iterator.is_end()))
+                {
+                    JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
+                }
+
+                if (is_string())
+                {
+                    AllocatorType<string_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);
+                    m_data.m_value.string = nullptr;
+                }
+                else if (is_binary())
+                {
+                    AllocatorType<binary_t> alloc;
+                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);
+                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);
+                    m_data.m_value.binary = nullptr;
+                }
+
+                m_data.m_type = value_t::null;
+                assert_invariant();
+                break;
+            }
+
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_data.m_value.object->erase(first.m_it.object_iterator,
+                                              last.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                result.m_it.array_iterator = m_data.m_value.array->erase(first.m_it.array_iterator,
+                                             last.m_it.array_iterator);
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+
+        return result;
+    }
+
+  private:
+    template < typename KeyType, detail::enable_if_t <
+                   detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    size_type erase_internal(KeyType && key)
+    {
+        // this erase only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+
+        return m_data.m_value.object->erase(std::forward<KeyType>(key));
+    }
+
+    template < typename KeyType, detail::enable_if_t <
+                   !detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+    size_type erase_internal(KeyType && key)
+    {
+        // this erase only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+
+        const auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
+        if (it != m_data.m_value.object->end())
+        {
+            m_data.m_value.object->erase(it);
+            return 1;
+        }
+        return 0;
+    }
+
+  public:
+
+    /// @brief remove element from a JSON object given a key
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    size_type erase(const typename object_t::key_type& key)
+    {
+        // the indirection via erase_internal() is added to avoid making this
+        // function a template and thus de-rank it during overload resolution
+        return erase_internal(key);
+    }
+
+    /// @brief remove element from a JSON object given a key
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    size_type erase(KeyType && key)
+    {
+        return erase_internal(std::forward<KeyType>(key));
+    }
+
+    /// @brief remove element from a JSON array given an index
+    /// @sa https://json.nlohmann.me/api/basic_json/erase/
+    void erase(const size_type idx)
+    {
+        // this erase only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            if (JSON_HEDLEY_UNLIKELY(idx >= size()))
+            {
+                JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+            }
+
+            m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx));
+        }
+        else
+        {
+            JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+        }
+    }
+
+    /// @}
+
+
+    ////////////
+    // lookup //
+    ////////////
+
+    /// @name lookup
+    /// @{
+
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    iterator find(const typename object_t::key_type& key)
+    {
+        auto result = end();
+
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_data.m_value.object->find(key);
+        }
+
+        return result;
+    }
+
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    const_iterator find(const typename object_t::key_type& key) const
+    {
+        auto result = cend();
+
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_data.m_value.object->find(key);
+        }
+
+        return result;
+    }
+
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    iterator find(KeyType && key)
+    {
+        auto result = end();
+
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
+        }
+
+        return result;
+    }
+
+    /// @brief find an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/find/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    const_iterator find(KeyType && key) const
+    {
+        auto result = cend();
+
+        if (is_object())
+        {
+            result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
+        }
+
+        return result;
+    }
+
+    /// @brief returns the number of occurrences of a key in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/count/
+    size_type count(const typename object_t::key_type& key) const
+    {
+        // return 0 for all nonobject types
+        return is_object() ? m_data.m_value.object->count(key) : 0;
+    }
+
+    /// @brief returns the number of occurrences of a key in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/count/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    size_type count(KeyType && key) const
+    {
+        // return 0 for all nonobject types
+        return is_object() ? m_data.m_value.object->count(std::forward<KeyType>(key)) : 0;
+    }
+
+    /// @brief check the existence of an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/contains/
+    bool contains(const typename object_t::key_type& key) const
+    {
+        return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end();
+    }
+
+    /// @brief check the existence of an element in a JSON object
+    /// @sa https://json.nlohmann.me/api/basic_json/contains/
+    template<class KeyType, detail::enable_if_t<
+                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+    bool contains(KeyType && key) const
+    {
+        return is_object() && m_data.m_value.object->find(std::forward<KeyType>(key)) != m_data.m_value.object->end();
+    }
+
+    /// @brief check the existence of an element in a JSON object given a JSON pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/contains/
+    bool contains(const json_pointer& ptr) const
+    {
+        return ptr.contains(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    bool contains(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+    {
+        return ptr.contains(this);
+    }
+
+    /// @}
+
+
+    ///////////////
+    // iterators //
+    ///////////////
+
+    /// @name iterators
+    /// @{
+
+    /// @brief returns an iterator to the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/begin/
+    iterator begin() noexcept
+    {
+        iterator result(this);
+        result.set_begin();
+        return result;
+    }
+
+    /// @brief returns an iterator to the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/begin/
+    const_iterator begin() const noexcept
+    {
+        return cbegin();
+    }
+
+    /// @brief returns a const iterator to the first element
+    /// @sa https://json.nlohmann.me/api/basic_json/cbegin/
+    const_iterator cbegin() const noexcept
+    {
+        const_iterator result(this);
+        result.set_begin();
+        return result;
+    }
+
+    /// @brief returns an iterator to one past the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/end/
+    iterator end() noexcept
+    {
+        iterator result(this);
+        result.set_end();
+        return result;
+    }
+
+    /// @brief returns an iterator to one past the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/end/
+    const_iterator end() const noexcept
+    {
+        return cend();
+    }
+
+    /// @brief returns an iterator to one past the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/cend/
+    const_iterator cend() const noexcept
+    {
+        const_iterator result(this);
+        result.set_end();
+        return result;
+    }
+
+    /// @brief returns an iterator to the reverse-beginning
+    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
+    reverse_iterator rbegin() noexcept
+    {
+        return reverse_iterator(end());
+    }
+
+    /// @brief returns an iterator to the reverse-beginning
+    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
+    const_reverse_iterator rbegin() const noexcept
+    {
+        return crbegin();
+    }
+
+    /// @brief returns an iterator to the reverse-end
+    /// @sa https://json.nlohmann.me/api/basic_json/rend/
+    reverse_iterator rend() noexcept
+    {
+        return reverse_iterator(begin());
+    }
+
+    /// @brief returns an iterator to the reverse-end
+    /// @sa https://json.nlohmann.me/api/basic_json/rend/
+    const_reverse_iterator rend() const noexcept
+    {
+        return crend();
+    }
+
+    /// @brief returns a const reverse iterator to the last element
+    /// @sa https://json.nlohmann.me/api/basic_json/crbegin/
+    const_reverse_iterator crbegin() const noexcept
+    {
+        return const_reverse_iterator(cend());
+    }
+
+    /// @brief returns a const reverse iterator to one before the first
+    /// @sa https://json.nlohmann.me/api/basic_json/crend/
+    const_reverse_iterator crend() const noexcept
+    {
+        return const_reverse_iterator(cbegin());
+    }
+
+  public:
+    /// @brief wrapper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use @ref items() instead;
+    ///             that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept
+    {
+        return ref.items();
+    }
+
+    /// @brief wrapper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+    ///         version 4.0.0 of the library. Please use @ref items() instead;
+    ///         that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept
+    {
+        return ref.items();
+    }
+
+    /// @brief helper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    iteration_proxy<iterator> items() noexcept
+    {
+        return iteration_proxy<iterator>(*this);
+    }
+
+    /// @brief helper to access iterator member functions in range-based for
+    /// @sa https://json.nlohmann.me/api/basic_json/items/
+    iteration_proxy<const_iterator> items() const noexcept
+    {
+        return iteration_proxy<const_iterator>(*this);
+    }
+
+    /// @}
+
+
+    //////////////
+    // capacity //
+    //////////////
+
+    /// @name capacity
+    /// @{
+
+    /// @brief checks whether the container is empty.
+    /// @sa https://json.nlohmann.me/api/basic_json/empty/
+    bool empty() const noexcept
+    {
+        switch (m_data.m_type)
+        {
+            case value_t::null:
+            {
+                // null values are empty
+                return true;
+            }
+
+            case value_t::array:
+            {
+                // delegate call to array_t::empty()
+                return m_data.m_value.array->empty();
+            }
+
+            case value_t::object:
+            {
+                // delegate call to object_t::empty()
+                return m_data.m_value.object->empty();
+            }
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // all other types are nonempty
+                return false;
+            }
+        }
+    }
+
+    /// @brief returns the number of elements
+    /// @sa https://json.nlohmann.me/api/basic_json/size/
+    size_type size() const noexcept
+    {
+        switch (m_data.m_type)
+        {
+            case value_t::null:
+            {
+                // null values are empty
+                return 0;
+            }
+
+            case value_t::array:
+            {
+                // delegate call to array_t::size()
+                return m_data.m_value.array->size();
+            }
+
+            case value_t::object:
+            {
+                // delegate call to object_t::size()
+                return m_data.m_value.object->size();
+            }
+
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // all other types have size 1
+                return 1;
+            }
+        }
+    }
+
+    /// @brief returns the maximum possible number of elements
+    /// @sa https://json.nlohmann.me/api/basic_json/max_size/
+    size_type max_size() const noexcept
+    {
+        switch (m_data.m_type)
+        {
+            case value_t::array:
+            {
+                // delegate call to array_t::max_size()
+                return m_data.m_value.array->max_size();
+            }
+
+            case value_t::object:
+            {
+                // delegate call to object_t::max_size()
+                return m_data.m_value.object->max_size();
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // all other types have max_size() == size()
+                return size();
+            }
+        }
+    }
+
+    /// @}
+
+
+    ///////////////
+    // modifiers //
+    ///////////////
+
+    /// @name modifiers
+    /// @{
+
+    /// @brief clears the contents
+    /// @sa https://json.nlohmann.me/api/basic_json/clear/
+    void clear() noexcept
+    {
+        switch (m_data.m_type)
+        {
+            case value_t::number_integer:
+            {
+                m_data.m_value.number_integer = 0;
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                m_data.m_value.number_unsigned = 0;
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                m_data.m_value.number_float = 0.0;
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                m_data.m_value.boolean = false;
+                break;
+            }
+
+            case value_t::string:
+            {
+                m_data.m_value.string->clear();
+                break;
+            }
+
+            case value_t::binary:
+            {
+                m_data.m_value.binary->clear();
+                break;
+            }
+
+            case value_t::array:
+            {
+                m_data.m_value.array->clear();
+                break;
+            }
+
+            case value_t::object:
+            {
+                m_data.m_value.object->clear();
+                break;
+            }
+
+            case value_t::null:
+            case value_t::discarded:
+            default:
+                break;
+        }
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(basic_json&& val)
+    {
+        // push_back only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+        {
+            JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+        }
+
+        // transform null object into an array
+        if (is_null())
+        {
+            m_data.m_type = value_t::array;
+            m_data.m_value = value_t::array;
+            assert_invariant();
+        }
+
+        // add element to array (move semantics)
+        const auto old_capacity = m_data.m_value.array->capacity();
+        m_data.m_value.array->push_back(std::move(val));
+        set_parent(m_data.m_value.array->back(), old_capacity);
+        // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(basic_json&& val)
+    {
+        push_back(std::move(val));
+        return *this;
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(const basic_json& val)
+    {
+        // push_back only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+        {
+            JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+        }
+
+        // transform null object into an array
+        if (is_null())
+        {
+            m_data.m_type = value_t::array;
+            m_data.m_value = value_t::array;
+            assert_invariant();
+        }
+
+        // add element to array
+        const auto old_capacity = m_data.m_value.array->capacity();
+        m_data.m_value.array->push_back(val);
+        set_parent(m_data.m_value.array->back(), old_capacity);
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(const basic_json& val)
+    {
+        push_back(val);
+        return *this;
+    }
+
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(const typename object_t::value_type& val)
+    {
+        // push_back only works for null objects or objects
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
+        {
+            JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+        }
+
+        // transform null object into an object
+        if (is_null())
+        {
+            m_data.m_type = value_t::object;
+            m_data.m_value = value_t::object;
+            assert_invariant();
+        }
+
+        // add element to object
+        auto res = m_data.m_value.object->insert(val);
+        set_parent(res.first->second);
+    }
+
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(const typename object_t::value_type& val)
+    {
+        push_back(val);
+        return *this;
+    }
+
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+    void push_back(initializer_list_t init)
+    {
+        if (is_object() && init.size() == 2 && (*init.begin())->is_string())
+        {
+            basic_json&& key = init.begin()->moved_or_copied();
+            push_back(typename object_t::value_type(
+                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));
+        }
+        else
+        {
+            push_back(basic_json(init));
+        }
+    }
+
+    /// @brief add an object to an object
+    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+    reference operator+=(initializer_list_t init)
+    {
+        push_back(init);
+        return *this;
+    }
+
+    /// @brief add an object to an array
+    /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/
+    template<class... Args>
+    reference emplace_back(Args&& ... args)
+    {
+        // emplace_back only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+        {
+            JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this));
+        }
+
+        // transform null object into an array
+        if (is_null())
+        {
+            m_data.m_type = value_t::array;
+            m_data.m_value = value_t::array;
+            assert_invariant();
+        }
+
+        // add element to array (perfect forwarding)
+        const auto old_capacity = m_data.m_value.array->capacity();
+        m_data.m_value.array->emplace_back(std::forward<Args>(args)...);
+        return set_parent(m_data.m_value.array->back(), old_capacity);
+    }
+
+    /// @brief add an object to an object if key does not exist
+    /// @sa https://json.nlohmann.me/api/basic_json/emplace/
+    template<class... Args>
+    std::pair<iterator, bool> emplace(Args&& ... args)
+    {
+        // emplace only works for null objects or arrays
+        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
+        {
+            JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this));
+        }
+
+        // transform null object into an object
+        if (is_null())
+        {
+            m_data.m_type = value_t::object;
+            m_data.m_value = value_t::object;
+            assert_invariant();
+        }
+
+        // add element to array (perfect forwarding)
+        auto res = m_data.m_value.object->emplace(std::forward<Args>(args)...);
+        set_parent(res.first->second);
+
+        // create result iterator and set iterator to the result of emplace
+        auto it = begin();
+        it.m_it.object_iterator = res.first;
+
+        // return pair of iterator and boolean
+        return {it, res.second};
+    }
+
+    /// Helper for insertion of an iterator
+    /// @note: This uses std::distance to support GCC 4.8,
+    ///        see https://github.com/nlohmann/json/pull/1257
+    template<typename... Args>
+    iterator insert_iterator(const_iterator pos, Args&& ... args)
+    {
+        iterator result(this);
+        JSON_ASSERT(m_data.m_value.array != nullptr);
+
+        auto insert_pos = std::distance(m_data.m_value.array->begin(), pos.m_it.array_iterator);
+        m_data.m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);
+        result.m_it.array_iterator = m_data.m_value.array->begin() + insert_pos;
+
+        // This could have been written as:
+        // result.m_it.array_iterator = m_data.m_value.array->insert(pos.m_it.array_iterator, cnt, val);
+        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.
+
+        set_parents();
+        return result;
+    }
+
+    /// @brief inserts element into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, const basic_json& val)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            // check if iterator pos fits to this JSON value
+            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+            {
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+            }
+
+            // insert to array and return iterator
+            return insert_iterator(pos, val);
+        }
+
+        JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+    }
+
+    /// @brief inserts element into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, basic_json&& val)
+    {
+        return insert(pos, val);
+    }
+
+    /// @brief inserts copies of element into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            // check if iterator pos fits to this JSON value
+            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+            {
+                JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+            }
+
+            // insert to array and return iterator
+            return insert_iterator(pos, cnt, val);
+        }
+
+        JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+    }
+
+    /// @brief inserts range of elements into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, const_iterator first, const_iterator last)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_UNLIKELY(!is_array()))
+        {
+            JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+        }
+
+        // check if range iterators belong to the same JSON object
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
+        {
+            JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this));
+        }
+
+        // insert to array and return iterator
+        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
+    }
+
+    /// @brief inserts elements from initializer list into array
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    iterator insert(const_iterator pos, initializer_list_t ilist)
+    {
+        // insert only works for arrays
+        if (JSON_HEDLEY_UNLIKELY(!is_array()))
+        {
+            JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+        }
+
+        // check if iterator pos fits to this JSON value
+        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+        }
+
+        // insert to array and return iterator
+        return insert_iterator(pos, ilist.begin(), ilist.end());
+    }
+
+    /// @brief inserts range of elements into object
+    /// @sa https://json.nlohmann.me/api/basic_json/insert/
+    void insert(const_iterator first, const_iterator last)
+    {
+        // insert only works for objects
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+        }
+
+        // check if range iterators belong to the same JSON object
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+        }
+
+        // passed iterators must belong to objects
+        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+        {
+            JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this));
+        }
+
+        m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
+    }
+
+    /// @brief updates a JSON object from another object, overwriting existing keys
+    /// @sa https://json.nlohmann.me/api/basic_json/update/
+    void update(const_reference j, bool merge_objects = false)
+    {
+        update(j.begin(), j.end(), merge_objects);
+    }
+
+    /// @brief updates a JSON object from another object, overwriting existing keys
+    /// @sa https://json.nlohmann.me/api/basic_json/update/
+    void update(const_iterator first, const_iterator last, bool merge_objects = false)
+    {
+        // implicitly convert null value to an empty object
+        if (is_null())
+        {
+            m_data.m_type = value_t::object;
+            m_data.m_value.object = create<object_t>();
+            assert_invariant();
+        }
+
+        if (JSON_HEDLEY_UNLIKELY(!is_object()))
+        {
+            JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
+        }
+
+        // check if range iterators belong to the same JSON object
+        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+        {
+            JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+        }
+
+        // passed iterators must belong to objects
+        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+        {
+            JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
+        }
+
+        for (auto it = first; it != last; ++it)
+        {
+            if (merge_objects && it.value().is_object())
+            {
+                auto it2 = m_data.m_value.object->find(it.key());
+                if (it2 != m_data.m_value.object->end())
+                {
+                    it2->second.update(it.value(), true);
+                    continue;
+                }
+            }
+            m_data.m_value.object->operator[](it.key()) = it.value();
+#if JSON_DIAGNOSTICS
+            m_data.m_value.object->operator[](it.key()).m_parent = this;
+#endif
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(reference other) noexcept (
+        std::is_nothrow_move_constructible<value_t>::value&&
+        std::is_nothrow_move_assignable<value_t>::value&&
+        std::is_nothrow_move_constructible<json_value>::value&&
+        std::is_nothrow_move_assignable<json_value>::value
+    )
+    {
+        std::swap(m_data.m_type, other.m_data.m_type);
+        std::swap(m_data.m_value, other.m_data.m_value);
+
+        set_parents();
+        other.set_parents();
+        assert_invariant();
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    friend void swap(reference left, reference right) noexcept (
+        std::is_nothrow_move_constructible<value_t>::value&&
+        std::is_nothrow_move_assignable<value_t>::value&&
+        std::is_nothrow_move_constructible<json_value>::value&&
+        std::is_nothrow_move_assignable<json_value>::value
+    )
+    {
+        left.swap(right);
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(array_t& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for arrays
+        if (JSON_HEDLEY_LIKELY(is_array()))
+        {
+            using std::swap;
+            swap(*(m_data.m_value.array), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(object_t& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for objects
+        if (JSON_HEDLEY_LIKELY(is_object()))
+        {
+            using std::swap;
+            swap(*(m_data.m_value.object), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(string_t& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for strings
+        if (JSON_HEDLEY_LIKELY(is_string()))
+        {
+            using std::swap;
+            swap(*(m_data.m_value.string), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(binary_t& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for strings
+        if (JSON_HEDLEY_LIKELY(is_binary()))
+        {
+            using std::swap;
+            swap(*(m_data.m_value.binary), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this));
+        }
+    }
+
+    /// @brief exchanges the values
+    /// @sa https://json.nlohmann.me/api/basic_json/swap/
+    void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)
+    {
+        // swap only works for strings
+        if (JSON_HEDLEY_LIKELY(is_binary()))
+        {
+            using std::swap;
+            swap(*(m_data.m_value.binary), other);
+        }
+        else
+        {
+            JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this));
+        }
+    }
+
+    /// @}
+
+    //////////////////////////////////////////
+    // lexicographical comparison operators //
+    //////////////////////////////////////////
+
+    /// @name lexicographical comparison operators
+    /// @{
+
+    // note parentheses around operands are necessary; see
+    // https://github.com/nlohmann/json/issues/1530
+#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result)                       \
+    const auto lhs_type = lhs.type();                                                                    \
+    const auto rhs_type = rhs.type();                                                                    \
+    \
+    if (lhs_type == rhs_type) /* NOLINT(readability/braces) */                                           \
+    {                                                                                                    \
+        switch (lhs_type)                                                                                \
+        {                                                                                                \
+            case value_t::array:                                                                         \
+                return (*lhs.m_data.m_value.array) op (*rhs.m_data.m_value.array);                                     \
+                \
+            case value_t::object:                                                                        \
+                return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object);                                   \
+                \
+            case value_t::null:                                                                          \
+                return (null_result);                                                                    \
+                \
+            case value_t::string:                                                                        \
+                return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string);                                   \
+                \
+            case value_t::boolean:                                                                       \
+                return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean);                                   \
+                \
+            case value_t::number_integer:                                                                \
+                return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer);                     \
+                \
+            case value_t::number_unsigned:                                                               \
+                return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned);                   \
+                \
+            case value_t::number_float:                                                                  \
+                return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float);                         \
+                \
+            case value_t::binary:                                                                        \
+                return (*lhs.m_data.m_value.binary) op (*rhs.m_data.m_value.binary);                                   \
+                \
+            case value_t::discarded:                                                                     \
+            default:                                                                                     \
+                return (unordered_result);                                                               \
+        }                                                                                                \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)                   \
+    {                                                                                                    \
+        return static_cast<number_float_t>(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_float;      \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)                   \
+    {                                                                                                    \
+        return lhs.m_data.m_value.number_float op static_cast<number_float_t>(rhs.m_data.m_value.number_integer);      \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)                  \
+    {                                                                                                    \
+        return static_cast<number_float_t>(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_float;     \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)                  \
+    {                                                                                                    \
+        return lhs.m_data.m_value.number_float op static_cast<number_float_t>(rhs.m_data.m_value.number_unsigned);     \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)                \
+    {                                                                                                    \
+        return static_cast<number_integer_t>(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_integer; \
+    }                                                                                                    \
+    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)                \
+    {                                                                                                    \
+        return lhs.m_data.m_value.number_integer op static_cast<number_integer_t>(rhs.m_data.m_value.number_unsigned); \
+    }                                                                                                    \
+    else if(compares_unordered(lhs, rhs))\
+    {\
+        return (unordered_result);\
+    }\
+    \
+    return (default_result);
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    // returns true if:
+    // - any operand is NaN and the other operand is of number type
+    // - any operand is discarded
+    // in legacy mode, discarded values are considered ordered if
+    // an operation is computed as an odd number of inverses of others
+    static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept
+    {
+        if ((lhs.is_number_float() && std::isnan(lhs.m_data.m_value.number_float) && rhs.is_number())
+                || (rhs.is_number_float() && std::isnan(rhs.m_data.m_value.number_float) && lhs.is_number()))
+        {
+            return true;
+        }
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+        return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;
+#else
+        static_cast<void>(inverse);
+        return lhs.is_discarded() || rhs.is_discarded();
+#endif
+    }
+
+  private:
+    bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept
+    {
+        return compares_unordered(*this, rhs, inverse);
+    }
+
+  public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    bool operator==(const_reference rhs) const noexcept
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        const_reference lhs = *this;
+        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+    }
+
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator==(ScalarType rhs) const noexcept
+    {
+        return *this == basic_json(rhs);
+    }
+
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+    bool operator!=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !operator==(rhs);
+    }
+
+    /// @brief comparison: 3-way
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+    std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
+    {
+        const_reference lhs = *this;
+        // default_result is used if we cannot compare values. In that case,
+        // we compare types.
+        JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
+                                std::partial_ordering::equivalent,
+                                std::partial_ordering::unordered,
+                                lhs_type <=> rhs_type) // *NOPAD*
+    }
+
+    /// @brief comparison: 3-way
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*
+    {
+        return *this <=> basic_json(rhs); // *NOPAD*
+    }
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+    // all operators that are computed as an odd number of inverses of others
+    // need to be overloaded to emulate the legacy comparison behavior
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+    bool operator<=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !(rhs < *this);
+    }
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator<=(ScalarType rhs) const noexcept
+    {
+        return *this <= basic_json(rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+    bool operator>=(const_reference rhs) const noexcept
+    {
+        if (compares_unordered(rhs, true))
+        {
+            return false;
+        }
+        return !(*this < rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    template<typename ScalarType>
+    requires std::is_scalar_v<ScalarType>
+    bool operator>=(ScalarType rhs) const noexcept
+    {
+        return *this >= basic_json(rhs);
+    }
+#endif
+#else
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    friend bool operator==(const_reference lhs, const_reference rhs) noexcept
+    {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+    }
+
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator==(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs == basic_json(rhs);
+    }
+
+    /// @brief comparison: equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator==(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) == rhs;
+    }
+
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
+    {
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
+        return !(lhs == rhs);
+    }
+
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs != basic_json(rhs);
+    }
+
+    /// @brief comparison: not equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) != rhs;
+    }
+
+    /// @brief comparison: less than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+    friend bool operator<(const_reference lhs, const_reference rhs) noexcept
+    {
+        // default_result is used if we cannot compare values. In that case,
+        // we compare types. Note we have to call the operator explicitly,
+        // because MSVC has problems otherwise.
+        JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
+    }
+
+    /// @brief comparison: less than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator<(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs < basic_json(rhs);
+    }
+
+    /// @brief comparison: less than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator<(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) < rhs;
+    }
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
+    {
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
+        return !(rhs < lhs);
+    }
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs <= basic_json(rhs);
+    }
+
+    /// @brief comparison: less than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) <= rhs;
+    }
+
+    /// @brief comparison: greater than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+    friend bool operator>(const_reference lhs, const_reference rhs) noexcept
+    {
+        // double inverse
+        if (compares_unordered(lhs, rhs))
+        {
+            return false;
+        }
+        return !(lhs <= rhs);
+    }
+
+    /// @brief comparison: greater than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator>(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs > basic_json(rhs);
+    }
+
+    /// @brief comparison: greater than
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator>(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) > rhs;
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
+    {
+        if (compares_unordered(lhs, rhs, true))
+        {
+            return false;
+        }
+        return !(lhs < rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept
+    {
+        return lhs >= basic_json(rhs);
+    }
+
+    /// @brief comparison: greater than or equal
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+    template<typename ScalarType, typename std::enable_if<
+                 std::is_scalar<ScalarType>::value, int>::type = 0>
+    friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
+    {
+        return basic_json(lhs) >= rhs;
+    }
+#endif
+
+#undef JSON_IMPLEMENT_OPERATOR
+
+    /// @}
+
+    ///////////////////
+    // serialization //
+    ///////////////////
+
+    /// @name serialization
+    /// @{
+#ifndef JSON_NO_IO
+    /// @brief serialize to stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
+    {
+        // read width member and use it as indentation parameter if nonzero
+        const bool pretty_print = o.width() > 0;
+        const auto indentation = pretty_print ? o.width() : 0;
+
+        // reset width to 0 for subsequent calls to this stream
+        o.width(0);
+
+        // do the actual serialization
+        serializer s(detail::output_adapter<char>(o), o.fill());
+        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));
+        return o;
+    }
+
+    /// @brief serialize to stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+    /// @deprecated This function is deprecated since 3.0.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use
+    ///             operator<<(std::ostream&, const basic_json&) instead; that is,
+    ///             replace calls like `j >> o;` with `o << j;`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))
+    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
+    {
+        return o << j;
+    }
+#endif  // JSON_NO_IO
+    /// @}
+
+
+    /////////////////////
+    // deserialization //
+    /////////////////////
+
+    /// @name deserialization
+    /// @{
+
+    /// @brief deserialize from a compatible input
+    /// @sa https://json.nlohmann.me/api/basic_json/parse/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json parse(InputType&& i,
+                            const parser_callback_t cb = nullptr,
+                            const bool allow_exceptions = true,
+                            const bool ignore_comments = false)
+    {
+        basic_json result;
+        parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);
+        return result;
+    }
+
+    /// @brief deserialize from a pair of character iterators
+    /// @sa https://json.nlohmann.me/api/basic_json/parse/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json parse(IteratorType first,
+                            IteratorType last,
+                            const parser_callback_t cb = nullptr,
+                            const bool allow_exceptions = true,
+                            const bool ignore_comments = false)
+    {
+        basic_json result;
+        parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);
+        return result;
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
+    static basic_json parse(detail::span_input_adapter&& i,
+                            const parser_callback_t cb = nullptr,
+                            const bool allow_exceptions = true,
+                            const bool ignore_comments = false)
+    {
+        basic_json result;
+        parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);
+        return result;
+    }
+
+    /// @brief check if the input is valid JSON
+    /// @sa https://json.nlohmann.me/api/basic_json/accept/
+    template<typename InputType>
+    static bool accept(InputType&& i,
+                       const bool ignore_comments = false)
+    {
+        return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);
+    }
+
+    /// @brief check if the input is valid JSON
+    /// @sa https://json.nlohmann.me/api/basic_json/accept/
+    template<typename IteratorType>
+    static bool accept(IteratorType first, IteratorType last,
+                       const bool ignore_comments = false)
+    {
+        return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
+    static bool accept(detail::span_input_adapter&& i,
+                       const bool ignore_comments = false)
+    {
+        return parser(i.get(), nullptr, false, ignore_comments).accept(true);
+    }
+
+    /// @brief generate SAX events
+    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+    template <typename InputType, typename SAX>
+    JSON_HEDLEY_NON_NULL(2)
+    static bool sax_parse(InputType&& i, SAX* sax,
+                          input_format_t format = input_format_t::json,
+                          const bool strict = true,
+                          const bool ignore_comments = false)
+    {
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        return format == input_format_t::json
+               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+    }
+
+    /// @brief generate SAX events
+    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+    template<class IteratorType, class SAX>
+    JSON_HEDLEY_NON_NULL(3)
+    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
+                          input_format_t format = input_format_t::json,
+                          const bool strict = true,
+                          const bool ignore_comments = false)
+    {
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        return format == input_format_t::json
+               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+    }
+
+    /// @brief generate SAX events
+    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+    /// @deprecated This function is deprecated since 3.8.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use
+    ///             sax_parse(ptr, ptr + len) instead.
+    template <typename SAX>
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))
+    JSON_HEDLEY_NON_NULL(2)
+    static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,
+                          input_format_t format = input_format_t::json,
+                          const bool strict = true,
+                          const bool ignore_comments = false)
+    {
+        auto ia = i.get();
+        return format == input_format_t::json
+               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+    }
+#ifndef JSON_NO_IO
+    /// @brief deserialize from stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+    /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in
+    ///             version 4.0.0 of the library. Please use
+    ///             operator>>(std::istream&, basic_json&) instead; that is,
+    ///             replace calls like `j << i;` with `i >> j;`.
+    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))
+    friend std::istream& operator<<(basic_json& j, std::istream& i)
+    {
+        return operator>>(i, j);
+    }
+
+    /// @brief deserialize from stream
+    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+    friend std::istream& operator>>(std::istream& i, basic_json& j)
+    {
+        parser(detail::input_adapter(i)).parse(false, j);
+        return i;
+    }
+#endif  // JSON_NO_IO
+    /// @}
+
+    ///////////////////////////
+    // convenience functions //
+    ///////////////////////////
+
+    /// @brief return the type as string
+    /// @sa https://json.nlohmann.me/api/basic_json/type_name/
+    JSON_HEDLEY_RETURNS_NON_NULL
+    const char* type_name() const noexcept
+    {
+        switch (m_data.m_type)
+        {
+            case value_t::null:
+                return "null";
+            case value_t::object:
+                return "object";
+            case value_t::array:
+                return "array";
+            case value_t::string:
+                return "string";
+            case value_t::boolean:
+                return "boolean";
+            case value_t::binary:
+                return "binary";
+            case value_t::discarded:
+                return "discarded";
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            default:
+                return "number";
+        }
+    }
+
+
+  JSON_PRIVATE_UNLESS_TESTED:
+    //////////////////////
+    // member variables //
+    //////////////////////
+
+    struct data
+    {
+        /// the type of the current element
+        value_t m_type = value_t::null;
+
+        /// the value of the current element
+        json_value m_value = {};
+
+        data(const value_t v)
+            : m_type(v), m_value(v)
+        {
+        }
+
+        data(size_type cnt, const basic_json& val)
+            : m_type(value_t::array)
+        {
+            m_value.array = create<array_t>(cnt, val);
+        }
+
+        data() noexcept = default;
+        data(data&&) noexcept = default;
+        data(const data&) noexcept = delete;
+        data& operator=(data&&) noexcept = delete;
+        data& operator=(const data&) noexcept = delete;
+
+        ~data() noexcept
+        {
+            m_value.destroy(m_type);
+        }
+    };
+
+    data m_data = {};
+
+#if JSON_DIAGNOSTICS
+    /// a pointer to a parent value (for debugging purposes)
+    basic_json* m_parent = nullptr;
+#endif
+
+    //////////////////////////////////////////
+    // binary serialization/deserialization //
+    //////////////////////////////////////////
+
+    /// @name binary serialization/deserialization support
+    /// @{
+
+  public:
+    /// @brief create a CBOR serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+    static std::vector<std::uint8_t> to_cbor(const basic_json& j)
+    {
+        std::vector<std::uint8_t> result;
+        to_cbor(j, result);
+        return result;
+    }
+
+    /// @brief create a CBOR serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+    static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+    {
+        binary_writer<std::uint8_t>(o).write_cbor(j);
+    }
+
+    /// @brief create a CBOR serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_cbor(j);
+    }
+
+    /// @brief create a MessagePack serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+    static std::vector<std::uint8_t> to_msgpack(const basic_json& j)
+    {
+        std::vector<std::uint8_t> result;
+        to_msgpack(j, result);
+        return result;
+    }
+
+    /// @brief create a MessagePack serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+    static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+    {
+        binary_writer<std::uint8_t>(o).write_msgpack(j);
+    }
+
+    /// @brief create a MessagePack serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_msgpack(j);
+    }
+
+    /// @brief create a UBJSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+    static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
+            const bool use_size = false,
+            const bool use_type = false)
+    {
+        std::vector<std::uint8_t> result;
+        to_ubjson(j, result, use_size, use_type);
+        return result;
+    }
+
+    /// @brief create a UBJSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+    static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);
+    }
+
+    /// @brief create a UBJSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<char>(o).write_ubjson(j, use_size, use_type);
+    }
+
+    /// @brief create a BJData serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+    static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
+            const bool use_size = false,
+            const bool use_type = false)
+    {
+        std::vector<std::uint8_t> result;
+        to_bjdata(j, result, use_size, use_type);
+        return result;
+    }
+
+    /// @brief create a BJData serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+    static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true);
+    }
+
+    /// @brief create a BJData serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+    static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
+                          const bool use_size = false, const bool use_type = false)
+    {
+        binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true);
+    }
+
+    /// @brief create a BSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+    static std::vector<std::uint8_t> to_bson(const basic_json& j)
+    {
+        std::vector<std::uint8_t> result;
+        to_bson(j, result);
+        return result;
+    }
+
+    /// @brief create a BSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+    static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+    {
+        binary_writer<std::uint8_t>(o).write_bson(j);
+    }
+
+    /// @brief create a BSON serialization of a given JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+    static void to_bson(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_bson(j);
+    }
+
+    /// @brief create a JSON value from an input in CBOR format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_cbor(InputType&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in CBOR format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_cbor(IteratorType first, IteratorType last,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+    static basic_json from_cbor(const T* ptr, std::size_t len,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
+    }
+
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+    static basic_json from_cbor(detail::span_input_adapter&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = i.get();
+        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in MessagePack format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_msgpack(InputType&& i,
+                                   const bool strict = true,
+                                   const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in MessagePack format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_msgpack(IteratorType first, IteratorType last,
+                                   const bool strict = true,
+                                   const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+    static basic_json from_msgpack(const T* ptr, std::size_t len,
+                                   const bool strict = true,
+                                   const bool allow_exceptions = true)
+    {
+        return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+    static basic_json from_msgpack(detail::span_input_adapter&& i,
+                                   const bool strict = true,
+                                   const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = i.get();
+        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in UBJSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_ubjson(InputType&& i,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in UBJSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_ubjson(IteratorType first, IteratorType last,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+    static basic_json from_ubjson(const T* ptr, std::size_t len,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+    static basic_json from_ubjson(detail::span_input_adapter&& i,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = i.get();
+        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+
+    /// @brief create a JSON value from an input in BJData format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bjdata(InputType&& i,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in BJData format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bjdata(IteratorType first, IteratorType last,
+                                  const bool strict = true,
+                                  const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in BSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
+    template<typename InputType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bson(InputType&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::forward<InputType>(i));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    /// @brief create a JSON value from an input in BSON format
+    /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
+    template<typename IteratorType>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json from_bson(IteratorType first, IteratorType last,
+                                const bool strict = true,
+                                const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = detail::input_adapter(std::move(first), std::move(last));
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+
+    template<typename T>
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+    static basic_json from_bson(const T* ptr, std::size_t len,
+                                const bool strict = true,
+                                const bool allow_exceptions = true)
+    {
+        return from_bson(ptr, ptr + len, strict, allow_exceptions);
+    }
+
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+    static basic_json from_bson(detail::span_input_adapter&& i,
+                                const bool strict = true,
+                                const bool allow_exceptions = true)
+    {
+        basic_json result;
+        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+        auto ia = i.get();
+        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+        return res ? result : basic_json(value_t::discarded);
+    }
+    /// @}
+
+    //////////////////////////
+    // JSON Pointer support //
+    //////////////////////////
+
+    /// @name JSON Pointer functions
+    /// @{
+
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    reference operator[](const json_pointer& ptr)
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+    const_reference operator[](const json_pointer& ptr) const
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    reference at(const json_pointer& ptr)
+    {
+        return ptr.get_checked(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
+    {
+        return ptr.get_checked(this);
+    }
+
+    /// @brief access specified element via JSON Pointer
+    /// @sa https://json.nlohmann.me/api/basic_json/at/
+    const_reference at(const json_pointer& ptr) const
+    {
+        return ptr.get_checked(this);
+    }
+
+    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+    const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+    {
+        return ptr.get_checked(this);
+    }
+
+    /// @brief return flattened JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/flatten/
+    basic_json flatten() const
+    {
+        basic_json result(value_t::object);
+        json_pointer::flatten("", *this, result);
+        return result;
+    }
+
+    /// @brief unflatten a previously flattened JSON value
+    /// @sa https://json.nlohmann.me/api/basic_json/unflatten/
+    basic_json unflatten() const
+    {
+        return json_pointer::unflatten(*this);
+    }
+
+    /// @}
+
+    //////////////////////////
+    // JSON Patch functions //
+    //////////////////////////
+
+    /// @name JSON Patch functions
+    /// @{
+
+    /// @brief applies a JSON patch in-place without copying the object
+    /// @sa https://json.nlohmann.me/api/basic_json/patch/
+    void patch_inplace(const basic_json& json_patch)
+    {
+        basic_json& result = *this;
+        // the valid JSON Patch operations
+        enum class patch_operations {add, remove, replace, move, copy, test, invalid};
+
+        const auto get_op = [](const std::string & op)
+        {
+            if (op == "add")
+            {
+                return patch_operations::add;
+            }
+            if (op == "remove")
+            {
+                return patch_operations::remove;
+            }
+            if (op == "replace")
+            {
+                return patch_operations::replace;
+            }
+            if (op == "move")
+            {
+                return patch_operations::move;
+            }
+            if (op == "copy")
+            {
+                return patch_operations::copy;
+            }
+            if (op == "test")
+            {
+                return patch_operations::test;
+            }
+
+            return patch_operations::invalid;
+        };
+
+        // wrapper for "add" operation; add value at ptr
+        const auto operation_add = [&result](json_pointer & ptr, basic_json val)
+        {
+            // adding to the root of the target document means replacing it
+            if (ptr.empty())
+            {
+                result = val;
+                return;
+            }
+
+            // make sure the top element of the pointer exists
+            json_pointer const top_pointer = ptr.top();
+            if (top_pointer != ptr)
+            {
+                result.at(top_pointer);
+            }
+
+            // get reference to parent of JSON pointer ptr
+            const auto last_path = ptr.back();
+            ptr.pop_back();
+            // parent must exist when performing patch add per RFC6902 specs
+            basic_json& parent = result.at(ptr);
+
+            switch (parent.m_data.m_type)
+            {
+                case value_t::null:
+                case value_t::object:
+                {
+                    // use operator[] to add value
+                    parent[last_path] = val;
+                    break;
+                }
+
+                case value_t::array:
+                {
+                    if (last_path == "-")
+                    {
+                        // special case: append to back
+                        parent.push_back(val);
+                    }
+                    else
+                    {
+                        const auto idx = json_pointer::template array_index<basic_json_t>(last_path);
+                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
+                        {
+                            // avoid undefined behavior
+                            JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
+                        }
+
+                        // default case: insert add offset
+                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
+                    }
+                    break;
+                }
+
+                // if there exists a parent it cannot be primitive
+                case value_t::string: // LCOV_EXCL_LINE
+                case value_t::boolean: // LCOV_EXCL_LINE
+                case value_t::number_integer: // LCOV_EXCL_LINE
+                case value_t::number_unsigned: // LCOV_EXCL_LINE
+                case value_t::number_float: // LCOV_EXCL_LINE
+                case value_t::binary: // LCOV_EXCL_LINE
+                case value_t::discarded: // LCOV_EXCL_LINE
+                default:            // LCOV_EXCL_LINE
+                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+            }
+        };
+
+        // wrapper for "remove" operation; remove value at ptr
+        const auto operation_remove = [this, &result](json_pointer & ptr)
+        {
+            // get reference to parent of JSON pointer ptr
+            const auto last_path = ptr.back();
+            ptr.pop_back();
+            basic_json& parent = result.at(ptr);
+
+            // remove child
+            if (parent.is_object())
+            {
+                // perform range check
+                auto it = parent.find(last_path);
+                if (JSON_HEDLEY_LIKELY(it != parent.end()))
+                {
+                    parent.erase(it);
+                }
+                else
+                {
+                    JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this));
+                }
+            }
+            else if (parent.is_array())
+            {
+                // note erase performs range check
+                parent.erase(json_pointer::template array_index<basic_json_t>(last_path));
+            }
+        };
+
+        // type check: top level value must be an array
+        if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
+        {
+            JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch));
+        }
+
+        // iterate and apply the operations
+        for (const auto& val : json_patch)
+        {
+            // wrapper to get a value for an operation
+            const auto get_value = [&val](const std::string & op,
+                                          const std::string & member,
+                                          bool string_type) -> basic_json &
+            {
+                // find value
+                auto it = val.m_data.m_value.object->find(member);
+
+                // context-sensitive error message
+                const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\'');
+
+                // check if desired value is present
+                if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end()))
+                {
+                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
+                }
+
+                // check if result is of type string
+                if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
+                {
+                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
+                }
+
+                // no error: return value
+                return it->second;
+            };
+
+            // type check: every element of the array must be an object
+            if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
+            {
+                JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val));
+            }
+
+            // collect mandatory members
+            const auto op = get_value("op", "op", true).template get<std::string>();
+            const auto path = get_value(op, "path", true).template get<std::string>();
+            json_pointer ptr(path);
+
+            switch (get_op(op))
+            {
+                case patch_operations::add:
+                {
+                    operation_add(ptr, get_value("add", "value", false));
+                    break;
+                }
+
+                case patch_operations::remove:
+                {
+                    operation_remove(ptr);
+                    break;
+                }
+
+                case patch_operations::replace:
+                {
+                    // the "path" location must exist - use at()
+                    result.at(ptr) = get_value("replace", "value", false);
+                    break;
+                }
+
+                case patch_operations::move:
+                {
+                    const auto from_path = get_value("move", "from", true).template get<std::string>();
+                    json_pointer from_ptr(from_path);
+
+                    // the "from" location must exist - use at()
+                    basic_json const v = result.at(from_ptr);
+
+                    // The move operation is functionally identical to a
+                    // "remove" operation on the "from" location, followed
+                    // immediately by an "add" operation at the target
+                    // location with the value that was just removed.
+                    operation_remove(from_ptr);
+                    operation_add(ptr, v);
+                    break;
+                }
+
+                case patch_operations::copy:
+                {
+                    const auto from_path = get_value("copy", "from", true).template get<std::string>();
+                    const json_pointer from_ptr(from_path);
+
+                    // the "from" location must exist - use at()
+                    basic_json const v = result.at(from_ptr);
+
+                    // The copy is functionally identical to an "add"
+                    // operation at the target location using the value
+                    // specified in the "from" member.
+                    operation_add(ptr, v);
+                    break;
+                }
+
+                case patch_operations::test:
+                {
+                    bool success = false;
+                    JSON_TRY
+                    {
+                        // check if "value" matches the one at "path"
+                        // the "path" location must exist - use at()
+                        success = (result.at(ptr) == get_value("test", "value", false));
+                    }
+                    JSON_INTERNAL_CATCH (out_of_range&)
+                    {
+                        // ignore out of range errors: success remains false
+                    }
+
+                    // throw an exception if test fails
+                    if (JSON_HEDLEY_UNLIKELY(!success))
+                    {
+                        JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val));
+                    }
+
+                    break;
+                }
+
+                case patch_operations::invalid:
+                default:
+                {
+                    // op must be "add", "remove", "replace", "move", "copy", or
+                    // "test"
+                    JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val));
+                }
+            }
+        }
+    }
+
+    /// @brief applies a JSON patch to a copy of the current object
+    /// @sa https://json.nlohmann.me/api/basic_json/patch/
+    basic_json patch(const basic_json& json_patch) const
+    {
+        basic_json result = *this;
+        result.patch_inplace(json_patch);
+        return result;
+    }
+
+    /// @brief creates a diff as a JSON patch
+    /// @sa https://json.nlohmann.me/api/basic_json/diff/
+    JSON_HEDLEY_WARN_UNUSED_RESULT
+    static basic_json diff(const basic_json& source, const basic_json& target,
+                           const std::string& path = "")
+    {
+        // the patch
+        basic_json result(value_t::array);
+
+        // if the values are the same, return empty patch
+        if (source == target)
+        {
+            return result;
+        }
+
+        if (source.type() != target.type())
+        {
+            // different types: replace value
+            result.push_back(
+            {
+                {"op", "replace"}, {"path", path}, {"value", target}
+            });
+            return result;
+        }
+
+        switch (source.type())
+        {
+            case value_t::array:
+            {
+                // first pass: traverse common elements
+                std::size_t i = 0;
+                while (i < source.size() && i < target.size())
+                {
+                    // recursive call to compare array values at index i
+                    auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i)));
+                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                    ++i;
+                }
+
+                // We now reached the end of at least one array
+                // in a second pass, traverse the remaining elements
+
+                // remove my remaining elements
+                const auto end_index = static_cast<difference_type>(result.size());
+                while (i < source.size())
+                {
+                    // add operations in reverse order to avoid invalid
+                    // indices
+                    result.insert(result.begin() + end_index, object(
+                    {
+                        {"op", "remove"},
+                        {"path", detail::concat(path, '/', std::to_string(i))}
+                    }));
+                    ++i;
+                }
+
+                // add other remaining elements
+                while (i < target.size())
+                {
+                    result.push_back(
+                    {
+                        {"op", "add"},
+                        {"path", detail::concat(path, "/-")},
+                        {"value", target[i]}
+                    });
+                    ++i;
+                }
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                // first pass: traverse this object's elements
+                for (auto it = source.cbegin(); it != source.cend(); ++it)
+                {
+                    // escape the key name to be used in a JSON patch
+                    const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
+
+                    if (target.find(it.key()) != target.end())
+                    {
+                        // recursive call to compare object values at key it
+                        auto temp_diff = diff(it.value(), target[it.key()], path_key);
+                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                    }
+                    else
+                    {
+                        // found a key that is not in o -> remove it
+                        result.push_back(object(
+                        {
+                            {"op", "remove"}, {"path", path_key}
+                        }));
+                    }
+                }
+
+                // second pass: traverse other object's elements
+                for (auto it = target.cbegin(); it != target.cend(); ++it)
+                {
+                    if (source.find(it.key()) == source.end())
+                    {
+                        // found a key that is not in this -> add it
+                        const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
+                        result.push_back(
+                        {
+                            {"op", "add"}, {"path", path_key},
+                            {"value", it.value()}
+                        });
+                    }
+                }
+
+                break;
+            }
+
+            case value_t::null:
+            case value_t::string:
+            case value_t::boolean:
+            case value_t::number_integer:
+            case value_t::number_unsigned:
+            case value_t::number_float:
+            case value_t::binary:
+            case value_t::discarded:
+            default:
+            {
+                // both primitive type: replace value
+                result.push_back(
+                {
+                    {"op", "replace"}, {"path", path}, {"value", target}
+                });
+                break;
+            }
+        }
+
+        return result;
+    }
+    /// @}
+
+    ////////////////////////////////
+    // JSON Merge Patch functions //
+    ////////////////////////////////
+
+    /// @name JSON Merge Patch functions
+    /// @{
+
+    /// @brief applies a JSON Merge Patch
+    /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/
+    void merge_patch(const basic_json& apply_patch)
+    {
+        if (apply_patch.is_object())
+        {
+            if (!is_object())
+            {
+                *this = object();
+            }
+            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)
+            {
+                if (it.value().is_null())
+                {
+                    erase(it.key());
+                }
+                else
+                {
+                    operator[](it.key()).merge_patch(it.value());
+                }
+            }
+        }
+        else
+        {
+            *this = apply_patch;
+        }
+    }
+
+    /// @}
+};
+
+/// @brief user-defined to_string function for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/to_string/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)
+{
+    return j.dump();
+}
+
+inline namespace literals
+{
+inline namespace json_literals
+{
+
+/// @brief user-defined string literal for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/
+JSON_HEDLEY_NON_NULL(1)
+inline nlohmann::json operator "" _json(const char* s, std::size_t n)
+{
+    return nlohmann::json::parse(s, s + n);
+}
+
+/// @brief user-defined string literal for JSON pointer
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/
+JSON_HEDLEY_NON_NULL(1)
+inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
+{
+    return nlohmann::json::json_pointer(std::string(s, n));
+}
+
+}  // namespace json_literals
+}  // namespace literals
+NLOHMANN_JSON_NAMESPACE_END
+
+///////////////////////
+// nonmember support //
+///////////////////////
+
+namespace std // NOLINT(cert-dcl58-cpp)
+{
+
+/// @brief hash value for JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_hash/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> // NOLINT(cert-dcl58-cpp)
+{
+    std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
+    {
+        return nlohmann::detail::hash(j);
+    }
+};
+
+// specialization for std::less<value_t>
+template<>
+struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679
+{
+    /*!
+    @brief compare two value_t enum values
+    @since version 3.0.0
+    */
+    bool operator()(::nlohmann::detail::value_t lhs,
+                    ::nlohmann::detail::value_t rhs) const noexcept
+    {
+#if JSON_HAS_THREE_WAY_COMPARISON
+        return std::is_lt(lhs <=> rhs); // *NOPAD*
+#else
+        return ::nlohmann::detail::operator<(lhs, rhs);
+#endif
+    }
+};
+
+// C++20 prohibit function specialization in the std namespace.
+#ifndef JSON_HAS_CPP_20
+
+/// @brief exchanges the values of two JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_swap/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept(  // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp)
+    is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&&                          // NOLINT(misc-redundant-expression)
+    is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)
+{
+    j1.swap(j2);
+}
+
+#endif
+
+}  // namespace std
+
+#if JSON_USE_GLOBAL_UDLS
+    using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+    using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+#endif
+
+// #include <nlohmann/detail/macro_unscope.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// restore clang diagnostic settings
+#if defined(__clang__)
+    #pragma clang diagnostic pop
+#endif
+
+// clean up
+#undef JSON_ASSERT
+#undef JSON_INTERNAL_CATCH
+#undef JSON_THROW
+#undef JSON_PRIVATE_UNLESS_TESTED
+#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
+#undef NLOHMANN_BASIC_JSON_TPL
+#undef JSON_EXPLICIT
+#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL
+#undef JSON_INLINE_VARIABLE
+#undef JSON_NO_UNIQUE_ADDRESS
+#undef JSON_DISABLE_ENUM_SERIALIZATION
+#undef JSON_USE_GLOBAL_UDLS
+
+#ifndef JSON_TEST_KEEP_MACROS
+    #undef JSON_CATCH
+    #undef JSON_TRY
+    #undef JSON_HAS_CPP_11
+    #undef JSON_HAS_CPP_14
+    #undef JSON_HAS_CPP_17
+    #undef JSON_HAS_CPP_20
+    #undef JSON_HAS_FILESYSTEM
+    #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+    #undef JSON_HAS_THREE_WAY_COMPARISON
+    #undef JSON_HAS_RANGES
+    #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
+//     __ _____ _____ _____
+//  __|  |   __|     |   | |  JSON for Modern C++
+// |  |  |__   |  |  | | | |  version 3.11.2
+// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#undef JSON_HEDLEY_ALWAYS_INLINE
+#undef JSON_HEDLEY_ARM_VERSION
+#undef JSON_HEDLEY_ARM_VERSION_CHECK
+#undef JSON_HEDLEY_ARRAY_PARAM
+#undef JSON_HEDLEY_ASSUME
+#undef JSON_HEDLEY_BEGIN_C_DECLS
+#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#undef JSON_HEDLEY_CLANG_HAS_WARNING
+#undef JSON_HEDLEY_COMPCERT_VERSION
+#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#undef JSON_HEDLEY_CONCAT
+#undef JSON_HEDLEY_CONCAT3
+#undef JSON_HEDLEY_CONCAT3_EX
+#undef JSON_HEDLEY_CONCAT_EX
+#undef JSON_HEDLEY_CONST
+#undef JSON_HEDLEY_CONSTEXPR
+#undef JSON_HEDLEY_CONST_CAST
+#undef JSON_HEDLEY_CPP_CAST
+#undef JSON_HEDLEY_CRAY_VERSION
+#undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#undef JSON_HEDLEY_C_DECL
+#undef JSON_HEDLEY_DEPRECATED
+#undef JSON_HEDLEY_DEPRECATED_FOR
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#undef JSON_HEDLEY_DIAGNOSTIC_POP
+#undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#undef JSON_HEDLEY_DMC_VERSION
+#undef JSON_HEDLEY_DMC_VERSION_CHECK
+#undef JSON_HEDLEY_EMPTY_BASES
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#undef JSON_HEDLEY_END_C_DECLS
+#undef JSON_HEDLEY_FLAGS
+#undef JSON_HEDLEY_FLAGS_CAST
+#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#undef JSON_HEDLEY_GCC_HAS_FEATURE
+#undef JSON_HEDLEY_GCC_HAS_WARNING
+#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#undef JSON_HEDLEY_GCC_VERSION
+#undef JSON_HEDLEY_GCC_VERSION_CHECK
+#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#undef JSON_HEDLEY_GNUC_HAS_WARNING
+#undef JSON_HEDLEY_GNUC_VERSION
+#undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#undef JSON_HEDLEY_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_BUILTIN
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_EXTENSION
+#undef JSON_HEDLEY_HAS_FEATURE
+#undef JSON_HEDLEY_HAS_WARNING
+#undef JSON_HEDLEY_IAR_VERSION
+#undef JSON_HEDLEY_IAR_VERSION_CHECK
+#undef JSON_HEDLEY_IBM_VERSION
+#undef JSON_HEDLEY_IBM_VERSION_CHECK
+#undef JSON_HEDLEY_IMPORT
+#undef JSON_HEDLEY_INLINE
+#undef JSON_HEDLEY_INTEL_CL_VERSION
+#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#undef JSON_HEDLEY_INTEL_VERSION
+#undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#undef JSON_HEDLEY_IS_CONSTANT
+#undef JSON_HEDLEY_IS_CONSTEXPR_
+#undef JSON_HEDLEY_LIKELY
+#undef JSON_HEDLEY_MALLOC
+#undef JSON_HEDLEY_MCST_LCC_VERSION
+#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#undef JSON_HEDLEY_MESSAGE
+#undef JSON_HEDLEY_MSVC_VERSION
+#undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#undef JSON_HEDLEY_NEVER_INLINE
+#undef JSON_HEDLEY_NON_NULL
+#undef JSON_HEDLEY_NO_ESCAPE
+#undef JSON_HEDLEY_NO_RETURN
+#undef JSON_HEDLEY_NO_THROW
+#undef JSON_HEDLEY_NULL
+#undef JSON_HEDLEY_PELLES_VERSION
+#undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#undef JSON_HEDLEY_PGI_VERSION
+#undef JSON_HEDLEY_PGI_VERSION_CHECK
+#undef JSON_HEDLEY_PREDICT
+#undef JSON_HEDLEY_PRINTF_FORMAT
+#undef JSON_HEDLEY_PRIVATE
+#undef JSON_HEDLEY_PUBLIC
+#undef JSON_HEDLEY_PURE
+#undef JSON_HEDLEY_REINTERPRET_CAST
+#undef JSON_HEDLEY_REQUIRE
+#undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#undef JSON_HEDLEY_REQUIRE_MSG
+#undef JSON_HEDLEY_RESTRICT
+#undef JSON_HEDLEY_RETURNS_NON_NULL
+#undef JSON_HEDLEY_SENTINEL
+#undef JSON_HEDLEY_STATIC_ASSERT
+#undef JSON_HEDLEY_STATIC_CAST
+#undef JSON_HEDLEY_STRINGIFY
+#undef JSON_HEDLEY_STRINGIFY_EX
+#undef JSON_HEDLEY_SUNPRO_VERSION
+#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#undef JSON_HEDLEY_TINYC_VERSION
+#undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#undef JSON_HEDLEY_TI_ARMCL_VERSION
+#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL2000_VERSION
+#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL430_VERSION
+#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL6X_VERSION
+#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL7X_VERSION
+#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CLPRU_VERSION
+#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#undef JSON_HEDLEY_TI_VERSION
+#undef JSON_HEDLEY_TI_VERSION_CHECK
+#undef JSON_HEDLEY_UNAVAILABLE
+#undef JSON_HEDLEY_UNLIKELY
+#undef JSON_HEDLEY_UNPREDICTABLE
+#undef JSON_HEDLEY_UNREACHABLE
+#undef JSON_HEDLEY_UNREACHABLE_RETURN
+#undef JSON_HEDLEY_VERSION
+#undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#undef JSON_HEDLEY_VERSION_ENCODE
+#undef JSON_HEDLEY_WARNING
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#undef JSON_HEDLEY_FALL_THROUGH
+
+
+
+#endif  // INCLUDE_NLOHMANN_JSON_HPP_
diff --git a/src/cpp-common/vendor/optional-lite/optional.hpp b/src/cpp-common/vendor/optional-lite/optional.hpp
new file mode 100644 (file)
index 0000000..85febc3
--- /dev/null
@@ -0,0 +1,1847 @@
+//
+// Copyright (c) 2014-2021 Martin Moene
+//
+// https://github.com/martinmoene/optional-lite
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#pragma once
+
+#ifndef NONSTD_OPTIONAL_LITE_HPP
+#define NONSTD_OPTIONAL_LITE_HPP
+
+#define optional_lite_MAJOR  3
+#define optional_lite_MINOR  6
+#define optional_lite_PATCH  0
+
+#define optional_lite_VERSION  optional_STRINGIFY(optional_lite_MAJOR) "." optional_STRINGIFY(optional_lite_MINOR) "." optional_STRINGIFY(optional_lite_PATCH)
+
+#define optional_STRINGIFY(  x )  optional_STRINGIFY_( x )
+#define optional_STRINGIFY_( x )  #x
+
+// optional-lite configuration:
+
+#define optional_OPTIONAL_DEFAULT  0
+#define optional_OPTIONAL_NONSTD   1
+#define optional_OPTIONAL_STD      2
+
+// tweak header support:
+
+#ifdef __has_include
+# if __has_include(<nonstd/optional.tweak.hpp>)
+#  include <nonstd/optional.tweak.hpp>
+# endif
+#define optional_HAVE_TWEAK_HEADER  1
+#else
+#define optional_HAVE_TWEAK_HEADER  0
+//# pragma message("optional.hpp: Note: Tweak header not supported.")
+#endif
+
+// optional selection and configuration:
+
+#if !defined( optional_CONFIG_SELECT_OPTIONAL )
+# define optional_CONFIG_SELECT_OPTIONAL  ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD )
+#endif
+
+// Control presence of extensions:
+
+#ifndef optional_CONFIG_NO_EXTENSIONS
+#define optional_CONFIG_NO_EXTENSIONS  0
+#endif
+
+// Control presence of exception handling (try and auto discover):
+
+#ifndef optional_CONFIG_NO_EXCEPTIONS
+# if defined(_MSC_VER)
+# include <cstddef>     // for _HAS_EXCEPTIONS
+# endif
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (defined(_HAS_EXCEPTIONS) && (_HAS_EXCEPTIONS))
+#  define optional_CONFIG_NO_EXCEPTIONS  0
+# else
+#  define optional_CONFIG_NO_EXCEPTIONS  1
+# endif
+#endif
+
+// C++ language version detection (C++23 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef   optional_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+#  define optional_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+#  define optional_CPLUSPLUS  __cplusplus
+# endif
+#endif
+
+#define optional_CPP98_OR_GREATER  ( optional_CPLUSPLUS >= 199711L )
+#define optional_CPP11_OR_GREATER  ( optional_CPLUSPLUS >= 201103L )
+#define optional_CPP11_OR_GREATER_ ( optional_CPLUSPLUS >= 201103L )
+#define optional_CPP14_OR_GREATER  ( optional_CPLUSPLUS >= 201402L )
+#define optional_CPP17_OR_GREATER  ( optional_CPLUSPLUS >= 201703L )
+#define optional_CPP20_OR_GREATER  ( optional_CPLUSPLUS >= 202002L )
+#define optional_CPP23_OR_GREATER  ( optional_CPLUSPLUS >= 202300L )
+
+// C++ language version (represent 98 as 3):
+
+#define optional_CPLUSPLUS_V  ( optional_CPLUSPLUS / 100 - (optional_CPLUSPLUS > 200000 ? 2000 : 1994) )
+
+// Use C++17 std::optional if available and requested:
+
+#if optional_CPP17_OR_GREATER && defined(__has_include )
+# if __has_include( <optional> )
+#  define optional_HAVE_STD_OPTIONAL  1
+# else
+#  define optional_HAVE_STD_OPTIONAL  0
+# endif
+#else
+# define  optional_HAVE_STD_OPTIONAL  0
+#endif
+
+#define optional_USES_STD_OPTIONAL  ( (optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_STD) || ((optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_DEFAULT) && optional_HAVE_STD_OPTIONAL) )
+
+//
+// in_place: code duplicated in any-lite, expected-lite, optional-lite, value-ptr-lite, variant-lite:
+//
+
+#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES
+#define nonstd_lite_HAVE_IN_PLACE_TYPES  1
+
+// C++17 std::in_place in <utility>:
+
+#if optional_CPP17_OR_GREATER
+
+#include <utility>
+
+namespace nonstd {
+
+using std::in_place;
+using std::in_place_type;
+using std::in_place_index;
+using std::in_place_t;
+using std::in_place_type_t;
+using std::in_place_index_t;
+
+#define nonstd_lite_in_place_t(      T)  std::in_place_t
+#define nonstd_lite_in_place_type_t( T)  std::in_place_type_t<T>
+#define nonstd_lite_in_place_index_t(K)  std::in_place_index_t<K>
+
+#define nonstd_lite_in_place(      T)    std::in_place_t{}
+#define nonstd_lite_in_place_type( T)    std::in_place_type_t<T>{}
+#define nonstd_lite_in_place_index(K)    std::in_place_index_t<K>{}
+
+} // namespace nonstd
+
+#else // optional_CPP17_OR_GREATER
+
+#include <cstddef>
+
+namespace nonstd {
+namespace detail {
+
+template< class T >
+struct in_place_type_tag {};
+
+template< std::size_t K >
+struct in_place_index_tag {};
+
+} // namespace detail
+
+struct in_place_t {};
+
+template< class T >
+inline in_place_t in_place( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() )
+{
+    return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() )
+{
+    return in_place_t();
+}
+
+template< class T >
+inline in_place_t in_place_type( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() )
+{
+    return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place_index( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() )
+{
+    return in_place_t();
+}
+
+// mimic templated typedef:
+
+#define nonstd_lite_in_place_t(      T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
+#define nonstd_lite_in_place_type_t( T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
+#define nonstd_lite_in_place_index_t(K)  nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag<K> )
+
+#define nonstd_lite_in_place(      T)    nonstd::in_place_type<T>
+#define nonstd_lite_in_place_type( T)    nonstd::in_place_type<T>
+#define nonstd_lite_in_place_index(K)    nonstd::in_place_index<K>
+
+} // namespace nonstd
+
+#endif // optional_CPP17_OR_GREATER
+#endif // nonstd_lite_HAVE_IN_PLACE_TYPES
+
+//
+// Using std::optional:
+//
+
+#if optional_USES_STD_OPTIONAL
+
+#include <optional>
+
+namespace nonstd {
+
+    using std::optional;
+    using std::bad_optional_access;
+    using std::hash;
+
+    using std::nullopt;
+    using std::nullopt_t;
+
+    using std::operator==;
+    using std::operator!=;
+    using std::operator<;
+    using std::operator<=;
+    using std::operator>;
+    using std::operator>=;
+    using std::make_optional;
+    using std::swap;
+}
+
+#else // optional_USES_STD_OPTIONAL
+
+#include <cassert>
+#include <utility>
+
+// optional-lite alignment configuration:
+
+#ifndef  optional_CONFIG_MAX_ALIGN_HACK
+# define optional_CONFIG_MAX_ALIGN_HACK  0
+#endif
+
+#ifndef  optional_CONFIG_ALIGN_AS
+// no default, used in #if defined()
+#endif
+
+#ifndef  optional_CONFIG_ALIGN_AS_FALLBACK
+# define optional_CONFIG_ALIGN_AS_FALLBACK  double
+#endif
+
+// Compiler warning suppression:
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wundef"
+#elif defined(__GNUC__)
+# pragma GCC   diagnostic push
+# pragma GCC   diagnostic ignored "-Wundef"
+#elif defined(_MSC_VER )
+# pragma warning( push )
+#endif
+
+// half-open range [lo..hi):
+#define optional_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Compiler versions:
+//
+// MSVC++  6.0  _MSC_VER == 1200  optional_COMPILER_MSVC_VERSION ==  60  (Visual Studio 6.0)
+// MSVC++  7.0  _MSC_VER == 1300  optional_COMPILER_MSVC_VERSION ==  70  (Visual Studio .NET 2002)
+// MSVC++  7.1  _MSC_VER == 1310  optional_COMPILER_MSVC_VERSION ==  71  (Visual Studio .NET 2003)
+// MSVC++  8.0  _MSC_VER == 1400  optional_COMPILER_MSVC_VERSION ==  80  (Visual Studio 2005)
+// MSVC++  9.0  _MSC_VER == 1500  optional_COMPILER_MSVC_VERSION ==  90  (Visual Studio 2008)
+// MSVC++ 10.0  _MSC_VER == 1600  optional_COMPILER_MSVC_VERSION == 100  (Visual Studio 2010)
+// MSVC++ 11.0  _MSC_VER == 1700  optional_COMPILER_MSVC_VERSION == 110  (Visual Studio 2012)
+// MSVC++ 12.0  _MSC_VER == 1800  optional_COMPILER_MSVC_VERSION == 120  (Visual Studio 2013)
+// MSVC++ 14.0  _MSC_VER == 1900  optional_COMPILER_MSVC_VERSION == 140  (Visual Studio 2015)
+// MSVC++ 14.1  _MSC_VER >= 1910  optional_COMPILER_MSVC_VERSION == 141  (Visual Studio 2017)
+// MSVC++ 14.2  _MSC_VER >= 1920  optional_COMPILER_MSVC_VERSION == 142  (Visual Studio 2019)
+
+#if defined(_MSC_VER ) && !defined(__clang__)
+# define optional_COMPILER_MSVC_VER      (_MSC_VER )
+# define optional_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
+#else
+# define optional_COMPILER_MSVC_VER      0
+# define optional_COMPILER_MSVC_VERSION  0
+#endif
+
+#define optional_COMPILER_VERSION( major, minor, patch )  ( 10 * (10 * (major) + (minor) ) + (patch) )
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define optional_COMPILER_GNUC_VERSION   optional_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define optional_COMPILER_GNUC_VERSION   0
+#endif
+
+#if defined(__clang__)
+# define optional_COMPILER_CLANG_VERSION  optional_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define optional_COMPILER_CLANG_VERSION  0
+#endif
+
+#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 140 )
+# pragma warning( disable: 4345 )   // initialization behavior changed
+#endif
+
+#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 150 )
+# pragma warning( disable: 4814 )   // in C++14 'constexpr' will not imply 'const'
+#endif
+
+// Presence of language and library features:
+
+#define optional_HAVE(FEATURE) ( optional_HAVE_##FEATURE )
+
+#ifdef _HAS_CPP0X
+# define optional_HAS_CPP0X  _HAS_CPP0X
+#else
+# define optional_HAS_CPP0X  0
+#endif
+
+// Unless defined otherwise below, consider VC14 as C++11 for optional-lite:
+
+#if optional_COMPILER_MSVC_VER >= 1900
+# undef  optional_CPP11_OR_GREATER
+# define optional_CPP11_OR_GREATER  1
+#endif
+
+#define optional_CPP11_90   (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1500)
+#define optional_CPP11_100  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1600)
+#define optional_CPP11_110  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1700)
+#define optional_CPP11_120  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1800)
+#define optional_CPP11_140  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1900)
+#define optional_CPP11_141  (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1910)
+
+#define optional_CPP14_000  (optional_CPP14_OR_GREATER)
+#define optional_CPP17_000  (optional_CPP17_OR_GREATER)
+
+// clang >= 2.9, gcc >= 4.9, msvc >= vc14.0/1900 (vs15):
+#define optional_CPP11_140_C290_G490    ((optional_CPP11_OR_GREATER_ && (optional_COMPILER_CLANG_VERSION >= 290 || optional_COMPILER_GNUC_VERSION >= 490)) || (optional_COMPILER_MSVC_VER >= 1900))
+
+// clang >= 3.5, msvc >= vc11 (vs12):
+#define optional_CPP11_110_C350         ( optional_CPP11_110 && !optional_BETWEEN( optional_COMPILER_CLANG_VERSION, 1, 350 ) )
+
+// clang >= 3.5, gcc >= 5.0, msvc >= vc11 (vs12):
+#define optional_CPP11_110_C350_G500 \
+    (  optional_CPP11_110 && \
+    !( optional_BETWEEN( optional_COMPILER_CLANG_VERSION, 1, 350 ) \
+    || optional_BETWEEN( optional_COMPILER_GNUC_VERSION , 1, 500 ) ) )
+
+// Presence of C++11 language features:
+
+#define optional_HAVE_CONSTEXPR_11      optional_CPP11_140
+#define optional_HAVE_IS_DEFAULT        optional_CPP11_140
+#define optional_HAVE_NOEXCEPT          optional_CPP11_140
+#define optional_HAVE_NULLPTR           optional_CPP11_100
+#define optional_HAVE_REF_QUALIFIER     optional_CPP11_140_C290_G490
+#define optional_HAVE_STATIC_ASSERT     optional_CPP11_110
+#define optional_HAVE_INITIALIZER_LIST  optional_CPP11_140
+
+// Presence of C++14 language features:
+
+#define optional_HAVE_CONSTEXPR_14      optional_CPP14_000
+
+// Presence of C++17 language features:
+
+#define optional_HAVE_NODISCARD         optional_CPP17_000
+
+// Presence of C++ library features:
+
+#define optional_HAVE_CONDITIONAL       optional_CPP11_120
+#define optional_HAVE_REMOVE_CV         optional_CPP11_120
+#define optional_HAVE_TYPE_TRAITS       optional_CPP11_90
+
+#define optional_HAVE_TR1_TYPE_TRAITS   (!! optional_COMPILER_GNUC_VERSION )
+#define optional_HAVE_TR1_ADD_POINTER   (!! optional_COMPILER_GNUC_VERSION )
+
+#define optional_HAVE_IS_ASSIGNABLE                     optional_CPP11_110_C350
+#define optional_HAVE_IS_MOVE_CONSTRUCTIBLE             optional_CPP11_110_C350
+#define optional_HAVE_IS_NOTHROW_MOVE_ASSIGNABLE        optional_CPP11_110_C350
+#define optional_HAVE_IS_NOTHROW_MOVE_CONSTRUCTIBLE     optional_CPP11_110_C350
+#define optional_HAVE_IS_TRIVIALLY_COPY_CONSTRUCTIBLE   optional_CPP11_110_C350_G500
+#define optional_HAVE_IS_TRIVIALLY_MOVE_CONSTRUCTIBLE   optional_CPP11_110_C350_G500
+
+// C++ feature usage:
+
+#if optional_HAVE( CONSTEXPR_11 )
+# define optional_constexpr  constexpr
+#else
+# define optional_constexpr  /*constexpr*/
+#endif
+
+#if optional_HAVE( IS_DEFAULT )
+# define optional_is_default  = default;
+#else
+# define optional_is_default  {}
+#endif
+
+#if optional_HAVE( CONSTEXPR_14 )
+# define optional_constexpr14  constexpr
+#else
+# define optional_constexpr14  /*constexpr*/
+#endif
+
+#if optional_HAVE( NODISCARD )
+# define optional_nodiscard  [[nodiscard]]
+#else
+# define optional_nodiscard  /*[[nodiscard]]*/
+#endif
+
+#if optional_HAVE( NOEXCEPT )
+# define optional_noexcept  noexcept
+#else
+# define optional_noexcept  /*noexcept*/
+#endif
+
+#if optional_HAVE( NULLPTR )
+# define optional_nullptr  nullptr
+#else
+# define optional_nullptr  NULL
+#endif
+
+#if optional_HAVE( REF_QUALIFIER )
+// NOLINTNEXTLINE( bugprone-macro-parentheses )
+# define optional_ref_qual  &
+# define optional_refref_qual  &&
+#else
+# define optional_ref_qual  /*&*/
+# define optional_refref_qual  /*&&*/
+#endif
+
+#if optional_HAVE( STATIC_ASSERT )
+# define optional_static_assert(expr, text)    static_assert(expr, text);
+#else
+# define optional_static_assert(expr, text)  /*static_assert(expr, text);*/
+#endif
+
+// additional includes:
+
+#if optional_CONFIG_NO_EXCEPTIONS
+// already included: <cassert>
+#else
+# include <stdexcept>
+#endif
+
+#if optional_CPP11_OR_GREATER
+# include <functional>
+#endif
+
+#if optional_HAVE( INITIALIZER_LIST )
+# include <initializer_list>
+#endif
+
+#if optional_HAVE( TYPE_TRAITS )
+# include <type_traits>
+#elif optional_HAVE( TR1_TYPE_TRAITS )
+# include <tr1/type_traits>
+#endif
+
+// Method enabling
+
+#if optional_CPP11_OR_GREATER
+
+#define optional_REQUIRES_0(...) \
+    template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 >
+
+#define optional_REQUIRES_T(...) \
+    , typename std::enable_if< (__VA_ARGS__), int >::type = 0
+
+#define optional_REQUIRES_R(R, ...) \
+    typename std::enable_if< (__VA_ARGS__), R>::type
+
+#define optional_REQUIRES_A(...) \
+    , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr
+
+#endif
+
+//
+// optional:
+//
+
+namespace nonstd { namespace optional_lite {
+
+namespace std11 {
+
+template< class T, T v > struct integral_constant { enum { value = v }; };
+template< bool B       > struct bool_constant : integral_constant<bool, B>{};
+
+typedef bool_constant< true  > true_type;
+typedef bool_constant< false > false_type;
+
+#if optional_CPP11_OR_GREATER
+    using std::move;
+#else
+    template< typename T > T & move( T & t ) { return t; }
+#endif
+
+#if optional_HAVE( CONDITIONAL )
+    using std::conditional;
+#else
+    template< bool B, typename T, typename F > struct conditional              { typedef T type; };
+    template<         typename T, typename F > struct conditional<false, T, F> { typedef F type; };
+#endif // optional_HAVE_CONDITIONAL
+
+#if optional_HAVE( IS_ASSIGNABLE )
+    using std::is_assignable;
+#else
+    template< class T, class U > struct is_assignable : std11::true_type{};
+#endif
+
+#if optional_HAVE( IS_MOVE_CONSTRUCTIBLE )
+    using std::is_move_constructible;
+#else
+    template< class T > struct is_move_constructible : std11::true_type{};
+#endif
+
+#if optional_HAVE( IS_NOTHROW_MOVE_ASSIGNABLE )
+    using std::is_nothrow_move_assignable;
+#else
+    template< class T > struct is_nothrow_move_assignable : std11::true_type{};
+#endif
+
+#if optional_HAVE( IS_NOTHROW_MOVE_CONSTRUCTIBLE )
+    using std::is_nothrow_move_constructible;
+#else
+    template< class T > struct is_nothrow_move_constructible : std11::true_type{};
+#endif
+
+#if optional_HAVE( IS_TRIVIALLY_COPY_CONSTRUCTIBLE )
+    using std::is_trivially_copy_constructible;
+#else
+    template< class T > struct is_trivially_copy_constructible : std11::true_type{};
+#endif
+
+#if optional_HAVE( IS_TRIVIALLY_MOVE_CONSTRUCTIBLE )
+    using std::is_trivially_move_constructible;
+#else
+    template< class T > struct is_trivially_move_constructible : std11::true_type{};
+#endif
+
+} // namespace std11
+
+#if optional_CPP11_OR_GREATER
+
+/// type traits C++17:
+
+namespace std17 {
+
+#if optional_CPP17_OR_GREATER
+
+using std::is_swappable;
+using std::is_nothrow_swappable;
+
+#elif optional_CPP11_OR_GREATER
+
+namespace detail {
+
+using std::swap;
+
+struct is_swappable
+{
+    template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) >
+    static std11::true_type test( int /*unused*/ );
+
+    template< typename >
+    static std11::false_type test(...);
+};
+
+struct is_nothrow_swappable
+{
+    // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015):
+
+    template< typename T >
+    static constexpr bool satisfies()
+    {
+        return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) );
+    }
+
+    template< typename T >
+    static auto test( int /*unused*/ ) -> std11::integral_constant<bool, satisfies<T>()>{}
+
+    template< typename >
+    static auto test(...) -> std11::false_type;
+};
+
+} // namespace detail
+
+// is [nothow] swappable:
+
+template< typename T >
+struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
+
+template< typename T >
+struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){};
+
+#endif // optional_CPP17_OR_GREATER
+
+} // namespace std17
+
+/// type traits C++20:
+
+namespace std20 {
+
+template< typename T >
+struct remove_cvref
+{
+    typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type;
+};
+
+} // namespace std20
+
+#endif // optional_CPP11_OR_GREATER
+
+/// class optional
+
+template< typename T >
+class optional;
+
+namespace detail {
+
+// C++11 emulation:
+
+struct nulltype{};
+
+template< typename Head, typename Tail >
+struct typelist
+{
+    typedef Head head;
+    typedef Tail tail;
+};
+
+#if optional_CONFIG_MAX_ALIGN_HACK
+
+// Max align, use most restricted type for alignment:
+
+#define optional_UNIQUE(  name )       optional_UNIQUE2( name, __LINE__ )
+#define optional_UNIQUE2( name, line ) optional_UNIQUE3( name, line )
+#define optional_UNIQUE3( name, line ) name ## line
+
+#define optional_ALIGN_TYPE( type ) \
+    type optional_UNIQUE( _t ); struct_t< type > optional_UNIQUE( _st )
+
+template< typename T >
+struct struct_t { T _; };
+
+union max_align_t
+{
+    optional_ALIGN_TYPE( char );
+    optional_ALIGN_TYPE( short int );
+    optional_ALIGN_TYPE( int );
+    optional_ALIGN_TYPE( long int  );
+    optional_ALIGN_TYPE( float  );
+    optional_ALIGN_TYPE( double );
+    optional_ALIGN_TYPE( long double );
+    optional_ALIGN_TYPE( char * );
+    optional_ALIGN_TYPE( short int * );
+    optional_ALIGN_TYPE( int *  );
+    optional_ALIGN_TYPE( long int * );
+    optional_ALIGN_TYPE( float * );
+    optional_ALIGN_TYPE( double * );
+    optional_ALIGN_TYPE( long double * );
+    optional_ALIGN_TYPE( void * );
+
+#ifdef HAVE_LONG_LONG
+    optional_ALIGN_TYPE( long long );
+#endif
+
+    struct Unknown;
+
+    Unknown ( * optional_UNIQUE(_) )( Unknown );
+    Unknown * Unknown::* optional_UNIQUE(_);
+    Unknown ( Unknown::* optional_UNIQUE(_) )( Unknown );
+
+    struct_t< Unknown ( * )( Unknown)         > optional_UNIQUE(_);
+    struct_t< Unknown * Unknown::*            > optional_UNIQUE(_);
+    struct_t< Unknown ( Unknown::* )(Unknown) > optional_UNIQUE(_);
+};
+
+#undef optional_UNIQUE
+#undef optional_UNIQUE2
+#undef optional_UNIQUE3
+
+#undef optional_ALIGN_TYPE
+
+#elif defined( optional_CONFIG_ALIGN_AS ) // optional_CONFIG_MAX_ALIGN_HACK
+
+// Use user-specified type for alignment:
+
+#define optional_ALIGN_AS( unused ) \
+    optional_CONFIG_ALIGN_AS
+
+#else // optional_CONFIG_MAX_ALIGN_HACK
+
+// Determine POD type to use for alignment:
+
+#define optional_ALIGN_AS( to_align ) \
+    typename type_of_size< alignment_types, alignment_of< to_align >::value >::type
+
+template< typename T >
+struct alignment_of;
+
+template< typename T >
+struct alignment_of_hack
+{
+    char c;
+    T t;
+    alignment_of_hack();
+};
+
+template< size_t A, size_t S >
+struct alignment_logic
+{
+    enum { value = A < S ? A : S };
+};
+
+template< typename T >
+struct alignment_of
+{
+    enum { value = alignment_logic<
+        sizeof( alignment_of_hack<T> ) - sizeof(T), sizeof(T) >::value };
+};
+
+template< typename List, size_t N >
+struct type_of_size
+{
+    typedef typename std11::conditional<
+        N == sizeof( typename List::head ),
+            typename List::head,
+            typename type_of_size<typename List::tail, N >::type >::type type;
+};
+
+template< size_t N >
+struct type_of_size< nulltype, N >
+{
+    typedef optional_CONFIG_ALIGN_AS_FALLBACK type;
+};
+
+template< typename T>
+struct struct_t { T _; };
+
+#define optional_ALIGN_TYPE( type ) \
+    typelist< type , typelist< struct_t< type >
+
+struct Unknown;
+
+typedef
+    optional_ALIGN_TYPE( char ),
+    optional_ALIGN_TYPE( short ),
+    optional_ALIGN_TYPE( int ),
+    optional_ALIGN_TYPE( long ),
+    optional_ALIGN_TYPE( float ),
+    optional_ALIGN_TYPE( double ),
+    optional_ALIGN_TYPE( long double ),
+
+    optional_ALIGN_TYPE( char *),
+    optional_ALIGN_TYPE( short * ),
+    optional_ALIGN_TYPE( int * ),
+    optional_ALIGN_TYPE( long * ),
+    optional_ALIGN_TYPE( float * ),
+    optional_ALIGN_TYPE( double * ),
+    optional_ALIGN_TYPE( long double * ),
+
+    optional_ALIGN_TYPE( Unknown ( * )( Unknown ) ),
+    optional_ALIGN_TYPE( Unknown * Unknown::*     ),
+    optional_ALIGN_TYPE( Unknown ( Unknown::* )( Unknown ) ),
+
+    nulltype
+    > > > > > > >    > > > > > > >
+    > > > > > > >    > > > > > > >
+    > > > > > >
+    alignment_types;
+
+#undef optional_ALIGN_TYPE
+
+#endif // optional_CONFIG_MAX_ALIGN_HACK
+
+/// C++03 constructed union to hold value.
+
+template< typename T >
+union storage_t
+{
+//private:
+//    template< typename > friend class optional;
+
+    typedef T value_type;
+
+    storage_t() optional_is_default
+
+    explicit storage_t( value_type const & v )
+    {
+        construct_value( v );
+    }
+
+    void construct_value( value_type const & v )
+    {
+        ::new( value_ptr() ) value_type( v );
+    }
+
+#if optional_CPP11_OR_GREATER
+
+    explicit storage_t( value_type && v )
+    {
+        construct_value( std::move( v ) );
+    }
+
+    void construct_value( value_type && v )
+    {
+        ::new( const_cast<void *>(static_cast<const volatile void *>(value_ptr())) ) value_type( std::move( v ) );
+    }
+
+    template< class... Args >
+    storage_t( nonstd_lite_in_place_t(T), Args&&... args )
+    {
+        emplace( std::forward<Args>(args)... );
+    }
+
+    template< class... Args >
+    void emplace( Args&&... args )
+    {
+        ::new( const_cast<void *>(static_cast<const volatile void *>(value_ptr())) ) value_type( std::forward<Args>(args)... );
+    }
+
+    template< class U, class... Args >
+    void emplace( std::initializer_list<U> il, Args&&... args )
+    {
+        ::new( const_cast<void *>(static_cast<const volatile void *>(value_ptr())) ) value_type( il, std::forward<Args>(args)... );
+    }
+
+#endif
+
+    void destruct_value()
+    {
+        value_ptr()->~T();
+    }
+
+    optional_nodiscard value_type const * value_ptr() const
+    {
+        return as<value_type>();
+    }
+
+    value_type * value_ptr()
+    {
+        return as<value_type>();
+    }
+
+    optional_nodiscard value_type const & value() const optional_ref_qual
+    {
+        return * value_ptr();
+    }
+
+    value_type & value() optional_ref_qual
+    {
+        return * value_ptr();
+    }
+
+#if optional_HAVE( REF_QUALIFIER )
+
+    optional_nodiscard value_type const && value() const optional_refref_qual
+    {
+        return std::move( value() );
+    }
+
+    value_type && value() optional_refref_qual
+    {
+        return std::move( value() );
+    }
+
+#endif
+
+#if optional_CPP11_OR_GREATER
+
+    using aligned_storage_t = typename std::aligned_storage< sizeof(value_type), alignof(value_type) >::type;
+    aligned_storage_t data;
+
+#elif optional_CONFIG_MAX_ALIGN_HACK
+
+    typedef struct { unsigned char data[ sizeof(value_type) ]; } aligned_storage_t;
+
+    max_align_t hack;
+    aligned_storage_t data;
+
+#else
+    typedef optional_ALIGN_AS(value_type) align_as_type;
+
+    typedef struct { align_as_type data[ 1 + ( sizeof(value_type) - 1 ) / sizeof(align_as_type) ]; } aligned_storage_t;
+    aligned_storage_t data;
+
+#   undef optional_ALIGN_AS
+
+#endif // optional_CONFIG_MAX_ALIGN_HACK
+
+    optional_nodiscard void * ptr() optional_noexcept
+    {
+        return &data;
+    }
+
+    optional_nodiscard void const * ptr() const optional_noexcept
+    {
+        return &data;
+    }
+
+    template <typename U>
+    optional_nodiscard U * as()
+    {
+        return reinterpret_cast<U*>( ptr() );
+    }
+
+    template <typename U>
+    optional_nodiscard U const * as() const
+    {
+        return reinterpret_cast<U const *>( ptr() );
+    }
+};
+
+} // namespace detail
+
+/// disengaged state tag
+
+struct nullopt_t
+{
+    struct init{};
+    explicit optional_constexpr nullopt_t( init /*unused*/ ) optional_noexcept {}
+};
+
+#if optional_HAVE( CONSTEXPR_11 )
+constexpr nullopt_t nullopt{ nullopt_t::init{} };
+#else
+// extra parenthesis to prevent the most vexing parse:
+const nullopt_t nullopt(( nullopt_t::init() ));
+#endif
+
+/// optional access error
+
+#if ! optional_CONFIG_NO_EXCEPTIONS
+
+class bad_optional_access : public std::logic_error
+{
+public:
+  explicit bad_optional_access()
+  : logic_error( "bad optional access" ) {}
+};
+
+#endif //optional_CONFIG_NO_EXCEPTIONS
+
+/// optional
+
+template< typename T>
+class optional
+{
+    optional_static_assert(( !std::is_same<typename std::remove_cv<T>::type, nullopt_t>::value  ),
+        "T in optional<T> must not be of type 'nullopt_t'.")
+
+    optional_static_assert(( !std::is_same<typename std::remove_cv<T>::type, in_place_t>::value ),
+        "T in optional<T> must not be of type 'in_place_t'.")
+
+    optional_static_assert(( std::is_object<T>::value && std::is_destructible<T>::value && !std::is_array<T>::value ),
+        "T in optional<T> must meet the Cpp17Destructible requirements.")
+
+private:
+    template< typename > friend class optional;
+
+    typedef void (optional::*safe_bool)() const;
+
+public:
+    typedef T value_type;
+
+     // x.x.3.1, constructors
+
+    // 1a - default construct
+    optional_constexpr optional() optional_noexcept
+    : has_value_( false )
+    , contained()
+    {}
+
+    // 1b - construct explicitly empty
+    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
+    optional_constexpr optional( nullopt_t /*unused*/ ) optional_noexcept
+    : has_value_( false )
+    , contained()
+    {}
+
+    // 2 - copy-construct
+#if optional_CPP11_OR_GREATER
+    // template< typename U = T
+    //     optional_REQUIRES_T(
+    //         std::is_copy_constructible<U>::value
+    //         || std11::is_trivially_copy_constructible<U>::value
+    //     )
+    // >
+#endif
+    optional_constexpr14 optional( optional const & other )
+    : has_value_( other.has_value() )
+    {
+        if ( other.has_value() )
+        {
+            contained.construct_value( other.contained.value() );
+        }
+    }
+
+#if optional_CPP11_OR_GREATER
+
+    // 3 (C++11) - move-construct from optional
+    template< typename U = T
+        optional_REQUIRES_T(
+            std11::is_move_constructible<U>::value
+            || std11::is_trivially_move_constructible<U>::value
+        )
+    >
+    optional_constexpr14 optional( optional && other )
+    // NOLINTNEXTLINE( performance-noexcept-move-constructor )
+        noexcept( std11::is_nothrow_move_constructible<T>::value )
+    : has_value_( other.has_value() )
+    {
+        if ( other.has_value() )
+        {
+            contained.construct_value( std::move( other.contained.value() ) );
+        }
+    }
+
+    // 4a (C++11) - explicit converting copy-construct from optional
+    template< typename U
+        optional_REQUIRES_T(
+            std::is_constructible<T, U const &>::value
+            && !std::is_constructible<T, optional<U> &          >::value
+            && !std::is_constructible<T, optional<U> &&         >::value
+            && !std::is_constructible<T, optional<U> const &    >::value
+            && !std::is_constructible<T, optional<U> const &&   >::value
+            && !std::is_convertible<     optional<U> &       , T>::value
+            && !std::is_convertible<     optional<U> &&      , T>::value
+            && !std::is_convertible<     optional<U> const & , T>::value
+            && !std::is_convertible<     optional<U> const &&, T>::value
+            && !std::is_convertible<               U const & , T>::value /*=> explicit */
+        )
+    >
+    explicit optional( optional<U> const & other )
+    : has_value_( other.has_value() )
+    {
+        if ( other.has_value() )
+        {
+            contained.construct_value( T{ other.contained.value() } );
+        }
+    }
+#endif // optional_CPP11_OR_GREATER
+
+    // 4b (C++98 and later) - non-explicit converting copy-construct from optional
+    template< typename U
+#if optional_CPP11_OR_GREATER
+        optional_REQUIRES_T(
+            std::is_constructible<T, U const &>::value
+            && !std::is_constructible<T, optional<U> &          >::value
+            && !std::is_constructible<T, optional<U> &&         >::value
+            && !std::is_constructible<T, optional<U> const &    >::value
+            && !std::is_constructible<T, optional<U> const &&   >::value
+            && !std::is_convertible<     optional<U> &       , T>::value
+            && !std::is_convertible<     optional<U> &&      , T>::value
+            && !std::is_convertible<     optional<U> const & , T>::value
+            && !std::is_convertible<     optional<U> const &&, T>::value
+            &&  std::is_convertible<               U const & , T>::value /*=> non-explicit */
+        )
+#endif // optional_CPP11_OR_GREATER
+    >
+    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
+    /*non-explicit*/ optional( optional<U> const & other )
+    : has_value_( other.has_value() )
+    {
+        if ( other.has_value() )
+        {
+            contained.construct_value( other.contained.value() );
+        }
+    }
+
+#if optional_CPP11_OR_GREATER
+
+    // 5a (C++11) - explicit converting move-construct from optional
+    template< typename U
+        optional_REQUIRES_T(
+            std::is_constructible<T, U &&>::value
+            && !std::is_constructible<T, optional<U> &          >::value
+            && !std::is_constructible<T, optional<U> &&         >::value
+            && !std::is_constructible<T, optional<U> const &    >::value
+            && !std::is_constructible<T, optional<U> const &&   >::value
+            && !std::is_convertible<     optional<U> &       , T>::value
+            && !std::is_convertible<     optional<U> &&      , T>::value
+            && !std::is_convertible<     optional<U> const & , T>::value
+            && !std::is_convertible<     optional<U> const &&, T>::value
+            && !std::is_convertible<                     U &&, T>::value /*=> explicit */
+        )
+    >
+    explicit optional( optional<U> && other
+    )
+    : has_value_( other.has_value() )
+    {
+        if ( other.has_value() )
+        {
+            contained.construct_value( T{ std::move( other.contained.value() ) } );
+        }
+    }
+
+    // 5a (C++11) - non-explicit converting move-construct from optional
+    template< typename U
+        optional_REQUIRES_T(
+            std::is_constructible<T, U &&>::value
+            && !std::is_constructible<T, optional<U> &          >::value
+            && !std::is_constructible<T, optional<U> &&         >::value
+            && !std::is_constructible<T, optional<U> const &    >::value
+            && !std::is_constructible<T, optional<U> const &&   >::value
+            && !std::is_convertible<     optional<U> &       , T>::value
+            && !std::is_convertible<     optional<U> &&      , T>::value
+            && !std::is_convertible<     optional<U> const & , T>::value
+            && !std::is_convertible<     optional<U> const &&, T>::value
+            &&  std::is_convertible<                     U &&, T>::value /*=> non-explicit */
+        )
+    >
+    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
+    /*non-explicit*/ optional( optional<U> && other )
+    : has_value_( other.has_value() )
+    {
+        if ( other.has_value() )
+        {
+            contained.construct_value( std::move( other.contained.value() ) );
+        }
+    }
+
+    // 6 (C++11) - in-place construct
+    template< typename... Args
+        optional_REQUIRES_T(
+            std::is_constructible<T, Args&&...>::value
+        )
+    >
+    optional_constexpr explicit optional( nonstd_lite_in_place_t(T), Args&&... args )
+    : has_value_( true )
+    , contained( in_place, std::forward<Args>(args)... )
+    {}
+
+    // 7 (C++11) - in-place construct,  initializer-list
+    template< typename U, typename... Args
+        optional_REQUIRES_T(
+            std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value
+        )
+    >
+    optional_constexpr explicit optional( nonstd_lite_in_place_t(T), std::initializer_list<U> il, Args&&... args )
+    : has_value_( true )
+    , contained( T( il, std::forward<Args>(args)...) )
+    {}
+
+    // 8a (C++11) - explicit move construct from value
+    template< typename U = T
+        optional_REQUIRES_T(
+            std::is_constructible<T, U&&>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
+            && !std::is_convertible<U&&, T>::value /*=> explicit */
+        )
+    >
+    optional_constexpr explicit optional( U && value )
+    : has_value_( true )
+    , contained( nonstd_lite_in_place(T), std::forward<U>( value ) )
+    {}
+
+    // 8b (C++11) - non-explicit move construct from value
+    template< typename U = T
+        optional_REQUIRES_T(
+            std::is_constructible<T, U&&>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
+            && std::is_convertible<U&&, T>::value /*=> non-explicit */
+        )
+    >
+    // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
+    optional_constexpr /*non-explicit*/ optional( U && value )
+    : has_value_( true )
+    , contained( nonstd_lite_in_place(T), std::forward<U>( value ) )
+    {}
+
+#else // optional_CPP11_OR_GREATER
+
+    // 8 (C++98)
+    optional( value_type const & value )
+    : has_value_( true )
+    , contained( value )
+    {}
+
+#endif // optional_CPP11_OR_GREATER
+
+    // x.x.3.2, destructor
+
+    ~optional()
+    {
+        if ( has_value() )
+        {
+            contained.destruct_value();
+        }
+    }
+
+    // x.x.3.3, assignment
+
+    // 1 (C++98and later) -  assign explicitly empty
+    optional & operator=( nullopt_t /*unused*/) optional_noexcept
+    {
+        reset();
+        return *this;
+    }
+
+    // 2 (C++98and later) - copy-assign from optional
+#if optional_CPP11_OR_GREATER
+    // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+    optional_REQUIRES_R(
+        optional &,
+        true
+//      std::is_copy_constructible<T>::value
+//      && std::is_copy_assignable<T>::value
+    )
+    operator=( optional const & other )
+        noexcept(
+            std11::is_nothrow_move_assignable<T>::value
+            && std11::is_nothrow_move_constructible<T>::value
+        )
+#else
+    optional & operator=( optional const & other )
+#endif
+    {
+        if      ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
+        else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( *other ); }
+        else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = *other; }
+        return *this;
+    }
+
+#if optional_CPP11_OR_GREATER
+
+    // 3 (C++11) - move-assign from optional
+    // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+    optional_REQUIRES_R(
+        optional &,
+        true
+//      std11::is_move_constructible<T>::value
+//      && std::is_move_assignable<T>::value
+    )
+    operator=( optional && other ) noexcept
+    {
+        if      ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
+        else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std::move( *other ) ); }
+        else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = std::move( *other ); }
+        return *this;
+    }
+
+    // 4 (C++11) - move-assign from value
+    template< typename U = T >
+        // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+        optional_REQUIRES_R(
+            optional &,
+            std::is_constructible<T , U>::value
+            && std11::is_assignable<T&, U>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
+            && !(std::is_scalar<T>::value && std::is_same<T, typename std::decay<U>::type>::value)
+        )
+    operator=( U && value )
+    {
+        if ( has_value() )
+        {
+            contained.value() = std::forward<U>( value );
+        }
+        else
+        {
+            initialize( T( std::forward<U>( value ) ) );
+        }
+        return *this;
+    }
+
+#else // optional_CPP11_OR_GREATER
+
+    // 4 (C++98) - copy-assign from value
+    template< typename U /*= T*/ >
+    optional & operator=( U const & value )
+    {
+        if ( has_value() ) contained.value() = value;
+        else               initialize( T( value ) );
+        return *this;
+    }
+
+#endif // optional_CPP11_OR_GREATER
+
+    // 5 (C++98 and later) - converting copy-assign from optional
+    template< typename U >
+#if optional_CPP11_OR_GREATER
+        // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+        optional_REQUIRES_R(
+            optional&,
+            std::is_constructible<  T , U const &>::value
+            &&  std11::is_assignable< T&, U const &>::value
+            && !std::is_constructible<T, optional<U> &          >::value
+            && !std::is_constructible<T, optional<U> &&         >::value
+            && !std::is_constructible<T, optional<U> const &    >::value
+            && !std::is_constructible<T, optional<U> const &&   >::value
+            && !std::is_convertible<     optional<U> &       , T>::value
+            && !std::is_convertible<     optional<U> &&      , T>::value
+            && !std::is_convertible<     optional<U> const & , T>::value
+            && !std::is_convertible<     optional<U> const &&, T>::value
+            && !std11::is_assignable<  T&, optional<U> &          >::value
+            && !std11::is_assignable<  T&, optional<U> &&         >::value
+            && !std11::is_assignable<  T&, optional<U> const &    >::value
+            && !std11::is_assignable<  T&, optional<U> const &&   >::value
+        )
+#else
+    optional&
+#endif // optional_CPP11_OR_GREATER
+    operator=( optional<U> const & other )
+    {
+        return *this = optional( other );
+    }
+
+#if optional_CPP11_OR_GREATER
+
+    // 6 (C++11) -  converting move-assign from optional
+    template< typename U >
+        // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+        optional_REQUIRES_R(
+            optional&,
+            std::is_constructible<  T , U>::value
+            &&  std11::is_assignable< T&, U>::value
+            && !std::is_constructible<T, optional<U> &          >::value
+            && !std::is_constructible<T, optional<U> &&         >::value
+            && !std::is_constructible<T, optional<U> const &    >::value
+            && !std::is_constructible<T, optional<U> const &&   >::value
+            && !std::is_convertible<     optional<U> &       , T>::value
+            && !std::is_convertible<     optional<U> &&      , T>::value
+            && !std::is_convertible<     optional<U> const & , T>::value
+            && !std::is_convertible<     optional<U> const &&, T>::value
+            && !std11::is_assignable<  T&, optional<U> &          >::value
+            && !std11::is_assignable<  T&, optional<U> &&         >::value
+            && !std11::is_assignable<  T&, optional<U> const &    >::value
+            && !std11::is_assignable<  T&, optional<U> const &&   >::value
+        )
+    operator=( optional<U> && other )
+    {
+        return *this = optional( std::move( other ) );
+    }
+
+    // 7 (C++11) - emplace
+    template< typename... Args
+        optional_REQUIRES_T(
+            std::is_constructible<T, Args&&...>::value
+        )
+    >
+    T& emplace( Args&&... args )
+    {
+        *this = nullopt;
+        contained.emplace( std::forward<Args>(args)...  );
+        has_value_ = true;
+        return contained.value();
+    }
+
+    // 8 (C++11) - emplace, initializer-list
+    template< typename U, typename... Args
+        optional_REQUIRES_T(
+            std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value
+        )
+    >
+    T& emplace( std::initializer_list<U> il, Args&&... args )
+    {
+        *this = nullopt;
+        contained.emplace( il, std::forward<Args>(args)...  );
+        has_value_ = true;
+        return contained.value();
+    }
+
+#endif // optional_CPP11_OR_GREATER
+
+    // x.x.3.4, swap
+
+    void swap( optional & other )
+#if optional_CPP11_OR_GREATER
+        noexcept(
+            std11::is_nothrow_move_constructible<T>::value
+            && std17::is_nothrow_swappable<T>::value
+        )
+#endif
+    {
+        using std::swap;
+        if      ( (has_value() == true ) && (other.has_value() == true ) ) { swap( **this, *other ); }
+        else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std11::move(*other) ); other.reset(); }
+        else if ( (has_value() == true ) && (other.has_value() == false) ) { other.initialize( std11::move(**this) ); reset(); }
+    }
+
+    // x.x.3.5, observers
+
+    optional_constexpr value_type const * operator ->() const
+    {
+        return assert( has_value() ),
+            contained.value_ptr();
+    }
+
+    optional_constexpr14 value_type * operator ->()
+    {
+        return assert( has_value() ),
+            contained.value_ptr();
+    }
+
+    optional_constexpr value_type const & operator *() const optional_ref_qual
+    {
+        return assert( has_value() ),
+            contained.value();
+    }
+
+    optional_constexpr14 value_type & operator *() optional_ref_qual
+    {
+        return assert( has_value() ),
+            contained.value();
+    }
+
+#if optional_HAVE( REF_QUALIFIER )
+
+    optional_constexpr value_type const && operator *() const optional_refref_qual
+    {
+        return std::move( **this );
+    }
+
+    optional_constexpr14 value_type && operator *() optional_refref_qual
+    {
+        return std::move( **this );
+    }
+
+#endif
+
+#if optional_CPP11_OR_GREATER
+    optional_constexpr explicit operator bool() const optional_noexcept
+    {
+        return has_value();
+    }
+#else
+    optional_constexpr operator safe_bool() const optional_noexcept
+    {
+        return has_value() ? &optional::this_type_does_not_support_comparisons : 0;
+    }
+#endif
+
+    // NOLINTNEXTLINE( modernize-use-nodiscard )
+    /*optional_nodiscard*/ optional_constexpr bool has_value() const optional_noexcept
+    {
+        return has_value_;
+    }
+
+    // NOLINTNEXTLINE( modernize-use-nodiscard )
+    /*optional_nodiscard*/ optional_constexpr14 value_type const & value() const optional_ref_qual
+    {
+#if optional_CONFIG_NO_EXCEPTIONS
+        assert( has_value() );
+#else
+        if ( ! has_value() )
+        {
+            throw bad_optional_access();
+        }
+#endif
+        return contained.value();
+    }
+
+    optional_constexpr14 value_type & value() optional_ref_qual
+    {
+#if optional_CONFIG_NO_EXCEPTIONS
+        assert( has_value() );
+#else
+        if ( ! has_value() )
+        {
+            throw bad_optional_access();
+        }
+#endif
+        return contained.value();
+    }
+
+#if optional_HAVE( REF_QUALIFIER )  &&  ( !optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490 )
+
+    // NOLINTNEXTLINE( modernize-use-nodiscard )
+    /*optional_nodiscard*/ optional_constexpr value_type const && value() const optional_refref_qual
+    {
+        return std::move( value() );
+    }
+
+    optional_constexpr14 value_type && value() optional_refref_qual
+    {
+        return std::move( value() );
+    }
+
+#endif
+
+#if optional_HAVE( REF_QUALIFIER )
+
+    template< typename U >
+    optional_constexpr value_type value_or( U && v ) const optional_ref_qual
+    {
+        return has_value() ? contained.value() : static_cast<T>(std::forward<U>( v ) );
+    }
+
+    template< typename U >
+    optional_constexpr14 value_type value_or( U && v ) optional_refref_qual
+    {
+#if optional_COMPILER_CLANG_VERSION
+        return has_value() ? /*std::move*/( contained.value() ) : static_cast<T>(std::forward<U>( v ) );
+#else
+        return has_value() ? std::move( contained.value() ) : static_cast<T>(std::forward<U>( v ) );
+#endif
+    }
+
+#else
+
+    template< typename U >
+    optional_constexpr value_type value_or( U const & v ) const
+    {
+        return has_value() ? contained.value() : static_cast<value_type>( v );
+    }
+
+#endif // optional_HAVE( REF_QUALIFIER )
+
+#if !optional_CONFIG_NO_EXTENSIONS
+#if  optional_HAVE( REF_QUALIFIER )
+
+    template< typename F >
+    optional_constexpr value_type value_or_eval( F f ) const &
+    {
+        return has_value() ? contained.value() : f();
+    }
+
+    template< typename F >
+    optional_constexpr14 value_type value_or_eval( F f ) &&
+    {
+        if ( has_value() )
+        {
+            return std::move( contained.value() );
+        }
+        else
+        {
+            return f();
+        }
+    }
+
+#else
+
+    template< typename F >
+    optional_constexpr value_type value_or_eval( F f ) const
+    {
+        return has_value() ? contained.value() : f();
+    }
+
+#endif //  optional_HAVE( REF_QUALIFIER )
+#endif // !optional_CONFIG_NO_EXTENSIONS
+
+    // x.x.3.6, modifiers
+
+    void reset() optional_noexcept
+    {
+        if ( has_value() )
+        {
+            contained.destruct_value();
+        }
+
+        has_value_ = false;
+    }
+
+private:
+    void this_type_does_not_support_comparisons() const {}
+
+    template< typename V >
+    void initialize( V const & value )
+    {
+        assert( ! has_value()  );
+        contained.construct_value( value );
+        has_value_ = true;
+    }
+
+#if optional_CPP11_OR_GREATER
+    template< typename V >
+    void initialize( V && value )
+    {
+        assert( ! has_value()  );
+        contained.construct_value( std::forward<V>( value ) );
+        has_value_ = true;
+    }
+
+#endif
+
+private:
+    bool has_value_;
+    detail::storage_t< value_type > contained;
+
+};
+
+// Relational operators
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator==( optional<T> const & x, optional<U> const & y )
+{
+    return bool(x) != bool(y) ? false : !bool( x ) ? true : *x == *y;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator!=( optional<T> const & x, optional<U> const & y )
+{
+    return !(x == y);
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator<( optional<T> const & x, optional<U> const & y )
+{
+    return (!y) ? false : (!x) ? true : *x < *y;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator>( optional<T> const & x, optional<U> const & y )
+{
+    return (y < x);
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator<=( optional<T> const & x, optional<U> const & y )
+{
+    return !(y < x);
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator>=( optional<T> const & x, optional<U> const & y )
+{
+    return !(x < y);
+}
+
+// Comparison with nullopt
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator==( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+{
+    return (!x);
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator==( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+{
+    return (!x);
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator!=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+{
+    return bool(x);
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator!=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+{
+    return bool(x);
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator<( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
+{
+    return false;
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator<( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+{
+    return bool(x);
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator<=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+{
+    return (!x);
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator<=( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
+{
+    return true;
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator>( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+{
+    return bool(x);
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator>( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
+{
+    return false;
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator>=( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
+{
+    return true;
+}
+
+template< typename T >
+optional_nodiscard optional_constexpr bool operator>=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+{
+    return (!x);
+}
+
+// Comparison with T
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator==( optional<T> const & x, U const & v )
+{
+    return bool(x) ? *x == v : false;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator==( U const & v, optional<T> const & x )
+{
+    return bool(x) ? v == *x : false;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator!=( optional<T> const & x, U const & v )
+{
+    return bool(x) ? *x != v : true;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator!=( U const & v, optional<T> const & x )
+{
+    return bool(x) ? v != *x : true;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator<( optional<T> const & x, U const & v )
+{
+    return bool(x) ? *x < v : true;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator<( U const & v, optional<T> const & x )
+{
+    return bool(x) ? v < *x : false;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator<=( optional<T> const & x, U const & v )
+{
+    return bool(x) ? *x <= v : true;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator<=( U const & v, optional<T> const & x )
+{
+    return bool(x) ? v <= *x : false;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator>( optional<T> const & x, U const & v )
+{
+    return bool(x) ? *x > v : false;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator>( U const & v, optional<T> const & x )
+{
+    return bool(x) ? v > *x : true;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator>=( optional<T> const & x, U const & v )
+{
+    return bool(x) ? *x >= v : false;
+}
+
+template< typename T, typename U >
+optional_nodiscard optional_constexpr bool operator>=( U const & v, optional<T> const & x )
+{
+    return bool(x) ? v >= *x : true;
+}
+
+// Specialized algorithms
+
+template< typename T
+#if optional_CPP11_OR_GREATER
+    optional_REQUIRES_T(
+        std11::is_move_constructible<T>::value
+        && std17::is_swappable<T>::value )
+#endif
+>
+void swap( optional<T> & x, optional<T> & y )
+#if optional_CPP11_OR_GREATER
+    noexcept( noexcept( x.swap(y) ) )
+#endif
+{
+    x.swap( y );
+}
+
+#if optional_CPP11_OR_GREATER
+
+template< typename T >
+optional_constexpr optional< typename std::decay<T>::type > make_optional( T && value )
+{
+    return optional< typename std::decay<T>::type >( std::forward<T>( value ) );
+}
+
+template< typename T, typename...Args >
+optional_constexpr optional<T> make_optional( Args&&... args )
+{
+    return optional<T>( nonstd_lite_in_place(T), std::forward<Args>(args)...);
+}
+
+template< typename T, typename U, typename... Args >
+optional_constexpr optional<T> make_optional( std::initializer_list<U> il, Args&&... args )
+{
+    return optional<T>( nonstd_lite_in_place(T), il, std::forward<Args>(args)...);
+}
+
+#else
+
+template< typename T >
+optional<T> make_optional( T const & value )
+{
+    return optional<T>( value );
+}
+
+#endif // optional_CPP11_OR_GREATER
+
+} // namespace optional_lite
+
+using optional_lite::optional;
+using optional_lite::nullopt_t;
+using optional_lite::nullopt;
+
+#if ! optional_CONFIG_NO_EXCEPTIONS
+using optional_lite::bad_optional_access;
+#endif
+
+using optional_lite::make_optional;
+
+} // namespace nonstd
+
+#if optional_CPP11_OR_GREATER
+
+// specialize the std::hash algorithm:
+
+namespace std {
+
+template< class T >
+struct hash< nonstd::optional<T> >
+{
+public:
+    std::size_t operator()( nonstd::optional<T> const & v ) const optional_noexcept
+    {
+        return bool( v ) ? std::hash<T>{}( *v ) : 0;
+    }
+};
+
+} //namespace std
+
+#endif // optional_CPP11_OR_GREATER
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#elif defined(__GNUC__)
+# pragma GCC   diagnostic pop
+#elif defined(_MSC_VER )
+# pragma warning( pop )
+#endif
+
+#endif // optional_USES_STD_OPTIONAL
+
+#endif // NONSTD_OPTIONAL_LITE_HPP
diff --git a/src/cpp-common/vendor/optional-lite/optional.hpp.license b/src/cpp-common/vendor/optional-lite/optional.hpp.license
new file mode 100644 (file)
index 0000000..12b35c0
--- /dev/null
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: 2014-2021 Martin Moene
+SPDX-License-Identifier: BSL-1.0
diff --git a/src/cpp-common/vendor/span-lite/span.hpp b/src/cpp-common/vendor/span-lite/span.hpp
new file mode 100644 (file)
index 0000000..3d2d86a
--- /dev/null
@@ -0,0 +1,1947 @@
+//
+// span for C++98 and later.
+// Based on http://wg21.link/p0122r7
+// For more information see https://github.com/martinmoene/span-lite
+//
+// Copyright 2018-2021 Martin Moene
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef NONSTD_SPAN_HPP_INCLUDED
+#define NONSTD_SPAN_HPP_INCLUDED
+
+#define span_lite_MAJOR  0
+#define span_lite_MINOR  11
+#define span_lite_PATCH  0
+
+#define span_lite_VERSION  span_STRINGIFY(span_lite_MAJOR) "." span_STRINGIFY(span_lite_MINOR) "." span_STRINGIFY(span_lite_PATCH)
+
+#define span_STRINGIFY(  x )  span_STRINGIFY_( x )
+#define span_STRINGIFY_( x )  #x
+
+// span configuration:
+
+#define span_SPAN_DEFAULT  0
+#define span_SPAN_NONSTD   1
+#define span_SPAN_STD      2
+
+// tweak header support:
+
+#ifdef __has_include
+# if __has_include(<nonstd/span.tweak.hpp>)
+#  include <nonstd/span.tweak.hpp>
+# endif
+#define span_HAVE_TWEAK_HEADER  1
+#else
+#define span_HAVE_TWEAK_HEADER  0
+//# pragma message("span.hpp: Note: Tweak header not supported.")
+#endif
+
+// span selection and configuration:
+
+#define span_HAVE( feature )  ( span_HAVE_##feature )
+
+#ifndef  span_CONFIG_SELECT_SPAN
+# define span_CONFIG_SELECT_SPAN  ( span_HAVE_STD_SPAN ? span_SPAN_STD : span_SPAN_NONSTD )
+#endif
+
+#ifndef  span_CONFIG_EXTENT_TYPE
+# define span_CONFIG_EXTENT_TYPE  std::size_t
+#endif
+
+#ifndef  span_CONFIG_SIZE_TYPE
+# define span_CONFIG_SIZE_TYPE  std::size_t
+#endif
+
+#ifdef span_CONFIG_INDEX_TYPE
+# error `span_CONFIG_INDEX_TYPE` is deprecated since v0.7.0; it is replaced by `span_CONFIG_SIZE_TYPE`.
+#endif
+
+// span configuration (features):
+
+#ifndef  span_FEATURE_WITH_INITIALIZER_LIST_P2447
+# define span_FEATURE_WITH_INITIALIZER_LIST_P2447  0
+#endif
+
+#ifndef  span_FEATURE_WITH_CONTAINER
+#ifdef   span_FEATURE_WITH_CONTAINER_TO_STD
+# define span_FEATURE_WITH_CONTAINER  span_IN_STD( span_FEATURE_WITH_CONTAINER_TO_STD )
+#else
+# define span_FEATURE_WITH_CONTAINER  0
+# define span_FEATURE_WITH_CONTAINER_TO_STD  0
+#endif
+#endif
+
+#ifndef  span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE
+# define span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE  0
+#endif
+
+#ifndef  span_FEATURE_MEMBER_AT
+# define span_FEATURE_MEMBER_AT  0
+#endif
+
+#ifndef  span_FEATURE_MEMBER_BACK_FRONT
+# define span_FEATURE_MEMBER_BACK_FRONT  1
+#endif
+
+#ifndef  span_FEATURE_MEMBER_CALL_OPERATOR
+# define span_FEATURE_MEMBER_CALL_OPERATOR  0
+#endif
+
+#ifndef  span_FEATURE_MEMBER_SWAP
+# define span_FEATURE_MEMBER_SWAP  0
+#endif
+
+#ifndef  span_FEATURE_NON_MEMBER_FIRST_LAST_SUB
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB  0
+#elif    span_FEATURE_NON_MEMBER_FIRST_LAST_SUB
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN       1
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER  1
+#endif
+
+#ifndef  span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN  0
+#endif
+
+#ifndef  span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER  0
+#endif
+
+#ifndef  span_FEATURE_COMPARISON
+# define span_FEATURE_COMPARISON  0  // Note: C++20 does not provide comparison
+#endif
+
+#ifndef  span_FEATURE_SAME
+# define span_FEATURE_SAME  0
+#endif
+
+#if span_FEATURE_SAME && !span_FEATURE_COMPARISON
+# error `span_FEATURE_SAME` requires `span_FEATURE_COMPARISON`
+#endif
+
+#ifndef  span_FEATURE_MAKE_SPAN
+#ifdef   span_FEATURE_MAKE_SPAN_TO_STD
+# define span_FEATURE_MAKE_SPAN  span_IN_STD( span_FEATURE_MAKE_SPAN_TO_STD )
+#else
+# define span_FEATURE_MAKE_SPAN  0
+# define span_FEATURE_MAKE_SPAN_TO_STD  0
+#endif
+#endif
+
+#ifndef  span_FEATURE_BYTE_SPAN
+# define span_FEATURE_BYTE_SPAN  0
+#endif
+
+// Control presence of exception handling (try and auto discover):
+
+#ifndef span_CONFIG_NO_EXCEPTIONS
+# if defined(_MSC_VER)
+#  include <cstddef>    // for _HAS_EXCEPTIONS
+# endif
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
+#  define span_CONFIG_NO_EXCEPTIONS  0
+# else
+#  define span_CONFIG_NO_EXCEPTIONS  1
+#  undef  span_CONFIG_CONTRACT_VIOLATION_THROWS
+#  undef  span_CONFIG_CONTRACT_VIOLATION_TERMINATES
+#  define span_CONFIG_CONTRACT_VIOLATION_THROWS  0
+#  define span_CONFIG_CONTRACT_VIOLATION_TERMINATES  1
+# endif
+#endif
+
+// Control pre- and postcondition violation behaviour:
+
+#if    defined( span_CONFIG_CONTRACT_LEVEL_ON )
+# define        span_CONFIG_CONTRACT_LEVEL_MASK  0x11
+#elif  defined( span_CONFIG_CONTRACT_LEVEL_OFF )
+# define        span_CONFIG_CONTRACT_LEVEL_MASK  0x00
+#elif  defined( span_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY )
+# define        span_CONFIG_CONTRACT_LEVEL_MASK  0x01
+#elif  defined( span_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY )
+# define        span_CONFIG_CONTRACT_LEVEL_MASK  0x10
+#else
+# define        span_CONFIG_CONTRACT_LEVEL_MASK  0x11
+#endif
+
+#if    defined( span_CONFIG_CONTRACT_VIOLATION_THROWS )
+# define        span_CONFIG_CONTRACT_VIOLATION_THROWS_V  span_CONFIG_CONTRACT_VIOLATION_THROWS
+#else
+# define        span_CONFIG_CONTRACT_VIOLATION_THROWS_V  0
+#endif
+
+#if    defined( span_CONFIG_CONTRACT_VIOLATION_THROWS     ) && span_CONFIG_CONTRACT_VIOLATION_THROWS && \
+       defined( span_CONFIG_CONTRACT_VIOLATION_TERMINATES ) && span_CONFIG_CONTRACT_VIOLATION_TERMINATES
+# error Please define none or one of span_CONFIG_CONTRACT_VIOLATION_THROWS and span_CONFIG_CONTRACT_VIOLATION_TERMINATES to 1, but not both.
+#endif
+
+// C++ language version detection (C++23 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef   span_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+#  define span_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+#  define span_CPLUSPLUS  __cplusplus
+# endif
+#endif
+
+#define span_CPP98_OR_GREATER  ( span_CPLUSPLUS >= 199711L )
+#define span_CPP11_OR_GREATER  ( span_CPLUSPLUS >= 201103L )
+#define span_CPP14_OR_GREATER  ( span_CPLUSPLUS >= 201402L )
+#define span_CPP17_OR_GREATER  ( span_CPLUSPLUS >= 201703L )
+#define span_CPP20_OR_GREATER  ( span_CPLUSPLUS >= 202002L )
+#define span_CPP23_OR_GREATER  ( span_CPLUSPLUS >= 202300L )
+
+// C++ language version (represent 98 as 3):
+
+#define span_CPLUSPLUS_V  ( span_CPLUSPLUS / 100 - (span_CPLUSPLUS > 200000 ? 2000 : 1994) )
+
+#define span_IN_STD( v )  ( ((v) == 98 ? 3 : (v)) >= span_CPLUSPLUS_V )
+
+#define span_CONFIG(         feature )  ( span_CONFIG_##feature )
+#define span_FEATURE(        feature )  ( span_FEATURE_##feature )
+#define span_FEATURE_TO_STD( feature )  ( span_IN_STD( span_FEATURE( feature##_TO_STD ) ) )
+
+// Use C++20 std::span if available and requested:
+
+#if span_CPP20_OR_GREATER && defined(__has_include )
+# if __has_include( <span> )
+#  define span_HAVE_STD_SPAN  1
+# else
+#  define span_HAVE_STD_SPAN  0
+# endif
+#else
+# define  span_HAVE_STD_SPAN  0
+#endif
+
+#define  span_USES_STD_SPAN  ( (span_CONFIG_SELECT_SPAN == span_SPAN_STD) || ((span_CONFIG_SELECT_SPAN == span_SPAN_DEFAULT) && span_HAVE_STD_SPAN) )
+
+//
+// Use C++20 std::span:
+//
+
+#if span_USES_STD_SPAN
+
+#include <span>
+
+namespace nonstd {
+
+using std::span;
+using std::dynamic_extent;
+
+// Note: C++20 does not provide comparison
+// using std::operator==;
+// using std::operator!=;
+// using std::operator<;
+// using std::operator<=;
+// using std::operator>;
+// using std::operator>=;
+}  // namespace nonstd
+
+#else  // span_USES_STD_SPAN
+
+#include <algorithm>
+
+// Compiler versions:
+//
+// MSVC++  6.0  _MSC_VER == 1200  span_COMPILER_MSVC_VERSION ==  60  (Visual Studio 6.0)
+// MSVC++  7.0  _MSC_VER == 1300  span_COMPILER_MSVC_VERSION ==  70  (Visual Studio .NET 2002)
+// MSVC++  7.1  _MSC_VER == 1310  span_COMPILER_MSVC_VERSION ==  71  (Visual Studio .NET 2003)
+// MSVC++  8.0  _MSC_VER == 1400  span_COMPILER_MSVC_VERSION ==  80  (Visual Studio 2005)
+// MSVC++  9.0  _MSC_VER == 1500  span_COMPILER_MSVC_VERSION ==  90  (Visual Studio 2008)
+// MSVC++ 10.0  _MSC_VER == 1600  span_COMPILER_MSVC_VERSION == 100  (Visual Studio 2010)
+// MSVC++ 11.0  _MSC_VER == 1700  span_COMPILER_MSVC_VERSION == 110  (Visual Studio 2012)
+// MSVC++ 12.0  _MSC_VER == 1800  span_COMPILER_MSVC_VERSION == 120  (Visual Studio 2013)
+// MSVC++ 14.0  _MSC_VER == 1900  span_COMPILER_MSVC_VERSION == 140  (Visual Studio 2015)
+// MSVC++ 14.1  _MSC_VER >= 1910  span_COMPILER_MSVC_VERSION == 141  (Visual Studio 2017)
+// MSVC++ 14.2  _MSC_VER >= 1920  span_COMPILER_MSVC_VERSION == 142  (Visual Studio 2019)
+
+#if defined(_MSC_VER ) && !defined(__clang__)
+# define span_COMPILER_MSVC_VER      (_MSC_VER )
+# define span_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
+#else
+# define span_COMPILER_MSVC_VER      0
+# define span_COMPILER_MSVC_VERSION  0
+#endif
+
+#define span_COMPILER_VERSION( major, minor, patch )  ( 10 * ( 10 * (major) + (minor) ) + (patch) )
+
+#if defined(__clang__)
+# define span_COMPILER_CLANG_VERSION  span_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define span_COMPILER_CLANG_VERSION  0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define span_COMPILER_GNUC_VERSION  span_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define span_COMPILER_GNUC_VERSION  0
+#endif
+
+// half-open range [lo..hi):
+#define span_BETWEEN( v, lo, hi )  ( (lo) <= (v) && (v) < (hi) )
+
+// Compiler warning suppression:
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wundef"
+# pragma clang diagnostic ignored "-Wmismatched-tags"
+# define span_RESTORE_WARNINGS()   _Pragma( "clang diagnostic pop" )
+
+#elif defined __GNUC__
+# pragma GCC   diagnostic push
+# pragma GCC   diagnostic ignored "-Wundef"
+# define span_RESTORE_WARNINGS()   _Pragma( "GCC diagnostic pop" )
+
+#elif span_COMPILER_MSVC_VER >= 1900
+# define span_DISABLE_MSVC_WARNINGS(codes)  __pragma(warning(push))  __pragma(warning(disable: codes))
+# define span_RESTORE_WARNINGS()            __pragma(warning(pop ))
+
+// Suppress the following MSVC GSL warnings:
+// - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept'
+// - C26440, gsl::f.6 : function 'function' can be declared 'noexcept'
+// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
+//                      use brace initialization, gsl::narrow_cast or gsl::narrow
+// - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same
+// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
+// - C26490: gsl::t.1 : don't use reinterpret_cast
+
+span_DISABLE_MSVC_WARNINGS( 26439 26440 26472 26473 26481 26490 )
+
+#else
+# define span_RESTORE_WARNINGS()  /*empty*/
+#endif
+
+// Presence of language and library features:
+
+#ifdef _HAS_CPP0X
+# define span_HAS_CPP0X  _HAS_CPP0X
+#else
+# define span_HAS_CPP0X  0
+#endif
+
+#define span_CPP11_80   (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1400)
+#define span_CPP11_90   (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1500)
+#define span_CPP11_100  (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1600)
+#define span_CPP11_110  (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1700)
+#define span_CPP11_120  (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1800)
+#define span_CPP11_140  (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1900)
+
+#define span_CPP14_000  (span_CPP14_OR_GREATER)
+#define span_CPP14_120  (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1800)
+#define span_CPP14_140  (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1900)
+
+#define span_CPP17_000  (span_CPP17_OR_GREATER)
+
+// Presence of C++11 language features:
+
+#define span_HAVE_ALIAS_TEMPLATE            span_CPP11_140
+#define span_HAVE_AUTO                      span_CPP11_100
+#define span_HAVE_CONSTEXPR_11              span_CPP11_140
+#define span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG  span_CPP11_120
+#define span_HAVE_EXPLICIT_CONVERSION       span_CPP11_140
+#define span_HAVE_INITIALIZER_LIST          span_CPP11_120
+#define span_HAVE_IS_DEFAULT                span_CPP11_140
+#define span_HAVE_IS_DELETE                 span_CPP11_140
+#define span_HAVE_NOEXCEPT                  span_CPP11_140
+#define span_HAVE_NORETURN                ( span_CPP11_140 && ! span_BETWEEN( span_COMPILER_GNUC_VERSION, 1, 480 ) )
+#define span_HAVE_NULLPTR                   span_CPP11_100
+#define span_HAVE_STATIC_ASSERT             span_CPP11_100
+
+// Presence of C++14 language features:
+
+#define span_HAVE_CONSTEXPR_14              span_CPP14_000
+
+// Presence of C++17 language features:
+
+#define span_HAVE_DEPRECATED                span_CPP17_000
+#define span_HAVE_NODISCARD                 span_CPP17_000
+
+// MSVC: template parameter deduction guides since Visual Studio 2017 v15.7
+
+#if defined(__cpp_deduction_guides)
+# define span_HAVE_DEDUCTION_GUIDES         1
+#else
+# define span_HAVE_DEDUCTION_GUIDES         (span_CPP17_OR_GREATER && ! span_BETWEEN( span_COMPILER_MSVC_VER, 1, 1913 ))
+#endif
+
+// Presence of C++ library features:
+
+#define span_HAVE_ADDRESSOF                 span_CPP17_000
+#define span_HAVE_ARRAY                     span_CPP11_110
+#define span_HAVE_BYTE                      span_CPP17_000
+#define span_HAVE_CONDITIONAL               span_CPP11_120
+#define span_HAVE_CONTAINER_DATA_METHOD    (span_CPP11_140 || ( span_COMPILER_MSVC_VER >= 1500 && span_HAS_CPP0X ))
+#define span_HAVE_DATA                      span_CPP17_000
+#define span_HAVE_LONGLONG                  span_CPP11_80
+#define span_HAVE_REMOVE_CONST              span_CPP11_110
+#define span_HAVE_SNPRINTF                  span_CPP11_140
+#define span_HAVE_STRUCT_BINDING            span_CPP11_120
+#define span_HAVE_TYPE_TRAITS               span_CPP11_90
+
+// Presence of byte-lite:
+
+#ifdef NONSTD_BYTE_LITE_HPP
+# define span_HAVE_NONSTD_BYTE  1
+#else
+# define span_HAVE_NONSTD_BYTE  0
+#endif
+
+// C++ feature usage:
+
+#if span_HAVE_ADDRESSOF
+# define span_ADDRESSOF(x)  std::addressof(x)
+#else
+# define span_ADDRESSOF(x)  (&x)
+#endif
+
+#if span_HAVE_CONSTEXPR_11
+# define span_constexpr constexpr
+#else
+# define span_constexpr /*span_constexpr*/
+#endif
+
+#if span_HAVE_CONSTEXPR_14
+# define span_constexpr14 constexpr
+#else
+# define span_constexpr14 /*span_constexpr*/
+#endif
+
+#if span_HAVE_EXPLICIT_CONVERSION
+# define span_explicit explicit
+#else
+# define span_explicit /*explicit*/
+#endif
+
+#if span_HAVE_IS_DELETE
+# define span_is_delete = delete
+#else
+# define span_is_delete
+#endif
+
+#if span_HAVE_IS_DELETE
+# define span_is_delete_access public
+#else
+# define span_is_delete_access private
+#endif
+
+#if span_HAVE_NOEXCEPT && ! span_CONFIG_CONTRACT_VIOLATION_THROWS_V
+# define span_noexcept noexcept
+#else
+# define span_noexcept /*noexcept*/
+#endif
+
+#if span_HAVE_NULLPTR
+# define span_nullptr nullptr
+#else
+# define span_nullptr NULL
+#endif
+
+#if span_HAVE_DEPRECATED
+# define span_deprecated(msg) [[deprecated(msg)]]
+#else
+# define span_deprecated(msg) /*[[deprecated]]*/
+#endif
+
+#if span_HAVE_NODISCARD
+# define span_nodiscard [[nodiscard]]
+#else
+# define span_nodiscard /*[[nodiscard]]*/
+#endif
+
+#if span_HAVE_NORETURN
+# define span_noreturn [[noreturn]]
+#else
+# define span_noreturn /*[[noreturn]]*/
+#endif
+
+// Other features:
+
+#define span_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR  span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG
+#define span_HAVE_ITERATOR_CTOR                    span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG
+
+// Additional includes:
+
+#if span_HAVE( ADDRESSOF )
+# include <memory>
+#endif
+
+#if span_HAVE( ARRAY )
+# include <array>
+#endif
+
+#if span_HAVE( BYTE )
+# include <cstddef>
+#endif
+
+#if span_HAVE( DATA )
+# include <iterator> // for std::data(), std::size()
+#endif
+
+#if span_HAVE( TYPE_TRAITS )
+# include <type_traits>
+#endif
+
+#if ! span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+# include <vector>
+#endif
+
+#if span_FEATURE( MEMBER_AT ) > 1
+# include <cstdio>
+#endif
+
+#if ! span_CONFIG( NO_EXCEPTIONS )
+# include <stdexcept>
+#endif
+
+// Contract violation
+
+#define span_ELIDE_CONTRACT_EXPECTS  ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x01 ) )
+#define span_ELIDE_CONTRACT_ENSURES  ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x10 ) )
+
+#if span_ELIDE_CONTRACT_EXPECTS
+# define span_constexpr_exp    span_constexpr
+# define span_EXPECTS( cond )  /* Expect elided */
+#else
+# define span_constexpr_exp    span_constexpr14
+# define span_EXPECTS( cond )  span_CONTRACT_CHECK( "Precondition", cond )
+#endif
+
+#if span_ELIDE_CONTRACT_ENSURES
+# define span_constexpr_ens    span_constexpr
+# define span_ENSURES( cond )  /* Ensures elided */
+#else
+# define span_constexpr_ens    span_constexpr14
+# define span_ENSURES( cond )  span_CONTRACT_CHECK( "Postcondition", cond )
+#endif
+
+#define span_CONTRACT_CHECK( type, cond ) \
+    cond ? static_cast< void >( 0 ) \
+         : nonstd::span_lite::detail::report_contract_violation( span_LOCATION( __FILE__, __LINE__ ) ": " type " violation." )
+
+#ifdef __GNUG__
+# define span_LOCATION( file, line )  file ":" span_STRINGIFY( line )
+#else
+# define span_LOCATION( file, line )  file "(" span_STRINGIFY( line ) ")"
+#endif
+
+// Method enabling
+
+#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+
+#define span_REQUIRES_0(VA) \
+    template< bool B = (VA), typename std::enable_if<B, int>::type = 0 >
+
+# if span_BETWEEN( span_COMPILER_MSVC_VERSION, 1, 140 )
+// VS 2013 and earlier seem to have trouble with SFINAE for default non-type arguments
+# define span_REQUIRES_T(VA) \
+    , typename = typename std::enable_if< ( VA ), nonstd::span_lite::detail::enabler >::type
+# else
+# define span_REQUIRES_T(VA) \
+    , typename std::enable_if< (VA), int >::type = 0
+# endif
+
+#define span_REQUIRES_R(R, VA) \
+    typename std::enable_if< (VA), R>::type
+
+#define span_REQUIRES_A(VA) \
+    , typename std::enable_if< (VA), void*>::type = nullptr
+
+#else
+
+# define span_REQUIRES_0(VA)    /*empty*/
+# define span_REQUIRES_T(VA)    /*empty*/
+# define span_REQUIRES_R(R, VA) R
+# define span_REQUIRES_A(VA)    /*empty*/
+
+#endif
+
+namespace nonstd {
+namespace span_lite {
+
+// [views.constants], constants
+
+typedef span_CONFIG_EXTENT_TYPE extent_t;
+typedef span_CONFIG_SIZE_TYPE   size_t;
+
+span_constexpr const extent_t dynamic_extent = static_cast<extent_t>( -1 );
+
+template< class T, extent_t Extent = dynamic_extent >
+class span;
+
+// Tag to select span constructor taking a container (prevent ms-gsl warning C26426):
+
+struct with_container_t { span_constexpr with_container_t() span_noexcept {} };
+const  span_constexpr   with_container_t with_container;
+
+// C++11 emulation:
+
+namespace std11 {
+
+#if span_HAVE( REMOVE_CONST )
+
+using std::remove_cv;
+using std::remove_const;
+using std::remove_volatile;
+
+#else
+
+template< class T > struct remove_const            { typedef T type; };
+template< class T > struct remove_const< T const > { typedef T type; };
+
+template< class T > struct remove_volatile               { typedef T type; };
+template< class T > struct remove_volatile< T volatile > { typedef T type; };
+
+template< class T >
+struct remove_cv
+{
+    typedef typename std11::remove_volatile< typename std11::remove_const< T >::type >::type type;
+};
+
+#endif  // span_HAVE( REMOVE_CONST )
+
+#if span_HAVE( TYPE_TRAITS )
+
+using std::is_same;
+using std::is_signed;
+using std::integral_constant;
+using std::true_type;
+using std::false_type;
+using std::remove_reference;
+
+#else
+
+template< class T, T v > struct integral_constant { enum { value = v }; };
+typedef integral_constant< bool, true  > true_type;
+typedef integral_constant< bool, false > false_type;
+
+template< class T, class U > struct is_same : false_type{};
+template< class T          > struct is_same<T, T> : true_type{};
+
+template< typename T >  struct is_signed : false_type {};
+template<> struct is_signed<signed char> : true_type {};
+template<> struct is_signed<signed int > : true_type {};
+template<> struct is_signed<signed long> : true_type {};
+
+#endif
+
+} // namespace std11
+
+// C++17 emulation:
+
+namespace std17 {
+
+template< bool v > struct bool_constant : std11::integral_constant<bool, v>{};
+
+#if span_CPP11_120
+
+template< class...>
+using void_t = void;
+
+#endif
+
+#if span_HAVE( DATA )
+
+using std::data;
+using std::size;
+
+#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+
+template< typename T, std::size_t N >
+inline span_constexpr auto size( const T(&)[N] ) span_noexcept -> size_t
+{
+    return N;
+}
+
+template< typename C >
+inline span_constexpr auto size( C const & cont ) -> decltype( cont.size() )
+{
+    return cont.size();
+}
+
+template< typename T, std::size_t N >
+inline span_constexpr auto data( T(&arr)[N] ) span_noexcept -> T*
+{
+    return &arr[0];
+}
+
+template< typename C >
+inline span_constexpr auto data( C & cont ) -> decltype( cont.data() )
+{
+    return cont.data();
+}
+
+template< typename C >
+inline span_constexpr auto data( C const & cont ) -> decltype( cont.data() )
+{
+    return cont.data();
+}
+
+template< typename E >
+inline span_constexpr auto data( std::initializer_list<E> il ) span_noexcept -> E const *
+{
+    return il.begin();
+}
+
+#endif // span_HAVE( DATA )
+
+#if span_HAVE( BYTE )
+using std::byte;
+#elif span_HAVE( NONSTD_BYTE )
+using nonstd::byte;
+#endif
+
+} // namespace std17
+
+// C++20 emulation:
+
+namespace std20 {
+
+#if span_HAVE( DEDUCTION_GUIDES )
+template< class T >
+using iter_reference_t = decltype( *std::declval<T&>() );
+#endif
+
+} // namespace std20
+
+// Implementation details:
+
+namespace detail {
+
+/*enum*/ struct enabler{};
+
+template< typename T >
+span_constexpr bool is_positive( T x )
+{
+    return std11::is_signed<T>::value ? x >= 0 : true;
+}
+
+#if span_HAVE( TYPE_TRAITS )
+
+template< class Q >
+struct is_span_oracle : std::false_type{};
+
+template< class T, span_CONFIG_EXTENT_TYPE Extent >
+struct is_span_oracle< span<T, Extent> > : std::true_type{};
+
+template< class Q >
+struct is_span : is_span_oracle< typename std::remove_cv<Q>::type >{};
+
+template< class Q >
+struct is_std_array_oracle : std::false_type{};
+
+#if span_HAVE( ARRAY )
+
+template< class T, std::size_t Extent >
+struct is_std_array_oracle< std::array<T, Extent> > : std::true_type{};
+
+#endif
+
+template< class Q >
+struct is_std_array : is_std_array_oracle< typename std::remove_cv<Q>::type >{};
+
+template< class Q >
+struct is_array : std::false_type {};
+
+template< class T >
+struct is_array<T[]> : std::true_type {};
+
+template< class T, std::size_t N >
+struct is_array<T[N]> : std::true_type {};
+
+#if span_CPP11_140 && ! span_BETWEEN( span_COMPILER_GNUC_VERSION, 1, 500 )
+
+template< class, class = void >
+struct has_size_and_data : std::false_type{};
+
+template< class C >
+struct has_size_and_data
+<
+    C, std17::void_t<
+        decltype( std17::size(std::declval<C>()) ),
+        decltype( std17::data(std::declval<C>()) ) >
+> : std::true_type{};
+
+template< class, class, class = void >
+struct is_compatible_element : std::false_type {};
+
+template< class C, class E >
+struct is_compatible_element
+<
+    C, E, std17::void_t<
+        decltype( std17::data(std::declval<C>()) ) >
+> : std::is_convertible< typename std::remove_pointer<decltype( std17::data( std::declval<C&>() ) )>::type(*)[], E(*)[] >{};
+
+template< class C >
+struct is_container : std17::bool_constant
+<
+    ! is_span< C >::value
+    && ! is_array< C >::value
+    && ! is_std_array< C >::value
+    &&   has_size_and_data< C >::value
+>{};
+
+template< class C, class E >
+struct is_compatible_container : std17::bool_constant
+<
+    is_container<C>::value
+    && is_compatible_element<C,E>::value
+>{};
+
+#else // span_CPP11_140
+
+template<
+    class C, class E
+        span_REQUIRES_T((
+            ! is_span< C >::value
+            && ! is_array< C >::value
+            && ! is_std_array< C >::value
+            && ( std::is_convertible< typename std::remove_pointer<decltype( std17::data( std::declval<C&>() ) )>::type(*)[], E(*)[] >::value)
+        //  &&   has_size_and_data< C >::value
+        ))
+        , class = decltype( std17::size(std::declval<C>()) )
+        , class = decltype( std17::data(std::declval<C>()) )
+>
+struct is_compatible_container : std::true_type{};
+
+#endif // span_CPP11_140
+
+#endif // span_HAVE( TYPE_TRAITS )
+
+#if ! span_CONFIG( NO_EXCEPTIONS )
+#if   span_FEATURE( MEMBER_AT ) > 1
+
+// format index and size:
+
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wlong-long"
+#elif defined __GNUC__
+# pragma GCC   diagnostic ignored "-Wformat=ll"
+# pragma GCC   diagnostic ignored "-Wlong-long"
+#endif
+
+span_noreturn inline void throw_out_of_range( size_t idx, size_t size )
+{
+    const char fmt[] = "span::at(): index '%lli' is out of range [0..%lli)";
+    char buffer[ 2 * 20 + sizeof fmt ];
+    sprintf( buffer, fmt, static_cast<long long>(idx), static_cast<long long>(size) );
+
+    throw std::out_of_range( buffer );
+}
+
+#else // MEMBER_AT
+
+span_noreturn inline void throw_out_of_range( size_t /*idx*/, size_t /*size*/ )
+{
+    throw std::out_of_range( "span::at(): index outside span" );
+}
+#endif  // MEMBER_AT
+#endif  // NO_EXCEPTIONS
+
+#if span_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+
+struct contract_violation : std::logic_error
+{
+    explicit contract_violation( char const * const message )
+        : std::logic_error( message )
+    {}
+};
+
+inline void report_contract_violation( char const * msg )
+{
+    throw contract_violation( msg );
+}
+
+#else // span_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+
+span_noreturn inline void report_contract_violation( char const * /*msg*/ ) span_noexcept
+{
+    std::terminate();
+}
+
+#endif // span_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+
+}  // namespace detail
+
+// Prevent signed-unsigned mismatch:
+
+#define span_sizeof(T)  static_cast<extent_t>( sizeof(T) )
+
+template< class T >
+inline span_constexpr size_t to_size( T size )
+{
+    return static_cast<size_t>( size );
+}
+
+//
+// [views.span] - A view over a contiguous, single-dimension sequence of objects
+//
+template< class T, extent_t Extent /*= dynamic_extent*/ >
+class span
+{
+public:
+    // constants and types
+
+    typedef T element_type;
+    typedef typename std11::remove_cv< T >::type value_type;
+
+    typedef T &       reference;
+    typedef T *       pointer;
+    typedef T const * const_pointer;
+    typedef T const & const_reference;
+
+    typedef size_t    size_type;
+    typedef extent_t  extent_type;
+
+    typedef pointer        iterator;
+    typedef const_pointer  const_iterator;
+
+    typedef std::ptrdiff_t difference_type;
+
+    typedef std::reverse_iterator< iterator >       reverse_iterator;
+    typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
+
+//    static constexpr extent_type extent = Extent;
+    enum { extent = Extent };
+
+    // 26.7.3.2 Constructors, copy, and assignment [span.cons]
+
+    span_REQUIRES_0(
+        ( Extent == 0 ) ||
+        ( Extent == dynamic_extent )
+    )
+    span_constexpr span() span_noexcept
+        : data_( span_nullptr )
+        , size_( 0 )
+    {
+        // span_EXPECTS( data() == span_nullptr );
+        // span_EXPECTS( size() == 0 );
+    }
+
+#if span_HAVE( ITERATOR_CTOR )
+    // Didn't yet succeed in combining the next two constructors:
+
+    span_constexpr_exp span( std::nullptr_t, size_type count )
+        : data_( span_nullptr )
+        , size_( count )
+    {
+        span_EXPECTS( data_ == span_nullptr && count == 0 );
+    }
+
+    template< typename It
+        span_REQUIRES_T((
+            std::is_convertible<decltype(*std::declval<It&>()), element_type &>::value
+        ))
+    >
+    span_constexpr_exp span( It first, size_type count )
+        : data_( to_address( first ) )
+        , size_( count )
+    {
+        span_EXPECTS(
+            ( data_ == span_nullptr && count == 0 ) ||
+            ( data_ != span_nullptr && detail::is_positive( count ) )
+        );
+    }
+#else
+    span_constexpr_exp span( pointer ptr, size_type count )
+        : data_( ptr )
+        , size_( count )
+    {
+        span_EXPECTS(
+            ( ptr == span_nullptr && count == 0 ) ||
+            ( ptr != span_nullptr && detail::is_positive( count ) )
+        );
+    }
+#endif
+
+#if span_HAVE( ITERATOR_CTOR )
+    template< typename It, typename End
+        span_REQUIRES_T((
+            std::is_convertible<decltype(&*std::declval<It&>()), element_type *>::value
+            && ! std::is_convertible<End, std::size_t>::value
+        ))
+     >
+    span_constexpr_exp span( It first, End last )
+        : data_( to_address( first ) )
+        , size_( to_size( last - first ) )
+    {
+        span_EXPECTS(
+             last - first >= 0
+        );
+    }
+#else
+    span_constexpr_exp span( pointer first, pointer last )
+        : data_( first )
+        , size_( to_size( last - first ) )
+    {
+        span_EXPECTS(
+            last - first >= 0
+        );
+    }
+#endif
+
+    template< std::size_t N
+        span_REQUIRES_T((
+            (Extent == dynamic_extent || Extent == static_cast<extent_t>(N))
+            && std::is_convertible< value_type(*)[], element_type(*)[] >::value
+        ))
+    >
+    span_constexpr span( element_type ( &arr )[ N ] ) span_noexcept
+        : data_( span_ADDRESSOF( arr[0] ) )
+        , size_( N  )
+    {}
+
+#if span_HAVE( ARRAY )
+
+    template< std::size_t N
+        span_REQUIRES_T((
+            (Extent == dynamic_extent || Extent == static_cast<extent_t>(N))
+            && std::is_convertible< value_type(*)[], element_type(*)[] >::value
+        ))
+    >
+# if span_FEATURE( CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE )
+        span_constexpr span( std::array< element_type, N > & arr ) span_noexcept
+# else
+        span_constexpr span( std::array< value_type, N > & arr ) span_noexcept
+# endif
+        : data_( arr.data() )
+        , size_( to_size( arr.size() ) )
+    {}
+
+    template< std::size_t N
+# if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+        span_REQUIRES_T((
+            (Extent == dynamic_extent || Extent == static_cast<extent_t>(N))
+            && std::is_convertible< value_type(*)[], element_type(*)[] >::value
+        ))
+# endif
+    >
+    span_constexpr span( std::array< value_type, N> const & arr ) span_noexcept
+        : data_( arr.data() )
+        , size_( to_size( arr.size() ) )
+    {}
+
+#endif // span_HAVE( ARRAY )
+
+#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+    template< class Container
+        span_REQUIRES_T((
+            detail::is_compatible_container< Container, element_type >::value
+        ))
+    >
+    span_constexpr span( Container & cont )
+        : data_( std17::data( cont ) )
+        , size_( to_size( std17::size( cont ) ) )
+    {}
+
+    template< class Container
+        span_REQUIRES_T((
+            std::is_const< element_type >::value
+            && detail::is_compatible_container< Container, element_type >::value
+        ))
+    >
+    span_constexpr span( Container const & cont )
+        : data_( std17::data( cont ) )
+        , size_( to_size( std17::size( cont ) ) )
+    {}
+
+#endif // span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+
+#if span_FEATURE( WITH_CONTAINER )
+
+    template< class Container >
+    span_constexpr span( with_container_t, Container & cont )
+        : data_( cont.size() == 0 ? span_nullptr : span_ADDRESSOF( cont[0] ) )
+        , size_( to_size( cont.size() ) )
+    {}
+
+    template< class Container >
+    span_constexpr span( with_container_t, Container const & cont )
+        : data_( cont.size() == 0 ? span_nullptr : const_cast<pointer>( span_ADDRESSOF( cont[0] ) ) )
+        , size_( to_size( cont.size() ) )
+    {}
+#endif
+
+#if span_FEATURE( WITH_INITIALIZER_LIST_P2447 ) && span_HAVE( INITIALIZER_LIST )
+
+    // constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il) noexcept;
+
+#if !span_BETWEEN( span_COMPILER_MSVC_VERSION, 120, 130 )
+
+    template< extent_t U = Extent
+        span_REQUIRES_T((
+            U != dynamic_extent
+        ))
+    >
+#if span_COMPILER_GNUC_VERSION >= 900   // prevent GCC's "-Winit-list-lifetime"
+    span_constexpr14 explicit span( std::initializer_list<value_type> il ) span_noexcept
+    {
+        data_ = il.begin();
+        size_ = il.size();
+    }
+#else
+    span_constexpr explicit span( std::initializer_list<value_type> il ) span_noexcept
+        : data_( il.begin() )
+        , size_( il.size()  )
+    {}
+#endif
+
+#endif // MSVC 120 (VS2013)
+
+    template< extent_t U = Extent
+        span_REQUIRES_T((
+            U == dynamic_extent
+        ))
+    >
+#if span_COMPILER_GNUC_VERSION >= 900   // prevent GCC's "-Winit-list-lifetime"
+    span_constexpr14 /*explicit*/ span( std::initializer_list<value_type> il ) span_noexcept
+    {
+        data_ = il.begin();
+        size_ = il.size();
+    }
+#else
+    span_constexpr /*explicit*/ span( std::initializer_list<value_type> il ) span_noexcept
+        : data_( il.begin() )
+        , size_( il.size()  )
+    {}
+#endif
+
+#endif // P2447
+
+#if span_HAVE( IS_DEFAULT )
+    span_constexpr span( span const & other ) span_noexcept = default;
+
+    ~span() span_noexcept = default;
+
+    span_constexpr14 span & operator=( span const & other ) span_noexcept = default;
+#else
+    span_constexpr span( span const & other ) span_noexcept
+        : data_( other.data_ )
+        , size_( other.size_ )
+    {}
+
+    ~span() span_noexcept
+    {}
+
+    span_constexpr14 span & operator=( span const & other ) span_noexcept
+    {
+        data_ = other.data_;
+        size_ = other.size_;
+
+        return *this;
+    }
+#endif
+
+    template< class OtherElementType, extent_type OtherExtent
+        span_REQUIRES_T((
+            (Extent == dynamic_extent || OtherExtent == dynamic_extent || Extent == OtherExtent)
+            && std::is_convertible<OtherElementType(*)[], element_type(*)[]>::value
+        ))
+    >
+    span_constexpr_exp span( span<OtherElementType, OtherExtent> const & other ) span_noexcept
+        : data_( other.data() )
+        , size_( other.size() )
+    {
+        span_EXPECTS( OtherExtent == dynamic_extent || other.size() == to_size(OtherExtent) );
+    }
+
+    // 26.7.3.3 Subviews [span.sub]
+
+    template< extent_type Count >
+    span_constexpr_exp span< element_type, Count >
+    first() const
+    {
+        span_EXPECTS( detail::is_positive( Count ) && Count <= size() );
+
+        return span< element_type, Count >( data(), Count );
+    }
+
+    template< extent_type Count >
+    span_constexpr_exp span< element_type, Count >
+    last() const
+    {
+        span_EXPECTS( detail::is_positive( Count ) && Count <= size() );
+
+        return span< element_type, Count >( data() + (size() - Count), Count );
+    }
+
+#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+    template< size_type Offset, extent_type Count = dynamic_extent >
+#else
+    template< size_type Offset, extent_type Count /*= dynamic_extent*/ >
+#endif
+    span_constexpr_exp span< element_type, Count >
+    subspan() const
+    {
+        span_EXPECTS(
+            ( detail::is_positive( Offset ) && Offset <= size() ) &&
+            ( Count == dynamic_extent || (detail::is_positive( Count ) && Count + Offset <= size()) )
+        );
+
+        return span< element_type, Count >(
+            data() + Offset, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : size() - Offset) );
+    }
+
+    span_constexpr_exp span< element_type, dynamic_extent >
+    first( size_type count ) const
+    {
+        span_EXPECTS( detail::is_positive( count ) && count <= size() );
+
+        return span< element_type, dynamic_extent >( data(), count );
+    }
+
+    span_constexpr_exp span< element_type, dynamic_extent >
+    last( size_type count ) const
+    {
+        span_EXPECTS( detail::is_positive( count ) && count <= size() );
+
+        return span< element_type, dynamic_extent >( data() + ( size() - count ), count );
+    }
+
+    span_constexpr_exp span< element_type, dynamic_extent >
+    subspan( size_type offset, size_type count = static_cast<size_type>(dynamic_extent) ) const
+    {
+        span_EXPECTS(
+            ( ( detail::is_positive( offset ) && offset <= size() ) ) &&
+            ( count == static_cast<size_type>(dynamic_extent) || ( detail::is_positive( count ) && offset + count <= size() ) )
+        );
+
+        return span< element_type, dynamic_extent >(
+            data() + offset, count == static_cast<size_type>(dynamic_extent) ? size() - offset : count );
+    }
+
+    // 26.7.3.4 Observers [span.obs]
+
+    span_constexpr size_type size() const span_noexcept
+    {
+        return size_;
+    }
+
+    span_constexpr std::ptrdiff_t ssize() const span_noexcept
+    {
+        return static_cast<std::ptrdiff_t>( size_ );
+    }
+
+    span_constexpr size_type size_bytes() const span_noexcept
+    {
+        return size() * to_size( sizeof( element_type ) );
+    }
+
+    span_nodiscard span_constexpr bool empty() const span_noexcept
+    {
+        return size() == 0;
+    }
+
+    // 26.7.3.5 Element access [span.elem]
+
+    span_constexpr_exp reference operator[]( size_type idx ) const
+    {
+        span_EXPECTS( detail::is_positive( idx ) && idx < size() );
+
+        return *( data() + idx );
+    }
+
+#if span_FEATURE( MEMBER_CALL_OPERATOR )
+    span_deprecated("replace operator() with operator[]")
+
+    span_constexpr_exp reference operator()( size_type idx ) const
+    {
+        span_EXPECTS( detail::is_positive( idx ) && idx < size() );
+
+        return *( data() + idx );
+    }
+#endif
+
+#if span_FEATURE( MEMBER_AT )
+    span_constexpr14 reference at( size_type idx ) const
+    {
+#if span_CONFIG( NO_EXCEPTIONS )
+        return this->operator[]( idx );
+#else
+        if ( !detail::is_positive( idx ) || size() <= idx )
+        {
+            detail::throw_out_of_range( idx, size() );
+        }
+        return *( data() + idx );
+#endif
+    }
+#endif
+
+    span_constexpr pointer data() const span_noexcept
+    {
+        return data_;
+    }
+
+#if span_FEATURE( MEMBER_BACK_FRONT )
+
+    span_constexpr_exp reference front() const span_noexcept
+    {
+        span_EXPECTS( ! empty() );
+
+        return *data();
+    }
+
+    span_constexpr_exp reference back() const span_noexcept
+    {
+        span_EXPECTS( ! empty() );
+
+        return *( data() + size() - 1 );
+    }
+
+#endif
+
+    // xx.x.x.x Modifiers [span.modifiers]
+
+#if span_FEATURE( MEMBER_SWAP )
+
+    span_constexpr14 void swap( span & other ) span_noexcept
+    {
+        using std::swap;
+        swap( data_, other.data_ );
+        swap( size_, other.size_ );
+    }
+#endif
+
+    // 26.7.3.6 Iterator support [span.iterators]
+
+    span_constexpr iterator begin() const span_noexcept
+    {
+#if span_CPP11_OR_GREATER
+        return { data() };
+#else
+        return iterator( data() );
+#endif
+    }
+
+    span_constexpr iterator end() const span_noexcept
+    {
+#if span_CPP11_OR_GREATER
+        return { data() + size() };
+#else
+        return iterator( data() + size() );
+#endif
+    }
+
+    span_constexpr const_iterator cbegin() const span_noexcept
+    {
+#if span_CPP11_OR_GREATER
+        return { data() };
+#else
+        return const_iterator( data() );
+#endif
+    }
+
+    span_constexpr const_iterator cend() const span_noexcept
+    {
+#if span_CPP11_OR_GREATER
+        return { data() + size() };
+#else
+        return const_iterator( data() + size() );
+#endif
+    }
+
+    span_constexpr reverse_iterator rbegin() const span_noexcept
+    {
+        return reverse_iterator( end() );
+    }
+
+    span_constexpr reverse_iterator rend() const span_noexcept
+    {
+        return reverse_iterator( begin() );
+    }
+
+    span_constexpr const_reverse_iterator crbegin() const span_noexcept
+    {
+        return const_reverse_iterator ( cend() );
+    }
+
+    span_constexpr const_reverse_iterator crend() const span_noexcept
+    {
+        return const_reverse_iterator( cbegin() );
+    }
+
+private:
+
+    // Note: C++20 has std::pointer_traits<Ptr>::to_address( it );
+
+#if span_HAVE( ITERATOR_CTOR )
+    static inline span_constexpr pointer to_address( std::nullptr_t ) span_noexcept
+    {
+        return nullptr;
+    }
+
+    template< typename U >
+    static inline span_constexpr U * to_address( U * p ) span_noexcept
+    {
+        return p;
+    }
+
+    template< typename Ptr
+        span_REQUIRES_T(( ! std::is_pointer<Ptr>::value ))
+    >
+    static inline span_constexpr pointer to_address( Ptr const & it ) span_noexcept
+    {
+        return to_address( it.operator->() );
+    }
+#endif // span_HAVE( ITERATOR_CTOR )
+
+private:
+    pointer   data_;
+    size_type size_;
+};
+
+// class template argument deduction guides:
+
+#if span_HAVE( DEDUCTION_GUIDES )
+
+template< class T, size_t N >
+span( T (&)[N] ) -> span<T, static_cast<extent_t>(N)>;
+
+template< class T, size_t N >
+span( std::array<T, N> & ) -> span<T, static_cast<extent_t>(N)>;
+
+template< class T, size_t N >
+span( std::array<T, N> const & ) -> span<const T, static_cast<extent_t>(N)>;
+
+#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+
+template< class Container >
+span( Container& ) -> span<typename Container::value_type>;
+
+template< class Container >
+span( Container const & ) -> span<const typename Container::value_type>;
+
+#endif
+
+// iterator: constraints: It satisfies contiguous_­iterator.
+
+template< class It, class EndOrSize >
+span( It, EndOrSize ) -> span< typename std11::remove_reference< typename std20::iter_reference_t<It> >::type >;
+
+#endif // span_HAVE( DEDUCTION_GUIDES )
+
+// 26.7.3.7 Comparison operators [span.comparison]
+
+#if span_FEATURE( COMPARISON )
+#if span_FEATURE( SAME )
+
+template< class T1, extent_t E1, class T2, extent_t E2  >
+inline span_constexpr bool same( span<T1,E1> const & l, span<T2,E2> const & r ) span_noexcept
+{
+    return std11::is_same<T1, T2>::value
+        && l.size() == r.size()
+        && static_cast<void const*>( l.data() ) == r.data();
+}
+
+#endif
+
+template< class T1, extent_t E1, class T2, extent_t E2  >
+inline span_constexpr bool operator==( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+    return
+#if span_FEATURE( SAME )
+        same( l, r ) ||
+#endif
+        ( l.size() == r.size() && std::equal( l.begin(), l.end(), r.begin() ) );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2  >
+inline span_constexpr bool operator<( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+    return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2  >
+inline span_constexpr bool operator!=( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+    return !( l == r );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2  >
+inline span_constexpr bool operator<=( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+    return !( r < l );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2  >
+inline span_constexpr bool operator>( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+    return ( r < l );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2  >
+inline span_constexpr bool operator>=( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+    return !( l < r );
+}
+
+#endif // span_FEATURE( COMPARISON )
+
+// 26.7.2.6 views of object representation [span.objectrep]
+
+#if span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE )
+
+// Avoid MSVC 14.1 (1910), VS 2017: warning C4307: '*': integral constant overflow:
+
+template< typename T, extent_t Extent >
+struct BytesExtent
+{
+#if span_CPP11_OR_GREATER
+    enum ET : extent_t { value = span_sizeof(T) * Extent };
+#else
+    enum ET { value = span_sizeof(T) * Extent };
+#endif
+};
+
+template< typename T >
+struct BytesExtent< T, dynamic_extent >
+{
+#if span_CPP11_OR_GREATER
+    enum ET : extent_t { value = dynamic_extent };
+#else
+    enum ET { value = dynamic_extent };
+#endif
+};
+
+template< class T, extent_t Extent >
+inline span_constexpr span< const std17::byte, BytesExtent<T, Extent>::value >
+as_bytes( span<T,Extent> spn ) span_noexcept
+{
+#if 0
+    return { reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() };
+#else
+    return span< const std17::byte, BytesExtent<T, Extent>::value >(
+        reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() );  // NOLINT
+#endif
+}
+
+template< class T, extent_t Extent >
+inline span_constexpr span< std17::byte, BytesExtent<T, Extent>::value >
+as_writable_bytes( span<T,Extent> spn ) span_noexcept
+{
+#if 0
+    return { reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() };
+#else
+    return span< std17::byte, BytesExtent<T, Extent>::value >(
+        reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() );  // NOLINT
+#endif
+}
+
+#endif // span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE )
+
+// 27.8 Container and view access [iterator.container]
+
+template< class T, extent_t Extent /*= dynamic_extent*/ >
+span_constexpr std::size_t size( span<T,Extent> const & spn )
+{
+    return static_cast<std::size_t>( spn.size() );
+}
+
+template< class T, extent_t Extent /*= dynamic_extent*/ >
+span_constexpr std::ptrdiff_t ssize( span<T,Extent> const & spn )
+{
+    return static_cast<std::ptrdiff_t>( spn.size() );
+}
+
+}  // namespace span_lite
+}  // namespace nonstd
+
+// make available in nonstd:
+
+namespace nonstd {
+
+using span_lite::dynamic_extent;
+
+using span_lite::span;
+
+using span_lite::with_container;
+
+#if span_FEATURE( COMPARISON )
+#if span_FEATURE( SAME )
+using span_lite::same;
+#endif
+
+using span_lite::operator==;
+using span_lite::operator!=;
+using span_lite::operator<;
+using span_lite::operator<=;
+using span_lite::operator>;
+using span_lite::operator>=;
+#endif
+
+#if span_HAVE( BYTE )
+using span_lite::as_bytes;
+using span_lite::as_writable_bytes;
+#endif
+
+using span_lite::size;
+using span_lite::ssize;
+
+}  // namespace nonstd
+
+#endif  // span_USES_STD_SPAN
+
+// make_span() [span-lite extension]:
+
+#if span_FEATURE( MAKE_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER )
+
+#if span_USES_STD_SPAN
+# define  span_constexpr  constexpr
+# define  span_noexcept   noexcept
+# define  span_nullptr    nullptr
+# ifndef  span_CONFIG_EXTENT_TYPE
+#  define span_CONFIG_EXTENT_TYPE  std::size_t
+# endif
+using extent_t = span_CONFIG_EXTENT_TYPE;
+#endif  // span_USES_STD_SPAN
+
+namespace nonstd {
+namespace span_lite {
+
+template< class T >
+inline span_constexpr span<T>
+make_span( T * ptr, size_t count ) span_noexcept
+{
+    return span<T>( ptr, count );
+}
+
+template< class T >
+inline span_constexpr span<T>
+make_span( T * first, T * last ) span_noexcept
+{
+    return span<T>( first, last );
+}
+
+template< class T, std::size_t N >
+inline span_constexpr span<T, static_cast<extent_t>(N)>
+make_span( T ( &arr )[ N ] ) span_noexcept
+{
+    return span<T, static_cast<extent_t>(N)>( &arr[ 0 ], N );
+}
+
+#if span_USES_STD_SPAN || span_HAVE( ARRAY )
+
+template< class T, std::size_t N >
+inline span_constexpr span<T, static_cast<extent_t>(N)>
+make_span( std::array< T, N > & arr ) span_noexcept
+{
+    return span<T, static_cast<extent_t>(N)>( arr );
+}
+
+template< class T, std::size_t N >
+inline span_constexpr span< const T, static_cast<extent_t>(N) >
+make_span( std::array< T, N > const & arr ) span_noexcept
+{
+    return span<const T, static_cast<extent_t>(N)>( arr );
+}
+
+#endif // span_HAVE( ARRAY )
+
+#if span_USES_STD_SPAN || span_HAVE( INITIALIZER_LIST )
+
+template< class T >
+inline span_constexpr span< const T >
+make_span( std::initializer_list<T> il ) span_noexcept
+{
+    return span<const T>( il.begin(), il.size() );
+}
+
+#endif // span_HAVE( INITIALIZER_LIST )
+
+#if span_USES_STD_SPAN
+
+template< class Container, class EP = decltype( std::data(std::declval<Container&>())) >
+inline span_constexpr auto
+make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer<EP>::type >
+{
+    return span< typename std::remove_pointer<EP>::type >( cont );
+}
+
+template< class Container, class EP = decltype( std::data(std::declval<Container&>())) >
+inline span_constexpr auto
+make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer<EP>::type >
+{
+    return span< const typename std::remove_pointer<EP>::type >( cont );
+}
+
+#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) && span_HAVE( AUTO )
+
+template< class Container, class EP = decltype( std17::data(std::declval<Container&>())) >
+inline span_constexpr auto
+make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer<EP>::type >
+{
+    return span< typename std::remove_pointer<EP>::type >( cont );
+}
+
+template< class Container, class EP = decltype( std17::data(std::declval<Container&>())) >
+inline span_constexpr auto
+make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer<EP>::type >
+{
+    return span< const typename std::remove_pointer<EP>::type >( cont );
+}
+
+#else
+
+template< class T >
+inline span_constexpr span<T>
+make_span( span<T> spn ) span_noexcept
+{
+    return spn;
+}
+
+template< class T, class Allocator >
+inline span_constexpr span<T>
+make_span( std::vector<T, Allocator> & cont ) span_noexcept
+{
+    return span<T>( with_container, cont );
+}
+
+template< class T, class Allocator >
+inline span_constexpr span<const T>
+make_span( std::vector<T, Allocator> const & cont ) span_noexcept
+{
+    return span<const T>( with_container, cont );
+}
+
+#endif // span_USES_STD_SPAN || ( ... )
+
+#if ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER )
+
+template< class Container >
+inline span_constexpr span<typename Container::value_type>
+make_span( with_container_t, Container & cont ) span_noexcept
+{
+    return span< typename Container::value_type >( with_container, cont );
+}
+
+template< class Container >
+inline span_constexpr span<const typename Container::value_type>
+make_span( with_container_t, Container const & cont ) span_noexcept
+{
+    return span< const typename Container::value_type >( with_container, cont );
+}
+
+#endif // ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER )
+
+// extensions: non-member views:
+// this feature implies the presence of make_span()
+
+#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN )
+
+template< extent_t Count, class T, extent_t Extent >
+span_constexpr span<T, Count>
+first( span<T, Extent> spn )
+{
+    return spn.template first<Count>();
+}
+
+template< class T, extent_t Extent >
+span_constexpr span<T>
+first( span<T, Extent> spn, size_t count )
+{
+    return spn.first( count );
+}
+
+template< extent_t Count, class T, extent_t Extent >
+span_constexpr span<T, Count>
+last( span<T, Extent> spn )
+{
+    return spn.template last<Count>();
+}
+
+template< class T, extent_t Extent >
+span_constexpr span<T>
+last( span<T, Extent> spn, size_t count )
+{
+    return spn.last( count );
+}
+
+template< size_t Offset, extent_t Count, class T, extent_t Extent >
+span_constexpr span<T, Count>
+subspan( span<T, Extent> spn )
+{
+    return spn.template subspan<Offset, Count>();
+}
+
+template< class T, extent_t Extent >
+span_constexpr span<T>
+subspan( span<T, Extent> spn, size_t offset, extent_t count = dynamic_extent )
+{
+    return spn.subspan( offset, count );
+}
+
+#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN )
+
+#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120
+
+template< extent_t Count, class T >
+span_constexpr auto
+first( T & t ) -> decltype( make_span(t).template first<Count>() )
+{
+    return make_span( t ).template first<Count>();
+}
+
+template< class T >
+span_constexpr auto
+first( T & t, size_t count ) -> decltype( make_span(t).first(count) )
+{
+    return make_span( t ).first( count );
+}
+
+template< extent_t Count, class T >
+span_constexpr auto
+last( T & t ) -> decltype( make_span(t).template last<Count>() )
+{
+    return make_span(t).template last<Count>();
+}
+
+template< class T >
+span_constexpr auto
+last( T & t, extent_t count ) -> decltype( make_span(t).last(count) )
+{
+    return make_span( t ).last( count );
+}
+
+template< size_t Offset, extent_t Count = dynamic_extent, class T >
+span_constexpr auto
+subspan( T & t ) -> decltype( make_span(t).template subspan<Offset, Count>() )
+{
+    return make_span( t ).template subspan<Offset, Count>();
+}
+
+template< class T >
+span_constexpr auto
+subspan( T & t, size_t offset, extent_t count = dynamic_extent ) -> decltype( make_span(t).subspan(offset, count) )
+{
+    return make_span( t ).subspan( offset, count );
+}
+
+#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER )
+
+}  // namespace span_lite
+}  // namespace nonstd
+
+// make available in nonstd:
+
+namespace nonstd {
+using span_lite::make_span;
+
+#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || ( span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 )
+
+using span_lite::first;
+using span_lite::last;
+using span_lite::subspan;
+
+#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_[SPAN|CONTAINER] )
+
+}  // namespace nonstd
+
+#endif // #if span_FEATURE_TO_STD( MAKE_SPAN )
+
+#if span_CPP11_OR_GREATER && span_FEATURE( BYTE_SPAN ) && ( span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) )
+
+namespace nonstd {
+namespace span_lite {
+
+template< class T >
+inline span_constexpr auto
+byte_span( T & t ) span_noexcept -> span< std17::byte, span_sizeof(T) >
+{
+    return span< std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte * >( &t ), span_sizeof(T) );
+}
+
+template< class T >
+inline span_constexpr auto
+byte_span( T const & t ) span_noexcept -> span< const std17::byte, span_sizeof(T) >
+{
+    return span< const std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte const * >( &t ), span_sizeof(T) );
+}
+
+}  // namespace span_lite
+}  // namespace nonstd
+
+// make available in nonstd:
+
+namespace nonstd {
+using span_lite::byte_span;
+}  // namespace nonstd
+
+#endif // span_FEATURE( BYTE_SPAN )
+
+#if span_HAVE( STRUCT_BINDING )
+
+#if   span_CPP14_OR_GREATER
+# include <tuple>
+#elif span_CPP11_OR_GREATER
+# include <tuple>
+namespace std {
+    template< std::size_t I, typename T >
+    using tuple_element_t = typename tuple_element<I, T>::type;
+}
+#else
+namespace std {
+    template< typename T >
+    class tuple_size; /*undefined*/
+
+    template< std::size_t I, typename T >
+    class tuple_element; /* undefined */
+}
+#endif // span_CPP14_OR_GREATER
+
+namespace std {
+
+// 26.7.X Tuple interface
+
+// std::tuple_size<>:
+
+template< typename ElementType, nonstd::span_lite::extent_t Extent >
+class tuple_size< nonstd::span<ElementType, Extent> > : public integral_constant<size_t, static_cast<size_t>(Extent)> {};
+
+// std::tuple_size<>: Leave undefined for dynamic extent:
+
+template< typename ElementType >
+class tuple_size< nonstd::span<ElementType, nonstd::dynamic_extent> >;
+
+// std::tuple_element<>:
+
+template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent >
+class tuple_element< I, nonstd::span<ElementType, Extent> >
+{
+public:
+#if span_HAVE( STATIC_ASSERT )
+    static_assert( Extent != nonstd::dynamic_extent && I < Extent, "tuple_element<I,span>: dynamic extent or index out of range" );
+#endif
+    using type = ElementType;
+};
+
+// std::get<>(), 2 variants:
+
+template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent >
+span_constexpr ElementType & get( nonstd::span<ElementType, Extent> & spn ) span_noexcept
+{
+#if span_HAVE( STATIC_ASSERT )
+    static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" );
+#endif
+    return spn[I];
+}
+
+template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent >
+span_constexpr ElementType const & get( nonstd::span<ElementType, Extent> const & spn ) span_noexcept
+{
+#if span_HAVE( STATIC_ASSERT )
+    static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" );
+#endif
+    return spn[I];
+}
+
+} // end namespace std
+
+#endif // span_HAVE( STRUCT_BINDING )
+
+#if ! span_USES_STD_SPAN
+span_RESTORE_WARNINGS()
+#endif  // span_USES_STD_SPAN
+
+#endif  // NONSTD_SPAN_HPP_INCLUDED
diff --git a/src/cpp-common/vendor/span-lite/span.hpp.license b/src/cpp-common/vendor/span-lite/span.hpp.license
new file mode 100644 (file)
index 0000000..c8fc2e9
--- /dev/null
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: 2018-2021 Martin Moene
+SPDX-License-Identifier: BSL-1.0
diff --git a/src/cpp-common/vendor/string-view-lite/string_view.hpp b/src/cpp-common/vendor/string-view-lite/string_view.hpp
new file mode 100644 (file)
index 0000000..84a565f
--- /dev/null
@@ -0,0 +1,1707 @@
+// Copyright 2017-2020 by Martin Moene
+//
+// string-view lite, a C++17-like string_view for C++98 and later.
+// For more information see https://github.com/martinmoene/string-view-lite
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#pragma once
+
+#ifndef NONSTD_SV_LITE_H_INCLUDED
+#define NONSTD_SV_LITE_H_INCLUDED
+
+#define string_view_lite_MAJOR  1
+#define string_view_lite_MINOR  7
+#define string_view_lite_PATCH  0
+
+#define string_view_lite_VERSION  nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
+
+#define nssv_STRINGIFY(  x )  nssv_STRINGIFY_( x )
+#define nssv_STRINGIFY_( x )  #x
+
+// string-view lite configuration:
+
+#define nssv_STRING_VIEW_DEFAULT  0
+#define nssv_STRING_VIEW_NONSTD   1
+#define nssv_STRING_VIEW_STD      2
+
+// tweak header support:
+
+#ifdef __has_include
+# if __has_include(<nonstd/string_view.tweak.hpp>)
+#  include <nonstd/string_view.tweak.hpp>
+# endif
+#define nssv_HAVE_TWEAK_HEADER  1
+#else
+#define nssv_HAVE_TWEAK_HEADER  0
+//# pragma message("string_view.hpp: Note: Tweak header not supported.")
+#endif
+
+// string_view selection and configuration:
+
+#if !defined( nssv_CONFIG_SELECT_STRING_VIEW )
+# define nssv_CONFIG_SELECT_STRING_VIEW  ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD )
+#endif
+
+#ifndef  nssv_CONFIG_STD_SV_OPERATOR
+# define nssv_CONFIG_STD_SV_OPERATOR  0
+#endif
+
+#ifndef  nssv_CONFIG_USR_SV_OPERATOR
+# define nssv_CONFIG_USR_SV_OPERATOR  1
+#endif
+
+#ifdef   nssv_CONFIG_CONVERSION_STD_STRING
+# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS   nssv_CONFIG_CONVERSION_STD_STRING
+# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS  nssv_CONFIG_CONVERSION_STD_STRING
+#endif
+
+#ifndef  nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS  1
+#endif
+
+#ifndef  nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS  1
+#endif
+
+#ifndef  nssv_CONFIG_NO_STREAM_INSERTION
+# define nssv_CONFIG_NO_STREAM_INSERTION  0
+#endif
+
+#ifndef  nssv_CONFIG_CONSTEXPR11_STD_SEARCH
+# define nssv_CONFIG_CONSTEXPR11_STD_SEARCH  1
+#endif
+
+// Control presence of exception handling (try and auto discover):
+
+#ifndef nssv_CONFIG_NO_EXCEPTIONS
+# if defined(_MSC_VER)
+#  include <cstddef>    // for _HAS_EXCEPTIONS
+# endif
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
+#  define nssv_CONFIG_NO_EXCEPTIONS  0
+# else
+#  define nssv_CONFIG_NO_EXCEPTIONS  1
+# endif
+#endif
+
+// C++ language version detection (C++23 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef   nssv_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+#  define nssv_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+#  define nssv_CPLUSPLUS  __cplusplus
+# endif
+#endif
+
+#define nssv_CPP98_OR_GREATER  ( nssv_CPLUSPLUS >= 199711L )
+#define nssv_CPP11_OR_GREATER  ( nssv_CPLUSPLUS >= 201103L )
+#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L )
+#define nssv_CPP14_OR_GREATER  ( nssv_CPLUSPLUS >= 201402L )
+#define nssv_CPP17_OR_GREATER  ( nssv_CPLUSPLUS >= 201703L )
+#define nssv_CPP20_OR_GREATER  ( nssv_CPLUSPLUS >= 202002L )
+#define nssv_CPP23_OR_GREATER  ( nssv_CPLUSPLUS >= 202300L )
+
+// use C++17 std::string_view if available and requested:
+
+#if nssv_CPP17_OR_GREATER && defined(__has_include )
+# if __has_include( <string_view> )
+#  define nssv_HAVE_STD_STRING_VIEW  1
+# else
+#  define nssv_HAVE_STD_STRING_VIEW  0
+# endif
+#else
+# define  nssv_HAVE_STD_STRING_VIEW  0
+#endif
+
+#define  nssv_USES_STD_STRING_VIEW  ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) )
+
+#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW )
+#define nssv_HAVE_ENDS_WITH     nssv_HAVE_STARTS_WITH
+
+//
+// Use C++17 std::string_view:
+//
+
+#if nssv_USES_STD_STRING_VIEW
+
+#include <string_view>
+
+// Extensions for std::string:
+
+#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+namespace nonstd {
+
+template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
+std::basic_string<CharT, Traits, Allocator>
+to_string( std::basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
+{
+    return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
+}
+
+template< class CharT, class Traits, class Allocator >
+std::basic_string_view<CharT, Traits>
+to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
+{
+    return std::basic_string_view<CharT, Traits>( s.data(), s.size() );
+}
+
+// Literal operators sv and _sv:
+
+#if nssv_CONFIG_STD_SV_OPERATOR
+
+using namespace std::literals::string_view_literals;
+
+#endif
+
+#if nssv_CONFIG_USR_SV_OPERATOR
+
+inline namespace literals {
+inline namespace string_view_literals {
+
+
+constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept  // (1)
+{
+    return std::string_view{ str, len };
+}
+
+constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept  // (2)
+{
+    return std::u16string_view{ str, len };
+}
+
+constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept  // (3)
+{
+    return std::u32string_view{ str, len };
+}
+
+constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept  // (4)
+{
+    return std::wstring_view{ str, len };
+}
+
+}} // namespace literals::string_view_literals
+
+#endif // nssv_CONFIG_USR_SV_OPERATOR
+
+} // namespace nonstd
+
+#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+namespace nonstd {
+
+using std::string_view;
+using std::wstring_view;
+using std::u16string_view;
+using std::u32string_view;
+using std::basic_string_view;
+
+// literal "sv" and "_sv", see above
+
+using std::operator==;
+using std::operator!=;
+using std::operator<;
+using std::operator<=;
+using std::operator>;
+using std::operator>=;
+
+using std::operator<<;
+
+} // namespace nonstd
+
+#else // nssv_HAVE_STD_STRING_VIEW
+
+//
+// Before C++17: use string_view lite:
+//
+
+// Compiler versions:
+//
+// MSVC++  6.0  _MSC_VER == 1200  nssv_COMPILER_MSVC_VERSION ==  60  (Visual Studio 6.0)
+// MSVC++  7.0  _MSC_VER == 1300  nssv_COMPILER_MSVC_VERSION ==  70  (Visual Studio .NET 2002)
+// MSVC++  7.1  _MSC_VER == 1310  nssv_COMPILER_MSVC_VERSION ==  71  (Visual Studio .NET 2003)
+// MSVC++  8.0  _MSC_VER == 1400  nssv_COMPILER_MSVC_VERSION ==  80  (Visual Studio 2005)
+// MSVC++  9.0  _MSC_VER == 1500  nssv_COMPILER_MSVC_VERSION ==  90  (Visual Studio 2008)
+// MSVC++ 10.0  _MSC_VER == 1600  nssv_COMPILER_MSVC_VERSION == 100  (Visual Studio 2010)
+// MSVC++ 11.0  _MSC_VER == 1700  nssv_COMPILER_MSVC_VERSION == 110  (Visual Studio 2012)
+// MSVC++ 12.0  _MSC_VER == 1800  nssv_COMPILER_MSVC_VERSION == 120  (Visual Studio 2013)
+// MSVC++ 14.0  _MSC_VER == 1900  nssv_COMPILER_MSVC_VERSION == 140  (Visual Studio 2015)
+// MSVC++ 14.1  _MSC_VER >= 1910  nssv_COMPILER_MSVC_VERSION == 141  (Visual Studio 2017)
+// MSVC++ 14.2  _MSC_VER >= 1920  nssv_COMPILER_MSVC_VERSION == 142  (Visual Studio 2019)
+
+#if defined(_MSC_VER ) && !defined(__clang__)
+# define nssv_COMPILER_MSVC_VER      (_MSC_VER )
+# define nssv_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
+#else
+# define nssv_COMPILER_MSVC_VER      0
+# define nssv_COMPILER_MSVC_VERSION  0
+#endif
+
+#define nssv_COMPILER_VERSION( major, minor, patch )  ( 10 * ( 10 * (major) + (minor) ) + (patch) )
+
+#if defined( __apple_build_version__ )
+# define nssv_COMPILER_APPLECLANG_VERSION  nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+# define nssv_COMPILER_CLANG_VERSION       0
+#elif defined( __clang__ )
+# define nssv_COMPILER_APPLECLANG_VERSION  0
+# define nssv_COMPILER_CLANG_VERSION       nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define nssv_COMPILER_APPLECLANG_VERSION  0
+# define nssv_COMPILER_CLANG_VERSION       0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define nssv_COMPILER_GNUC_VERSION  nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define nssv_COMPILER_GNUC_VERSION  0
+#endif
+
+// half-open range [lo..hi):
+#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Presence of language and library features:
+
+#ifdef _HAS_CPP0X
+# define nssv_HAS_CPP0X  _HAS_CPP0X
+#else
+# define nssv_HAS_CPP0X  0
+#endif
+
+// Unless defined otherwise below, consider VC14 as C++11 for string-view-lite:
+
+#if nssv_COMPILER_MSVC_VER >= 1900
+# undef  nssv_CPP11_OR_GREATER
+# define nssv_CPP11_OR_GREATER  1
+#endif
+
+#define nssv_CPP11_90   (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
+#define nssv_CPP11_100  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
+#define nssv_CPP11_110  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
+#define nssv_CPP11_120  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
+#define nssv_CPP11_140  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
+#define nssv_CPP11_141  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
+
+#define nssv_CPP14_000  (nssv_CPP14_OR_GREATER)
+#define nssv_CPP17_000  (nssv_CPP17_OR_GREATER)
+
+// Presence of C++11 language features:
+
+#define nssv_HAVE_CONSTEXPR_11          nssv_CPP11_140
+#define nssv_HAVE_EXPLICIT_CONVERSION   nssv_CPP11_140
+#define nssv_HAVE_INLINE_NAMESPACE      nssv_CPP11_140
+#define nssv_HAVE_IS_DEFAULT            nssv_CPP11_140
+#define nssv_HAVE_IS_DELETE             nssv_CPP11_140
+#define nssv_HAVE_NOEXCEPT              nssv_CPP11_140
+#define nssv_HAVE_NULLPTR               nssv_CPP11_100
+#define nssv_HAVE_REF_QUALIFIER         nssv_CPP11_140
+#define nssv_HAVE_UNICODE_LITERALS      nssv_CPP11_140
+#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
+#define nssv_HAVE_WCHAR16_T             nssv_CPP11_100
+#define nssv_HAVE_WCHAR32_T             nssv_CPP11_100
+
+#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
+# define nssv_HAVE_STD_DEFINED_LITERALS  nssv_CPP11_140
+#else
+# define nssv_HAVE_STD_DEFINED_LITERALS  0
+#endif
+
+// Presence of C++14 language features:
+
+#define nssv_HAVE_CONSTEXPR_14          nssv_CPP14_000
+
+// Presence of C++17 language features:
+
+#define nssv_HAVE_NODISCARD             nssv_CPP17_000
+
+// Presence of C++ library features:
+
+#define nssv_HAVE_STD_HASH              nssv_CPP11_120
+
+// Presence of compiler intrinsics:
+
+// Providing char-type specializations for compare() and length() that
+// use compiler intrinsics can improve compile- and run-time performance.
+//
+// The challenge is in using the right combinations of builtin availability
+// and its constexpr-ness.
+//
+// | compiler | __builtin_memcmp (constexpr) | memcmp  (constexpr) |
+// |----------|------------------------------|---------------------|
+// | clang    | 4.0              (>= 4.0   ) | any     (?        ) |
+// | clang-a  | 9.0              (>= 9.0   ) | any     (?        ) |
+// | gcc      | any              (constexpr) | any     (?        ) |
+// | msvc     | >= 14.2 C++17    (>= 14.2  ) | any     (?        ) |
+
+#define nssv_HAVE_BUILTIN_VER     ( (nssv_CPP17_000 && nssv_COMPILER_MSVC_VERSION >= 142) || nssv_COMPILER_GNUC_VERSION > 0 || nssv_COMPILER_CLANG_VERSION >= 400 || nssv_COMPILER_APPLECLANG_VERSION >= 900 )
+#define nssv_HAVE_BUILTIN_CE      (  nssv_HAVE_BUILTIN_VER )
+
+#define nssv_HAVE_BUILTIN_MEMCMP  ( (nssv_HAVE_CONSTEXPR_14 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_14 )
+#define nssv_HAVE_BUILTIN_STRLEN  ( (nssv_HAVE_CONSTEXPR_11 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_11 )
+
+#ifdef __has_builtin
+# define nssv_HAVE_BUILTIN( x )  __has_builtin( x )
+#else
+# define nssv_HAVE_BUILTIN( x )  0
+#endif
+
+#if nssv_HAVE_BUILTIN(__builtin_memcmp) || nssv_HAVE_BUILTIN_VER
+# define nssv_BUILTIN_MEMCMP  __builtin_memcmp
+#else
+# define nssv_BUILTIN_MEMCMP  memcmp
+#endif
+
+#if nssv_HAVE_BUILTIN(__builtin_strlen) || nssv_HAVE_BUILTIN_VER
+# define nssv_BUILTIN_STRLEN  __builtin_strlen
+#else
+# define nssv_BUILTIN_STRLEN  strlen
+#endif
+
+// C++ feature usage:
+
+#if nssv_HAVE_CONSTEXPR_11
+# define nssv_constexpr  constexpr
+#else
+# define nssv_constexpr  /*constexpr*/
+#endif
+
+#if  nssv_HAVE_CONSTEXPR_14
+# define nssv_constexpr14  constexpr
+#else
+# define nssv_constexpr14  /*constexpr*/
+#endif
+
+#if nssv_HAVE_EXPLICIT_CONVERSION
+# define nssv_explicit  explicit
+#else
+# define nssv_explicit  /*explicit*/
+#endif
+
+#if nssv_HAVE_INLINE_NAMESPACE
+# define nssv_inline_ns  inline
+#else
+# define nssv_inline_ns  /*inline*/
+#endif
+
+#if nssv_HAVE_NOEXCEPT
+# define nssv_noexcept  noexcept
+#else
+# define nssv_noexcept  /*noexcept*/
+#endif
+
+//#if nssv_HAVE_REF_QUALIFIER
+//# define nssv_ref_qual  &
+//# define nssv_refref_qual  &&
+//#else
+//# define nssv_ref_qual  /*&*/
+//# define nssv_refref_qual  /*&&*/
+//#endif
+
+#if nssv_HAVE_NULLPTR
+# define nssv_nullptr  nullptr
+#else
+# define nssv_nullptr  NULL
+#endif
+
+#if nssv_HAVE_NODISCARD
+# define nssv_nodiscard  [[nodiscard]]
+#else
+# define nssv_nodiscard  /*[[nodiscard]]*/
+#endif
+
+// Additional includes:
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <limits>
+#include <string>   // std::char_traits<>
+
+#if ! nssv_CONFIG_NO_STREAM_INSERTION
+# include <ostream>
+#endif
+
+#if ! nssv_CONFIG_NO_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+#if nssv_CPP11_OR_GREATER
+# include <type_traits>
+#endif
+
+// Clang, GNUC, MSVC warning suppression macros:
+
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wuser-defined-literals"
+#elif nssv_COMPILER_GNUC_VERSION >= 480
+#  pragma  GCC  diagnostic push
+#  pragma  GCC  diagnostic ignored "-Wliteral-suffix"
+#endif // __clang__
+
+#if nssv_COMPILER_MSVC_VERSION >= 140
+# define nssv_SUPPRESS_MSGSL_WARNING(expr)        [[gsl::suppress(expr)]]
+# define nssv_SUPPRESS_MSVC_WARNING(code, descr)  __pragma(warning(suppress: code) )
+# define nssv_DISABLE_MSVC_WARNINGS(codes)        __pragma(warning(push))  __pragma(warning(disable: codes))
+#else
+# define nssv_SUPPRESS_MSGSL_WARNING(expr)
+# define nssv_SUPPRESS_MSVC_WARNING(code, descr)
+# define nssv_DISABLE_MSVC_WARNINGS(codes)
+#endif
+
+#if defined(__clang__)
+# define nssv_RESTORE_WARNINGS()  _Pragma("clang diagnostic pop")
+#elif nssv_COMPILER_GNUC_VERSION >= 480
+#  define nssv_RESTORE_WARNINGS()  _Pragma("GCC diagnostic pop")
+#elif nssv_COMPILER_MSVC_VERSION >= 140
+# define nssv_RESTORE_WARNINGS()  __pragma(warning(pop ))
+#else
+# define nssv_RESTORE_WARNINGS()
+#endif
+
+// Suppress the following MSVC (GSL) warnings:
+// - C4455, non-gsl   : 'operator ""sv': literal suffix identifiers that do not
+//                      start with an underscore are reserved
+// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
+//                      use brace initialization, gsl::narrow_cast or gsl::narow
+// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
+
+nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 )
+//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
+//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
+
+namespace nonstd { namespace sv_lite {
+
+//
+// basic_string_view declaration:
+//
+
+template
+<
+    class CharT,
+    class Traits = std::char_traits<CharT>
+>
+class basic_string_view;
+
+namespace detail {
+
+// support constexpr comparison in C++14;
+// for C++17 and later, use provided traits:
+
+template< typename CharT >
+inline nssv_constexpr14 int compare( CharT const * s1, CharT const * s2, std::size_t count )
+{
+    while ( count-- != 0 )
+    {
+        if ( *s1 < *s2 ) return -1;
+        if ( *s1 > *s2 ) return +1;
+        ++s1; ++s2;
+    }
+    return 0;
+}
+
+#if nssv_HAVE_BUILTIN_MEMCMP
+
+// specialization of compare() for char, see also generic compare() above:
+
+inline nssv_constexpr14 int compare( char const * s1, char const * s2, std::size_t count )
+{
+    return nssv_BUILTIN_MEMCMP( s1, s2, count );
+}
+
+#endif
+
+#if nssv_HAVE_BUILTIN_STRLEN
+
+// specialization of length() for char, see also generic length() further below:
+
+inline nssv_constexpr std::size_t length( char const * s )
+{
+    return nssv_BUILTIN_STRLEN( s );
+}
+
+#endif
+
+#if defined(__OPTIMIZE__)
+
+// gcc, clang provide __OPTIMIZE__
+// Expect tail call optimization to make length() non-recursive:
+
+template< typename CharT >
+inline nssv_constexpr std::size_t length( CharT * s, std::size_t result = 0 )
+{
+    return *s == '\0' ? result : length( s + 1, result + 1 );
+}
+
+#else // OPTIMIZE
+
+// non-recursive:
+
+template< typename CharT >
+inline nssv_constexpr14 std::size_t length( CharT * s )
+{
+    std::size_t result = 0;
+    while ( *s++ != '\0' )
+    {
+       ++result;
+    }
+    return result;
+}
+
+#endif // OPTIMIZE
+
+#if nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER
+#if defined(__OPTIMIZE__)
+
+// gcc, clang provide __OPTIMIZE__
+// Expect tail call optimization to make search() non-recursive:
+
+template< class CharT, class Traits = std::char_traits<CharT> >
+constexpr const CharT* search( basic_string_view<CharT, Traits> haystack, basic_string_view<CharT, Traits> needle )
+{
+    return haystack.starts_with( needle ) ? haystack.begin() :
+        haystack.empty() ? haystack.end() : search( haystack.substr(1), needle );
+}
+
+#else // OPTIMIZE
+
+// non-recursive:
+
+#if nssv_CONFIG_CONSTEXPR11_STD_SEARCH
+
+template< class CharT, class Traits = std::char_traits<CharT> >
+constexpr const CharT* search( basic_string_view<CharT, Traits> haystack, basic_string_view<CharT, Traits> needle )
+{
+    return std::search( haystack.begin(), haystack.end(), needle.begin(), needle.end() );
+}
+
+#else // nssv_CONFIG_CONSTEXPR11_STD_SEARCH
+
+template< class CharT, class Traits = std::char_traits<CharT> >
+nssv_constexpr14 const CharT* search( basic_string_view<CharT, Traits> haystack, basic_string_view<CharT, Traits> needle )
+{
+    while ( needle.size() <= haystack.size() )
+    {
+        if  ( haystack.starts_with(needle) )
+        {
+            return haystack.cbegin();
+        }
+        haystack = basic_string_view<CharT, Traits>{ haystack.begin() + 1, haystack.size() - 1U };
+    }
+    return haystack.cend();
+}
+#endif // nssv_CONFIG_CONSTEXPR11_STD_SEARCH
+
+#endif // OPTIMIZE
+#endif // nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER
+
+} // namespace detail
+
+//
+// basic_string_view:
+//
+
+template
+<
+    class CharT,
+    class Traits /* = std::char_traits<CharT> */
+>
+class basic_string_view
+{
+public:
+    // Member types:
+
+    typedef Traits traits_type;
+    typedef CharT  value_type;
+
+    typedef CharT       * pointer;
+    typedef CharT const * const_pointer;
+    typedef CharT       & reference;
+    typedef CharT const & const_reference;
+
+    typedef const_pointer iterator;
+    typedef const_pointer const_iterator;
+    typedef std::reverse_iterator< const_iterator > reverse_iterator;
+    typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
+
+    typedef std::size_t     size_type;
+    typedef std::ptrdiff_t  difference_type;
+
+    // 24.4.2.1 Construction and assignment:
+
+    nssv_constexpr basic_string_view() nssv_noexcept
+        : data_( nssv_nullptr )
+        , size_( 0 )
+    {}
+
+#if nssv_CPP11_OR_GREATER
+    nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default;
+#else
+    nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept
+        : data_( other.data_)
+        , size_( other.size_)
+    {}
+#endif
+
+    nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept
+        : data_( s )
+        , size_( count )
+    {}
+
+    nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept
+        : data_( s )
+#if nssv_CPP17_OR_GREATER
+        , size_( Traits::length(s) )
+#elif nssv_CPP11_OR_GREATER
+        , size_( detail::length(s) )
+#else
+        , size_( Traits::length(s) )
+#endif
+    {}
+
+#if  nssv_HAVE_NULLPTR
+# if nssv_HAVE_IS_DELETE
+    nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept = delete;
+# else
+    private: nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept; public:
+# endif
+#endif
+
+    // Assignment:
+
+#if nssv_CPP11_OR_GREATER
+    nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default;
+#else
+    nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept
+    {
+        data_ = other.data_;
+        size_ = other.size_;
+        return *this;
+    }
+#endif
+
+    // 24.4.2.2 Iterator support:
+
+    nssv_constexpr const_iterator begin()  const nssv_noexcept { return data_;         }
+    nssv_constexpr const_iterator end()    const nssv_noexcept { return data_ + size_; }
+
+    nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
+    nssv_constexpr const_iterator cend()   const nssv_noexcept { return end();   }
+
+    nssv_constexpr const_reverse_iterator rbegin()  const nssv_noexcept { return const_reverse_iterator( end() );   }
+    nssv_constexpr const_reverse_iterator rend()    const nssv_noexcept { return const_reverse_iterator( begin() ); }
+
+    nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
+    nssv_constexpr const_reverse_iterator crend()   const nssv_noexcept { return rend();   }
+
+    // 24.4.2.3 Capacity:
+
+    nssv_constexpr size_type size()     const nssv_noexcept { return size_; }
+    nssv_constexpr size_type length()   const nssv_noexcept { return size_; }
+    nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); }
+
+    // since C++20
+    nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept
+    {
+        return 0 == size_;
+    }
+
+    // 24.4.2.4 Element access:
+
+    nssv_constexpr const_reference operator[]( size_type pos ) const
+    {
+        return data_at( pos );
+    }
+
+    nssv_constexpr14 const_reference at( size_type pos ) const
+    {
+#if nssv_CONFIG_NO_EXCEPTIONS
+        assert( pos < size() );
+#else
+        if ( pos >= size() )
+        {
+            throw std::out_of_range("nonstd::string_view::at()");
+        }
+#endif
+        return data_at( pos );
+    }
+
+    nssv_constexpr const_reference front() const { return data_at( 0 );          }
+    nssv_constexpr const_reference back()  const { return data_at( size() - 1 ); }
+
+    nssv_constexpr const_pointer   data()  const nssv_noexcept { return data_; }
+
+    // 24.4.2.5 Modifiers:
+
+    nssv_constexpr14 void remove_prefix( size_type n )
+    {
+        assert( n <= size() );
+        data_ += n;
+        size_ -= n;
+    }
+
+    nssv_constexpr14 void remove_suffix( size_type n )
+    {
+        assert( n <= size() );
+        size_ -= n;
+    }
+
+    nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept
+    {
+        const basic_string_view tmp(other);
+        other = *this;
+        *this = tmp;
+    }
+
+    // 24.4.2.6 String operations:
+
+    size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const
+    {
+#if nssv_CONFIG_NO_EXCEPTIONS
+        assert( pos <= size() );
+#else
+        if ( pos > size() )
+        {
+            throw std::out_of_range("nonstd::string_view::copy()");
+        }
+#endif
+        const size_type rlen = (std::min)( n, size() - pos );
+
+        (void) Traits::copy( dest, data() + pos, rlen );
+
+        return rlen;
+    }
+
+    nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const
+    {
+#if nssv_CONFIG_NO_EXCEPTIONS
+        assert( pos <= size() );
+#else
+        if ( pos > size() )
+        {
+            throw std::out_of_range("nonstd::string_view::substr()");
+        }
+#endif
+        return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
+    }
+
+    // compare(), 6x:
+
+    nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1)
+    {
+#if nssv_CPP17_OR_GREATER
+        if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
+#else
+        if ( const int result = detail::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
+#endif
+        {
+            return result;
+        }
+
+        return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
+    }
+
+    nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2)
+    {
+        return substr( pos1, n1 ).compare( other );
+    }
+
+    nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3)
+    {
+        return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) );
+    }
+
+    nssv_constexpr int compare( CharT const * s ) const // (4)
+    {
+        return compare( basic_string_view( s ) );
+    }
+
+    nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5)
+    {
+        return substr( pos1, n1 ).compare( basic_string_view( s ) );
+    }
+
+    nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6)
+    {
+        return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) );
+    }
+
+    // 24.4.2.7 Searching:
+
+    // starts_with(), 3x, since C++20:
+
+    nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept  // (1)
+    {
+        return size() >= v.size() && compare( 0, v.size(), v ) == 0;
+    }
+
+    nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept  // (2)
+    {
+        return starts_with( basic_string_view( &c, 1 ) );
+    }
+
+    nssv_constexpr bool starts_with( CharT const * s ) const  // (3)
+    {
+        return starts_with( basic_string_view( s ) );
+    }
+
+    // ends_with(), 3x, since C++20:
+
+    nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept  // (1)
+    {
+        return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0;
+    }
+
+    nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept  // (2)
+    {
+        return ends_with( basic_string_view( &c, 1 ) );
+    }
+
+    nssv_constexpr bool ends_with( CharT const * s ) const  // (3)
+    {
+        return ends_with( basic_string_view( s ) );
+    }
+
+    // find(), 4x:
+
+    nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept  // (1)
+    {
+        return assert( v.size() == 0 || v.data() != nssv_nullptr )
+            , pos >= size()
+            ? npos : to_pos(
+#if nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER
+                detail::search( substr(pos), v )
+#else
+                std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq )
+#endif
+            );
+    }
+
+    nssv_constexpr size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept  // (2)
+    {
+        return find( basic_string_view( &c, 1 ), pos );
+    }
+
+    nssv_constexpr size_type find( CharT const * s, size_type pos, size_type n ) const  // (3)
+    {
+        return find( basic_string_view( s, n ), pos );
+    }
+
+    nssv_constexpr size_type find( CharT const * s, size_type pos = 0 ) const  // (4)
+    {
+        return find( basic_string_view( s ), pos );
+    }
+
+    // rfind(), 4x:
+
+    nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
+    {
+        if ( size() < v.size() )
+        {
+            return npos;
+        }
+
+        if ( v.empty() )
+        {
+            return (std::min)( size(), pos );
+        }
+
+        const_iterator last   = cbegin() + (std::min)( size() - v.size(), pos ) + v.size();
+        const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq );
+
+        return result != last ? size_type( result - cbegin() ) : npos;
+    }
+
+    nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept  // (2)
+    {
+        return rfind( basic_string_view( &c, 1 ), pos );
+    }
+
+    nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const  // (3)
+    {
+        return rfind( basic_string_view( s, n ), pos );
+    }
+
+    nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const  // (4)
+    {
+        return rfind( basic_string_view( s ), pos );
+    }
+
+    // find_first_of(), 4x:
+
+    nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept  // (1)
+    {
+        return pos >= size()
+            ? npos
+            : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
+    }
+
+    nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept  // (2)
+    {
+        return find_first_of( basic_string_view( &c, 1 ), pos );
+    }
+
+    nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const  // (3)
+    {
+        return find_first_of( basic_string_view( s, n ), pos );
+    }
+
+    nssv_constexpr size_type find_first_of(  CharT const * s, size_type pos = 0 ) const  // (4)
+    {
+        return find_first_of( basic_string_view( s ), pos );
+    }
+
+    // find_last_of(), 4x:
+
+    nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
+    {
+        return empty()
+            ? npos
+            : pos >= size()
+            ? find_last_of( v, size() - 1 )
+            : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) );
+    }
+
+    nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept  // (2)
+    {
+        return find_last_of( basic_string_view( &c, 1 ), pos );
+    }
+
+    nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const  // (3)
+    {
+        return find_last_of( basic_string_view( s, count ), pos );
+    }
+
+    nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const  // (4)
+    {
+        return find_last_of( basic_string_view( s ), pos );
+    }
+
+    // find_first_not_of(), 4x:
+
+    nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept  // (1)
+    {
+        return pos >= size()
+            ? npos
+            : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) );
+    }
+
+    nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept  // (2)
+    {
+        return find_first_not_of( basic_string_view( &c, 1 ), pos );
+    }
+
+    nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const  // (3)
+    {
+        return find_first_not_of( basic_string_view( s, count ), pos );
+    }
+
+    nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const  // (4)
+    {
+        return find_first_not_of( basic_string_view( s ), pos );
+    }
+
+    // find_last_not_of(), 4x:
+
+    nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
+    {
+        return empty()
+            ? npos
+            : pos >= size()
+            ? find_last_not_of( v, size() - 1 )
+            : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) );
+    }
+
+    nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept  // (2)
+    {
+        return find_last_not_of( basic_string_view( &c, 1 ), pos );
+    }
+
+    nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const  // (3)
+    {
+        return find_last_not_of( basic_string_view( s, count ), pos );
+    }
+
+    nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const  // (4)
+    {
+        return find_last_not_of( basic_string_view( s ), pos );
+    }
+
+    // Constants:
+
+#if nssv_CPP17_OR_GREATER
+    static nssv_constexpr size_type npos = size_type(-1);
+#elif nssv_CPP11_OR_GREATER
+    enum : size_type { npos = size_type(-1) };
+#else
+    enum { npos = size_type(-1) };
+#endif
+
+private:
+    struct not_in_view
+    {
+        const basic_string_view v;
+
+        nssv_constexpr explicit not_in_view( basic_string_view v_ ) : v( v_ ) {}
+
+        nssv_constexpr bool operator()( CharT c ) const
+        {
+            return npos == v.find_first_of( c );
+        }
+    };
+
+    nssv_constexpr size_type to_pos( const_iterator it ) const
+    {
+        return it == cend() ? npos : size_type( it - cbegin() );
+    }
+
+    nssv_constexpr size_type to_pos( const_reverse_iterator it ) const
+    {
+        return it == crend() ? npos : size_type( crend() - it - 1 );
+    }
+
+    nssv_constexpr const_reference data_at( size_type pos ) const
+    {
+#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 )
+        return data_[pos];
+#else
+        return assert( pos < size() ), data_[pos];
+#endif
+    }
+
+private:
+    const_pointer data_;
+    size_type     size_;
+
+public:
+#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+
+    template< class Allocator >
+    basic_string_view( std::basic_string<CharT, Traits, Allocator> const & s ) nssv_noexcept
+        : data_( s.data() )
+        , size_( s.size() )
+    {}
+
+#if nssv_HAVE_EXPLICIT_CONVERSION
+
+    template< class Allocator >
+    explicit operator std::basic_string<CharT, Traits, Allocator>() const
+    {
+        return to_string( Allocator() );
+    }
+
+#endif // nssv_HAVE_EXPLICIT_CONVERSION
+
+#if nssv_CPP11_OR_GREATER
+
+    template< class Allocator = std::allocator<CharT> >
+    std::basic_string<CharT, Traits, Allocator>
+    to_string( Allocator const & a = Allocator() ) const
+    {
+        return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
+    }
+
+#else
+
+    std::basic_string<CharT, Traits>
+    to_string() const
+    {
+        return std::basic_string<CharT, Traits>( begin(), end() );
+    }
+
+    template< class Allocator >
+    std::basic_string<CharT, Traits, Allocator>
+    to_string( Allocator const & a ) const
+    {
+        return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
+    }
+
+#endif // nssv_CPP11_OR_GREATER
+
+#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+};
+
+//
+// Non-member functions:
+//
+
+// 24.4.3 Non-member comparison functions:
+// lexicographically compare two string views (function template):
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator== (
+    basic_string_view <CharT, Traits> lhs,
+    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator!= (
+    basic_string_view <CharT, Traits> lhs,
+    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator< (
+    basic_string_view <CharT, Traits> lhs,
+    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator<= (
+    basic_string_view <CharT, Traits> lhs,
+    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator> (
+    basic_string_view <CharT, Traits> lhs,
+    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator>= (
+    basic_string_view <CharT, Traits> lhs,
+    basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+// Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
+// Implementations shall provide sufficient additional overloads marked
+// constexpr and noexcept so that an object t with an implicit conversion
+// to S can be compared according to Table 67.
+
+#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
+
+// accommodate for older compilers:
+
+// ==
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    basic_string_view<CharT, Traits> lhs,
+    CharT const * rhs ) nssv_noexcept
+{ return lhs.size() == detail::length( rhs ) && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    CharT const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return detail::length( lhs ) == rhs.size() && rhs.compare( lhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+// !=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    basic_string_view<CharT, Traits> lhs,
+    CharT const * rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    CharT const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+// <
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    basic_string_view<CharT, Traits> lhs,
+    CharT const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    CharT const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) > 0; }
+
+// <=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    basic_string_view<CharT, Traits> lhs,
+    CharT const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    CharT const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) >= 0; }
+
+// >
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    basic_string_view<CharT, Traits> lhs,
+    CharT const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    CharT const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) < 0; }
+
+// >=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    basic_string_view<CharT, Traits> lhs,
+    CharT const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    CharT const * lhs,
+    basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    basic_string_view<CharT, Traits> lhs,
+    std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+    std::basic_string<CharT, Traits> rhs,
+    basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) <= 0; }
+
+#else // newer compilers:
+
+#define nssv_BASIC_STRING_VIEW_I(T,U)  typename std::decay< basic_string_view<T,U> >::type
+
+#if defined(_MSC_VER)       // issue 40
+# define nssv_MSVC_ORDER(x)  , int=x
+#else
+# define nssv_MSVC_ORDER(x)  /*, int=x*/
+#endif
+
+// ==
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator==(
+         basic_string_view  <CharT, Traits> lhs,
+    nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator==(
+    nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
+         basic_string_view  <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+// !=
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator!= (
+         basic_string_view  < CharT, Traits > lhs,
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator!= (
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+// <
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator< (
+         basic_string_view  < CharT, Traits > lhs,
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator< (
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+// <=
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator<= (
+         basic_string_view  < CharT, Traits > lhs,
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator<= (
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+// >
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator> (
+         basic_string_view  < CharT, Traits > lhs,
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator> (
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+// >=
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator>= (
+         basic_string_view  < CharT, Traits > lhs,
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator>= (
+    nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+         basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+#undef nssv_MSVC_ORDER
+#undef nssv_BASIC_STRING_VIEW_I
+
+#endif // compiler-dependent approach to comparisons
+
+// 24.4.4 Inserters and extractors:
+
+#if ! nssv_CONFIG_NO_STREAM_INSERTION
+
+namespace detail {
+
+template< class Stream >
+void write_padding( Stream & os, std::streamsize n )
+{
+    for ( std::streamsize i = 0; i < n; ++i )
+        os.rdbuf()->sputc( os.fill() );
+}
+
+template< class Stream, class View >
+Stream & write_to_stream( Stream & os, View const & sv )
+{
+    typename Stream::sentry sentry( os );
+
+    if ( !sentry )
+        return os;
+
+    const std::streamsize length = static_cast<std::streamsize>( sv.length() );
+
+    // Whether, and how, to pad:
+    const bool      pad = ( length < os.width() );
+    const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right;
+
+    if ( left_pad )
+        write_padding( os, os.width() - length );
+
+    // Write span characters:
+    os.rdbuf()->sputn( sv.begin(), length );
+
+    if ( pad && !left_pad )
+        write_padding( os, os.width() - length );
+
+    // Reset output stream width:
+    os.width( 0 );
+
+    return os;
+}
+
+} // namespace detail
+
+template< class CharT, class Traits >
+std::basic_ostream<CharT, Traits> &
+operator<<(
+    std::basic_ostream<CharT, Traits>& os,
+    basic_string_view <CharT, Traits> sv )
+{
+    return detail::write_to_stream( os, sv );
+}
+
+#endif // nssv_CONFIG_NO_STREAM_INSERTION
+
+// Several typedefs for common character types are provided:
+
+typedef basic_string_view<char>      string_view;
+typedef basic_string_view<wchar_t>   wstring_view;
+#if nssv_HAVE_WCHAR16_T
+typedef basic_string_view<char16_t>  u16string_view;
+typedef basic_string_view<char32_t>  u32string_view;
+#endif
+
+}} // namespace nonstd::sv_lite
+
+//
+// 24.4.6 Suffix for basic_string_view literals:
+//
+
+#if nssv_HAVE_USER_DEFINED_LITERALS
+
+namespace nonstd {
+nssv_inline_ns namespace literals {
+nssv_inline_ns namespace string_view_literals {
+
+#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
+
+nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept  // (1)
+{
+    return nonstd::sv_lite::string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept  // (2)
+{
+    return nonstd::sv_lite::u16string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept  // (3)
+{
+    return nonstd::sv_lite::u32string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept  // (4)
+{
+    return nonstd::sv_lite::wstring_view{ str, len };
+}
+
+#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
+
+#if nssv_CONFIG_USR_SV_OPERATOR
+
+nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept  // (1)
+{
+    return nonstd::sv_lite::string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept  // (2)
+{
+    return nonstd::sv_lite::u16string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept  // (3)
+{
+    return nonstd::sv_lite::u32string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept  // (4)
+{
+    return nonstd::sv_lite::wstring_view{ str, len };
+}
+
+#endif // nssv_CONFIG_USR_SV_OPERATOR
+
+}}} // namespace nonstd::literals::string_view_literals
+
+#endif
+
+//
+// Extensions for std::string:
+//
+
+#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+namespace nonstd {
+namespace sv_lite {
+
+// Exclude MSVC 14 (19.00): it yields ambiguous to_string():
+
+#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
+
+template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
+std::basic_string<CharT, Traits, Allocator>
+to_string( basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
+{
+    return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
+}
+
+#else
+
+template< class CharT, class Traits >
+std::basic_string<CharT, Traits>
+to_string( basic_string_view<CharT, Traits> v )
+{
+    return std::basic_string<CharT, Traits>( v.begin(), v.end() );
+}
+
+template< class CharT, class Traits, class Allocator >
+std::basic_string<CharT, Traits, Allocator>
+to_string( basic_string_view<CharT, Traits> v, Allocator const & a )
+{
+    return std::basic_string<CharT, Traits, Allocator>( v.begin(), v.end(), a );
+}
+
+#endif // nssv_CPP11_OR_GREATER
+
+template< class CharT, class Traits, class Allocator >
+basic_string_view<CharT, Traits>
+to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
+{
+    return basic_string_view<CharT, Traits>( s.data(), s.size() );
+}
+
+}} // namespace nonstd::sv_lite
+
+#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+//
+// make types and algorithms available in namespace nonstd:
+//
+
+namespace nonstd {
+
+using sv_lite::basic_string_view;
+using sv_lite::string_view;
+using sv_lite::wstring_view;
+
+#if nssv_HAVE_WCHAR16_T
+using sv_lite::u16string_view;
+#endif
+#if nssv_HAVE_WCHAR32_T
+using sv_lite::u32string_view;
+#endif
+
+// literal "sv"
+
+using sv_lite::operator==;
+using sv_lite::operator!=;
+using sv_lite::operator<;
+using sv_lite::operator<=;
+using sv_lite::operator>;
+using sv_lite::operator>=;
+
+#if ! nssv_CONFIG_NO_STREAM_INSERTION
+using sv_lite::operator<<;
+#endif
+
+#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+using sv_lite::to_string;
+using sv_lite::to_string_view;
+#endif
+
+} // namespace nonstd
+
+// 24.4.5 Hash support (C++11):
+
+// Note: The hash value of a string view object is equal to the hash value of
+// the corresponding string object.
+
+#if nssv_HAVE_STD_HASH
+
+#include <functional>
+
+namespace std {
+
+template<>
+struct hash< nonstd::string_view >
+{
+public:
+    std::size_t operator()( nonstd::string_view v ) const nssv_noexcept
+    {
+        return std::hash<std::string>()( std::string( v.data(), v.size() ) );
+    }
+};
+
+template<>
+struct hash< nonstd::wstring_view >
+{
+public:
+    std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept
+    {
+        return std::hash<std::wstring>()( std::wstring( v.data(), v.size() ) );
+    }
+};
+
+template<>
+struct hash< nonstd::u16string_view >
+{
+public:
+    std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept
+    {
+        return std::hash<std::u16string>()( std::u16string( v.data(), v.size() ) );
+    }
+};
+
+template<>
+struct hash< nonstd::u32string_view >
+{
+public:
+    std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept
+    {
+        return std::hash<std::u32string>()( std::u32string( v.data(), v.size() ) );
+    }
+};
+
+} // namespace std
+
+#endif // nssv_HAVE_STD_HASH
+
+nssv_RESTORE_WARNINGS()
+
+#endif // nssv_HAVE_STD_STRING_VIEW
+#endif // NONSTD_SV_LITE_H_INCLUDED
diff --git a/src/cpp-common/vendor/string-view-lite/string_view.hpp.license b/src/cpp-common/vendor/string-view-lite/string_view.hpp.license
new file mode 100644 (file)
index 0000000..adc1c89
--- /dev/null
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: 2017-2020  Martin Moene
+SPDX-License-Identifier: BSL-1.0
diff --git a/src/cpp-common/vendor/wise-enum/optional.h b/src/cpp-common/vendor/wise-enum/optional.h
new file mode 100644 (file)
index 0000000..6b0dab1
--- /dev/null
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "optional_common.h"
+
+#include <stdexcept>
+#include <type_traits>
+
+#if __cplusplus == 201103
+#define WISE_ENUM_CONSTEXPR_14
+#else
+#define WISE_ENUM_CONSTEXPR_14 constexpr
+#endif
+
+namespace wise_enum {
+
+/* A simple, *forward* compatible optional implementation. That is, it does not
+ * provide the full std::optional interface, but all the interface it does
+ * provide is found on std::optional, so it should not be a breaking change to
+ * upgrade to std::optional.
+ */
+template <class T>
+class optional {
+public:
+  static_assert(std::is_enum<T>::value,
+                "wise enum optional is only for enum types");
+  optional() = default;
+  optional(T t) : m_t(t), m_active(true) {}
+
+  WISE_ENUM_CONSTEXPR_14 T &operator*() & { return m_t; }
+  constexpr const T &operator*() const & { return m_t; }
+  WISE_ENUM_CONSTEXPR_14 T &&operator*() && { return m_t; }
+  constexpr const T &&operator*() const && { return m_t; }
+
+  constexpr explicit operator bool() const noexcept { return m_active; }
+  constexpr bool has_value() const noexcept { return m_active; }
+
+  WISE_ENUM_CONSTEXPR_14 T &value() & {
+    if (m_active)
+      return m_t;
+    else
+      WISE_ENUM_OPTIONAL_BAD_ACCESS;
+  }
+  WISE_ENUM_CONSTEXPR_14 const T &value() const & {
+    if (m_active)
+      return m_t;
+    else
+      WISE_ENUM_OPTIONAL_BAD_ACCESS;
+  }
+
+  WISE_ENUM_CONSTEXPR_14 T &&value() && {
+    if (m_active)
+      return m_t;
+    else
+      WISE_ENUM_OPTIONAL_BAD_ACCESS;
+  }
+  WISE_ENUM_CONSTEXPR_14 const T &&value() const && {
+    if (m_active)
+      return m_t;
+    else
+      WISE_ENUM_OPTIONAL_BAD_ACCESS;
+  }
+
+  template <class U>
+  WISE_ENUM_CONSTEXPR_14 T value_or(U &&u) {
+    if (m_active)
+      return m_t;
+    else
+      return std::forward<U>(u);
+  }
+
+  void reset() noexcept { m_active = false; }
+
+  optional(const optional &other) = default;
+  optional(optional &&other) = default;
+  optional &operator=(const optional &other) = default;
+  optional &operator=(optional &&other) = default;
+
+private:
+  T m_t;
+  bool m_active = false;
+};
+} // namespace wise_enum
diff --git a/src/cpp-common/vendor/wise-enum/optional_common.h b/src/cpp-common/vendor/wise-enum/optional_common.h
new file mode 100644 (file)
index 0000000..c8f7b4f
--- /dev/null
@@ -0,0 +1,23 @@
+#pragma once
+
+#ifdef WISE_ENUM_NO_EXCEPT
+
+#include <cstdlib>
+#define WISE_ENUM_OPTIONAL_BAD_ACCESS std::abort()
+
+#else
+
+#include <stdexcept>
+
+namespace wise_enum {
+
+struct bad_optional_access : std::exception {
+  const char *what() const noexcept override {
+    return "Error, attempt to access valueless optional!";
+  }
+};
+} // namespace wise_enum
+
+#define WISE_ENUM_OPTIONAL_BAD_ACCESS throw bad_optional_access{}
+
+#endif
diff --git a/src/cpp-common/vendor/wise-enum/wise_enum.h b/src/cpp-common/vendor/wise-enum/wise_enum.h
new file mode 100644 (file)
index 0000000..db50027
--- /dev/null
@@ -0,0 +1,108 @@
+#pragma once
+
+#include "wise_enum_detail.h"
+#include "wise_enum_generated.h"
+
+#include <algorithm>
+#include <array>
+#include <type_traits>
+#include <utility>
+
+/*
+ Macro interface
+
+ The first argument to any macro enum is either the name of the enum, or a
+ parenthesized pair, (name, storage). The first form results in unfixed
+ underlying type for the enum, while the second form defines it explicitly.
+ After that, the macros take a variadic enumerator list. Each entry in the list
+ can be either a legal identifier name, or it can be a parenthesized pair where
+ the first entry is the identifier and the second entry is the initializer.
+
+ Usage notes:
+   - A semi-colon after the macro invocation is not necessary, and your
+     compiler may warn for it
+*/
+
+// Declare an enum at namespace scope
+#define WISE_ENUM(name, ...) WISE_ENUM_IMPL(enum, name, , __VA_ARGS__)
+
+// Declare an enum class at namespace scope
+#define WISE_ENUM_CLASS(name, ...)                                             \
+  WISE_ENUM_IMPL(enum class, name, , __VA_ARGS__)
+
+// Declare an enum at class scope
+#define WISE_ENUM_MEMBER(name, ...)                                            \
+  WISE_ENUM_IMPL(enum, name, friend, __VA_ARGS__)
+
+// Declare an enum class at class scope
+#define WISE_ENUM_CLASS_MEMBER(name, ...)                                      \
+  WISE_ENUM_IMPL(enum class, name, friend, __VA_ARGS__)
+
+/*
+ Adapt an existing enum into the wise enum API. This macro must be used at
+ global scope. The first argument must be the name of the enum (qualified),
+ followed by all the enumerators of the enum.
+*/
+#define WISE_ENUM_ADAPT(name, ...) WISE_ENUM_IMPL_ADAPT(name, __VA_ARGS__)
+
+namespace wise_enum {
+
+// Returns the string representation of an enumerator
+template <class T>
+constexpr string_type to_string(T t) {
+  return wise_enum_detail_to_string(t, detail::Tag<T>{});
+}
+
+// Enumerators trait class. Each value is also available as a template variable
+// for C++14 and on
+template <class T>
+struct enumerators {
+  // For a given wise enum type, this variable allows iteration over enumerators
+  // and their string names in the declared order. Each iterated object is a
+  // struct with members { T value; string_type name; }
+  static constexpr decltype(wise_enum_detail_array(detail::Tag<T>{})) range =
+      wise_enum_detail_array(detail::Tag<T>{});
+
+  // This variable is equal to the number of enumerators for the wise enum type.
+  static constexpr std::size_t size = range.size();
+};
+
+template <class T>
+constexpr decltype(
+    wise_enum_detail_array(detail::Tag<T>{})) enumerators<T>::range;
+
+template <class T>
+constexpr std::size_t enumerators<T>::size;
+
+#if __cplusplus >= 201402
+template <class T>
+constexpr auto &range = enumerators<T>::range;
+
+template <class T>
+constexpr std::size_t size = enumerators<T>::size;
+#endif
+
+// A type trait; this allows checking if a type is a wise_enum in generic code
+template <class T>
+using is_wise_enum = detail::is_wise_enum<T>;
+
+#if __cplusplus >= 201402
+template <class T>
+static constexpr bool is_wise_enum_v = is_wise_enum<T>::value;
+#endif
+
+// Converts a string literal into a wise enum. Returns an optional<T>; if no
+// enumerator has name matching the string, the optional is returned empty.
+template <class T>
+WISE_ENUM_CONSTEXPR_14 optional_type<T> from_string(string_type s) {
+  auto it =
+      std::find_if(enumerators<T>::range.begin(), enumerators<T>::range.end(),
+                   [=](const detail::value_and_name<T> &x) {
+                     return ::wise_enum::detail::compare(x.name, s);
+                   });
+  if (it == enumerators<T>::range.end())
+    return {};
+
+  return it->value;
+}
+} // namespace wise_enum
diff --git a/src/cpp-common/vendor/wise-enum/wise_enum_detail.h b/src/cpp-common/vendor/wise-enum/wise_enum_detail.h
new file mode 100644 (file)
index 0000000..1accbe7
--- /dev/null
@@ -0,0 +1,208 @@
+#pragma once
+
+#include <array>
+#include <type_traits>
+#include <utility>
+
+// optional type needed for interface
+#ifndef WISE_ENUM_OPTIONAL_TYPE
+#if __cplusplus >= 201703L
+#include <optional>
+namespace wise_enum {
+template <class T>
+using optional_type = std::optional<T>;
+}
+#else
+#include "optional.h"
+namespace wise_enum {
+template <class T>
+using optional_type = wise_enum::optional<T>;
+}
+#endif
+#else
+namespace wise_enum {
+template <class T>
+using optional_type = WISE_ENUM_OPTIONAL_TYPE<T>;
+}
+#endif
+
+// Choice of string_view if type defined, otherwise use string literal
+#ifndef WISE_ENUM_STRING_TYPE
+#if __cplusplus >= 201703L
+#include <string_view>
+namespace wise_enum {
+using string_type = std::string_view;
+}
+#else
+namespace wise_enum {
+using string_type = const char *;
+}
+#endif
+#else
+namespace wise_enum {
+using string_type = WISE_ENUM_STRING_TYPE;
+}
+#endif
+
+#if __cplusplus == 201103
+#define WISE_ENUM_CONSTEXPR_14
+#else
+#define WISE_ENUM_CONSTEXPR_14 constexpr
+#endif
+
+namespace wise_enum {
+namespace detail {
+
+template <class T>
+struct value_and_name {
+  T value;
+  string_type name;
+};
+
+template <class T>
+struct Tag {};
+
+constexpr void wise_enum_detail_array(...);
+
+template <class T>
+struct is_wise_enum
+    : std::integral_constant<
+          bool, !std::is_same<void, decltype(wise_enum_detail_array(
+                                        Tag<T>{}))>::value> {};
+
+WISE_ENUM_CONSTEXPR_14 inline int strcmp(const char *s1, const char *s2) {
+  while (*s1 && (*s1 == *s2))
+    s1++, s2++;
+  if (*s1 < *s2) {
+    return -1;
+  }
+  if (*s1 > *s2) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+WISE_ENUM_CONSTEXPR_14 inline bool compare(const char *s1, const char *s2) {
+  return strcmp(s1, s2) == 0;
+}
+
+template <class U, class = typename std::enable_if<
+                       !std::is_same<U, const char *>::value>::type>
+WISE_ENUM_CONSTEXPR_14 bool compare(U u1, U u2) {
+  return u1 == u2;
+}
+} // namespace detail
+} // namespace wise_enum
+
+
+// Needed for expansion of variadic macro arguments in MSVC
+// MSVC expands __VA_ARGS__ after passing it, while gcc expands it before
+#define WISE_ENUM_IMPL_EXPAND(x) x
+
+#define WISE_ENUM_IMPL_NARG(...)                                               \
+  WISE_ENUM_IMPL_NARG_(__VA_ARGS__, WISE_ENUM_IMPL_RSEQ_N())
+#define WISE_ENUM_IMPL_NARG_(...) WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_ARG_N(__VA_ARGS__))
+
+// ARG_N and RSEQ_N defined in wise_enum_generated.h
+
+// Building blocks; credit to:
+// https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
+
+#define WISE_ENUM_IMPL_COMMA() ,
+#define WISE_ENUM_IMPL_NOTHING()
+
+#define WISE_ENUM_IMPL_CAT(a, ...) WISE_ENUM_IMPL_PRIMITIVE_CAT(a, __VA_ARGS__)
+#define WISE_ENUM_IMPL_PRIMITIVE_CAT(a, ...) a##__VA_ARGS__
+
+#define WISE_ENUM_IMPL_XSTR(s) #s
+#define WISE_ENUM_IMPL_STR(s) WISE_ENUM_IMPL_XSTR(s)
+
+#define WISE_ENUM_IMPL_IIF(c) WISE_ENUM_IMPL_CAT(WISE_ENUM_IMPL_IIF_, c)
+#define WISE_ENUM_IMPL_IIF_0(t, f) f
+#define WISE_ENUM_IMPL_IIF_1(t, f) t
+
+#define WISE_ENUM_IMPL_CHECK_N(x, n, ...) n
+#define WISE_ENUM_IMPL_CHECK(...)                                              \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_CHECK_N(__VA_ARGS__, 0, ))
+#define WISE_ENUM_IMPL_PROBE(x) x, 1,
+
+#define WISE_ENUM_IMPL_IS_PAREN(x)                                             \
+  WISE_ENUM_IMPL_CHECK(WISE_ENUM_IMPL_IS_PAREN_PROBE x)
+#define WISE_ENUM_IMPL_IS_PAREN_PROBE(...) WISE_ENUM_IMPL_PROBE(~)
+
+#define WISE_ENUM_IMPL_FIRST_ARG(x, ...) x
+
+#define WISE_ENUM_IMPL_ONLY_OR_FIRST(x)                                        \
+  WISE_ENUM_IMPL_IIF(WISE_ENUM_IMPL_IS_PAREN(x))                               \
+  (WISE_ENUM_IMPL_FIRST_ARG x, x)
+
+// Use building blocks to conditionally process enumerators; they can either be
+// just an identifier, or (identifier, value)
+#define WISE_ENUM_IMPL_ENUM_INIT_2(x, ...) x = __VA_ARGS__
+#define WISE_ENUM_IMPL_ENUM_INIT(name, x)                                      \
+  WISE_ENUM_IMPL_IIF(WISE_ENUM_IMPL_IS_PAREN(x))                               \
+  (WISE_ENUM_IMPL_ENUM_INIT_2 x, x)
+
+#define WISE_ENUM_IMPL_ENUM_STR(x)                                             \
+  WISE_ENUM_IMPL_STR(WISE_ENUM_IMPL_ONLY_OR_FIRST(x))
+
+#define WISE_ENUM_IMPL_DESC_PAIR(name, x)                                      \
+  { name::WISE_ENUM_IMPL_ONLY_OR_FIRST(x), WISE_ENUM_IMPL_ENUM_STR(x) }
+
+#define WISE_ENUM_IMPL_SWITCH_CASE(name, x)                                    \
+  case name::WISE_ENUM_IMPL_ONLY_OR_FIRST(x):                                  \
+    return WISE_ENUM_IMPL_ENUM_STR(x);
+
+#define WISE_ENUM_IMPL_STORAGE_2(x, y) y
+
+#define WISE_ENUM_IMPL_STORAGE(x)                                              \
+  WISE_ENUM_IMPL_IIF(WISE_ENUM_IMPL_IS_PAREN(x))                               \
+  ( : WISE_ENUM_IMPL_STORAGE_2 x, )
+
+#define WISE_ENUM_IMPL(type, name_storage, friendly, ...)                      \
+  WISE_ENUM_IMPL_2(type, WISE_ENUM_IMPL_ONLY_OR_FIRST(name_storage),           \
+                   WISE_ENUM_IMPL_STORAGE(name_storage), friendly,             \
+                   WISE_ENUM_IMPL_NARG(__VA_ARGS__), __VA_ARGS__)
+
+#define WISE_ENUM_IMPL_2(type, name, storage, friendly, num_enums, ...)        \
+  WISE_ENUM_IMPL_3(type, name, storage, friendly, num_enums,                   \
+                   WISE_ENUM_IMPL_CAT(WISE_ENUM_IMPL_LOOP_, num_enums),        \
+                   __VA_ARGS__)
+
+
+#define WISE_ENUM_IMPL_3(type, name, storage, friendly, num_enums, loop, ...)  \
+  type name storage{                                                           \
+      WISE_ENUM_IMPL_EXPAND(loop(WISE_ENUM_IMPL_ENUM_INIT, _,                  \
+                             WISE_ENUM_IMPL_COMMA, __VA_ARGS__))};             \
+  WISE_ENUM_IMPL_ADAPT_3(name, friendly, num_enums, loop, __VA_ARGS__)
+
+#define WISE_ENUM_IMPL_ADAPT(name, ...)                                        \
+  namespace wise_enum {                                                        \
+  namespace detail {                                                           \
+  WISE_ENUM_IMPL_ADAPT_2(name, WISE_ENUM_IMPL_NARG(__VA_ARGS__), __VA_ARGS__)  \
+  }                                                                            \
+  }
+
+#define WISE_ENUM_IMPL_ADAPT_2(name, num_enums, ...)                           \
+  WISE_ENUM_IMPL_ADAPT_3(name, , num_enums,                                    \
+                         WISE_ENUM_IMPL_CAT(WISE_ENUM_IMPL_LOOP_, num_enums),  \
+                         __VA_ARGS__)
+
+#define WISE_ENUM_IMPL_ADAPT_3(name, friendly, num_enums, loop, ...)           \
+  friendly constexpr std::array<::wise_enum::detail::value_and_name<name>,     \
+                                num_enums>                                     \
+  wise_enum_detail_array(::wise_enum::detail::Tag<name>) {                     \
+    return {{WISE_ENUM_IMPL_EXPAND(loop(WISE_ENUM_IMPL_DESC_PAIR, name,        \
+                  WISE_ENUM_IMPL_COMMA, __VA_ARGS__))}};                       \
+  }                                                                            \
+                                                                               \
+  template <class T>                                                           \
+  friendly WISE_ENUM_CONSTEXPR_14 ::wise_enum::string_type                     \
+  wise_enum_detail_to_string(T e, ::wise_enum::detail::Tag<name>) {            \
+    switch (e) {                                                               \
+      WISE_ENUM_IMPL_EXPAND(loop(WISE_ENUM_IMPL_SWITCH_CASE, name,             \
+           WISE_ENUM_IMPL_NOTHING, __VA_ARGS__))                               \
+    }                                                                          \
+    return {};                                                                 \
+  }
diff --git a/src/cpp-common/vendor/wise-enum/wise_enum_generated.h b/src/cpp-common/vendor/wise-enum/wise_enum_generated.h
new file mode 100644 (file)
index 0000000..f83c8ae
--- /dev/null
@@ -0,0 +1,406 @@
+#define WISE_ENUM_IMPL_ARG_N(                                                           \
+    _1, _2, _3, _4, _5, _6, _7, _8, _9, _10,                                            \
+    _11, _12, _13, _14, _15, _16, _17, _18, _19, _20,                                   \
+    _21, _22, _23, _24, _25, _26, _27, _28, _29, _30,                                   \
+    _31, _32, _33, _34, _35, _36, _37, _38, _39, _40,                                   \
+    _41, _42, _43, _44, _45, _46, _47, _48, _49, _50,                                   \
+    _51, _52, _53, _54, _55, _56, _57, _58, _59, _60,                                   \
+    _61, _62, _63, _64, _65, _66, _67, _68, _69, _70,                                   \
+    _71, _72, _73, _74, _75, _76, _77, _78, _79, _80,                                   \
+    _81, _82, _83, _84, _85, _86, _87, _88, _89, _90,                                   \
+    _91, _92, _93, _94, _95, _96, _97, _98, _99, _100,                                  \
+    _101, _102, _103, _104, _105, _106, _107, _108, _109, _110,                         \
+    _111, _112, _113, _114, _115, _116, _117, _118, _119, _120,                         \
+    _121, _122, _123, _124, _125, N, ...                                                \
+  )                                                                                     \
+  N                                                                                     \
+
+#define WISE_ENUM_IMPL_RSEQ_N()                                                         \
+  125, 124, 123, 122, 121, 120, 119, 118, 117, 116,                                     \
+  115, 114, 113, 112, 111, 110, 109, 108, 107, 106,                                     \
+  105, 104, 103, 102, 101, 100, 99, 98, 97, 96,                                         \
+  95, 94, 93, 92, 91, 90, 89, 88, 87, 86,                                               \
+  85, 84, 83, 82, 81, 80, 79, 78, 77, 76,                                               \
+  75, 74, 73, 72, 71, 70, 69, 68, 67, 66,                                               \
+  65, 64, 63, 62, 61, 60, 59, 58, 57, 56,                                               \
+  55, 54, 53, 52, 51, 50, 49, 48, 47, 46,                                               \
+  45, 44, 43, 42, 41, 40, 39, 38, 37, 36,                                               \
+  35, 34, 33, 32, 31, 30, 29, 28, 27, 26,                                               \
+  25, 24, 23, 22, 21, 20, 19, 18, 17, 16,                                               \
+  15, 14, 13, 12, 11, 10, 9, 8, 7, 6,                                                   \
+  5, 4, 3, 2, 1, 0                                                                      \
+
+#define WISE_ENUM_IMPL_LOOP_1(M, C, D, x) M(C, x)
+
+#define WISE_ENUM_IMPL_LOOP_2(M, C, D, x, ...) M(C, x) D()                              \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_1(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_3(M, C, D, x, ...) M(C, x) D()                              \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_2(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_4(M, C, D, x, ...) M(C, x) D()                              \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_3(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_5(M, C, D, x, ...) M(C, x) D()                              \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_4(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_6(M, C, D, x, ...) M(C, x) D()                              \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_5(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_7(M, C, D, x, ...) M(C, x) D()                              \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_6(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_8(M, C, D, x, ...) M(C, x) D()                              \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_7(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_9(M, C, D, x, ...) M(C, x) D()                              \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_8(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_10(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_9(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_11(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_10(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_12(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_11(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_13(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_12(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_14(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_13(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_15(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_14(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_16(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_15(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_17(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_16(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_18(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_17(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_19(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_18(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_20(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_19(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_21(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_20(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_22(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_21(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_23(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_22(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_24(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_23(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_25(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_24(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_26(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_25(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_27(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_26(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_28(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_27(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_29(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_28(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_30(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_29(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_31(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_30(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_32(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_31(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_33(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_32(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_34(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_33(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_35(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_34(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_36(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_35(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_37(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_36(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_38(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_37(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_39(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_38(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_40(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_39(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_41(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_40(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_42(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_41(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_43(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_42(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_44(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_43(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_45(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_44(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_46(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_45(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_47(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_46(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_48(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_47(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_49(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_48(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_50(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_49(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_51(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_50(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_52(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_51(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_53(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_52(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_54(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_53(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_55(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_54(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_56(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_55(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_57(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_56(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_58(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_57(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_59(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_58(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_60(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_59(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_61(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_60(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_62(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_61(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_63(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_62(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_64(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_63(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_65(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_64(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_66(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_65(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_67(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_66(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_68(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_67(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_69(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_68(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_70(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_69(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_71(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_70(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_72(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_71(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_73(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_72(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_74(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_73(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_75(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_74(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_76(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_75(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_77(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_76(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_78(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_77(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_79(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_78(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_80(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_79(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_81(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_80(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_82(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_81(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_83(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_82(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_84(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_83(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_85(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_84(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_86(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_85(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_87(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_86(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_88(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_87(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_89(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_88(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_90(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_89(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_91(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_90(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_92(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_91(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_93(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_92(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_94(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_93(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_95(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_94(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_96(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_95(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_97(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_96(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_98(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_97(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_99(M, C, D, x, ...) M(C, x) D()                             \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_98(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_100(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_99(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_101(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_100(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_102(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_101(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_103(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_102(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_104(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_103(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_105(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_104(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_106(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_105(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_107(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_106(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_108(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_107(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_109(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_108(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_110(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_109(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_111(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_110(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_112(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_111(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_113(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_112(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_114(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_113(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_115(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_114(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_116(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_115(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_117(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_116(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_118(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_117(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_119(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_118(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_120(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_119(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_121(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_120(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_122(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_121(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_123(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_122(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_124(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_123(M, C, D, __VA_ARGS__))
+
+#define WISE_ENUM_IMPL_LOOP_125(M, C, D, x, ...) M(C, x) D()                            \
+  WISE_ENUM_IMPL_EXPAND(WISE_ENUM_IMPL_LOOP_124(M, C, D, __VA_ARGS__))
+
diff --git a/src/cpp-tester/Makefile.am b/src/cpp-tester/Makefile.am
deleted file mode 100644 (file)
index 18a254d..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_PROGRAMS = cpp-tester
-
-cpp_tester_SOURCES = cpp-tester.cpp
-cpp_tester_LDADD = \
-       $(top_builddir)/src/lib/libbabeltrace2.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la
diff --git a/src/cpp-tester/cpp-tester.cpp b/src/cpp-tester/cpp-tester.cpp
deleted file mode 100644 (file)
index 8e46fe4..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <iostream>
-#include <utility>
-
-#include "cpp-common/bt2/value.hpp"
-
-void f(bt2::BoolValue::Shared lel)
-{
-}
-
-int main()
-{
-    f(bt2::NullValue {}.shared());
-}
diff --git a/src/ctf-writer/Makefile.am b/src/ctf-writer/Makefile.am
deleted file mode 100644 (file)
index 2e979b0..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-lib_LTLIBRARIES = libbabeltrace2-ctf-writer.la
-
-libbabeltrace2_ctf_writer_la_SOURCES = \
-       assert-pre.h \
-       attributes.c \
-       attributes.h \
-       clock.c \
-       clock-class.c \
-       clock-class.h \
-       clock.h \
-       event.c \
-       event-class.c \
-       event-class.h \
-       event.h \
-       field-path.c \
-       field-path.h \
-       fields.c \
-       fields.h \
-       field-types.c \
-       field-types.h \
-       field-wrapper.c \
-       field-wrapper.h \
-       functor.c \
-       functor.h \
-       logging.c \
-       logging.h \
-       object.c \
-       object.h \
-       object-pool.c \
-       object-pool.h \
-       resolve.c \
-       resolve.h \
-       stream.c \
-       stream-class.c \
-       stream-class.h \
-       stream.h \
-       trace.c \
-       trace.h \
-       utils.c \
-       utils.h \
-       validation.c \
-       validation.h \
-       values.c \
-       values.h \
-       visitor.c \
-       visitor.h \
-       writer.c \
-       writer.h
-
-libbabeltrace2_ctf_writer_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LT_NO_UNDEFINED) \
-       -version-info $(BABELTRACE_LIBRARY_VERSION)
-
-libbabeltrace2_ctf_writer_la_LIBADD = \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/ctfser/libbabeltrace2-ctfser.la \
-       $(top_builddir)/src/compat/libcompat.la
index 3d41037c6a6640a41a3b229c1516658726d9d318..38e3ac2c98644e96d875ed0efab7b728cf97eb53 100644 (file)
@@ -58,7 +58,7 @@
  */
 # define BT_CTF_ASSERT_PRE_MSG(_fmt, ...)                              \
        do {                                                            \
-               _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__,      \
+               bt_log_write_printf(__FILE__, __func__,                 \
                        __LINE__, BT_LOG_FATAL, BT_LOG_TAG, (_fmt),     \
                        ##__VA_ARGS__);                                 \
        } while (0)
index 3196be35f0471010ff8312f0f546c77caa26e226..27b689e2e508b9f64c1c57610800c27731e8a987 100644 (file)
@@ -23,7 +23,6 @@
 #define BT_CTF_ATTR_NAME_INDEX         0
 #define BT_CTF_ATTR_VALUE_INDEX                1
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_attributes_create(void)
 {
        struct bt_ctf_private_value *attr_obj;
@@ -54,20 +53,17 @@ struct bt_ctf_private_value *bt_ctf_attributes_create(void)
        return attr_obj;
 }
 
-BT_HIDDEN
 void bt_ctf_attributes_destroy(struct bt_ctf_private_value *attr_obj)
 {
        BT_LOGD("Destroying attributes object: addr=%p", attr_obj);
        bt_ctf_object_put_ref(attr_obj);
 }
 
-BT_HIDDEN
 int64_t bt_ctf_attributes_get_count(struct bt_ctf_private_value *attr_obj)
 {
        return bt_ctf_value_array_get_length(bt_ctf_private_value_as_value(attr_obj));
 }
 
-BT_HIDDEN
 const char *bt_ctf_attributes_get_field_name(struct bt_ctf_private_value *attr_obj,
                uint64_t index)
 {
@@ -112,7 +108,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value(struct bt_ctf_private_value *attr_obj,
                uint64_t index)
 {
@@ -203,7 +198,6 @@ error:
        return value_obj;
 }
 
-BT_HIDDEN
 int bt_ctf_attributes_set_field_value(struct bt_ctf_private_value *attr_obj,
                const char *name, struct bt_ctf_private_value *value_obj)
 {
@@ -256,7 +250,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value_by_name(
                struct bt_ctf_private_value *attr_obj, const char *name)
 {
@@ -288,7 +281,6 @@ end:
        return value_obj;
 }
 
-BT_HIDDEN
 int bt_ctf_attributes_freeze(struct bt_ctf_private_value *attr_obj)
 {
        uint64_t i;
index 6072fde70f3aeb118fc47a521a2da0b274823703..c139af34f01853e0174af8294fa181d86c97e8c9 100644 (file)
@@ -17,32 +17,24 @@ extern "C" {
 
 #include "values.h"
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_attributes_create(void);
 
-BT_HIDDEN
 void bt_ctf_attributes_destroy(struct bt_ctf_private_value *attr_obj);
 
-BT_HIDDEN
 int64_t bt_ctf_attributes_get_count(struct bt_ctf_private_value *attr_obj);
 
-BT_HIDDEN
 const char *bt_ctf_attributes_get_field_name(struct bt_ctf_private_value *attr_obj,
                uint64_t index);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value(struct bt_ctf_private_value *attr_obj,
                uint64_t index);
 
-BT_HIDDEN
 int bt_ctf_attributes_set_field_value(struct bt_ctf_private_value *attr_obj,
                const char *name, struct bt_ctf_private_value *value_obj);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value_by_name(
                struct bt_ctf_private_value *attr_obj, const char *name);
 
-BT_HIDDEN
 int bt_ctf_attributes_freeze(struct bt_ctf_private_value *attr_obj);
 
 #ifdef __cplusplus
index 8f185cfa5ae7b484d5f6dc8d5f319fa4465511b5..94a5c8df77c9adc7a5d720eb22816fd68378f2fb 100644 (file)
@@ -13,7 +13,6 @@
 #include <babeltrace2-ctf-writer/utils.h>
 #include <babeltrace2-ctf-writer/object.h>
 #include "compat/compiler.h"
-#include <babeltrace2/types.h>
 #include "compat/string.h"
 #include <stdbool.h>
 #include <inttypes.h>
 static
 void bt_ctf_clock_class_destroy(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_clock_class_is_valid(struct bt_ctf_clock_class *clock_class)
 {
        return clock_class && clock_class->name;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_set_name(struct bt_ctf_clock_class *clock_class,
                const char *name)
 {
@@ -95,7 +92,6 @@ end:
        return is_valid;
 }
 
-BT_HIDDEN
 struct bt_ctf_clock_class *bt_ctf_clock_class_create(const char *name,
                uint64_t freq)
 {
@@ -136,7 +132,6 @@ error:
        return clock_class;
 }
 
-BT_HIDDEN
 const char *bt_ctf_clock_class_get_name(struct bt_ctf_clock_class *clock_class)
 {
        const char *ret = NULL;
@@ -154,7 +149,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 const char *bt_ctf_clock_class_get_description(
                struct bt_ctf_clock_class *clock_class)
 {
@@ -172,7 +166,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_set_description(struct bt_ctf_clock_class *clock_class,
                const char *desc)
 {
@@ -203,7 +196,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 uint64_t bt_ctf_clock_class_get_frequency(
                struct bt_ctf_clock_class *clock_class)
 {
@@ -219,7 +211,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_set_frequency(struct bt_ctf_clock_class *clock_class,
                uint64_t freq)
 {
@@ -253,7 +244,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 uint64_t bt_ctf_clock_class_get_precision(struct bt_ctf_clock_class *clock_class)
 {
        uint64_t ret = -1ULL;
@@ -268,7 +258,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_set_precision(struct bt_ctf_clock_class *clock_class,
                uint64_t precision)
 {
@@ -298,7 +287,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_get_offset_s(struct bt_ctf_clock_class *clock_class,
                int64_t *offset_s)
 {
@@ -318,7 +306,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_set_offset_s(struct bt_ctf_clock_class *clock_class,
                int64_t offset_s)
 {
@@ -346,7 +333,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_get_offset_cycles(struct bt_ctf_clock_class *clock_class,
                int64_t *offset)
 {
@@ -366,7 +352,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_set_offset_cycles(struct bt_ctf_clock_class *clock_class,
                int64_t offset)
 {
@@ -392,7 +377,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_clock_class_is_absolute(struct bt_ctf_clock_class *clock_class)
 {
        int ret = -1;
@@ -407,7 +391,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_set_is_absolute(struct bt_ctf_clock_class *clock_class,
                bt_ctf_bool is_absolute)
 {
@@ -434,7 +417,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 const uint8_t *bt_ctf_clock_class_get_uuid(
                struct bt_ctf_clock_class *clock_class)
 {
@@ -458,7 +440,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_set_uuid(struct bt_ctf_clock_class *clock_class,
                const uint8_t *uuid)
 {
@@ -489,7 +470,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctf_clock_class_freeze(struct bt_ctf_clock_class *clock_class)
 {
        if (!clock_class || clock_class->frozen) {
@@ -521,7 +501,6 @@ void bt_ctf_clock_class_destroy(struct bt_ctf_object *obj)
        g_free(clock_class);
 }
 
-BT_HIDDEN
 int bt_ctf_clock_class_compare(struct bt_ctf_clock_class *clock_class_a,
                struct bt_ctf_clock_class *clock_class_b)
 {
index 6948ed35c7088429d741d8802b2a346a61f03c0f..adbba43a39e68b5795464cac6bb1a0bb915e8c0a 100644 (file)
@@ -35,66 +35,46 @@ struct bt_ctf_clock_class {
        int frozen;
 };
 
-BT_HIDDEN
 void bt_ctf_clock_class_freeze(struct bt_ctf_clock_class *clock_class);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_clock_class_is_valid(struct bt_ctf_clock_class *clock_class);
 
-BT_HIDDEN
 int bt_ctf_clock_class_compare(struct bt_ctf_clock_class *clock_class_a,
                struct bt_ctf_clock_class *clock_class_b);
 
-BT_HIDDEN
 struct bt_ctf_clock_class *bt_ctf_clock_class_create(const char *name,
                uint64_t freq);
-BT_HIDDEN
 const char *bt_ctf_clock_class_get_name(
                struct bt_ctf_clock_class *clock_class);
-BT_HIDDEN
 int bt_ctf_clock_class_set_name(struct bt_ctf_clock_class *clock_class,
                const char *name);
-BT_HIDDEN
 const char *bt_ctf_clock_class_get_description(
                struct bt_ctf_clock_class *clock_class);
-BT_HIDDEN
 int bt_ctf_clock_class_set_description(
                struct bt_ctf_clock_class *clock_class,
                const char *desc);
-BT_HIDDEN
 uint64_t bt_ctf_clock_class_get_frequency(
                struct bt_ctf_clock_class *clock_class);
-BT_HIDDEN
 int bt_ctf_clock_class_set_frequency(
                struct bt_ctf_clock_class *clock_class, uint64_t freq);
-BT_HIDDEN
 uint64_t bt_ctf_clock_class_get_precision(
                struct bt_ctf_clock_class *clock_class);
-BT_HIDDEN
 int bt_ctf_clock_class_set_precision(
                struct bt_ctf_clock_class *clock_class, uint64_t precision);
-BT_HIDDEN
 int bt_ctf_clock_class_get_offset_s(
                struct bt_ctf_clock_class *clock_class, int64_t *seconds);
-BT_HIDDEN
 int bt_ctf_clock_class_set_offset_s(
                struct bt_ctf_clock_class *clock_class, int64_t seconds);
-BT_HIDDEN
 int bt_ctf_clock_class_get_offset_cycles(
                struct bt_ctf_clock_class *clock_class, int64_t *cycles);
-BT_HIDDEN
 int bt_ctf_clock_class_set_offset_cycles(
                struct bt_ctf_clock_class *clock_class, int64_t cycles);
-BT_HIDDEN
 bt_ctf_bool bt_ctf_clock_class_is_absolute(
                struct bt_ctf_clock_class *clock_class);
-BT_HIDDEN
 int bt_ctf_clock_class_set_is_absolute(
                struct bt_ctf_clock_class *clock_class, bt_ctf_bool is_absolute);
-BT_HIDDEN
 const uint8_t *bt_ctf_clock_class_get_uuid(
                struct bt_ctf_clock_class *clock_class);
-BT_HIDDEN
 int bt_ctf_clock_class_set_uuid(struct bt_ctf_clock_class *clock_class,
                const uint8_t *uuid);
 
index 463fcbd05f0984353e8b90fb516fe7435ba0eaa5..5d7211922e8f214f2e09446c7282054e0f4ec654 100644 (file)
@@ -25,6 +25,7 @@
 static
 void bt_ctf_clock_destroy(struct bt_ctf_object *obj);
 
+BT_EXPORT
 struct bt_ctf_clock *bt_ctf_clock_create(const char *name)
 {
        int ret;
@@ -58,18 +59,21 @@ error:
        return clock;
 }
 
+BT_EXPORT
 const char *bt_ctf_clock_get_name(struct bt_ctf_clock *clock)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
        return bt_ctf_clock_class_get_name(clock->clock_class);
 }
 
+BT_EXPORT
 const char *bt_ctf_clock_get_description(struct bt_ctf_clock *clock)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
        return bt_ctf_clock_class_get_description(clock->clock_class);
 }
 
+BT_EXPORT
 int bt_ctf_clock_set_description(struct bt_ctf_clock *clock, const char *desc)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
@@ -77,12 +81,14 @@ int bt_ctf_clock_set_description(struct bt_ctf_clock *clock, const char *desc)
                desc);
 }
 
+BT_EXPORT
 uint64_t bt_ctf_clock_get_frequency(struct bt_ctf_clock *clock)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
        return bt_ctf_clock_class_get_frequency(clock->clock_class);
 }
 
+BT_EXPORT
 int bt_ctf_clock_set_frequency(struct bt_ctf_clock *clock, uint64_t freq)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
@@ -90,12 +96,14 @@ int bt_ctf_clock_set_frequency(struct bt_ctf_clock *clock, uint64_t freq)
                freq);
 }
 
+BT_EXPORT
 uint64_t bt_ctf_clock_get_precision(struct bt_ctf_clock *clock)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
        return bt_ctf_clock_class_get_precision(clock->clock_class);
 }
 
+BT_EXPORT
 int bt_ctf_clock_set_precision(struct bt_ctf_clock *clock, uint64_t precision)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
@@ -103,6 +111,7 @@ int bt_ctf_clock_set_precision(struct bt_ctf_clock *clock, uint64_t precision)
                precision);
 }
 
+BT_EXPORT
 int bt_ctf_clock_get_offset_s(struct bt_ctf_clock *clock, int64_t *offset_s)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
@@ -110,6 +119,7 @@ int bt_ctf_clock_get_offset_s(struct bt_ctf_clock *clock, int64_t *offset_s)
                offset_s);
 }
 
+BT_EXPORT
 int bt_ctf_clock_set_offset_s(struct bt_ctf_clock *clock, int64_t offset_s)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
@@ -117,6 +127,7 @@ int bt_ctf_clock_set_offset_s(struct bt_ctf_clock *clock, int64_t offset_s)
                offset_s);
 }
 
+BT_EXPORT
 int bt_ctf_clock_get_offset(struct bt_ctf_clock *clock, int64_t *offset)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
@@ -124,6 +135,7 @@ int bt_ctf_clock_get_offset(struct bt_ctf_clock *clock, int64_t *offset)
                offset);
 }
 
+BT_EXPORT
 int bt_ctf_clock_set_offset(struct bt_ctf_clock *clock, int64_t offset)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
@@ -131,12 +143,14 @@ int bt_ctf_clock_set_offset(struct bt_ctf_clock *clock, int64_t offset)
                offset);
 }
 
+BT_EXPORT
 int bt_ctf_clock_get_is_absolute(struct bt_ctf_clock *clock)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
        return bt_ctf_clock_class_is_absolute(clock->clock_class);
 }
 
+BT_EXPORT
 int bt_ctf_clock_set_is_absolute(struct bt_ctf_clock *clock, int is_absolute)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
@@ -144,18 +158,21 @@ int bt_ctf_clock_set_is_absolute(struct bt_ctf_clock *clock, int is_absolute)
                is_absolute);
 }
 
+BT_EXPORT
 const uint8_t *bt_ctf_clock_get_uuid(struct bt_ctf_clock *clock)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
        return bt_ctf_clock_class_get_uuid(clock->clock_class);
 }
 
+BT_EXPORT
 int bt_ctf_clock_set_uuid(struct bt_ctf_clock *clock, const uint8_t *uuid)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
        return bt_ctf_clock_class_set_uuid(clock->clock_class, uuid);
 }
 
+BT_EXPORT
 int bt_ctf_clock_set_time(struct bt_ctf_clock *clock, int64_t time)
 {
        int64_t value;
@@ -180,7 +197,6 @@ int bt_ctf_clock_set_time(struct bt_ctf_clock *clock, int64_t time)
        return 0;
 }
 
-BT_HIDDEN
 int bt_ctf_clock_get_value(struct bt_ctf_clock *clock, uint64_t *value)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock");
@@ -199,7 +215,6 @@ void bt_ctf_clock_destroy(struct bt_ctf_object *obj)
        g_free(clock);
 }
 
-BT_HIDDEN
 void bt_ctf_clock_class_serialize(struct bt_ctf_clock_class *clock_class,
                struct metadata_context *context)
 {
index 68e464f214b7e420bf1cc03f7849b09606547e2e..cad188e7cd7bd3cce35c98897adeaadcb4912c68 100644 (file)
@@ -23,10 +23,8 @@ struct bt_ctf_clock {
 
 struct metadata_context;
 
-BT_HIDDEN
 int bt_ctf_clock_get_value(struct bt_ctf_clock *clock, uint64_t *value);
 
-BT_HIDDEN
 void bt_ctf_clock_class_serialize(struct bt_ctf_clock_class *clock_class,
                struct metadata_context *context);
 
index 1f49549f1fb804df892317b58081d20b4da93d49..22053f296a283fb165e8c0a2874b12d786987fb1 100644 (file)
@@ -17,7 +17,6 @@
 #include <babeltrace2-ctf-writer/object.h>
 #include <babeltrace2-ctf-writer/stream-class.h>
 #include <babeltrace2-ctf-writer/utils.h>
-#include <babeltrace2/types.h>
 
 #include "common/assert.h"
 #include "compat/compiler.h"
@@ -36,7 +35,6 @@
 #include "values.h"
 #include "writer.h"
 
-BT_HIDDEN
 void bt_ctf_event_class_common_finalize(struct bt_ctf_object *obj)
 {
        struct bt_ctf_event_class_common *event_class;
@@ -60,7 +58,6 @@ void bt_ctf_event_class_common_finalize(struct bt_ctf_object *obj)
        bt_ctf_object_put_ref(event_class->payload_field_type);
 }
 
-BT_HIDDEN
 int bt_ctf_event_class_common_initialize(struct bt_ctf_event_class_common *event_class,
                const char *name, bt_ctf_object_release_func release_func,
                bt_ctf_field_type_structure_create_func ft_struct_create_func)
@@ -99,7 +96,6 @@ error:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctf_event_class_common_freeze(struct bt_ctf_event_class_common *event_class)
 {
        BT_ASSERT_DBG(event_class);
@@ -118,7 +114,6 @@ void bt_ctf_event_class_common_freeze(struct bt_ctf_event_class_common *event_cl
        bt_ctf_field_type_common_freeze(event_class->payload_field_type);
 }
 
-BT_HIDDEN
 int bt_ctf_event_class_common_validate_single_clock_class(
                struct bt_ctf_event_class_common *event_class,
                struct bt_ctf_clock_class **expected_clock_class)
@@ -174,6 +169,7 @@ void bt_ctf_event_class_destroy(struct bt_ctf_object *obj)
        g_free(obj);
 }
 
+BT_EXPORT
 struct bt_ctf_event_class *bt_ctf_event_class_create(const char *name)
 {
        struct bt_ctf_event_class *ctf_event_class = NULL;
@@ -209,28 +205,33 @@ end:
        return ctf_event_class;
 }
 
+BT_EXPORT
 const char *bt_ctf_event_class_get_name(struct bt_ctf_event_class *event_class)
 {
        return bt_ctf_event_class_common_get_name(BT_CTF_TO_COMMON(event_class));
 }
 
+BT_EXPORT
 int64_t bt_ctf_event_class_get_id(struct bt_ctf_event_class *event_class)
 {
        return bt_ctf_event_class_common_get_id(BT_CTF_TO_COMMON(event_class));
 }
 
+BT_EXPORT
 int bt_ctf_event_class_set_id(struct bt_ctf_event_class *event_class,
                uint64_t id)
 {
        return bt_ctf_event_class_common_set_id(BT_CTF_TO_COMMON(event_class), id);
 }
 
+BT_EXPORT
 enum bt_ctf_event_class_log_level bt_ctf_event_class_get_log_level(
                struct bt_ctf_event_class *event_class)
 {
        return bt_ctf_event_class_common_get_log_level(BT_CTF_TO_COMMON(event_class));
 }
 
+BT_EXPORT
 int bt_ctf_event_class_set_log_level(struct bt_ctf_event_class *event_class,
                enum bt_ctf_event_class_log_level log_level)
 {
@@ -238,12 +239,14 @@ int bt_ctf_event_class_set_log_level(struct bt_ctf_event_class *event_class,
                log_level);
 }
 
+BT_EXPORT
 const char *bt_ctf_event_class_get_emf_uri(
                struct bt_ctf_event_class *event_class)
 {
        return bt_ctf_event_class_common_get_emf_uri(BT_CTF_TO_COMMON(event_class));
 }
 
+BT_EXPORT
 int bt_ctf_event_class_set_emf_uri(struct bt_ctf_event_class *event_class,
                const char *emf_uri)
 {
@@ -251,6 +254,7 @@ int bt_ctf_event_class_set_emf_uri(struct bt_ctf_event_class *event_class,
                emf_uri);
 }
 
+BT_EXPORT
 struct bt_ctf_stream_class *bt_ctf_event_class_get_stream_class(
                struct bt_ctf_event_class *event_class)
 {
@@ -259,6 +263,7 @@ struct bt_ctf_stream_class *bt_ctf_event_class_get_stream_class(
                BT_CTF_TO_COMMON(event_class)));
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_event_class_get_payload_field_type(
                struct bt_ctf_event_class *event_class)
 {
@@ -266,6 +271,7 @@ struct bt_ctf_field_type *bt_ctf_event_class_get_payload_field_type(
                BT_CTF_TO_COMMON(event_class)));
 }
 
+BT_EXPORT
 int bt_ctf_event_class_set_payload_field_type(
                struct bt_ctf_event_class *event_class,
                struct bt_ctf_field_type *field_type)
@@ -274,6 +280,7 @@ int bt_ctf_event_class_set_payload_field_type(
                BT_CTF_TO_COMMON(event_class), (void *) field_type);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_event_class_get_context_field_type(
                struct bt_ctf_event_class *event_class)
 {
@@ -281,6 +288,7 @@ struct bt_ctf_field_type *bt_ctf_event_class_get_context_field_type(
                BT_CTF_TO_COMMON(event_class)));
 }
 
+BT_EXPORT
 int bt_ctf_event_class_set_context_field_type(
                struct bt_ctf_event_class *event_class,
                struct bt_ctf_field_type *field_type)
@@ -289,6 +297,7 @@ int bt_ctf_event_class_set_context_field_type(
                BT_CTF_TO_COMMON(event_class), (void *) field_type);
 }
 
+BT_EXPORT
 int bt_ctf_event_class_add_field(struct bt_ctf_event_class *event_class,
                struct bt_ctf_field_type *type,
                const char *name)
@@ -346,6 +355,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 int64_t bt_ctf_event_class_get_payload_type_field_count(
                struct bt_ctf_event_class *event_class)
 {
@@ -375,6 +385,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 int bt_ctf_event_class_get_payload_type_field_by_index(
                struct bt_ctf_event_class *event_class,
                const char **field_name, struct bt_ctf_field_type **field_type,
@@ -408,6 +419,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *
 bt_ctf_event_class_get_payload_type_field_type_by_name(
                struct bt_ctf_event_class *event_class, const char *name)
@@ -451,7 +463,6 @@ end:
        return field_type;
 }
 
-BT_HIDDEN
 int bt_ctf_event_class_serialize(struct bt_ctf_event_class *event_class,
                struct metadata_context *context)
 {
@@ -529,6 +540,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_event_class_get_field_by_name(
                struct bt_ctf_event_class *event_class, const char *name)
 {
index cc2ec9ee25c2d108523ac9b35c099b84300ecaec..429888a735f4999c1b3d0a4d2a938378365f7e80 100644 (file)
@@ -41,10 +41,8 @@ struct bt_ctf_event_class_common {
        GString *emf_uri;
 };
 
-BT_HIDDEN
 void bt_ctf_event_class_common_freeze(struct bt_ctf_event_class_common *event_class);
 
-BT_HIDDEN
 void bt_ctf_event_class_common_set_native_byte_order(
                struct bt_ctf_event_class_common *event_class, int byte_order);
 
@@ -59,15 +57,12 @@ struct bt_ctf_stream_class_common *bt_ctf_event_class_common_borrow_stream_class
 typedef struct bt_ctf_field_type_common *
        (*bt_ctf_field_type_structure_create_func)(void);
 
-BT_HIDDEN
 int bt_ctf_event_class_common_initialize(struct bt_ctf_event_class_common *event_class,
                const char *name, bt_ctf_object_release_func release_func,
                bt_ctf_field_type_structure_create_func ft_struct_create_func);
 
-BT_HIDDEN
 void bt_ctf_event_class_common_finalize(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 int bt_ctf_event_class_common_validate_single_clock_class(
                struct bt_ctf_event_class_common *event_class,
                struct bt_ctf_clock_class **expected_clock_class);
index 0cd79c585a3fc2119ba625b5005d3ea4e3f663ec..3b67685623e53047a8436aa015966b22c1305ecb 100644 (file)
@@ -198,7 +198,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int _bt_ctf_event_common_validate(struct bt_ctf_event_common *event)
 {
        int ret = 0;
@@ -257,7 +256,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void _bt_ctf_event_common_set_is_frozen(struct bt_ctf_event_common *event,
                bool is_frozen)
 {
@@ -294,7 +292,6 @@ void _bt_ctf_event_common_set_is_frozen(struct bt_ctf_event_common *event,
        event->frozen = is_frozen;
 }
 
-BT_HIDDEN
 int bt_ctf_event_common_initialize(struct bt_ctf_event_common *event,
                struct bt_ctf_event_class_common *event_class,
                struct bt_ctf_clock_class *init_expected_clock_class,
@@ -522,7 +519,7 @@ int map_clock_classes_func(struct bt_ctf_stream_class_common *stream_class,
 
 static
 void destroy_event_header_field(struct bt_ctf_field_wrapper *field_wrapper,
-               struct bt_ctf_stream_class *stream_class)
+               struct bt_ctf_stream_class *stream_class __attribute__((unused)))
 {
        BT_ASSERT_DBG(field_wrapper);
        bt_ctf_object_put_ref(field_wrapper->field);
@@ -564,7 +561,7 @@ end:
 
 static
 void release_event_header_field(struct bt_ctf_field_wrapper *field_wrapper,
-               struct bt_ctf_event_common *event_common)
+               struct bt_ctf_event_common *event_common __attribute__((unused)))
 {
        BT_ASSERT_DBG(field_wrapper);
        BT_CTF_OBJECT_PUT_REF_AND_RESET(field_wrapper->field);
@@ -579,6 +576,7 @@ void bt_ctf_event_destroy(struct bt_ctf_object *obj)
        g_free(obj);
 }
 
+BT_EXPORT
 struct bt_ctf_event *bt_ctf_event_create(struct bt_ctf_event_class *event_class)
 {
        int ret;
@@ -625,6 +623,7 @@ end:
        return event;
 }
 
+BT_EXPORT
 struct bt_ctf_event_class *bt_ctf_event_get_class(struct bt_ctf_event *event)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(event, "Event");
@@ -639,12 +638,14 @@ struct bt_ctf_stream *bt_ctf_event_borrow_stream(struct bt_ctf_event *event)
                bt_ctf_object_borrow_parent(&BT_CTF_TO_COMMON(event)->base);
 }
 
+BT_EXPORT
 struct bt_ctf_stream *bt_ctf_event_get_stream(struct bt_ctf_event *event)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(event, "Event");
        return bt_ctf_object_get_ref(bt_ctf_event_borrow_stream(event));
 }
 
+BT_EXPORT
 int bt_ctf_event_set_payload(struct bt_ctf_event *event, const char *name,
                struct bt_ctf_field *field)
 {
@@ -655,6 +656,7 @@ int bt_ctf_event_set_payload(struct bt_ctf_event *event, const char *name,
                (void *) event->common.payload_field, name, field);
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_event_get_payload(struct bt_ctf_event *event,
                const char *name)
 {
@@ -673,22 +675,26 @@ struct bt_ctf_field *bt_ctf_event_get_payload(struct bt_ctf_event *event,
        return field;
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_event_get_payload_field(
                struct bt_ctf_event *event)
 {
        return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_payload(BT_CTF_TO_COMMON(event)));
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_event_get_header(struct bt_ctf_event *event)
 {
        return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_header(BT_CTF_TO_COMMON(event)));
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_event_get_context(struct bt_ctf_event *event)
 {
        return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_context(BT_CTF_TO_COMMON(event)));
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_event_get_stream_event_context(
                struct bt_ctf_event *event)
 {
@@ -696,7 +702,6 @@ struct bt_ctf_field *bt_ctf_event_get_stream_event_context(
                BT_CTF_TO_COMMON(event)));
 }
 
-BT_HIDDEN
 int bt_ctf_event_serialize(struct bt_ctf_event *event,
                struct bt_ctfser *ctfser,
                enum bt_ctf_byte_order native_byte_order)
@@ -742,6 +747,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 int bt_ctf_event_set_header(struct bt_ctf_event *event,
                struct bt_ctf_field *header)
 {
@@ -814,6 +820,7 @@ int bt_ctf_event_common_set_payload(struct bt_ctf_event *event,
        return 0;
 }
 
+BT_EXPORT
 int bt_ctf_event_set_context(struct bt_ctf_event *event,
                struct bt_ctf_field *context)
 {
@@ -847,6 +854,7 @@ int bt_ctf_event_set_context(struct bt_ctf_event *event,
        return 0;
 }
 
+BT_EXPORT
 int bt_ctf_event_set_stream_event_context(struct bt_ctf_event *event,
                struct bt_ctf_field *stream_event_context)
 {
index dc1187cd564706d35b4815374c5e36f335ba7169..52e4c5200a7e745fd1bd7fdaff51d9c11671b1fd 100644 (file)
@@ -44,10 +44,8 @@ struct bt_ctf_event_common {
        int frozen;
 };
 
-BT_HIDDEN
 int _bt_ctf_event_common_validate(struct bt_ctf_event_common *event);
 
-BT_HIDDEN
 void _bt_ctf_event_common_set_is_frozen(struct bt_ctf_event_common *event,
                bool is_frozen);
 
@@ -75,7 +73,6 @@ typedef void (*release_field_func_type)(void *);
 typedef void *(*create_header_field_func_type)(void *, void *);
 typedef void (*release_header_field_func_type)(void *, void *);
 
-BT_HIDDEN
 int bt_ctf_event_common_initialize(struct bt_ctf_event_common *event,
                struct bt_ctf_event_class_common *event_class,
                struct bt_ctf_clock_class *init_expected_clock_class,
@@ -90,7 +87,6 @@ int bt_ctf_event_common_initialize(struct bt_ctf_event_common *event,
                create_header_field_func_type create_header_field_func,
                release_header_field_func_type release_header_field_func);
 
-BT_HIDDEN
 int bt_ctf_event_common_set_payload(struct bt_ctf_event *event,
                struct bt_ctf_field *payload);
 
@@ -238,11 +234,9 @@ struct bt_ctf_event_class {
        struct bt_ctf_event_class_common common;
 };
 
-BT_HIDDEN
 int bt_ctf_event_class_serialize(struct bt_ctf_event_class *event_class,
                struct metadata_context *context);
 
-BT_HIDDEN
 int bt_ctf_event_serialize(struct bt_ctf_event *event,
                struct bt_ctfser *pos,
                enum bt_ctf_byte_order native_byte_order);
index 6372959a0302054c80dd1209eae4035a31ad1052..7689a6d8f842a9f7a9e51e808989a5e40dd3919d 100644 (file)
@@ -39,7 +39,6 @@ void field_path_destroy(struct bt_ctf_object *obj)
        g_free(field_path);
 }
 
-BT_HIDDEN
 struct bt_ctf_field_path *bt_ctf_field_path_create(void)
 {
        struct bt_ctf_field_path *field_path = NULL;
@@ -68,7 +67,6 @@ error:
        return NULL;
 }
 
-BT_HIDDEN
 void bt_ctf_field_path_clear(struct bt_ctf_field_path *field_path)
 {
        if (field_path->indexes->len > 0) {
@@ -77,7 +75,6 @@ void bt_ctf_field_path_clear(struct bt_ctf_field_path *field_path)
        }
 }
 
-BT_HIDDEN
 struct bt_ctf_field_path *bt_ctf_field_path_copy(
                struct bt_ctf_field_path *path)
 {
@@ -150,7 +147,7 @@ int bt_ctf_field_path_get_index(const struct bt_ctf_field_path *field_path,
                goto end;
        }
 
-       ret = g_array_index(field_path->indexes, int, index);
+       ret = bt_g_array_index(field_path->indexes, int, index);
 
 end:
        return ret;
index a346dc2bf5a5461e0a7023da9509b8230bfb6d36..c823f009467004d4ea96d7b6e93ab307679a8b09 100644 (file)
@@ -31,23 +31,20 @@ struct bt_ctf_field_path {
        GArray *indexes;
 };
 
-BT_HIDDEN
 struct bt_ctf_field_path *bt_ctf_field_path_create(void);
 
-BT_HIDDEN
 void bt_ctf_field_path_clear(struct bt_ctf_field_path *field_path);
 
-BT_HIDDEN
 struct bt_ctf_field_path *bt_ctf_field_path_copy(
                struct bt_ctf_field_path *path);
 
-BT_HIDDEN enum bt_ctf_scope bt_ctf_field_path_get_root_scope(
+enum bt_ctf_scope bt_ctf_field_path_get_root_scope(
                const struct bt_ctf_field_path *field_path);
 
-BT_HIDDEN int64_t bt_ctf_field_path_get_index_count(
+int64_t bt_ctf_field_path_get_index_count(
                const struct bt_ctf_field_path *field_path);
 
-BT_HIDDEN int bt_ctf_field_path_get_index(
+int bt_ctf_field_path_get_index(
                const struct bt_ctf_field_path *field_path, uint64_t index);
 
 #endif /* BABELTRACE_CTF_WRITER_FIELD_PATH_INTERNAL */
index b97a0ab74c4cbb33291dd7d139864d37bd73845d..a2986abf9c30545197bdd7030c7727814cc7758e 100644 (file)
@@ -36,7 +36,6 @@ void destroy_enumeration_mapping(struct bt_ctf_enumeration_mapping *mapping)
        g_free(mapping);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_initialize(struct bt_ctf_field_type_common *ft,
                bool init_bo, bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods)
@@ -60,7 +59,6 @@ void bt_ctf_field_type_common_initialize(struct bt_ctf_field_type_common *ft,
        ft->alignment = 1;
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_integer_initialize(
                struct bt_ctf_field_type_common *ft,
                unsigned int size, bt_ctf_object_release_func release_func,
@@ -80,7 +78,6 @@ void bt_ctf_field_type_common_integer_initialize(
                ft, size);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_floating_point_initialize(
                struct bt_ctf_field_type_common *ft,
                bt_ctf_object_release_func release_func,
@@ -98,7 +95,6 @@ void bt_ctf_field_type_common_floating_point_initialize(
                flt_ft->mant_dig);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_enumeration_initialize(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *container_ft,
@@ -120,7 +116,6 @@ void bt_ctf_field_type_common_enumeration_initialize(
                bt_ctf_field_type_common_integer_get_size(container_ft));
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_string_initialize(
                struct bt_ctf_field_type_common *ft,
                bt_ctf_object_release_func release_func,
@@ -136,7 +131,6 @@ void bt_ctf_field_type_common_string_initialize(
        BT_LOGD("Initialized common string field type object: addr=%p", ft);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_structure_initialize(
                struct bt_ctf_field_type_common *ft,
                bt_ctf_object_release_func release_func,
@@ -153,7 +147,6 @@ void bt_ctf_field_type_common_structure_initialize(
        BT_LOGD("Initialized common structure field type object: addr=%p", ft);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_array_initialize(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *element_ft,
@@ -173,7 +166,6 @@ void bt_ctf_field_type_common_array_initialize(
                "element-ft-addr=%p, length=%u", ft, element_ft, length);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_sequence_initialize(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *element_ft,
@@ -197,7 +189,6 @@ void bt_ctf_field_type_common_sequence_initialize(
                ft, element_ft, length_field_name);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_variant_initialize(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *tag_ft,
@@ -229,7 +220,6 @@ void bt_ctf_field_type_common_variant_initialize(
                ft, tag_ft, tag_name);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_integer_destroy(struct bt_ctf_object *obj)
 {
        struct bt_ctf_field_type_common_integer *ft = (void *) obj;
@@ -244,7 +234,6 @@ void bt_ctf_field_type_common_integer_destroy(struct bt_ctf_object *obj)
        g_free(ft);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_floating_point_destroy(struct bt_ctf_object *obj)
 {
        struct bt_ctf_field_type_common_floating_point *ft = (void *) obj;
@@ -257,7 +246,6 @@ void bt_ctf_field_type_common_floating_point_destroy(struct bt_ctf_object *obj)
        g_free(ft);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_enumeration_destroy_recursive(struct bt_ctf_object *obj)
 {
        struct bt_ctf_field_type_common_enumeration *ft = (void *) obj;
@@ -273,7 +261,6 @@ void bt_ctf_field_type_common_enumeration_destroy_recursive(struct bt_ctf_object
        g_free(ft);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_string_destroy(struct bt_ctf_object *obj)
 {
        struct bt_ctf_field_type_common_string *ft = (void *) obj;
@@ -301,7 +288,6 @@ void bt_ctf_field_type_common_structure_field_finalize(
        bt_ctf_object_put_ref(field->type);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_structure_destroy_recursive(struct bt_ctf_object *obj)
 {
        struct bt_ctf_field_type_common_structure *ft = (void *) obj;
@@ -330,7 +316,6 @@ void bt_ctf_field_type_common_structure_destroy_recursive(struct bt_ctf_object *
        g_free(ft);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_array_destroy_recursive(struct bt_ctf_object *obj)
 {
        struct bt_ctf_field_type_common_array *ft = (void *) obj;
@@ -345,7 +330,6 @@ void bt_ctf_field_type_common_array_destroy_recursive(struct bt_ctf_object *obj)
        g_free(ft);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_sequence_destroy_recursive(struct bt_ctf_object *obj)
 {
        struct bt_ctf_field_type_common_sequence *ft = (void *) obj;
@@ -382,7 +366,6 @@ void bt_ctf_field_type_common_variant_choice_finalize(
        }
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_variant_destroy_recursive(struct bt_ctf_object *obj)
 {
        struct bt_ctf_field_type_common_variant *ft = (void *) obj;
@@ -533,7 +516,7 @@ int add_structure_variant_member(GArray *members,
 
        if (is_variant) {
                struct bt_ctf_field_type_common_variant_choice *choice =
-                       &g_array_index(members,
+                       &bt_g_array_index(members,
                                struct bt_ctf_field_type_common_variant_choice,
                                members->len - 1);
 
@@ -545,7 +528,7 @@ int add_structure_variant_member(GArray *members,
                BT_ASSERT_DBG(choice->ranges);
        } else {
                struct bt_ctf_field_type_common_structure_field *field =
-                       &g_array_index(members,
+                       &bt_g_array_index(members,
                                struct bt_ctf_field_type_common_structure_field,
                                members->len - 1);
 
@@ -565,7 +548,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_validate(struct bt_ctf_field_type_common *ft)
 {
        int ret = 0;
@@ -625,7 +607,6 @@ end:
        return iter;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_enumeration_mapping_iterator *
 bt_ctf_field_type_common_enumeration_find_mappings_by_name(
                struct bt_ctf_field_type_common *ft, const char *name)
@@ -676,7 +657,6 @@ end:
        return mapping;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_enumeration_mapping_iterator_next(
                struct bt_ctf_field_type_enumeration_mapping_iterator *iter)
 {
@@ -732,7 +712,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_enumeration_mapping_iterator *
 bt_ctf_field_type_common_enumeration_signed_find_mappings_by_value(
                struct bt_ctf_field_type_common *ft, int64_t value)
@@ -763,7 +742,6 @@ error:
        return NULL;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_enumeration_mapping_iterator *
 bt_ctf_field_type_common_enumeration_unsigned_find_mappings_by_value(
                struct bt_ctf_field_type_common *ft, uint64_t value)
@@ -794,7 +772,6 @@ error:
        return NULL;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_enumeration_mapping_iterator_signed_get(
                struct bt_ctf_field_type_enumeration_mapping_iterator *iter,
                const char **mapping_name, int64_t *range_begin,
@@ -809,7 +786,6 @@ int bt_ctf_field_type_enumeration_mapping_iterator_signed_get(
                        mapping_name, range_begin, range_end);
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_enumeration_mapping_iterator_unsigned_get(
                struct bt_ctf_field_type_enumeration_mapping_iterator *iter,
                const char **mapping_name, uint64_t *range_begin,
@@ -877,7 +853,6 @@ end:
        }
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_validate_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -905,7 +880,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_sequence_validate_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -931,7 +905,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_array_validate_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -948,7 +921,6 @@ int bt_ctf_field_type_common_array_validate_recursive(
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_validate_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -992,7 +964,6 @@ bt_ctf_bool bt_ctf_field_type_common_enumeration_has_overlapping_ranges(
        return enum_ft->has_overlapping_ranges;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_validate_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -1078,7 +1049,6 @@ end:
  * of the given field type and the properties of its children if
  * applicable.
  */
-BT_HIDDEN
 int bt_ctf_field_type_common_validate(struct bt_ctf_field_type_common *ft)
 {
        int ret = 0;
@@ -1104,7 +1074,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_get_size(struct bt_ctf_field_type_common *ft)
 {
        struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft);
@@ -1115,7 +1084,6 @@ int bt_ctf_field_type_common_integer_get_size(struct bt_ctf_field_type_common *f
        return (int) int_ft->size;
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_type_common_integer_is_signed(struct bt_ctf_field_type_common *ft)
 {
        struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft);
@@ -1126,7 +1094,6 @@ bt_ctf_bool bt_ctf_field_type_common_integer_is_signed(struct bt_ctf_field_type_
        return int_ft->is_signed;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_is_signed(struct bt_ctf_field_type_common *ft,
                bt_ctf_bool is_signed)
 {
@@ -1162,7 +1129,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_size(struct bt_ctf_field_type_common *ft,
                unsigned int size)
 {
@@ -1205,7 +1171,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_integer_base bt_ctf_field_type_common_integer_get_base(
                struct bt_ctf_field_type_common *ft)
 {
@@ -1217,7 +1182,6 @@ enum bt_ctf_integer_base bt_ctf_field_type_common_integer_get_base(
        return int_ft->base;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_base(struct bt_ctf_field_type_common *ft,
                enum bt_ctf_integer_base base)
 {
@@ -1268,7 +1232,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_string_encoding bt_ctf_field_type_common_integer_get_encoding(
                struct bt_ctf_field_type_common *ft)
 {
@@ -1280,7 +1243,6 @@ enum bt_ctf_string_encoding bt_ctf_field_type_common_integer_get_encoding(
        return int_ft->encoding;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_encoding(struct bt_ctf_field_type_common *ft,
                enum bt_ctf_string_encoding encoding)
 {
@@ -1325,7 +1287,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_ctf_clock_class *bt_ctf_field_type_common_integer_borrow_mapped_clock_class(
                struct bt_ctf_field_type_common *ft)
 {
@@ -1337,7 +1298,6 @@ struct bt_ctf_clock_class *bt_ctf_field_type_common_integer_borrow_mapped_clock_
        return int_ft->mapped_clock_class;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_clock_class *clock_class)
@@ -1377,7 +1337,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_mapped_clock_class(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_clock_class *clock_class)
@@ -1404,7 +1363,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index(
                struct bt_ctf_field_type_common *ft, uint64_t index,
                const char **mapping_name, int64_t *range_begin,
@@ -1441,7 +1399,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index(
                struct bt_ctf_field_type_common *ft, uint64_t index,
                const char **mapping_name, uint64_t *range_begin,
@@ -1477,7 +1434,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_enumeration_borrow_container_field_type(
                struct bt_ctf_field_type_common *ft)
@@ -1489,7 +1445,6 @@ bt_ctf_field_type_common_enumeration_borrow_container_field_type(
        return BT_CTF_TO_COMMON(enum_ft->container_ft);
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_signed_add_mapping(
                struct bt_ctf_field_type_common *ft, const char *string,
                int64_t range_start, int64_t range_end)
@@ -1579,7 +1534,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_unsigned_add_mapping(
                struct bt_ctf_field_type_common *ft, const char *string,
                uint64_t range_start, uint64_t range_end)
@@ -1669,7 +1623,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_enumeration_get_mapping_count(
                struct bt_ctf_field_type_common *ft)
 {
@@ -1680,7 +1633,6 @@ int64_t bt_ctf_field_type_common_enumeration_get_mapping_count(
        return (int64_t) enum_ft->entries->len;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_get_exponent_digits(
                struct bt_ctf_field_type_common *ft)
 {
@@ -1692,7 +1644,6 @@ int bt_ctf_field_type_common_floating_point_get_exponent_digits(
        return (int) flt_ft->exp_dig;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_set_exponent_digits(
                struct bt_ctf_field_type_common *ft,
                unsigned int exponent_digits)
@@ -1739,7 +1690,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_get_mantissa_digits(
                struct bt_ctf_field_type_common *ft)
 {
@@ -1751,7 +1701,6 @@ int bt_ctf_field_type_common_floating_point_get_mantissa_digits(
        return (int) flt_ft->mant_dig;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_set_mantissa_digits(
                struct bt_ctf_field_type_common *ft, unsigned int mantissa_digits)
 {
@@ -1796,7 +1745,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_replace_field(
                struct bt_ctf_field_type_common *ft,
                const char *field_name,
@@ -1826,7 +1774,6 @@ int bt_ctf_field_type_common_structure_replace_field(
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_add_field(struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *field_type,
                const char *field_name)
@@ -1890,7 +1837,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_structure_get_field_count(
                struct bt_ctf_field_type_common *ft)
 {
@@ -1902,7 +1848,6 @@ int64_t bt_ctf_field_type_common_structure_get_field_count(
        return (int64_t) struct_ft->fields->len;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_borrow_field_by_index(
                struct bt_ctf_field_type_common *ft,
                const char **field_name,
@@ -1932,7 +1877,6 @@ int bt_ctf_field_type_common_structure_borrow_field_by_index(
        return 0;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_structure_borrow_field_type_by_name(
                struct bt_ctf_field_type_common *ft, const char *name)
@@ -1970,7 +1914,6 @@ end:
        return field_type;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_variant_borrow_tag_field_type(
                struct bt_ctf_field_type_common *ft)
@@ -1994,7 +1937,6 @@ end:
        return tag_ft;
 }
 
-BT_HIDDEN
 const char *bt_ctf_field_type_common_variant_get_tag_name(
                struct bt_ctf_field_type_common *ft)
 {
@@ -2017,7 +1959,6 @@ end:
        return tag_name;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_set_tag_name(
                struct bt_ctf_field_type_common *ft, const char *name)
 {
@@ -2060,7 +2001,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_add_field(struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *field_type,
                const char *field_name)
@@ -2148,7 +2088,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_variant_borrow_field_type_by_name(
                struct bt_ctf_field_type_common *ft,
@@ -2187,7 +2126,6 @@ end:
        return field_type;
 }
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_variant_get_field_count(
                struct bt_ctf_field_type_common *ft)
 {
@@ -2199,7 +2137,6 @@ int64_t bt_ctf_field_type_common_variant_get_field_count(
        return (int64_t) var_ft->choices->len;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_borrow_field_by_index(
                struct bt_ctf_field_type_common *ft,
                const char **field_name,
@@ -2229,7 +2166,6 @@ int bt_ctf_field_type_common_variant_borrow_field_by_index(
        return 0;
 }
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_variant_find_choice_index(
                struct bt_ctf_field_type_common *ft, uint64_t uval,
                bool is_signed)
@@ -2254,7 +2190,7 @@ int64_t bt_ctf_field_type_common_variant_find_choice_index(
 
                for (range_i = 0; range_i < choice->ranges->len; range_i++) {
                        struct bt_ctf_field_type_common_variant_choice_range *range =
-                               &g_array_index(
+                               &bt_g_array_index(
                                        choice->ranges,
                                        struct bt_ctf_field_type_common_variant_choice_range,
                                        range_i);
@@ -2286,7 +2222,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_array_borrow_element_field_type(
                struct bt_ctf_field_type_common *ft)
@@ -2300,7 +2235,6 @@ bt_ctf_field_type_common_array_borrow_element_field_type(
        return array_ft->element_ft;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_array_set_element_field_type(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *element_ft)
@@ -2340,7 +2274,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_array_get_length(struct bt_ctf_field_type_common *ft)
 {
        struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft);
@@ -2351,7 +2284,6 @@ int64_t bt_ctf_field_type_common_array_get_length(struct bt_ctf_field_type_commo
        return (int64_t) array_ft->length;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *bt_ctf_field_type_common_sequence_borrow_element_field_type(
                struct bt_ctf_field_type_common *ft)
 {
@@ -2363,7 +2295,6 @@ struct bt_ctf_field_type_common *bt_ctf_field_type_common_sequence_borrow_elemen
        return seq_ft->element_ft;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_sequence_set_element_field_type(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *element_ft)
@@ -2404,7 +2335,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 const char *bt_ctf_field_type_common_sequence_get_length_field_name(
                struct bt_ctf_field_type_common *ft)
 {
@@ -2417,7 +2347,6 @@ const char *bt_ctf_field_type_common_sequence_get_length_field_name(
                seq_ft->length_field_name->str : NULL;
 }
 
-BT_HIDDEN
 enum bt_ctf_string_encoding bt_ctf_field_type_common_string_get_encoding(
                struct bt_ctf_field_type_common *ft)
 {
@@ -2429,7 +2358,6 @@ enum bt_ctf_string_encoding bt_ctf_field_type_common_string_get_encoding(
        return string_ft->encoding;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_string_set_encoding(struct bt_ctf_field_type_common *ft,
                enum bt_ctf_string_encoding encoding)
 {
@@ -2466,7 +2394,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_get_alignment(struct bt_ctf_field_type_common *ft)
 {
        int ret;
@@ -2547,7 +2474,6 @@ int is_power_of_two(unsigned int value)
        return ((value & (value - 1)) == 0) && value > 0;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_set_alignment(struct bt_ctf_field_type_common *ft,
                unsigned int alignment)
 {
@@ -2610,7 +2536,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_byte_order bt_ctf_field_type_common_get_byte_order(
                struct bt_ctf_field_type_common *ft)
 {
@@ -2659,7 +2584,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_set_byte_order(struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order)
 {
@@ -2700,7 +2624,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_field_type_id bt_ctf_field_type_common_get_type_id(
                struct bt_ctf_field_type_common *ft)
 {
@@ -2708,7 +2631,6 @@ enum bt_ctf_field_type_id bt_ctf_field_type_common_get_type_id(
        return ft->id;
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_freeze(struct bt_ctf_field_type_common *ft)
 {
        if (!ft || ft->frozen) {
@@ -2719,7 +2641,6 @@ void bt_ctf_field_type_common_freeze(struct bt_ctf_field_type_common *ft)
        ft->methods->freeze(ft);
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_variant_borrow_field_type_signed(
                struct bt_ctf_field_type_common_variant *var_ft,
@@ -2756,7 +2677,6 @@ end:
        return field_type;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_variant_borrow_field_type_unsigned(
                struct bt_ctf_field_type_common_variant *var_ft,
@@ -2793,7 +2713,6 @@ end:
        return field_type;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *bt_ctf_field_type_common_copy(
                struct bt_ctf_field_type_common *ft)
 {
@@ -2813,7 +2732,6 @@ end:
        return ft_copy;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_get_field_name_index(
                struct bt_ctf_field_type_common *ft, const char *name)
 {
@@ -2852,7 +2770,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_get_field_name_index(
                struct bt_ctf_field_type_common *ft, const char *name)
 {
@@ -2890,7 +2807,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_sequence_set_length_field_path(
                struct bt_ctf_field_type_common *ft, struct bt_ctf_field_path *path)
 {
@@ -2920,7 +2836,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_set_tag_field_path(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_path *path)
@@ -2951,7 +2866,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_set_tag_field_type(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *tag_ft)
@@ -2988,13 +2902,11 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_generic_freeze(struct bt_ctf_field_type_common *ft)
 {
        ft->frozen = 1;
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_enumeration_freeze_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -3008,7 +2920,6 @@ void bt_ctf_field_type_common_enumeration_freeze_recursive(
        bt_ctf_field_type_common_freeze(BT_CTF_TO_COMMON(enum_ft->container_ft));
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_structure_freeze_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -3031,7 +2942,6 @@ void bt_ctf_field_type_common_structure_freeze_recursive(
        }
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_update_choices(struct bt_ctf_field_type_common *ft)
 {
        struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft);
@@ -3088,7 +2998,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_variant_freeze_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -3109,7 +3018,6 @@ void bt_ctf_field_type_common_variant_freeze_recursive(
        }
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_array_freeze_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -3124,7 +3032,6 @@ void bt_ctf_field_type_common_array_freeze_recursive(
        bt_ctf_field_type_common_freeze(array_ft->element_ft);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_sequence_freeze_recursive(
                struct bt_ctf_field_type_common *ft)
 {
@@ -3139,7 +3046,6 @@ void bt_ctf_field_type_common_sequence_freeze_recursive(
        bt_ctf_field_type_common_freeze(seq_ft->element_ft);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_integer_set_byte_order(
                struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order)
 {
@@ -3148,7 +3054,6 @@ void bt_ctf_field_type_common_integer_set_byte_order(
        int_ft->user_byte_order = byte_order;
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_enumeration_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order)
 {
@@ -3158,7 +3063,6 @@ void bt_ctf_field_type_common_enumeration_set_byte_order_recursive(
                byte_order);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_floating_point_set_byte_order(
                struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order)
 {
@@ -3167,7 +3071,6 @@ void bt_ctf_field_type_common_floating_point_set_byte_order(
        flt_ft->user_byte_order = byte_order;
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_structure_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order)
@@ -3185,7 +3088,6 @@ void bt_ctf_field_type_common_structure_set_byte_order_recursive(
        }
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_variant_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order)
@@ -3203,7 +3105,6 @@ void bt_ctf_field_type_common_variant_set_byte_order_recursive(
        }
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_array_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order)
@@ -3213,7 +3114,6 @@ void bt_ctf_field_type_common_array_set_byte_order_recursive(
        bt_ctf_field_type_common_set_byte_order(array_ft->element_ft, byte_order);
 }
 
-BT_HIDDEN
 void bt_ctf_field_type_common_sequence_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order)
@@ -3224,7 +3124,6 @@ void bt_ctf_field_type_common_sequence_set_byte_order_recursive(
 }
 
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_compare(struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b)
 {
@@ -3305,7 +3204,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_compare(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b)
@@ -3392,7 +3290,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b)
@@ -3446,7 +3343,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_string_compare(struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b)
 {
@@ -3500,7 +3396,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b)
@@ -3554,7 +3449,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b)
@@ -3616,7 +3510,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_array_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b)
@@ -3646,7 +3539,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_sequence_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b)
@@ -3679,7 +3571,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_compare(struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b)
 {
@@ -3734,7 +3625,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_get_field_count(struct bt_ctf_field_type_common *ft)
 {
        int64_t field_count = -1;
@@ -3763,7 +3653,6 @@ int64_t bt_ctf_field_type_common_get_field_count(struct bt_ctf_field_type_common
        return field_count;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *bt_ctf_field_type_common_borrow_field_at_index(
                struct bt_ctf_field_type_common *ft, int index)
 {
@@ -3806,7 +3695,6 @@ end:
        return field_type;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_get_field_index(struct bt_ctf_field_type_common *ft,
                const char *name)
 {
@@ -3828,7 +3716,6 @@ int bt_ctf_field_type_common_get_field_index(struct bt_ctf_field_type_common *ft
        return field_index;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_path *bt_ctf_field_type_common_variant_borrow_tag_field_path(
                struct bt_ctf_field_type_common *ft)
 {
@@ -3840,7 +3727,6 @@ struct bt_ctf_field_path *bt_ctf_field_type_common_variant_borrow_tag_field_path
        return var_ft->tag_field_path;
 }
 
-BT_HIDDEN
 struct bt_ctf_field_path *bt_ctf_field_type_common_sequence_borrow_length_field_path(
                struct bt_ctf_field_type_common *ft)
 {
@@ -3852,7 +3738,6 @@ struct bt_ctf_field_path *bt_ctf_field_type_common_sequence_borrow_length_field_
        return seq_ft->length_field_path;
 }
 
-BT_HIDDEN
 int bt_ctf_field_type_common_validate_single_clock_class(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_clock_class **expected_clock_class)
@@ -4098,7 +3983,6 @@ static struct bt_ctf_field_type_common_methods bt_ctf_field_type_variant_methods
 typedef int (*bt_ctf_field_type_serialize_func)(struct bt_ctf_field_type_common *,
                struct metadata_context *);
 
-BT_HIDDEN
 int bt_ctf_field_type_serialize_recursive(struct bt_ctf_field_type *type,
                struct metadata_context *context)
 {
@@ -4541,6 +4425,7 @@ int bt_ctf_field_type_string_serialize(struct bt_ctf_field_type_common *type,
        return 0;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_integer_create(unsigned int size)
 {
        struct bt_ctf_field_type_common_integer *integer = NULL;
@@ -4575,16 +4460,19 @@ end:
        return (void *) integer;
 }
 
+BT_EXPORT
 int bt_ctf_field_type_integer_get_size(struct bt_ctf_field_type *ft)
 {
        return bt_ctf_field_type_common_integer_get_size((void *) ft);
 }
 
+BT_EXPORT
 bt_ctf_bool bt_ctf_field_type_integer_is_signed(struct bt_ctf_field_type *ft)
 {
        return bt_ctf_field_type_common_integer_is_signed((void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_integer_set_is_signed(struct bt_ctf_field_type *ft,
                bt_ctf_bool is_signed)
 {
@@ -4592,18 +4480,21 @@ int bt_ctf_field_type_integer_set_is_signed(struct bt_ctf_field_type *ft,
                is_signed);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_integer_set_size(struct bt_ctf_field_type *ft,
                unsigned int size)
 {
        return bt_ctf_field_type_common_integer_set_size((void *) ft, size);
 }
 
+BT_EXPORT
 enum bt_ctf_integer_base bt_ctf_field_type_integer_get_base(
                struct bt_ctf_field_type *ft)
 {
        return (int) bt_ctf_field_type_common_integer_get_base((void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_integer_set_base(struct bt_ctf_field_type *ft,
                enum bt_ctf_integer_base base)
 {
@@ -4611,12 +4502,14 @@ int bt_ctf_field_type_integer_set_base(struct bt_ctf_field_type *ft,
                (int) base);
 }
 
+BT_EXPORT
 enum bt_ctf_string_encoding bt_ctf_field_type_integer_get_encoding(
                struct bt_ctf_field_type *ft)
 {
        return (int) bt_ctf_field_type_common_integer_get_encoding((void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_integer_set_encoding(struct bt_ctf_field_type *ft,
                enum bt_ctf_string_encoding encoding)
 {
@@ -4624,6 +4517,7 @@ int bt_ctf_field_type_integer_set_encoding(struct bt_ctf_field_type *ft,
                (int) encoding);
 }
 
+BT_EXPORT
 struct bt_ctf_clock_class *bt_ctf_field_type_integer_get_mapped_clock_class(
                struct bt_ctf_field_type *ft)
 {
@@ -4631,6 +4525,7 @@ struct bt_ctf_clock_class *bt_ctf_field_type_integer_get_mapped_clock_class(
                (void *) ft));
 }
 
+BT_EXPORT
 int bt_ctf_field_type_integer_set_mapped_clock_class(
                struct bt_ctf_field_type *ft,
                struct bt_ctf_clock_class *clock_class)
@@ -4639,6 +4534,7 @@ int bt_ctf_field_type_integer_set_mapped_clock_class(
                clock_class);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_enumeration_signed_get_mapping_by_index(
                struct bt_ctf_field_type *ft, uint64_t index,
                const char **mapping_name, int64_t *range_begin,
@@ -4648,6 +4544,7 @@ int bt_ctf_field_type_enumeration_signed_get_mapping_by_index(
                (void *) ft, index, mapping_name, range_begin, range_end);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(
                struct bt_ctf_field_type *ft, uint64_t index,
                const char **mapping_name, uint64_t *range_begin,
@@ -4657,6 +4554,7 @@ int bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(
                (void *) ft, index, mapping_name, range_begin, range_end);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_enumeration_create(
                struct bt_ctf_field_type *container_ft)
 {
@@ -4702,6 +4600,7 @@ end:
        return (void *) enumeration;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_enumeration_get_container_field_type(
                struct bt_ctf_field_type *ft)
 {
@@ -4710,6 +4609,7 @@ struct bt_ctf_field_type *bt_ctf_field_type_enumeration_get_container_field_type
                        (void *) ft));
 }
 
+BT_EXPORT
 int bt_ctf_field_type_enumeration_signed_add_mapping(
                struct bt_ctf_field_type *ft, const char *string,
                int64_t range_start, int64_t range_end)
@@ -4718,6 +4618,7 @@ int bt_ctf_field_type_enumeration_signed_add_mapping(
                (void *) ft, string, range_start, range_end);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_enumeration_unsigned_add_mapping(
                struct bt_ctf_field_type *ft, const char *string,
                uint64_t range_start, uint64_t range_end)
@@ -4726,12 +4627,14 @@ int bt_ctf_field_type_enumeration_unsigned_add_mapping(
                (void *) ft, string, range_start, range_end);
 }
 
+BT_EXPORT
 int64_t bt_ctf_field_type_enumeration_get_mapping_count(
                struct bt_ctf_field_type *ft)
 {
        return bt_ctf_field_type_common_enumeration_get_mapping_count((void *) ft);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_floating_point_create(void)
 {
        struct bt_ctf_field_type_common_floating_point *floating_point =
@@ -4758,6 +4661,7 @@ end:
        return (void *) floating_point;
 }
 
+BT_EXPORT
 int bt_ctf_field_type_floating_point_get_exponent_digits(
                struct bt_ctf_field_type *ft)
 {
@@ -4765,6 +4669,7 @@ int bt_ctf_field_type_floating_point_get_exponent_digits(
                (void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_floating_point_set_exponent_digits(
                struct bt_ctf_field_type *ft, unsigned int exponent_digits)
 {
@@ -4772,6 +4677,7 @@ int bt_ctf_field_type_floating_point_set_exponent_digits(
                (void *) ft, exponent_digits);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_floating_point_get_mantissa_digits(
                struct bt_ctf_field_type *ft)
 {
@@ -4779,6 +4685,7 @@ int bt_ctf_field_type_floating_point_get_mantissa_digits(
                (void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_floating_point_set_mantissa_digits(
                struct bt_ctf_field_type *ft, unsigned int mantissa_digits)
 {
@@ -4786,6 +4693,7 @@ int bt_ctf_field_type_floating_point_set_mantissa_digits(
                (void *) ft, mantissa_digits);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_structure_create(void)
 {
        struct bt_ctf_field_type_common_structure *structure =
@@ -4814,6 +4722,7 @@ end:
        return (void *) structure;
 }
 
+BT_EXPORT
 int bt_ctf_field_type_structure_add_field(struct bt_ctf_field_type *ft,
                struct bt_ctf_field_type *field_type,
                const char *field_name)
@@ -4822,11 +4731,13 @@ int bt_ctf_field_type_structure_add_field(struct bt_ctf_field_type *ft,
                (void *) field_type, field_name);
 }
 
+BT_EXPORT
 int64_t bt_ctf_field_type_structure_get_field_count(struct bt_ctf_field_type *ft)
 {
        return bt_ctf_field_type_common_structure_get_field_count((void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_structure_get_field_by_index(
                struct bt_ctf_field_type *ft,
                const char **field_name,
@@ -4842,6 +4753,7 @@ int bt_ctf_field_type_structure_get_field_by_index(
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_structure_get_field_type_by_name(
                struct bt_ctf_field_type *ft, const char *name)
 {
@@ -4849,6 +4761,7 @@ struct bt_ctf_field_type *bt_ctf_field_type_structure_get_field_type_by_name(
                (void *) ft, name));
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_variant_create(
        struct bt_ctf_field_type *tag_ft, const char *tag_name)
 {
@@ -4889,6 +4802,7 @@ end:
        return (void *) var_ft;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_variant_get_tag_field_type(
                struct bt_ctf_field_type *ft)
 {
@@ -4896,17 +4810,20 @@ struct bt_ctf_field_type *bt_ctf_field_type_variant_get_tag_field_type(
                (void *) ft));
 }
 
+BT_EXPORT
 const char *bt_ctf_field_type_variant_get_tag_name(struct bt_ctf_field_type *ft)
 {
        return bt_ctf_field_type_common_variant_get_tag_name((void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_variant_set_tag_name(
                struct bt_ctf_field_type *ft, const char *name)
 {
        return bt_ctf_field_type_common_variant_set_tag_name((void *) ft, name);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_variant_add_field(struct bt_ctf_field_type *ft,
                struct bt_ctf_field_type *field_type,
                const char *field_name)
@@ -4915,6 +4832,7 @@ int bt_ctf_field_type_variant_add_field(struct bt_ctf_field_type *ft,
                (void *) field_type, field_name);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type_by_name(
                struct bt_ctf_field_type *ft,
                const char *field_name)
@@ -4923,6 +4841,7 @@ struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type_by_name(
                (void *) ft, field_name));
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type_from_tag(
                struct bt_ctf_field_type *ft,
                struct bt_ctf_field *tag_field)
@@ -4978,11 +4897,13 @@ end:
        return ret_ft;
 }
 
+BT_EXPORT
 int64_t bt_ctf_field_type_variant_get_field_count(struct bt_ctf_field_type *ft)
 {
        return bt_ctf_field_type_common_variant_get_field_count((void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_variant_get_field_by_index(struct bt_ctf_field_type *ft,
                const char **field_name, struct bt_ctf_field_type **field_type,
                uint64_t index)
@@ -4997,6 +4918,7 @@ int bt_ctf_field_type_variant_get_field_by_index(struct bt_ctf_field_type *ft,
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_array_create(
                struct bt_ctf_field_type *element_ft, unsigned int length)
 {
@@ -5039,6 +4961,7 @@ end:
        return (void *) array;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_array_get_element_field_type(
                struct bt_ctf_field_type *ft)
 {
@@ -5046,11 +4969,13 @@ struct bt_ctf_field_type *bt_ctf_field_type_array_get_element_field_type(
                (void *) ft));
 }
 
+BT_EXPORT
 int64_t bt_ctf_field_type_array_get_length(struct bt_ctf_field_type *ft)
 {
        return bt_ctf_field_type_common_array_get_length((void *) ft);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_sequence_create(
                struct bt_ctf_field_type *element_ft,
                const char *length_field_name)
@@ -5095,6 +5020,7 @@ end:
        return (void *) sequence;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_sequence_get_element_field_type(
                struct bt_ctf_field_type *ft)
 {
@@ -5102,12 +5028,14 @@ struct bt_ctf_field_type *bt_ctf_field_type_sequence_get_element_field_type(
                (void *) ft));
 }
 
+BT_EXPORT
 const char *bt_ctf_field_type_sequence_get_length_field_name(
                struct bt_ctf_field_type *ft)
 {
        return bt_ctf_field_type_common_sequence_get_length_field_name((void *) ft);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_type_string_create(void)
 {
        struct bt_ctf_field_type_common_string *string =
@@ -5129,12 +5057,14 @@ struct bt_ctf_field_type *bt_ctf_field_type_string_create(void)
        return (void *) string;
 }
 
+BT_EXPORT
 enum bt_ctf_string_encoding bt_ctf_field_type_string_get_encoding(
                struct bt_ctf_field_type *ft)
 {
        return (int) bt_ctf_field_type_common_string_get_encoding((void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_string_set_encoding(struct bt_ctf_field_type *ft,
                enum bt_ctf_string_encoding encoding)
 {
@@ -5142,23 +5072,27 @@ int bt_ctf_field_type_string_set_encoding(struct bt_ctf_field_type *ft,
                (int) encoding);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_get_alignment(struct bt_ctf_field_type *ft)
 {
        return bt_ctf_field_type_common_get_alignment((void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_set_alignment(struct bt_ctf_field_type *ft,
                unsigned int alignment)
 {
        return bt_ctf_field_type_common_set_alignment((void *) ft, alignment);
 }
 
+BT_EXPORT
 enum bt_ctf_byte_order bt_ctf_field_type_get_byte_order(
                struct bt_ctf_field_type *ft)
 {
        return (int) bt_ctf_field_type_common_get_byte_order((void *) ft);
 }
 
+BT_EXPORT
 int bt_ctf_field_type_set_byte_order(struct bt_ctf_field_type *ft,
                enum bt_ctf_byte_order byte_order)
 {
@@ -5166,13 +5100,13 @@ int bt_ctf_field_type_set_byte_order(struct bt_ctf_field_type *ft,
                (int) byte_order);
 }
 
+BT_EXPORT
 enum bt_ctf_field_type_id bt_ctf_field_type_get_type_id(
                struct bt_ctf_field_type *ft)
 {
        return (int) bt_ctf_field_type_common_get_type_id((void *) ft);
 }
 
-BT_HIDDEN
 struct bt_ctf_field_type *bt_ctf_field_type_copy(struct bt_ctf_field_type *ft)
 {
        return (void *) bt_ctf_field_type_common_copy((void *) ft);
index a891b068614b732f2d82b409e26e7758de1968e4..f5dbd99ea0fad4139faec4fe5569fb71aa99f1bb 100644 (file)
        BT_CTF_ASSERT_PRE_HOT((_ft), (_name), ": ft-addr=%p", (_ft))
 
 #define BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(_ft, _index) \
-       (&g_array_index(((struct bt_ctf_field_type_common_structure *) (_ft))->fields, \
+       (&bt_g_array_index(((struct bt_ctf_field_type_common_structure *) (_ft))->fields, \
                struct bt_ctf_field_type_common_structure_field, (_index)))
 
 #define BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(_ft, _index)  \
-       (&g_array_index(((struct bt_ctf_field_type_common_variant *) (_ft))->choices, \
+       (&bt_g_array_index(((struct bt_ctf_field_type_common_variant *) (_ft))->choices, \
                struct bt_ctf_field_type_common_variant_choice, (_index)))
 
 struct bt_ctf_field_common;
@@ -73,7 +73,7 @@ struct bt_ctf_field_type_common {
 
        /*
         * A type can't be modified once it is added to an event or after a
-        * a field has been instanciated from it.
+        * a field has been instantiated from it.
         */
        int frozen;
 
@@ -264,50 +264,42 @@ struct bt_ctf_field_type_common_string {
 typedef struct bt_ctf_field_common *(* bt_ctf_field_common_create_func)(
                struct bt_ctf_field_type_common *);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_initialize(struct bt_ctf_field_type_common *ft,
                bool init_bo, bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_integer_initialize(
                struct bt_ctf_field_type_common *ft,
                unsigned int size, bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_floating_point_initialize(
                struct bt_ctf_field_type_common *ft,
                bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_enumeration_initialize(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *container_ft,
                bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_string_initialize(
                struct bt_ctf_field_type_common *ft,
                bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_structure_initialize(
                struct bt_ctf_field_type_common *ft,
                bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_array_initialize(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *element_ft,
                unsigned int length, bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_sequence_initialize(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *element_ft,
@@ -315,7 +307,6 @@ void bt_ctf_field_type_common_sequence_initialize(
                bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_variant_initialize(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *tag_ft,
@@ -323,469 +314,361 @@ void bt_ctf_field_type_common_variant_initialize(
                bt_ctf_object_release_func release_func,
                struct bt_ctf_field_type_common_methods *methods);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_integer_destroy(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_floating_point_destroy(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_enumeration_destroy_recursive(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_string_destroy(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_structure_destroy_recursive(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_array_destroy_recursive(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_sequence_destroy_recursive(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_variant_destroy_recursive(struct bt_ctf_object *obj);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_validate(struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_validate_recursive(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_sequence_validate_recursive(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_array_validate_recursive(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_validate_recursive(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_validate_recursive(
                struct bt_ctf_field_type_common *type);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_validate(struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_get_size(struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_type_common_integer_is_signed(struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_is_signed(struct bt_ctf_field_type_common *ft,
                bt_ctf_bool is_signed);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_size(struct bt_ctf_field_type_common *ft,
                unsigned int size);
 
-BT_HIDDEN
 enum bt_ctf_integer_base bt_ctf_field_type_common_integer_get_base(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_base(struct bt_ctf_field_type_common *ft,
                enum bt_ctf_integer_base base);
 
-BT_HIDDEN
 enum bt_ctf_string_encoding bt_ctf_field_type_common_integer_get_encoding(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_encoding(struct bt_ctf_field_type_common *ft,
                enum bt_ctf_string_encoding encoding);
 
-BT_HIDDEN
 struct bt_ctf_clock_class *bt_ctf_field_type_common_integer_borrow_mapped_clock_class(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_clock_class *clock_class);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_set_mapped_clock_class(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_clock_class *clock_class);
 
-BT_HIDDEN
 struct bt_ctf_field_type_enumeration_mapping_iterator *
 bt_ctf_field_type_common_enumeration_find_mappings_by_name(
                struct bt_ctf_field_type_common *ft, const char *name);
 
-BT_HIDDEN
 struct bt_ctf_field_type_enumeration_mapping_iterator *
 bt_ctf_field_type_common_enumeration_signed_find_mappings_by_value(
                struct bt_ctf_field_type_common *ft, int64_t value);
 
-BT_HIDDEN
 struct bt_ctf_field_type_enumeration_mapping_iterator *
 bt_ctf_field_type_common_enumeration_unsigned_find_mappings_by_value(
                struct bt_ctf_field_type_common *ft, uint64_t value);
 
-BT_HIDDEN
 int bt_ctf_field_type_enumeration_mapping_iterator_next(
                struct bt_ctf_field_type_enumeration_mapping_iterator *iter);
 
-BT_HIDDEN
 int bt_ctf_field_type_enumeration_mapping_iterator_signed_get(
                struct bt_ctf_field_type_enumeration_mapping_iterator *iter,
                const char **mapping_name, int64_t *range_begin,
                int64_t *range_end);
 
-BT_HIDDEN
 int bt_ctf_field_type_enumeration_mapping_iterator_unsigned_get(
                struct bt_ctf_field_type_enumeration_mapping_iterator *iter,
                const char **mapping_name, uint64_t *range_begin,
                uint64_t *range_end);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index(
                struct bt_ctf_field_type_common *ft, uint64_t index,
                const char **mapping_name, int64_t *range_begin,
                int64_t *range_end);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index(
                struct bt_ctf_field_type_common *ft, uint64_t index,
                const char **mapping_name, uint64_t *range_begin,
                uint64_t *range_end);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_enumeration_borrow_container_field_type(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_signed_add_mapping(
                struct bt_ctf_field_type_common *ft, const char *string,
                int64_t range_start, int64_t range_end);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_unsigned_add_mapping(
                struct bt_ctf_field_type_common *ft, const char *string,
                uint64_t range_start, uint64_t range_end);
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_enumeration_get_mapping_count(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_get_exponent_digits(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_set_exponent_digits(
                struct bt_ctf_field_type_common *ft,
                unsigned int exponent_digits);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_get_mantissa_digits(
                struct bt_ctf_field_type_common *type);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_set_mantissa_digits(
                struct bt_ctf_field_type_common *ft, unsigned int mantissa_digits);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_replace_field(
                struct bt_ctf_field_type_common *ft,
                const char *field_name,
                struct bt_ctf_field_type_common *field_type);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_add_field(struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *field_type,
                const char *field_name);
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_structure_get_field_count(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_borrow_field_by_index(
                struct bt_ctf_field_type_common *ft,
                const char **field_name,
                struct bt_ctf_field_type_common **field_type, uint64_t index);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_structure_borrow_field_type_by_name(
                struct bt_ctf_field_type_common *ft, const char *name);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_variant_borrow_tag_field_type(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 const char *bt_ctf_field_type_common_variant_get_tag_name(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_set_tag_name(
                struct bt_ctf_field_type_common *ft, const char *name);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_add_field(struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *field_type,
                const char *field_name);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_update_choices(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_variant_borrow_field_type_by_name(
                struct bt_ctf_field_type_common *ft,
                const char *field_name);
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_variant_get_field_count(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_borrow_field_by_index(
                struct bt_ctf_field_type_common *ft,
                const char **field_name,
                struct bt_ctf_field_type_common **field_type, uint64_t index);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_array_borrow_element_field_type(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_array_set_element_field_type(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *element_ft);
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_array_get_length(struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_sequence_borrow_element_field_type(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_sequence_set_element_field_type(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *element_ft);
 
-BT_HIDDEN
 const char *bt_ctf_field_type_common_sequence_get_length_field_name(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 enum bt_ctf_string_encoding bt_ctf_field_type_common_string_get_encoding(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_string_set_encoding(struct bt_ctf_field_type_common *ft,
                enum bt_ctf_string_encoding encoding);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_get_alignment(struct bt_ctf_field_type_common *type);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_set_alignment(struct bt_ctf_field_type_common *ft,
                unsigned int alignment);
 
-BT_HIDDEN
 enum bt_ctf_byte_order bt_ctf_field_type_common_get_byte_order(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_set_byte_order(struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order);
 
-BT_HIDDEN
 enum bt_ctf_field_type_id bt_ctf_field_type_common_get_type_id(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_freeze(struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_variant_borrow_field_type_signed(
                struct bt_ctf_field_type_common_variant *var_ft,
                int64_t tag_value);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *
 bt_ctf_field_type_common_variant_borrow_field_type_unsigned(
                struct bt_ctf_field_type_common_variant *var_ft,
                uint64_t tag_value);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *bt_ctf_field_type_common_copy(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_get_field_name_index(
                struct bt_ctf_field_type_common *ft, const char *name);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_get_field_name_index(
                struct bt_ctf_field_type_common *ft, const char *name);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_sequence_set_length_field_path(
                struct bt_ctf_field_type_common *ft, struct bt_ctf_field_path *path);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_set_tag_field_path(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_path *path);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_set_tag_field_type(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_field_type_common *tag_ft);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_generic_freeze(struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_enumeration_freeze_recursive(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_structure_freeze_recursive(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_variant_freeze_recursive(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_array_freeze_recursive(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_sequence_freeze_recursive(
                struct bt_ctf_field_type_common *type);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_integer_set_byte_order(
                struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_enumeration_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_floating_point_set_byte_order(
                struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_structure_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_variant_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_array_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order);
 
-BT_HIDDEN
 void bt_ctf_field_type_common_sequence_set_byte_order_recursive(
                struct bt_ctf_field_type_common *ft,
                enum bt_ctf_byte_order byte_order);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_integer_compare(struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_floating_point_compare(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_enumeration_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_string_compare(struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_structure_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_variant_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_array_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_sequence_compare_recursive(
                struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_compare(struct bt_ctf_field_type_common *ft_a,
                struct bt_ctf_field_type_common *ft_b);
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_get_field_count(struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 struct bt_ctf_field_type_common *bt_ctf_field_type_common_borrow_field_at_index(
                struct bt_ctf_field_type_common *ft, int index);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_get_field_index(struct bt_ctf_field_type_common *ft,
                const char *name);
 
-BT_HIDDEN
 struct bt_ctf_field_path *bt_ctf_field_type_common_variant_borrow_tag_field_path(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 struct bt_ctf_field_path *bt_ctf_field_type_common_sequence_borrow_length_field_path(
                struct bt_ctf_field_type_common *ft);
 
-BT_HIDDEN
 int bt_ctf_field_type_common_validate_single_clock_class(
                struct bt_ctf_field_type_common *ft,
                struct bt_ctf_clock_class **expected_clock_class);
 
-BT_HIDDEN
 int64_t bt_ctf_field_type_common_variant_find_choice_index(
                struct bt_ctf_field_type_common *ft, uint64_t uval,
                bool is_signed);
 
-BT_HIDDEN
 int bt_ctf_field_type_serialize_recursive(struct bt_ctf_field_type *type,
                struct metadata_context *context);
 
-BT_HIDDEN
 struct bt_ctf_field_type *bt_ctf_field_type_copy(struct bt_ctf_field_type *ft);
 
 #endif /* BABELTRACE_CTF_WRITER_FIELD_TYPES_INTERNAL_H */
index 9c84051239c2c2c4740340f080373def1ac1232e..c801de4bfad36a6a4133575f4220507165fccb5d 100644 (file)
@@ -13,8 +13,8 @@
 #include "field-wrapper.h"
 #include "object.h"
 
-BT_HIDDEN
-struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_new(void *data)
+struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_new(
+               void *data __attribute__((unused)))
 {
        struct bt_ctf_field_wrapper *field_wrapper =
                g_new0(struct bt_ctf_field_wrapper, 1);
@@ -34,7 +34,6 @@ end:
        return field_wrapper;
 }
 
-BT_HIDDEN
 void bt_ctf_field_wrapper_destroy(struct bt_ctf_field_wrapper *field_wrapper)
 {
        BT_LOGD("Destroying field wrapper: addr=%p", field_wrapper);
@@ -43,7 +42,6 @@ void bt_ctf_field_wrapper_destroy(struct bt_ctf_field_wrapper *field_wrapper)
        g_free(field_wrapper);
 }
 
-BT_HIDDEN
 struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_create(
                struct bt_ctf_object_pool *pool, struct bt_ctf_field_type *ft)
 {
index e2c1227fe9e9f3d18ef47a2a80c7737a5fb83f3a..2b3286d2116d8b660bcba70ed0eabd868df2fb10 100644 (file)
@@ -20,13 +20,10 @@ struct bt_ctf_field_wrapper {
        struct bt_ctf_field_common *field;
 };
 
-BT_HIDDEN
 struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_new(void *data);
 
-BT_HIDDEN
 void bt_ctf_field_wrapper_destroy(struct bt_ctf_field_wrapper *field);
 
-BT_HIDDEN
 struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_create(
                struct bt_ctf_object_pool *pool, struct bt_ctf_field_type *ft);
 
index 587ed14fd1047cb728274118b77694ef19827e76..e05bcbcba9bd28f4a485ccab39a2a96cc05150df 100644 (file)
@@ -32,7 +32,6 @@
                _name " is not an integer or an enumeration field: "    \
                "field-addr=%p", (_field))
 
-BT_HIDDEN
 struct bt_ctf_field_common *bt_ctf_field_common_copy(struct bt_ctf_field_common *field)
 {
        struct bt_ctf_field_common *copy = NULL;
@@ -52,7 +51,6 @@ end:
        return copy;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_structure_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
@@ -96,7 +94,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_variant_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
@@ -148,7 +145,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_string_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
@@ -166,7 +162,8 @@ int bt_ctf_field_common_string_initialize(struct bt_ctf_field_common *field,
                goto end;
        }
 
-       g_array_index(string->buf, char, 0) = '\0';
+       g_array_set_size(string->buf, 1);
+       bt_g_array_index(string->buf, char, 0) = '\0';
        BT_LOGD("Initialized common string field object: addr=%p, ft-addr=%p",
                field, type);
 
@@ -174,7 +171,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_array_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
@@ -218,7 +214,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_sequence_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
@@ -246,13 +241,11 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_generic_validate(struct bt_ctf_field_common *field)
 {
        return (field && field->payload_set) ? 0 : -1;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_structure_validate_recursive(struct bt_ctf_field_common *field)
 {
        int64_t i;
@@ -284,7 +277,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_variant_validate_recursive(struct bt_ctf_field_common *field)
 {
        int ret = 0;
@@ -303,7 +295,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_array_validate_recursive(struct bt_ctf_field_common *field)
 {
        int64_t i;
@@ -327,7 +318,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_common_sequence_validate_recursive(struct bt_ctf_field_common *field)
 {
        int64_t i;
@@ -351,14 +341,12 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_generic_reset(struct bt_ctf_field_common *field)
 {
        BT_ASSERT_DBG(field);
        field->payload_set = false;
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_structure_reset_recursive(struct bt_ctf_field_common *field)
 {
        int64_t i;
@@ -382,7 +370,6 @@ void bt_ctf_field_common_structure_reset_recursive(struct bt_ctf_field_common *f
        }
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_variant_reset_recursive(struct bt_ctf_field_common *field)
 {
        struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field);
@@ -391,7 +378,6 @@ void bt_ctf_field_common_variant_reset_recursive(struct bt_ctf_field_common *fie
        variant->current_field = NULL;
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_array_reset_recursive(struct bt_ctf_field_common *field)
 {
        size_t i;
@@ -414,7 +400,6 @@ void bt_ctf_field_common_array_reset_recursive(struct bt_ctf_field_common *field
        }
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_sequence_reset_recursive(struct bt_ctf_field_common *field)
 {
        struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field);
@@ -432,14 +417,12 @@ void bt_ctf_field_common_sequence_reset_recursive(struct bt_ctf_field_common *fi
        sequence->length = 0;
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_generic_set_is_frozen(struct bt_ctf_field_common *field,
                bool is_frozen)
 {
        field->frozen = is_frozen;
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_structure_set_is_frozen_recursive(
                struct bt_ctf_field_common *field, bool is_frozen)
 {
@@ -462,7 +445,6 @@ void bt_ctf_field_common_structure_set_is_frozen_recursive(
        bt_ctf_field_common_generic_set_is_frozen(field, is_frozen);
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_variant_set_is_frozen_recursive(
                struct bt_ctf_field_common *field, bool is_frozen)
 {
@@ -483,7 +465,6 @@ void bt_ctf_field_common_variant_set_is_frozen_recursive(
        bt_ctf_field_common_generic_set_is_frozen(field, is_frozen);
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_array_set_is_frozen_recursive(
                struct bt_ctf_field_common *field, bool is_frozen)
 {
@@ -505,7 +486,6 @@ void bt_ctf_field_common_array_set_is_frozen_recursive(
        bt_ctf_field_common_generic_set_is_frozen(field, is_frozen);
 }
 
-BT_HIDDEN
 void bt_ctf_field_common_sequence_set_is_frozen_recursive(
                struct bt_ctf_field_common *field, bool is_frozen)
 {
@@ -528,7 +508,6 @@ void bt_ctf_field_common_sequence_set_is_frozen_recursive(
        bt_ctf_field_common_generic_set_is_frozen(field, is_frozen);
 }
 
-BT_HIDDEN
 void _bt_ctf_field_common_set_is_frozen_recursive(struct bt_ctf_field_common *field,
                bool is_frozen)
 {
@@ -546,13 +525,11 @@ end:
        return;
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_generic_is_set(struct bt_ctf_field_common *field)
 {
        return field && field->payload_set;
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_structure_is_set_recursive(
                struct bt_ctf_field_common *field)
 {
@@ -574,7 +551,6 @@ end:
        return is_set;
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_variant_is_set_recursive(struct bt_ctf_field_common *field)
 {
        struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field);
@@ -590,7 +566,6 @@ bt_ctf_bool bt_ctf_field_common_variant_is_set_recursive(struct bt_ctf_field_com
        return is_set;
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_array_is_set_recursive(struct bt_ctf_field_common *field)
 {
        size_t i;
@@ -610,7 +585,6 @@ end:
        return is_set;
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_sequence_is_set_recursive(struct bt_ctf_field_common *field)
 {
        size_t i;
@@ -840,7 +814,6 @@ void bt_ctf_field_string_destroy(struct bt_ctf_field *field)
        g_free(field);
 }
 
-BT_HIDDEN
 int bt_ctf_field_serialize_recursive(struct bt_ctf_field *field,
                struct bt_ctfser *ctfser,
                enum bt_ctf_byte_order native_byte_order)
@@ -1121,6 +1094,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_field_create(struct bt_ctf_field_type *type)
 {
        struct bt_ctf_field *field = NULL;
@@ -1142,11 +1116,13 @@ end:
        return field;
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_field_get_type(struct bt_ctf_field *field)
 {
        return bt_ctf_object_get_ref(bt_ctf_field_common_borrow_type((void *) field));
 }
 
+BT_EXPORT
 enum bt_ctf_field_type_id bt_ctf_field_get_type_id(struct bt_ctf_field *field)
 {
        struct bt_ctf_field_common *field_common = (void *) field;
@@ -1155,6 +1131,7 @@ enum bt_ctf_field_type_id bt_ctf_field_get_type_id(struct bt_ctf_field *field)
        return (int) field_common->type->id;
 }
 
+BT_EXPORT
 int bt_ctf_field_sequence_set_length(struct bt_ctf_field *field,
                struct bt_ctf_field *length_field)
 {
@@ -1182,6 +1159,7 @@ int bt_ctf_field_sequence_set_length(struct bt_ctf_field *field,
                length, (bt_ctf_field_common_create_func) bt_ctf_field_create);
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_field_structure_get_field_by_index(
                struct bt_ctf_field *field, uint64_t index)
 {
@@ -1189,6 +1167,7 @@ struct bt_ctf_field *bt_ctf_field_structure_get_field_by_index(
                (void *) field, index));
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_field_structure_get_field_by_name(
                struct bt_ctf_field *field, const char *name)
 {
@@ -1196,6 +1175,7 @@ struct bt_ctf_field *bt_ctf_field_structure_get_field_by_name(
                (void *) field, name));
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_field_array_get_field(
                struct bt_ctf_field *field, uint64_t index)
 {
@@ -1203,6 +1183,7 @@ struct bt_ctf_field *bt_ctf_field_array_get_field(
                bt_ctf_field_common_array_borrow_field((void *) field, index));
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_field_sequence_get_field(
                struct bt_ctf_field *field, uint64_t index)
 {
@@ -1210,6 +1191,7 @@ struct bt_ctf_field *bt_ctf_field_sequence_get_field(
                bt_ctf_field_common_sequence_borrow_field((void *) field, index));
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_field_variant_get_field(struct bt_ctf_field *field,
                struct bt_ctf_field *tag_field)
 {
@@ -1270,6 +1252,7 @@ end:
        return current_field;
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_field_variant_get_current_field(
                struct bt_ctf_field *variant_field)
 {
@@ -1277,7 +1260,6 @@ struct bt_ctf_field *bt_ctf_field_variant_get_current_field(
                (void *) variant_field));
 }
 
-BT_HIDDEN
 struct bt_ctf_field *bt_ctf_field_enumeration_borrow_container(
                struct bt_ctf_field *field)
 {
@@ -1290,12 +1272,14 @@ struct bt_ctf_field *bt_ctf_field_enumeration_borrow_container(
        return (void *) enumeration->container;
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_field_enumeration_get_container(
                struct bt_ctf_field *field)
 {
        return bt_ctf_object_get_ref(bt_ctf_field_enumeration_borrow_container(field));
 }
 
+BT_EXPORT
 int bt_ctf_field_integer_signed_get_value(struct bt_ctf_field *field,
                int64_t *value)
 {
@@ -1313,6 +1297,7 @@ int bt_ctf_field_integer_signed_get_value(struct bt_ctf_field *field,
        return 0;
 }
 
+BT_EXPORT
 int bt_ctf_field_integer_signed_set_value(struct bt_ctf_field *field,
                int64_t value)
 {
@@ -1336,6 +1321,7 @@ int bt_ctf_field_integer_signed_set_value(struct bt_ctf_field *field,
        return ret;
 }
 
+BT_EXPORT
 int bt_ctf_field_integer_unsigned_get_value(struct bt_ctf_field *field,
                uint64_t *value)
 {
@@ -1353,6 +1339,7 @@ int bt_ctf_field_integer_unsigned_get_value(struct bt_ctf_field *field,
        return 0;
 }
 
+BT_EXPORT
 int bt_ctf_field_integer_unsigned_set_value(struct bt_ctf_field *field,
                uint64_t value)
 {
@@ -1375,39 +1362,46 @@ int bt_ctf_field_integer_unsigned_set_value(struct bt_ctf_field *field,
        return 0;
 }
 
+BT_EXPORT
 int bt_ctf_field_floating_point_get_value(struct bt_ctf_field *field,
                double *value)
 {
        return bt_ctf_field_common_floating_point_get_value((void *) field, value);
 }
 
+BT_EXPORT
 int bt_ctf_field_floating_point_set_value(struct bt_ctf_field *field,
                double value)
 {
        return bt_ctf_field_common_floating_point_set_value((void *) field, value);
 }
 
+BT_EXPORT
 const char *bt_ctf_field_string_get_value(struct bt_ctf_field *field)
 {
        return bt_ctf_field_common_string_get_value((void *) field);
 }
 
+BT_EXPORT
 int bt_ctf_field_string_set_value(struct bt_ctf_field *field, const char *value)
 {
        return bt_ctf_field_common_string_set_value((void *) field, value);
 }
 
+BT_EXPORT
 int bt_ctf_field_string_append(struct bt_ctf_field *field, const char *value)
 {
        return bt_ctf_field_common_string_append((void *) field, value);
 }
 
+BT_EXPORT
 int bt_ctf_field_string_append_len(struct bt_ctf_field *field,
                const char *value, unsigned int length)
 {
        return bt_ctf_field_common_string_append_len((void *) field, value, length);
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_field_copy(struct bt_ctf_field *field)
 {
        return (void *) bt_ctf_field_common_copy((void *) field);
@@ -1802,7 +1796,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_field_structure_set_field_by_name(struct bt_ctf_field *field,
                const char *name, struct bt_ctf_field *value)
 {
index 2d6013ab65272a693a1acb3024711cbf6759779f..3c13c3efa215636229cd7f35a83e05134f0fb4e2 100644 (file)
@@ -18,7 +18,6 @@
 #include <string.h>
 
 #include <babeltrace2-ctf-writer/fields.h>
-#include <babeltrace2/types.h>
 
 #include "common/macros.h"
 #include "common/common.h"
@@ -142,10 +141,8 @@ struct bt_ctf_field_common_string {
        size_t size;
 };
 
-BT_HIDDEN
 struct bt_ctf_field_common *bt_ctf_field_common_copy(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 int bt_ctf_field_common_structure_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
@@ -153,7 +150,6 @@ int bt_ctf_field_common_structure_initialize(struct bt_ctf_field_common *field,
                bt_ctf_field_common_create_func field_create_func,
                GDestroyNotify field_release_func);
 
-BT_HIDDEN
 int bt_ctf_field_common_array_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
@@ -161,14 +157,12 @@ int bt_ctf_field_common_array_initialize(struct bt_ctf_field_common *field,
                bt_ctf_field_common_create_func field_create_func,
                GDestroyNotify field_destroy_func);
 
-BT_HIDDEN
 int bt_ctf_field_common_sequence_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
                struct bt_ctf_field_common_methods *methods,
                GDestroyNotify field_destroy_func);
 
-BT_HIDDEN
 int bt_ctf_field_common_variant_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
@@ -176,80 +170,58 @@ int bt_ctf_field_common_variant_initialize(struct bt_ctf_field_common *field,
                bt_ctf_field_common_create_func field_create_func,
                GDestroyNotify field_release_func);
 
-BT_HIDDEN
 int bt_ctf_field_common_string_initialize(struct bt_ctf_field_common *field,
                struct bt_ctf_field_type_common *type,
                bool is_shared, bt_ctf_object_release_func release_func,
                struct bt_ctf_field_common_methods *methods);
 
-BT_HIDDEN
 int bt_ctf_field_common_generic_validate(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 int bt_ctf_field_common_structure_validate_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 int bt_ctf_field_common_variant_validate_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 int bt_ctf_field_common_array_validate_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 int bt_ctf_field_common_sequence_validate_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 void bt_ctf_field_common_generic_reset(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 void bt_ctf_field_common_structure_reset_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 void bt_ctf_field_common_variant_reset_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 void bt_ctf_field_common_array_reset_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 void bt_ctf_field_common_sequence_reset_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 void bt_ctf_field_common_generic_set_is_frozen(struct bt_ctf_field_common *field,
                bool is_frozen);
 
-BT_HIDDEN
 void bt_ctf_field_common_structure_set_is_frozen_recursive(
                struct bt_ctf_field_common *field, bool is_frozen);
 
-BT_HIDDEN
 void bt_ctf_field_common_variant_set_is_frozen_recursive(
                struct bt_ctf_field_common *field, bool is_frozen);
 
-BT_HIDDEN
 void bt_ctf_field_common_array_set_is_frozen_recursive(
                struct bt_ctf_field_common *field, bool is_frozen);
 
-BT_HIDDEN
 void bt_ctf_field_common_sequence_set_is_frozen_recursive(
                struct bt_ctf_field_common *field, bool is_frozen);
 
-BT_HIDDEN
 void _bt_ctf_field_common_set_is_frozen_recursive(struct bt_ctf_field_common *field,
                bool is_frozen);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_generic_is_set(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_structure_is_set_recursive(
                struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_variant_is_set_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_array_is_set_recursive(struct bt_ctf_field_common *field);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_field_common_sequence_is_set_recursive(struct bt_ctf_field_common *field);
 
 #ifdef BT_DEV_MODE
@@ -812,21 +784,25 @@ struct bt_ctf_field_variant {
        struct bt_ctf_field_enumeration *tag;
 };
 
-BT_HIDDEN
 int bt_ctf_field_serialize_recursive(struct bt_ctf_field *field,
                struct bt_ctfser *ctfser,
                enum bt_ctf_byte_order native_byte_order);
 
-BT_HIDDEN
 int bt_ctf_field_structure_set_field_by_name(struct bt_ctf_field *field,
                const char *name, struct bt_ctf_field *value);
 
-BT_HIDDEN
 struct bt_ctf_field *bt_ctf_field_enumeration_borrow_container(
                struct bt_ctf_field *field);
 
+#ifndef BT_DEV_MODE
+#define BT_FIELD_UNUSED_ATTR __attribute__((unused))
+#else
+#define BT_FIELD_UNUSED_ATTR
+#endif
+
 static inline
-bt_ctf_bool bt_ctf_field_is_set_recursive(struct bt_ctf_field *field)
+bt_ctf_bool bt_ctf_field_is_set_recursive(
+               struct bt_ctf_field *field BT_FIELD_UNUSED_ATTR)
 {
        return bt_ctf_field_common_is_set_recursive((void *) field);
 }
index 0dc6dca795bb0af946b0d5d95f6bf0332ffb27fe..d9cbd495dfc300f53ff0b4a43d371a87b2491e24 100644 (file)
@@ -11,7 +11,6 @@
 #include "functor.h"
 #include "utils.h"
 
-BT_HIDDEN
 void value_exists(gpointer element, gpointer search_query)
 {
        if (element == ((struct bt_ctf_search_query *)search_query)->value) {
index 056caee9ac060949a4d2560d5075793806e2711e..0b13d47e88ca0b27672e0421b43da81eab776524 100644 (file)
@@ -10,7 +10,6 @@
 #include <glib.h>
 #include "common/macros.h"
 
-BT_HIDDEN
 void value_exists(gpointer element, gpointer search_query);
 
 #endif /* BABELTRACE_CTF_WRITER_FUNCTOR_INTERNAL_H */
index 9631801a359f452f6224c713badf7d9659f2acb9..bfd937c261011af5dc0af0f4d41742c110633058 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "object-pool.h"
 
+BT_EXPORT
 int bt_ctf_object_pool_initialize(struct bt_ctf_object_pool *pool,
                bt_ctf_object_pool_new_object_func new_object_func,
                bt_ctf_object_pool_destroy_object_func destroy_object_func,
@@ -49,6 +50,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 void bt_ctf_object_pool_finalize(struct bt_ctf_object_pool *pool)
 {
        uint64_t i;
index ff71e79cd66cc1ecac27bd8a00f27075266de68b..cf4e3f082d2acdd4346e3f5c49e1d8d1385c7adb 100644 (file)
@@ -7,6 +7,7 @@
 #include "object.h"
 #include <babeltrace2-ctf-writer/object.h>
 
+BT_EXPORT
 void *bt_ctf_object_get_ref(void *obj)
 {
        if (G_UNLIKELY(!obj)) {
@@ -19,6 +20,7 @@ end:
        return obj;
 }
 
+BT_EXPORT
 void bt_ctf_object_put_ref(void *obj)
 {
        if (G_UNLIKELY(!obj)) {
index c6316c0bfee0e40ebb1473e5065aa7dfc9bb2be3..daa2b32b7ee1ec1ba78a2d43d33f972244031273 100644 (file)
@@ -18,7 +18,6 @@
 #include <babeltrace2-ctf-writer/field-types.h>
 #include <babeltrace2-ctf-writer/object.h>
 #include <babeltrace2-ctf-writer/stream-class.h>
-#include <babeltrace2/types.h>
 
 #include "common/macros.h"
 #include "common/assert.h"
@@ -278,7 +277,8 @@ end:
  * Destroys a path token.
  */
 static
-void ptokens_destroy_func(gpointer ptoken, gpointer data)
+void ptokens_destroy_func(gpointer ptoken,
+               gpointer data __attribute__((unused)))
 {
        g_string_free(ptoken, TRUE);
 }
@@ -541,7 +541,7 @@ int relative_ptokens_to_field_path(GList *ptokens,
                        }
 
                        for (i = 0; i < tail_field_path_len; i++) {
-                               int index = g_array_index(
+                               int index = bt_g_array_index(
                                        tail_field_path->indexes,
                                        int, i);
 
@@ -662,7 +662,7 @@ struct bt_ctf_field_path *pathstr_to_field_path(const char *pathstr,
                GString *field_path_pretty =
                        bt_ctf_field_path_string(field_path);
                const char *field_path_pretty_str =
-                       field_path_pretty ? field_path_pretty->str : NULL;
+                       field_path_pretty ? field_path_pretty->str : "(null)";
 
                BT_LOGT("Found field path: path=\"%s\", field-path=\"%s\"",
                        pathstr, field_path_pretty_str);
@@ -709,7 +709,7 @@ struct bt_ctf_field_type_common *field_path_to_field_type(
        for (i = 0; i < field_path->indexes->len; i++) {
                struct bt_ctf_field_type_common *child_type;
                int child_index =
-                       g_array_index(field_path->indexes, int, i);
+                       bt_g_array_index(field_path->indexes, int, i);
 
                /* Get child field type */
                child_type = bt_ctf_field_type_common_borrow_field_at_index(type,
@@ -785,9 +785,9 @@ int get_field_paths_lca_index(struct bt_ctf_field_path *field_path1,
                GString *field_path2_pretty =
                        bt_ctf_field_path_string(field_path2);
                const char *field_path1_pretty_str =
-                       field_path1_pretty ? field_path1_pretty->str : NULL;
+                       field_path1_pretty ? field_path1_pretty->str : "(null)";
                const char *field_path2_pretty_str =
-                       field_path2_pretty ? field_path2_pretty->str : NULL;
+                       field_path2_pretty ? field_path2_pretty->str : "(null)";
 
                BT_LOGT("Finding lowest common ancestor (LCA) between two field paths: "
                        "field-path-1=\"%s\", field-path-2=\"%s\"",
@@ -827,9 +827,9 @@ int get_field_paths_lca_index(struct bt_ctf_field_path *field_path1,
                        break;
                }
 
-               target_index = g_array_index(field_path1->indexes, int,
+               target_index = bt_g_array_index(field_path1->indexes, int,
                        lca_index);
-               ctx_index = g_array_index(field_path2->indexes, int,
+               ctx_index = bt_g_array_index(field_path2->indexes, int,
                        lca_index);
 
                if (target_index != ctx_index) {
@@ -910,9 +910,9 @@ int validate_target_field_path(struct bt_ctf_field_path *target_field_path,
                 * Make sure the target field path is located before the
                 * context field path.
                 */
-               target_index = g_array_index(target_field_path->indexes,
+               target_index = bt_g_array_index(target_field_path->indexes,
                        int, lca_index);
-               ctx_index = g_array_index(ctx_field_path->indexes,
+               ctx_index = bt_g_array_index(ctx_field_path->indexes,
                        int, lca_index);
 
                if (target_index >= ctx_index) {
@@ -1048,7 +1048,7 @@ int resolve_sequence_or_variant_type(struct bt_ctf_field_type_common *type,
                ret = bt_ctf_field_type_common_variant_set_tag_field_path(
                        type, target_field_path);
                if (ret) {
-                       BT_LOGW("Cannot set varaint field type's tag field path: "
+                       BT_LOGW("Cannot set variant field type's tag field path: "
                                "ret=%d, ft-addr=%p, path=\"%s\", target-field-path=\"%s\"",
                                ret, type, pathstr,
                                target_field_path_pretty_str);
@@ -1058,7 +1058,7 @@ int resolve_sequence_or_variant_type(struct bt_ctf_field_type_common *type,
                ret = bt_ctf_field_type_common_variant_set_tag_field_type(
                        type, target_type);
                if (ret) {
-                       BT_LOGW("Cannot set varaint field type's tag field type: "
+                       BT_LOGW("Cannot set variant field type's tag field type: "
                                "ret=%d, ft-addr=%p, path=\"%s\", target-field-path=\"%s\"",
                                ret, type, pathstr,
                                target_field_path_pretty_str);
@@ -1197,7 +1197,6 @@ int resolve_root_type(enum bt_ctf_scope root_scope, struct resolve_context *ctx)
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_resolve_types(
                struct bt_ctf_private_value *environment,
                struct bt_ctf_field_type_common *packet_header_type,
index 51f3e693bbfdddc2ab9941f7e0cb764bf4b34f43..7d72e9343bf94beab3034d6ee0f2a8d0a494d68d 100644 (file)
@@ -39,7 +39,6 @@ enum bt_ctf_resolve_flag {
  *
  * All parameters are owned by the caller.
  */
-BT_HIDDEN
 int bt_ctf_resolve_types(struct bt_ctf_private_value *environment,
                struct bt_ctf_field_type_common *packet_header_type,
                struct bt_ctf_field_type_common *packet_context_type,
index fa3b54fb6ef8bb661bed139cef957acc10570a2a..1c6245dea199970c60d42dffe8ca35106026aae3 100644 (file)
@@ -34,7 +34,6 @@
 #include "visitor.h"
 #include "writer.h"
 
-BT_HIDDEN
 int bt_ctf_stream_class_common_initialize(struct bt_ctf_stream_class_common *stream_class,
                const char *name, bt_ctf_object_release_func release_func)
 {
@@ -64,7 +63,6 @@ error:
        return -1;
 }
 
-BT_HIDDEN
 void bt_ctf_stream_class_common_finalize(struct bt_ctf_stream_class_common *stream_class)
 {
        BT_LOGD("Finalizing common stream class: addr=%p, name=\"%s\", id=%" PRId64,
@@ -129,7 +127,6 @@ end:
        return;
 }
 
-BT_HIDDEN
 int bt_ctf_stream_class_common_add_event_class(
                struct bt_ctf_stream_class_common *stream_class,
                struct bt_ctf_event_class_common *event_class,
@@ -401,7 +398,6 @@ int visit_event_class(void *object, bt_ctf_visitor visitor,void *data)
        return visitor(&obj, data);
 }
 
-BT_HIDDEN
 int bt_ctf_stream_class_common_visit(struct bt_ctf_stream_class_common *stream_class,
                bt_ctf_visitor visitor, void *data)
 {
@@ -428,7 +424,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_stream_class_visit(struct bt_ctf_stream_class *stream_class,
                bt_ctf_visitor visitor, void *data)
 {
@@ -436,7 +431,6 @@ int bt_ctf_stream_class_visit(struct bt_ctf_stream_class *stream_class,
                visitor, data);
 }
 
-BT_HIDDEN
 void bt_ctf_stream_class_common_freeze(struct bt_ctf_stream_class_common *stream_class)
 {
        if (!stream_class || stream_class->frozen) {
@@ -453,7 +447,6 @@ void bt_ctf_stream_class_common_freeze(struct bt_ctf_stream_class_common *stream
        bt_ctf_clock_class_freeze(stream_class->clock_class);
 }
 
-BT_HIDDEN
 int bt_ctf_stream_class_common_validate_single_clock_class(
                struct bt_ctf_stream_class_common *stream_class,
                struct bt_ctf_clock_class **expected_clock_class)
@@ -680,6 +673,7 @@ void bt_ctf_stream_class_destroy(struct bt_ctf_object *obj)
        g_free(stream_class);
 }
 
+BT_EXPORT
 struct bt_ctf_stream_class *bt_ctf_stream_class_create(const char *name)
 {
        struct bt_ctf_stream_class *stream_class;
@@ -785,7 +779,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_stream_class_map_clock_class(
                struct bt_ctf_stream_class *stream_class,
                struct bt_ctf_field_type *packet_context_type,
@@ -829,6 +822,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_clock *bt_ctf_stream_class_get_clock(
                struct bt_ctf_stream_class *stream_class)
 {
@@ -854,6 +848,7 @@ end:
        return clock;
 }
 
+BT_EXPORT
 int bt_ctf_stream_class_set_clock(
                struct bt_ctf_stream_class *stream_class,
                struct bt_ctf_clock *clock)
@@ -894,7 +889,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class,
                struct metadata_context *context)
 {
@@ -923,7 +917,7 @@ int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class,
         * The reference to the trace is only borrowed since the
         * serialization of the stream class might have been triggered
         * by the trace's destruction. In such a case, the trace's
-        * reference count would, unexepectedly, go through the sequence
+        * reference count would, unexpectedly, go through the sequence
         * 1 -> 0 -> 1 -> 0 -> ..., provoking an endless loop of destruction
         * and serialization.
         */
@@ -1018,6 +1012,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_trace *bt_ctf_stream_class_get_trace(
                struct bt_ctf_stream_class *stream_class)
 {
@@ -1025,12 +1020,14 @@ struct bt_ctf_trace *bt_ctf_stream_class_get_trace(
                BT_CTF_TO_COMMON(stream_class)));
 }
 
+BT_EXPORT
 const char *bt_ctf_stream_class_get_name(
                struct bt_ctf_stream_class *stream_class)
 {
        return bt_ctf_stream_class_common_get_name(BT_CTF_TO_COMMON(stream_class));
 }
 
+BT_EXPORT
 int bt_ctf_stream_class_set_name(
                struct bt_ctf_stream_class *stream_class, const char *name)
 {
@@ -1038,18 +1035,21 @@ int bt_ctf_stream_class_set_name(
                name);
 }
 
+BT_EXPORT
 int64_t bt_ctf_stream_class_get_id(
                struct bt_ctf_stream_class *stream_class)
 {
        return bt_ctf_stream_class_common_get_id(BT_CTF_TO_COMMON(stream_class));
 }
 
+BT_EXPORT
 int bt_ctf_stream_class_set_id(
                struct bt_ctf_stream_class *stream_class, uint64_t id)
 {
        return bt_ctf_stream_class_common_set_id(BT_CTF_TO_COMMON(stream_class), id);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_stream_class_get_packet_context_type(
                struct bt_ctf_stream_class *stream_class)
 {
@@ -1058,6 +1058,7 @@ struct bt_ctf_field_type *bt_ctf_stream_class_get_packet_context_type(
                        BT_CTF_TO_COMMON(stream_class)));
 }
 
+BT_EXPORT
 int bt_ctf_stream_class_set_packet_context_type(
                struct bt_ctf_stream_class *stream_class,
                struct bt_ctf_field_type *packet_context_type)
@@ -1066,6 +1067,7 @@ int bt_ctf_stream_class_set_packet_context_type(
                BT_CTF_TO_COMMON(stream_class), (void *) packet_context_type);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *
 bt_ctf_stream_class_get_event_header_type(
                struct bt_ctf_stream_class *stream_class)
@@ -1075,6 +1077,7 @@ bt_ctf_stream_class_get_event_header_type(
                        BT_CTF_TO_COMMON(stream_class)));
 }
 
+BT_EXPORT
 int bt_ctf_stream_class_set_event_header_type(
                struct bt_ctf_stream_class *stream_class,
                struct bt_ctf_field_type *event_header_type)
@@ -1083,6 +1086,7 @@ int bt_ctf_stream_class_set_event_header_type(
                BT_CTF_TO_COMMON(stream_class), (void *) event_header_type);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *
 bt_ctf_stream_class_get_event_context_type(
                struct bt_ctf_stream_class *stream_class)
@@ -1092,6 +1096,7 @@ bt_ctf_stream_class_get_event_context_type(
                        BT_CTF_TO_COMMON(stream_class)));
 }
 
+BT_EXPORT
 int bt_ctf_stream_class_set_event_context_type(
                struct bt_ctf_stream_class *stream_class,
                struct bt_ctf_field_type *event_context_type)
@@ -1100,6 +1105,7 @@ int bt_ctf_stream_class_set_event_context_type(
                BT_CTF_TO_COMMON(stream_class), (void *) event_context_type);
 }
 
+BT_EXPORT
 int64_t bt_ctf_stream_class_get_event_class_count(
                struct bt_ctf_stream_class *stream_class)
 {
@@ -1107,6 +1113,7 @@ int64_t bt_ctf_stream_class_get_event_class_count(
                BT_CTF_TO_COMMON(stream_class));
 }
 
+BT_EXPORT
 struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_index(
                struct bt_ctf_stream_class *stream_class, uint64_t index)
 {
@@ -1115,6 +1122,7 @@ struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_index(
                        BT_CTF_TO_COMMON(stream_class), index));
 }
 
+BT_EXPORT
 struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_id(
                struct bt_ctf_stream_class *stream_class, uint64_t id)
 {
@@ -1123,6 +1131,7 @@ struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_id(
                        BT_CTF_TO_COMMON(stream_class), id));
 }
 
+BT_EXPORT
 int bt_ctf_stream_class_add_event_class(
                struct bt_ctf_stream_class *stream_class,
                struct bt_ctf_event_class *event_class)
index 9cb78fe6c2c6b3682c0836ba76bb33059d11cb0a..ab54ce73db762c161437ebf565a1343b0749af36 100644 (file)
@@ -70,14 +70,11 @@ struct bt_ctf_stream_class_common {
 
 struct bt_ctf_event_class_common;
 
-BT_HIDDEN
 int bt_ctf_stream_class_common_initialize(struct bt_ctf_stream_class_common *stream_class,
                const char *name, bt_ctf_object_release_func release_func);
 
-BT_HIDDEN
 void bt_ctf_stream_class_common_finalize(struct bt_ctf_stream_class_common *stream_class);
 
-BT_HIDDEN
 void bt_ctf_stream_class_common_freeze(struct bt_ctf_stream_class_common *stream_class);
 
 static inline
@@ -110,26 +107,21 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctf_stream_class_common_set_byte_order(
                struct bt_ctf_stream_class_common *stream_class, int byte_order);
 
-BT_HIDDEN
 int bt_ctf_stream_class_common_validate_single_clock_class(
                struct bt_ctf_stream_class_common *stream_class,
                struct bt_ctf_clock_class **expected_clock_class);
 
-BT_HIDDEN
 int bt_ctf_stream_class_common_add_event_class(
                struct bt_ctf_stream_class_common *stream_class,
                struct bt_ctf_event_class_common *event_class,
                bt_ctf_validation_flag_copy_field_type_func copy_field_type_func);
 
-BT_HIDDEN
 int bt_ctf_stream_class_common_visit(struct bt_ctf_stream_class_common *stream_class,
                bt_ctf_visitor visitor, void *data);
 
-BT_HIDDEN
 int bt_ctf_stream_class_visit(struct bt_ctf_stream_class *stream_class,
                bt_ctf_visitor visitor, void *data);
 
@@ -508,11 +500,9 @@ struct bt_ctf_stream_class {
 
 struct metadata_context;
 
-BT_HIDDEN
 int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class,
                struct metadata_context *context);
 
-BT_HIDDEN
 int bt_ctf_stream_class_map_clock_class(
                struct bt_ctf_stream_class *stream_class,
                struct bt_ctf_field_type *packet_context_type,
index ccd7cc605bc797d01c3ab3df2fa8e644ed6aad27..43150a880f2fb722f6b0c1fad6110fc45fdd43c6 100644 (file)
@@ -33,7 +33,6 @@
 #include "trace.h"
 #include "writer.h"
 
-BT_HIDDEN
 void bt_ctf_stream_common_finalize(struct bt_ctf_stream_common *stream)
 {
        BT_LOGD("Finalizing common stream object: addr=%p, name=\"%s\"",
@@ -44,7 +43,6 @@ void bt_ctf_stream_common_finalize(struct bt_ctf_stream_common *stream)
        }
 }
 
-BT_HIDDEN
 int bt_ctf_stream_common_initialize(
                struct bt_ctf_stream_common *stream,
                struct bt_ctf_stream_class_common *stream_class, const char *name,
@@ -480,7 +478,7 @@ static
 void update_clock_value(uint64_t *val, uint64_t new_val,
                unsigned int new_val_size)
 {
-       const uint64_t pow2 = 1ULL << new_val_size;
+       const uint64_t pow2 = new_val_size == 64 ? 0 : 1ULL << new_val_size;
        const uint64_t mask = pow2 - 1;
        uint64_t val_masked;
 
@@ -1030,7 +1028,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_ctf_stream *bt_ctf_stream_create_with_id(
                struct bt_ctf_stream_class *stream_class,
                const char *name, uint64_t id)
@@ -1161,6 +1158,7 @@ end:
        return stream;
 }
 
+BT_EXPORT
 struct bt_ctf_stream *bt_ctf_stream_create(
                struct bt_ctf_stream_class *stream_class,
                const char *name, uint64_t id_param)
@@ -1169,6 +1167,7 @@ struct bt_ctf_stream *bt_ctf_stream_create(
                name, id_param);
 }
 
+BT_EXPORT
 int bt_ctf_stream_get_discarded_events_count(
                struct bt_ctf_stream *stream, uint64_t *count)
 {
@@ -1223,6 +1222,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 void bt_ctf_stream_append_discarded_events(struct bt_ctf_stream *stream,
                uint64_t event_count)
 {
@@ -1364,6 +1364,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 int bt_ctf_stream_append_event(struct bt_ctf_stream *stream,
                struct bt_ctf_event *event)
 {
@@ -1450,6 +1451,7 @@ error:
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_stream_get_packet_context(struct bt_ctf_stream *stream)
 {
        struct bt_ctf_field *packet_context = NULL;
@@ -1467,6 +1469,7 @@ end:
        return packet_context;
 }
 
+BT_EXPORT
 int bt_ctf_stream_set_packet_context(struct bt_ctf_stream *stream,
                struct bt_ctf_field *field)
 {
@@ -1503,6 +1506,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_field *bt_ctf_stream_get_packet_header(struct bt_ctf_stream *stream)
 {
        struct bt_ctf_field *packet_header = NULL;
@@ -1520,6 +1524,7 @@ end:
        return packet_header;
 }
 
+BT_EXPORT
 int bt_ctf_stream_set_packet_header(struct bt_ctf_stream *stream,
                struct bt_ctf_field *field)
 {
@@ -1591,6 +1596,7 @@ void reset_structure_field(struct bt_ctf_field *structure, const char *name)
        }
 }
 
+BT_EXPORT
 int bt_ctf_stream_flush(struct bt_ctf_stream *stream)
 {
        int ret = 0;
@@ -1917,7 +1923,7 @@ end:
  * Returns the following codes:
  * 1 if the field was found and set,
  * 0 if nothing was done (field not found, or was already set),
- * <0 if an error was encoutered
+ * <0 if an error was encountered
  */
 static
 int try_set_structure_field_integer(struct bt_ctf_field *structure, const char *name,
@@ -1926,17 +1932,20 @@ int try_set_structure_field_integer(struct bt_ctf_field *structure, const char *
        return _set_structure_field_integer(structure, name, value, BT_CTF_FALSE);
 }
 
+BT_EXPORT
 struct bt_ctf_stream_class *bt_ctf_stream_get_class(
                struct bt_ctf_stream *stream)
 {
        return bt_ctf_object_get_ref(bt_ctf_stream_common_borrow_class(BT_CTF_TO_COMMON(stream)));
 }
 
+BT_EXPORT
 const char *bt_ctf_stream_get_name(struct bt_ctf_stream *stream)
 {
        return bt_ctf_stream_common_get_name(BT_CTF_TO_COMMON(stream));
 }
 
+BT_EXPORT
 int64_t bt_ctf_stream_get_id(struct bt_ctf_stream *stream)
 {
        return bt_ctf_stream_common_get_id(BT_CTF_TO_COMMON(stream));
index 3e45bbb7171c5da263171c9a1556a3456e903318..605a3e55e2619d9eff8e706e9f7d3f643e7f629b 100644 (file)
@@ -30,13 +30,11 @@ struct bt_ctf_stream_common {
        GString *name;
 };
 
-BT_HIDDEN
 int bt_ctf_stream_common_initialize(
                struct bt_ctf_stream_common *stream,
                struct bt_ctf_stream_class_common *stream_class, const char *name,
                uint64_t id, bt_ctf_object_release_func release_func);
 
-BT_HIDDEN
 void bt_ctf_stream_common_finalize(struct bt_ctf_stream_common *stream);
 
 static inline
@@ -82,7 +80,6 @@ struct bt_ctf_stream {
        uint64_t last_ts_end;
 };
 
-BT_HIDDEN
 struct bt_ctf_stream *bt_ctf_stream_create_with_id(
                struct bt_ctf_stream_class *stream_class,
                const char *name, uint64_t id);
index 58d6b3a262bffed99561ca6403ee966186fb8e8b..e2e9d4deaba3239902d803f0d991875e065b94e3 100644 (file)
@@ -17,7 +17,6 @@
 #include <babeltrace2-ctf-writer/event.h>
 #include <babeltrace2-ctf-writer/object.h>
 #include <babeltrace2-ctf-writer/utils.h>
-#include <babeltrace2/types.h>
 
 #include "common/assert.h"
 #include "compat/compiler.h"
@@ -43,7 +42,6 @@
 #define DEFAULT_IDENTIFIER_SIZE                128
 #define DEFAULT_METADATA_STRING_SIZE   4096
 
-BT_HIDDEN
 int bt_ctf_trace_common_initialize(struct bt_ctf_trace_common *trace,
                bt_ctf_object_release_func release_func)
 {
@@ -90,7 +88,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctf_trace_common_finalize(struct bt_ctf_trace_common *trace)
 {
        BT_LOGD("Finalizing common trace object: addr=%p, name=\"%s\"",
@@ -124,7 +121,6 @@ void bt_ctf_trace_common_finalize(struct bt_ctf_trace_common *trace)
        bt_ctf_object_put_ref(trace->packet_header_field_type);
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_name(struct bt_ctf_trace_common *trace, const char *name)
 {
        int ret = 0;
@@ -163,7 +159,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_uuid(struct bt_ctf_trace_common *trace,
                const uint8_t *uuid)
 {
@@ -200,7 +195,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_environment_field(struct bt_ctf_trace_common *trace,
                const char *name, struct bt_ctf_private_value *value)
 {
@@ -287,7 +281,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_environment_field_string(struct bt_ctf_trace_common *trace,
                const char *name, const char *value)
 {
@@ -316,7 +309,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_environment_field_integer(
                struct bt_ctf_trace_common *trace, const char *name, int64_t value)
 {
@@ -339,7 +331,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_add_clock_class(struct bt_ctf_trace_common *trace,
                struct bt_ctf_clock_class *clock_class)
 {
@@ -584,8 +575,9 @@ end:
 }
 
 static
-bool packet_context_field_type_is_valid(struct bt_ctf_trace_common *trace,
-               struct bt_ctf_stream_class_common *stream_class,
+bool packet_context_field_type_is_valid(
+               struct bt_ctf_trace_common *trace __attribute__((unused)),
+               struct bt_ctf_stream_class_common *stream_class __attribute__((unused)),
                struct bt_ctf_field_type_common *packet_context_type,
                bool check_ts_begin_end_mapped)
 {
@@ -754,7 +746,8 @@ end:
 }
 
 static
-bool event_header_field_type_is_valid(struct bt_ctf_trace_common *trace,
+bool event_header_field_type_is_valid(
+               struct bt_ctf_trace_common *trace __attribute__((unused)),
                struct bt_ctf_stream_class_common *stream_class,
                struct bt_ctf_field_type_common *event_header_type)
 {
@@ -860,7 +853,6 @@ int check_packet_header_type_has_no_clock_class(struct bt_ctf_trace_common *trac
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_add_stream_class(struct bt_ctf_trace_common *trace,
                struct bt_ctf_stream_class_common *stream_class,
                bt_ctf_validation_flag_copy_field_type_func copy_field_type_func,
@@ -1224,7 +1216,9 @@ int bt_ctf_trace_common_add_stream_class(struct bt_ctf_trace_common *trace,
 
 end:
        if (ret) {
-               bt_ctf_object_set_parent(&stream_class->base, NULL);
+               if (stream_class) {
+                       bt_ctf_object_set_parent(&stream_class->base, NULL);
+               }
 
                if (ec_validation_outputs) {
                        for (i = 0; i < event_class_count; i++) {
@@ -1240,7 +1234,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_trace_common_has_clock_class(struct bt_ctf_trace_common *trace,
                struct bt_ctf_clock_class *clock_class)
 {
@@ -1253,7 +1246,6 @@ bt_ctf_bool bt_ctf_trace_common_has_clock_class(struct bt_ctf_trace_common *trac
        return query.found;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_native_byte_order(struct bt_ctf_trace_common *trace,
                enum bt_ctf_byte_order byte_order, bool allow_unspecified)
 {
@@ -1302,7 +1294,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_packet_header_field_type(struct bt_ctf_trace_common *trace,
                struct bt_ctf_field_type_common *packet_header_type)
 {
@@ -1403,7 +1394,6 @@ void bt_ctf_trace_destroy(struct bt_ctf_object *obj)
        g_free(trace);
 }
 
-BT_HIDDEN
 struct bt_ctf_trace *bt_ctf_trace_create(void)
 {
        struct bt_ctf_trace *trace = NULL;
@@ -1431,17 +1421,20 @@ error:
        return trace;
 }
 
+BT_EXPORT
 const uint8_t *bt_ctf_trace_get_uuid(struct bt_ctf_trace *trace)
 {
        return bt_ctf_trace_common_get_uuid(BT_CTF_TO_COMMON(trace));
 }
 
+BT_EXPORT
 int bt_ctf_trace_set_uuid(struct bt_ctf_trace *trace,
                const uint8_t *uuid)
 {
        return bt_ctf_trace_common_set_uuid(BT_CTF_TO_COMMON(trace), uuid);
 }
 
+BT_EXPORT
 int bt_ctf_trace_set_environment_field_string(struct bt_ctf_trace *trace,
                const char *name, const char *value)
 {
@@ -1449,6 +1442,7 @@ int bt_ctf_trace_set_environment_field_string(struct bt_ctf_trace *trace,
                name, value);
 }
 
+BT_EXPORT
 int bt_ctf_trace_set_environment_field_integer(
                struct bt_ctf_trace *trace, const char *name, int64_t value)
 {
@@ -1483,7 +1477,6 @@ struct bt_ctf_value *bt_ctf_trace_get_environment_field_value_by_name(
                BT_CTF_TO_COMMON(trace), name));
 }
 
-BT_HIDDEN
 int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace,
                struct bt_ctf_clock_class *clock_class)
 {
@@ -1491,13 +1484,11 @@ int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace,
                (void *) clock_class);
 }
 
-BT_HIDDEN
 int64_t bt_ctf_trace_get_clock_class_count(struct bt_ctf_trace *trace)
 {
        return bt_ctf_trace_common_get_clock_class_count(BT_CTF_TO_COMMON(trace));
 }
 
-BT_HIDDEN
 struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_index(
                struct bt_ctf_trace *trace, uint64_t index)
 {
@@ -1522,6 +1513,7 @@ int map_clock_classes_func(struct bt_ctf_stream_class_common *stream_class,
        return ret;
 }
 
+BT_EXPORT
 int bt_ctf_trace_add_stream_class(struct bt_ctf_trace *trace,
                struct bt_ctf_stream_class *stream_class)
 {
@@ -1610,11 +1602,13 @@ end:
        return ret;
 }
 
+BT_EXPORT
 int64_t bt_ctf_trace_get_stream_count(struct bt_ctf_trace *trace)
 {
        return bt_ctf_trace_common_get_stream_count(BT_CTF_TO_COMMON(trace));
 }
 
+BT_EXPORT
 struct bt_ctf_stream *bt_ctf_trace_get_stream_by_index(
                struct bt_ctf_trace *trace, uint64_t index)
 {
@@ -1622,11 +1616,13 @@ struct bt_ctf_stream *bt_ctf_trace_get_stream_by_index(
                BT_CTF_TO_COMMON(trace), index));
 }
 
+BT_EXPORT
 int64_t bt_ctf_trace_get_stream_class_count(struct bt_ctf_trace *trace)
 {
        return bt_ctf_trace_common_get_stream_class_count(BT_CTF_TO_COMMON(trace));
 }
 
+BT_EXPORT
 struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_index(
                struct bt_ctf_trace *trace, uint64_t index)
 {
@@ -1634,6 +1630,7 @@ struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_index(
                BT_CTF_TO_COMMON(trace), index));
 }
 
+BT_EXPORT
 struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_id(
                struct bt_ctf_trace *trace, uint64_t id)
 {
@@ -1641,7 +1638,6 @@ struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_id(
                BT_CTF_TO_COMMON(trace), id));
 }
 
-BT_HIDDEN
 struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_name(
                struct bt_ctf_trace *trace, const char *name)
 {
@@ -1820,12 +1816,14 @@ end:
        return metadata;
 }
 
+BT_EXPORT
 enum bt_ctf_byte_order bt_ctf_trace_get_native_byte_order(
                struct bt_ctf_trace *trace)
 {
        return (int) bt_ctf_trace_common_get_native_byte_order(BT_CTF_TO_COMMON(trace));
 }
 
+BT_EXPORT
 int bt_ctf_trace_set_native_byte_order(struct bt_ctf_trace *trace,
                enum bt_ctf_byte_order byte_order)
 {
@@ -1833,6 +1831,7 @@ int bt_ctf_trace_set_native_byte_order(struct bt_ctf_trace *trace,
                (int) byte_order, false);
 }
 
+BT_EXPORT
 struct bt_ctf_field_type *bt_ctf_trace_get_packet_header_field_type(
                struct bt_ctf_trace *trace)
 {
@@ -1840,6 +1839,7 @@ struct bt_ctf_field_type *bt_ctf_trace_get_packet_header_field_type(
                BT_CTF_TO_COMMON(trace)));
 }
 
+BT_EXPORT
 int bt_ctf_trace_set_packet_header_field_type(struct bt_ctf_trace *trace,
                struct bt_ctf_field_type *packet_header_type)
 {
@@ -1847,6 +1847,7 @@ int bt_ctf_trace_set_packet_header_field_type(struct bt_ctf_trace *trace,
                (void *) packet_header_type);
 }
 
+BT_EXPORT
 const char *bt_ctf_trace_get_name(struct bt_ctf_trace *trace)
 {
        return bt_ctf_trace_common_get_name(BT_CTF_TO_COMMON(trace));
index 7fe067d2f75fc65290f1585442e7b28ac929316d..29df2f4290eee5de3c53274f44bfeadb2ec21184 100644 (file)
@@ -15,7 +15,6 @@
 #include <babeltrace2-ctf-writer/field-types.h>
 #include <babeltrace2-ctf-writer/fields.h>
 #include <babeltrace2-ctf-writer/trace.h>
-#include <babeltrace2/types.h>
 #include <glib.h>
 #include <sys/types.h>
 
@@ -48,15 +47,12 @@ struct bt_ctf_trace_common {
        int valid;
 };
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_trace_common_has_clock_class(struct bt_ctf_trace_common *trace,
                struct bt_ctf_clock_class *clock_class);
 
-BT_HIDDEN
 int bt_ctf_trace_common_initialize(struct bt_ctf_trace_common *trace,
                bt_ctf_object_release_func release_func);
 
-BT_HIDDEN
 void bt_ctf_trace_common_finalize(struct bt_ctf_trace_common *trace);
 
 static inline
@@ -66,7 +62,6 @@ const char *bt_ctf_trace_common_get_name(struct bt_ctf_trace_common *trace)
        return trace->name ? trace->name->str : NULL;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_name(struct bt_ctf_trace_common *trace, const char *name);
 
 static inline
@@ -76,18 +71,14 @@ const uint8_t *bt_ctf_trace_common_get_uuid(struct bt_ctf_trace_common *trace)
        return trace->uuid_set ? trace->uuid : NULL;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_uuid(struct bt_ctf_trace_common *trace, const uint8_t *uuid);
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_environment_field(struct bt_ctf_trace_common *trace,
                const char *name, struct bt_ctf_private_value *value);
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_environment_field_string(struct bt_ctf_trace_common *trace,
                const char *name, const char *value);
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_environment_field_integer(struct bt_ctf_trace_common *trace,
                const char *name, int64_t value);
 
@@ -132,7 +123,6 @@ bt_ctf_trace_common_borrow_environment_field_value_by_name(
                name);
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_add_clock_class(struct bt_ctf_trace_common *trace,
                struct bt_ctf_clock_class *clock_class);
 
@@ -260,7 +250,6 @@ enum bt_ctf_byte_order bt_ctf_trace_common_get_native_byte_order(
        return trace->native_byte_order;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_native_byte_order(struct bt_ctf_trace_common *trace,
                enum bt_ctf_byte_order byte_order, bool allow_unspecified);
 
@@ -272,7 +261,6 @@ struct bt_ctf_field_type_common *bt_ctf_trace_common_borrow_packet_header_field_
        return trace->packet_header_field_type;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_set_packet_header_field_type(struct bt_ctf_trace_common *trace,
                struct bt_ctf_field_type_common *packet_header_field_type);
 
@@ -306,7 +294,6 @@ void bt_ctf_trace_common_freeze(struct bt_ctf_trace_common *trace)
        trace->frozen = 1;
 }
 
-BT_HIDDEN
 int bt_ctf_trace_common_add_stream_class(struct bt_ctf_trace_common *trace,
                struct bt_ctf_stream_class_common *stream_class,
                bt_ctf_validation_flag_copy_field_type_func copy_field_type_func,
@@ -330,47 +317,36 @@ struct bt_ctf_trace {
  *
  * Returns the metadata string on success, NULL on error.
  */
-BT_HIDDEN
 char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace);
 
-BT_HIDDEN
 struct bt_ctf_trace *bt_ctf_trace_create(void);
 
-BT_HIDDEN
 int64_t bt_ctf_trace_get_clock_class_count(
                struct bt_ctf_trace *trace);
 
-BT_HIDDEN
 struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_index(
                struct bt_ctf_trace *trace, uint64_t index);
 
-BT_HIDDEN
 struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_name(
                struct bt_ctf_trace *trace, const char *name);
 
-BT_HIDDEN
 int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace,
                struct bt_ctf_clock_class *clock_class);
 
-BT_HIDDEN
 int64_t bt_ctf_trace_get_environment_field_count(
                struct bt_ctf_trace *trace);
 
-BT_HIDDEN
 const char *bt_ctf_trace_get_environment_field_name_by_index(
                struct bt_ctf_trace *trace, uint64_t index);
 
-BT_HIDDEN
 struct bt_ctf_value *
 bt_ctf_trace_get_environment_field_value_by_index(struct bt_ctf_trace *trace,
                uint64_t index);
 
-BT_HIDDEN
 struct bt_ctf_value *
 bt_ctf_trace_get_environment_field_value_by_name(
                struct bt_ctf_trace *trace, const char *name);
 
-BT_HIDDEN
 int bt_ctf_trace_visit(struct bt_ctf_trace *trace,
                bt_ctf_visitor visitor, void *data);
 
index 6c4b0ed8ffc415ed254dfe00c7425ee6208e89ae..781aa67c12836e349fdd5b3d825953a2065b61ea 100644 (file)
@@ -62,6 +62,7 @@ void trace_finalize(void)
        }
 }
 
+BT_EXPORT
 bt_ctf_bool bt_ctf_identifier_is_valid(const char *identifier)
 {
        bt_ctf_bool is_valid = BT_CTF_TRUE;
index c344bcbedc62bbee90c4aab392e3c4e8e10ab090..1ea5406a67c4e7058383060967de9af08397b30e 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "field-path.h"
 
-#define BT_CTF_TO_COMMON(_obj)         (&(_obj)->common)
+#define BT_CTF_TO_COMMON(_obj)         ((typeof(&_obj->common)) _obj)
 #define BT_CTF_FROM_COMMON(_obj)       ((void *) _obj)
 
 struct bt_ctf_search_query {
index 86d68626c56611e58ce6df4436dd9e8a14d9ce06..13fd9eb80bf3c355568cbfd5392ce806b2f08920 100644 (file)
@@ -245,7 +245,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctf_validate_class_types(struct bt_ctf_private_value *environment,
                struct bt_ctf_field_type_common *packet_header_type,
                struct bt_ctf_field_type_common *packet_context_type,
@@ -590,7 +589,6 @@ error:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctf_validation_replace_types(struct bt_ctf_trace_common *trace,
                struct bt_ctf_stream_class_common *stream_class,
                struct bt_ctf_event_class_common *event_class,
@@ -623,7 +621,6 @@ void bt_ctf_validation_replace_types(struct bt_ctf_trace_common *trace,
        }
 }
 
-BT_HIDDEN
 void bt_ctf_validation_output_put_types(
                struct bt_ctf_validation_output *output)
 {
index 7434c42ba2843e6e5ae829b7bf77f3846fac95f0..fef42a6da542e932249114cf8199910d8f621593 100644 (file)
@@ -64,7 +64,6 @@ struct bt_ctf_validation_output {
  *
  * All parameters are owned by the caller.
  */
-BT_HIDDEN
 int bt_ctf_validate_class_types(struct bt_ctf_private_value *environment,
                struct bt_ctf_field_type_common *packet_header_type,
                struct bt_ctf_field_type_common *packet_context_type,
@@ -92,7 +91,6 @@ int bt_ctf_validate_class_types(struct bt_ctf_private_value *environment,
  *
  * All parameters are owned by the caller.
  */
-BT_HIDDEN
 void bt_ctf_validation_replace_types(struct bt_ctf_trace_common *trace,
                struct bt_ctf_stream_class_common *stream_class,
                struct bt_ctf_event_class_common *event_class,
@@ -105,7 +103,6 @@ void bt_ctf_validation_replace_types(struct bt_ctf_trace_common *trace,
  *
  * `output` is owned by the caller and is not freed here.
  */
-BT_HIDDEN
 void bt_ctf_validation_output_put_types(
                struct bt_ctf_validation_output *output);
 
index 3eeed2d3664e1c8259162a1d0434ab7ae82a2f15..e1a9854203ceb40136c8851a39f70df102b94415 100644 (file)
@@ -13,7 +13,6 @@
 #include <inttypes.h>
 
 #include <babeltrace2-ctf-writer/object.h>
-#include <babeltrace2/types.h>
 
 #include "common/assert.h"
 #include "common/common.h"
@@ -70,7 +69,10 @@ struct bt_ctf_value bt_ctf_value_null_instance = {
        .frozen = BT_CTF_TRUE,
 };
 
+BT_EXPORT
 struct bt_ctf_value *const bt_ctf_value_null = &bt_ctf_value_null_instance;
+
+BT_EXPORT
 struct bt_ctf_private_value *const bt_ctf_private_value_null =
        (void *) &bt_ctf_value_null_instance;
 
@@ -149,7 +151,8 @@ void (* const destroy_funcs[])(struct bt_ctf_value *) = {
 };
 
 static
-struct bt_ctf_private_value *bt_ctf_value_null_copy(const struct bt_ctf_value *null_obj)
+struct bt_ctf_private_value *bt_ctf_value_null_copy(
+               const struct bt_ctf_value *null_obj __attribute__((unused)))
 {
        return (void *) bt_ctf_value_null;
 }
@@ -300,8 +303,9 @@ struct bt_ctf_private_value *(* const copy_funcs[])(const struct bt_ctf_value *)
 };
 
 static
-bt_ctf_bool bt_ctf_value_null_compare(const struct bt_ctf_value *object_a,
-               const struct bt_ctf_value *object_b)
+bt_ctf_bool bt_ctf_value_null_compare(
+               const struct bt_ctf_value *object_a __attribute__((unused)),
+               const struct bt_ctf_value *object_b __attribute__((unused)))
 {
        /*
         * Always BT_CTF_TRUE since bt_ctf_value_compare() already checks if both
@@ -474,7 +478,7 @@ bt_ctf_bool (* const compare_funcs[])(const struct bt_ctf_value *,
 };
 
 static
-void bt_ctf_value_null_freeze(struct bt_ctf_value *object)
+void bt_ctf_value_null_freeze(struct bt_ctf_value *object __attribute__((unused)))
 {
 }
 
@@ -545,7 +549,6 @@ void bt_ctf_value_destroy(struct bt_ctf_object *obj)
        g_free(value);
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status _bt_ctf_value_freeze(struct bt_ctf_value *object)
 {
        enum bt_ctf_value_status ret = BT_CTF_VALUE_STATUS_OK;
@@ -563,7 +566,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_type bt_ctf_value_get_type(const struct bt_ctf_value *object)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(object, "Value object");
@@ -581,7 +583,6 @@ struct bt_ctf_value bt_ctf_value_create_base(enum bt_ctf_value_type type)
        return value;
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_bool_create_init(bt_ctf_bool val)
 {
        struct bt_ctf_value_bool *bool_obj;
@@ -601,13 +602,11 @@ end:
        return (void *) BT_CTF_VALUE_FROM_CONCRETE(bool_obj);
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_bool_create(void)
 {
        return bt_ctf_private_value_bool_create_init(BT_CTF_FALSE);
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_integer_create_init(int64_t val)
 {
        struct bt_ctf_value_integer *integer_obj;
@@ -628,13 +627,11 @@ end:
        return (void *) BT_CTF_VALUE_FROM_CONCRETE(integer_obj);
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_integer_create(void)
 {
        return bt_ctf_private_value_integer_create_init(0);
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_real_create_init(double val)
 {
        struct bt_ctf_value_real *real_obj;
@@ -655,13 +652,11 @@ end:
        return (void *) BT_CTF_VALUE_FROM_CONCRETE(real_obj);
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_real_create(void)
 {
        return bt_ctf_private_value_real_create_init(0.);
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_string_create_init(const char *val)
 {
        struct bt_ctf_value_string *string_obj = NULL;
@@ -694,13 +689,11 @@ end:
        return (void *) BT_CTF_VALUE_FROM_CONCRETE(string_obj);
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_string_create(void)
 {
        return bt_ctf_private_value_string_create_init("");
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_array_create(void)
 {
        struct bt_ctf_value_array *array_obj;
@@ -729,7 +722,6 @@ end:
        return (void *) BT_CTF_VALUE_FROM_CONCRETE(array_obj);
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_map_create(void)
 {
        struct bt_ctf_value_map *map_obj;
@@ -758,7 +750,6 @@ end:
        return (void *) BT_CTF_VALUE_FROM_CONCRETE(map_obj);
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_value_bool_get(const struct bt_ctf_value *bool_obj)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(bool_obj, "Value object");
@@ -766,7 +757,6 @@ bt_ctf_bool bt_ctf_value_bool_get(const struct bt_ctf_value *bool_obj)
        return BT_CTF_VALUE_TO_BOOL(bool_obj)->value;
 }
 
-BT_HIDDEN
 void bt_ctf_private_value_bool_set(struct bt_ctf_private_value *bool_obj, bt_ctf_bool val)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(bool_obj, "Value object");
@@ -777,7 +767,6 @@ void bt_ctf_private_value_bool_set(struct bt_ctf_private_value *bool_obj, bt_ctf
                bool_obj, val);
 }
 
-BT_HIDDEN
 int64_t bt_ctf_value_integer_get(const struct bt_ctf_value *integer_obj)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(integer_obj, "Value object");
@@ -785,7 +774,6 @@ int64_t bt_ctf_value_integer_get(const struct bt_ctf_value *integer_obj)
        return BT_CTF_VALUE_TO_INTEGER(integer_obj)->value;
 }
 
-BT_HIDDEN
 void bt_ctf_private_value_integer_set(struct bt_ctf_private_value *integer_obj,
                int64_t val)
 {
@@ -797,7 +785,6 @@ void bt_ctf_private_value_integer_set(struct bt_ctf_private_value *integer_obj,
                integer_obj, val);
 }
 
-BT_HIDDEN
 double bt_ctf_value_real_get(const struct bt_ctf_value *real_obj)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(real_obj, "Value object");
@@ -805,7 +792,6 @@ double bt_ctf_value_real_get(const struct bt_ctf_value *real_obj)
        return BT_CTF_VALUE_TO_REAL(real_obj)->value;
 }
 
-BT_HIDDEN
 void bt_ctf_private_value_real_set(struct bt_ctf_private_value *real_obj, double val)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(real_obj, "Value object");
@@ -816,7 +802,6 @@ void bt_ctf_private_value_real_set(struct bt_ctf_private_value *real_obj, double
                real_obj, val);
 }
 
-BT_HIDDEN
 const char *bt_ctf_value_string_get(const struct bt_ctf_value *string_obj)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(string_obj, "Value object");
@@ -824,7 +809,6 @@ const char *bt_ctf_value_string_get(const struct bt_ctf_value *string_obj)
        return BT_CTF_VALUE_TO_STRING(string_obj)->gstr->str;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_string_set(
                struct bt_ctf_private_value *string_obj, const char *val)
 {
@@ -837,7 +821,6 @@ enum bt_ctf_value_status bt_ctf_private_value_string_set(
        return BT_CTF_VALUE_STATUS_OK;
 }
 
-BT_HIDDEN
 uint64_t bt_ctf_value_array_get_length(const struct bt_ctf_value *array_obj)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(array_obj, "Value object");
@@ -845,7 +828,6 @@ uint64_t bt_ctf_value_array_get_length(const struct bt_ctf_value *array_obj)
        return (uint64_t) BT_CTF_VALUE_TO_ARRAY(array_obj)->garray->len;
 }
 
-BT_HIDDEN
 struct bt_ctf_value *bt_ctf_value_array_borrow_element_by_index(
                const struct bt_ctf_value *array_obj,
                uint64_t index)
@@ -860,7 +842,6 @@ struct bt_ctf_value *bt_ctf_value_array_borrow_element_by_index(
        return g_ptr_array_index(typed_array_obj->garray, index);
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_array_borrow_element_by_index(
                const struct bt_ctf_private_value *array_obj,
                uint64_t index)
@@ -869,7 +850,6 @@ struct bt_ctf_private_value *bt_ctf_private_value_array_borrow_element_by_index(
                (void *) array_obj, index);
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_element(
                struct bt_ctf_private_value *array_obj,
                struct bt_ctf_value *element_obj)
@@ -889,7 +869,6 @@ enum bt_ctf_value_status bt_ctf_private_value_array_append_element(
        return BT_CTF_VALUE_STATUS_OK;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_bool_element(
                struct bt_ctf_private_value *array_obj, bt_ctf_bool val)
 {
@@ -903,7 +882,6 @@ enum bt_ctf_value_status bt_ctf_private_value_array_append_bool_element(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_integer_element(
                struct bt_ctf_private_value *array_obj, int64_t val)
 {
@@ -917,7 +895,6 @@ enum bt_ctf_value_status bt_ctf_private_value_array_append_integer_element(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_real_element(
                struct bt_ctf_private_value *array_obj, double val)
 {
@@ -931,7 +908,6 @@ enum bt_ctf_value_status bt_ctf_private_value_array_append_real_element(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_string_element(
                struct bt_ctf_private_value *array_obj, const char *val)
 {
@@ -945,7 +921,6 @@ enum bt_ctf_value_status bt_ctf_private_value_array_append_string_element(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_array_element(
                struct bt_ctf_private_value *array_obj)
 {
@@ -959,7 +934,6 @@ enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_array_element(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_map_element(
                struct bt_ctf_private_value *array_obj)
 {
@@ -973,7 +947,6 @@ enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_map_element(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_set_element_by_index(
                struct bt_ctf_private_value *array_obj, uint64_t index,
                struct bt_ctf_value *element_obj)
@@ -996,7 +969,6 @@ enum bt_ctf_value_status bt_ctf_private_value_array_set_element_by_index(
        return BT_CTF_VALUE_STATUS_OK;
 }
 
-BT_HIDDEN
 uint64_t bt_ctf_value_map_get_size(const struct bt_ctf_value *map_obj)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object");
@@ -1004,7 +976,6 @@ uint64_t bt_ctf_value_map_get_size(const struct bt_ctf_value *map_obj)
        return (uint64_t) g_hash_table_size(BT_CTF_VALUE_TO_MAP(map_obj)->ght);
 }
 
-BT_HIDDEN
 struct bt_ctf_value *bt_ctf_value_map_borrow_entry_value(const struct bt_ctf_value *map_obj,
                const char *key)
 {
@@ -1015,14 +986,12 @@ struct bt_ctf_value *bt_ctf_value_map_borrow_entry_value(const struct bt_ctf_val
                GUINT_TO_POINTER(g_quark_from_string(key)));
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_map_borrow_entry_value(
                const struct bt_ctf_private_value *map_obj, const char *key)
 {
        return (void *) bt_ctf_value_map_borrow_entry_value((void *) map_obj, key);
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_value_map_has_entry(const struct bt_ctf_value *map_obj, const char *key)
 {
        BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object");
@@ -1032,7 +1001,6 @@ bt_ctf_bool bt_ctf_value_map_has_entry(const struct bt_ctf_value *map_obj, const
                GUINT_TO_POINTER(g_quark_from_string(key)));
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_entry(
                struct bt_ctf_private_value *map_obj,
                const char *key, struct bt_ctf_value *element_obj)
@@ -1051,7 +1019,6 @@ enum bt_ctf_value_status bt_ctf_private_value_map_insert_entry(
        return BT_CTF_VALUE_STATUS_OK;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_bool_entry(
                struct bt_ctf_private_value *map_obj, const char *key, bt_ctf_bool val)
 {
@@ -1065,7 +1032,6 @@ enum bt_ctf_value_status bt_ctf_private_value_map_insert_bool_entry(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_integer_entry(
                struct bt_ctf_private_value *map_obj, const char *key, int64_t val)
 {
@@ -1079,7 +1045,6 @@ enum bt_ctf_value_status bt_ctf_private_value_map_insert_integer_entry(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_real_entry(
                struct bt_ctf_private_value *map_obj, const char *key, double val)
 {
@@ -1093,7 +1058,6 @@ enum bt_ctf_value_status bt_ctf_private_value_map_insert_real_entry(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_string_entry(
                struct bt_ctf_private_value *map_obj, const char *key,
                const char *val)
@@ -1108,7 +1072,6 @@ enum bt_ctf_value_status bt_ctf_private_value_map_insert_string_entry(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_array_entry(
                struct bt_ctf_private_value *map_obj, const char *key)
 {
@@ -1122,7 +1085,6 @@ enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_array_entry(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_map_entry(
                struct bt_ctf_private_value *map_obj, const char *key)
 {
@@ -1136,7 +1098,6 @@ enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_map_entry(
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_value_map_foreach_entry(const struct bt_ctf_value *map_obj,
                bt_ctf_value_map_foreach_entry_cb cb, void *data)
 {
@@ -1165,7 +1126,6 @@ enum bt_ctf_value_status bt_ctf_value_map_foreach_entry(const struct bt_ctf_valu
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_foreach_entry(
                const struct bt_ctf_private_value *map_obj,
                bt_ctf_private_value_map_foreach_entry_cb cb, void *data)
@@ -1221,7 +1181,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_value_map_extend(
                struct bt_ctf_private_value **extended_map_obj,
                const struct bt_ctf_value *base_map_obj,
@@ -1283,7 +1242,6 @@ end:
        return extend_data.status;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_value_copy(struct bt_ctf_private_value **copy_obj,
                const struct bt_ctf_value *object)
 {
@@ -1305,7 +1263,6 @@ enum bt_ctf_value_status bt_ctf_value_copy(struct bt_ctf_private_value **copy_ob
        return status;
 }
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_value_compare(const struct bt_ctf_value *object_a,
        const struct bt_ctf_value *object_b)
 {
index 440619326d0604f2e4f963bd4f296428ee1aaff8..ad29a9fb356cf7eeb9c84779cb0227f6b9529923 100644 (file)
@@ -29,7 +29,6 @@ enum bt_ctf_value_status {
        BT_CTF_VALUE_STATUS_OK          = 0,
 };
 
-BT_HIDDEN
 enum bt_ctf_value_status _bt_ctf_value_freeze(struct bt_ctf_value *object);
 
 #ifdef BT_DEV_MODE
@@ -63,7 +62,6 @@ enum bt_ctf_value_type {
        BT_CTF_VALUE_TYPE_MAP =         6,
 };
 
-BT_HIDDEN
 enum bt_ctf_value_type bt_ctf_value_get_type(const struct bt_ctf_value *object);
 
 static inline
@@ -108,27 +106,20 @@ bt_ctf_bool bt_ctf_value_is_map(const struct bt_ctf_value *object)
        return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_MAP;
 }
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_value_copy(struct bt_ctf_private_value **copy,
                const struct bt_ctf_value *object);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_value_compare(const struct bt_ctf_value *object_a,
                const struct bt_ctf_value *object_b);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_value_bool_get(const struct bt_ctf_value *bool_obj);
 
-BT_HIDDEN
 int64_t bt_ctf_value_integer_get(const struct bt_ctf_value *integer_obj);
 
-BT_HIDDEN
 double bt_ctf_value_real_get(const struct bt_ctf_value *real_obj);
 
-BT_HIDDEN
 const char *bt_ctf_value_string_get(const struct bt_ctf_value *string_obj);
 
-BT_HIDDEN
 uint64_t bt_ctf_value_array_get_length(const struct bt_ctf_value *array_obj);
 
 static inline
@@ -137,11 +128,9 @@ bt_ctf_bool bt_ctf_value_array_is_empty(const struct bt_ctf_value *array_obj)
        return bt_ctf_value_array_get_length(array_obj) == 0;
 }
 
-BT_HIDDEN
 struct bt_ctf_value *bt_ctf_value_array_borrow_element_by_index(
                const struct bt_ctf_value *array_obj, uint64_t index);
 
-BT_HIDDEN
 uint64_t bt_ctf_value_map_get_size(const struct bt_ctf_value *map_obj);
 
 static inline
@@ -150,23 +139,19 @@ bt_ctf_bool bt_ctf_value_map_is_empty(const struct bt_ctf_value *map_obj)
        return bt_ctf_value_map_get_size(map_obj) == 0;
 }
 
-BT_HIDDEN
 struct bt_ctf_value *bt_ctf_value_map_borrow_entry_value(
                const struct bt_ctf_value *map_obj, const char *key);
 
 typedef bt_ctf_bool (* bt_ctf_value_map_foreach_entry_cb)(const char *key,
        struct bt_ctf_value *object, void *data);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_value_map_foreach_entry(
                const struct bt_ctf_value *map_obj,
                bt_ctf_value_map_foreach_entry_cb cb, void *data);
 
-BT_HIDDEN
 bt_ctf_bool bt_ctf_value_map_has_entry(const struct bt_ctf_value *map_obj,
                const char *key);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_value_map_extend(
                struct bt_ctf_private_value **extended_map_obj,
                const struct bt_ctf_value *base_map_obj,
@@ -185,135 +170,103 @@ struct bt_ctf_value *bt_ctf_private_value_as_value(
        return (void *) priv_value;
 }
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_bool_create(void);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_bool_create_init(bt_ctf_bool val);
 
-BT_HIDDEN
 void bt_ctf_private_value_bool_set(struct bt_ctf_private_value *bool_obj,
                bt_ctf_bool val);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_integer_create(void);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_integer_create_init(
                int64_t val);
 
-BT_HIDDEN
 void bt_ctf_private_value_integer_set(
                struct bt_ctf_private_value *integer_obj, int64_t val);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_real_create(void);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_real_create_init(double val);
 
-BT_HIDDEN
 void bt_ctf_private_value_real_set(
                struct bt_ctf_private_value *real_obj, double val);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_string_create(void);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_string_create_init(
                const char *val);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_string_set(
                struct bt_ctf_private_value *string_obj,
                const char *val);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_array_create(void);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_array_borrow_element_by_index(
                const struct bt_ctf_private_value *array_obj, uint64_t index);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_element(
                struct bt_ctf_private_value *array_obj,
                struct bt_ctf_value *element_obj);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_bool_element(
                struct bt_ctf_private_value *array_obj,
                bt_ctf_bool val);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_integer_element(
                struct bt_ctf_private_value *array_obj,
                int64_t val);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_real_element(
                struct bt_ctf_private_value *array_obj,
                double val);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_string_element(
                struct bt_ctf_private_value *array_obj, const char *val);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_array_element(
                struct bt_ctf_private_value *array_obj);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_map_element(
                struct bt_ctf_private_value *array_obj);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_array_set_element_by_index(
                struct bt_ctf_private_value *array_obj, uint64_t index,
                struct bt_ctf_value *element_obj);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_map_create(void);
 
-BT_HIDDEN
 struct bt_ctf_private_value *bt_ctf_private_value_map_borrow_entry_value(
                const struct bt_ctf_private_value *map_obj, const char *key);
 
 typedef bt_ctf_bool (* bt_ctf_private_value_map_foreach_entry_cb)(const char *key,
                struct bt_ctf_private_value *object, void *data);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_foreach_entry(
                const struct bt_ctf_private_value *map_obj,
                bt_ctf_private_value_map_foreach_entry_cb cb, void *data);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_entry(
                struct bt_ctf_private_value *map_obj, const char *key,
                struct bt_ctf_value *element_obj);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_bool_entry(
                struct bt_ctf_private_value *map_obj, const char *key, bt_ctf_bool val);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_integer_entry(
                struct bt_ctf_private_value *map_obj, const char *key, int64_t val);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_real_entry(
                struct bt_ctf_private_value *map_obj, const char *key, double val);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_string_entry(
                struct bt_ctf_private_value *map_obj, const char *key,
                const char *val);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_array_entry(
                struct bt_ctf_private_value *map_obj, const char *key);
 
-BT_HIDDEN
 enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_map_entry(
                struct bt_ctf_private_value *map_obj, const char *key);
 
index 711a03dd075cb228e86246c3412601cdb1c50abf..573dfe45beb722f5f91579e4aed99cf1188c2030 100644 (file)
@@ -12,7 +12,6 @@
 
 #include "visitor.h"
 
-BT_HIDDEN
 int bt_ctf_visitor_helper(struct bt_ctf_visitor_object *root,
                bt_ctf_child_count_accessor child_counter,
                bt_ctf_child_accessor child_accessor,
@@ -51,6 +50,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 enum bt_ctf_visitor_object_type bt_ctf_visitor_object_get_type(
                struct bt_ctf_visitor_object *object)
 {
@@ -65,6 +65,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 void *bt_ctf_visitor_object_get_object(struct bt_ctf_visitor_object *object)
 {
        void *ret = NULL;
index c0007111bb62881aa80c63b557feeeb7ff519dcd..bdbf2283bef773e23f8b2ca24ebe0d978208e6b9 100644 (file)
@@ -22,7 +22,6 @@ struct bt_ctf_visitor_object {
        void *object;
 };
 
-BT_HIDDEN
 int bt_ctf_visitor_helper(struct bt_ctf_visitor_object *root,
                bt_ctf_child_count_accessor child_counter,
                bt_ctf_child_accessor child_accessor,
index 9b969dd02d83a43d08d7a55c5ddce635a44d0ad9..c3b83c48fb6bec8795055a10f0d0d457d7312aec 100644 (file)
@@ -85,6 +85,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 struct bt_ctf_writer *bt_ctf_writer_create(const char *path)
 {
        int ret;
@@ -158,6 +159,7 @@ error:
        return writer;
 }
 
+BT_EXPORT
 void bt_ctf_writer_destroy(struct bt_ctf_object *obj)
 {
        struct bt_ctf_writer *writer;
@@ -178,6 +180,7 @@ void bt_ctf_writer_destroy(struct bt_ctf_object *obj)
        g_free(writer);
 }
 
+BT_EXPORT
 struct bt_ctf_trace *bt_ctf_writer_get_trace(struct bt_ctf_writer *writer)
 {
        struct bt_ctf_trace *trace = NULL;
@@ -192,6 +195,7 @@ end:
        return trace;
 }
 
+BT_EXPORT
 struct bt_ctf_stream *bt_ctf_writer_create_stream(struct bt_ctf_writer *writer,
                struct bt_ctf_stream_class *stream_class)
 {
@@ -247,6 +251,7 @@ error:
        return stream;
 }
 
+BT_EXPORT
 int bt_ctf_writer_add_environment_field(struct bt_ctf_writer *writer,
                const char *name,
                const char *value)
@@ -263,6 +268,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 int bt_ctf_writer_add_environment_field_int64(struct bt_ctf_writer *writer,
                const char *name, int64_t value)
 {
@@ -278,6 +284,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 int bt_ctf_writer_add_clock(struct bt_ctf_writer *writer,
                struct bt_ctf_clock *clock)
 {
@@ -292,6 +299,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 char *bt_ctf_writer_get_metadata_string(struct bt_ctf_writer *writer)
 {
        char *metadata_string = NULL;
@@ -306,6 +314,7 @@ end:
        return metadata_string;
 }
 
+BT_EXPORT
 void bt_ctf_writer_flush_metadata(struct bt_ctf_writer *writer)
 {
        int ret;
@@ -341,6 +350,7 @@ end:
        g_free(metadata_string);
 }
 
+BT_EXPORT
 int bt_ctf_writer_set_byte_order(struct bt_ctf_writer *writer,
                enum bt_ctf_byte_order byte_order)
 {
@@ -365,7 +375,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctf_writer_freeze(struct bt_ctf_writer *writer)
 {
        writer->frozen = 1;
@@ -389,7 +398,6 @@ const unsigned int field_type_aliases_sizes[] = {
        [FIELD_TYPE_ALIAS_UINT64_T] = 64,
 };
 
-BT_HIDDEN
 struct bt_ctf_field_type *get_field_type(enum field_type_alias alias)
 {
        int ret;
@@ -411,7 +419,6 @@ end:
        return field_type;
 }
 
-BT_HIDDEN
 const char *bt_ctf_get_byte_order_string(enum bt_ctf_byte_order byte_order)
 {
        const char *string;
index 4c6e516383b534b82aa133d542190281819f05fb..24c92c36b1d7d9f59112988f5053a9cdf3b851f3 100644 (file)
@@ -42,13 +42,10 @@ enum field_type_alias {
        NR_FIELD_TYPE_ALIAS,
 };
 
-BT_HIDDEN
 struct bt_ctf_field_type *get_field_type(enum field_type_alias alias);
 
-BT_HIDDEN
 const char *bt_ctf_get_byte_order_string(enum bt_ctf_byte_order byte_order);
 
-BT_HIDDEN
 void bt_ctf_writer_freeze(struct bt_ctf_writer *writer);
 
 #endif /* BABELTRACE_CTF_WRITER_WRITER_INTERNAL_H */
diff --git a/src/ctfser/Makefile.am b/src/ctfser/Makefile.am
deleted file mode 100644 (file)
index 19c96f1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-ctfser.la
-
-libbabeltrace2_ctfser_la_SOURCES = \
-       ctfser.c \
-       ctfser.h
index 30bb91df177e3480e438ec3dcd72d3badd791e63..fc0d12c18caa5e5d0665fe683b95afcebeab81f8 100644 (file)
@@ -41,7 +41,6 @@ void mmap_align_ctfser(struct bt_ctfser *ctfser)
                MAP_SHARED, ctfser->fd, ctfser->mmap_offset, ctfser->log_level);
 }
 
-BT_HIDDEN
 int _bt_ctfser_increase_cur_packet_size(struct bt_ctfser *ctfser)
 {
        int ret;
@@ -94,7 +93,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctfser_init(struct bt_ctfser *ctfser, const char *path, int log_level)
 {
        int ret = 0;
@@ -118,7 +116,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctfser_fini(struct bt_ctfser *ctfser)
 {
        int ret = 0;
@@ -176,7 +173,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int bt_ctfser_open_packet(struct bt_ctfser *ctfser)
 {
        int ret = 0;
@@ -241,7 +237,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void bt_ctfser_close_current_packet(struct bt_ctfser *ctfser,
                uint64_t packet_size_bytes)
 {
index d2fb0b9126cc0fe68706065efbe981e7db9ec2b9..33d48d2042d6cc915faf0dc76e6f148b5832709b 100644 (file)
@@ -24,7 +24,6 @@
 #include "compat/endian.h"
 #include "common/common.h"
 #include "common/mmap-align.h"
-#include <babeltrace2/types.h>
 #include "common/assert.h"
 #include "common/macros.h"
 #include "compat/bitfield.h"
@@ -67,7 +66,7 @@ struct bt_ctfser {
  *
  * This function opens the file `path` for writing.
  */
-BT_EXTERN_C BT_HIDDEN
+BT_EXTERN_C
 int bt_ctfser_init(struct bt_ctfser *ctfser, const char *path,
                int log_level);
 
@@ -77,7 +76,7 @@ int bt_ctfser_init(struct bt_ctfser *ctfser, const char *path,
  * This function truncates the stream file so that there's no extra
  * padding after the last packet, and then closes the file.
  */
-BT_EXTERN_C BT_HIDDEN
+BT_EXTERN_C
 int bt_ctfser_fini(struct bt_ctfser *ctfser);
 
 /*
@@ -85,17 +84,17 @@ int bt_ctfser_fini(struct bt_ctfser *ctfser);
  *
  * All the next writing functions are performed within this new packet.
  */
-BT_EXTERN_C BT_HIDDEN
+BT_EXTERN_C
 int bt_ctfser_open_packet(struct bt_ctfser *ctfser);
 
 /*
  * Closes the current packet, making its size `packet_size_bytes`.
  */
-BT_EXTERN_C BT_HIDDEN
+BT_EXTERN_C
 void bt_ctfser_close_current_packet(struct bt_ctfser *ctfser,
                uint64_t packet_size_bytes);
 
-BT_EXTERN_C BT_HIDDEN
+BT_EXTERN_C
 int _bt_ctfser_increase_cur_packet_size(struct bt_ctfser *ctfser);
 
 static inline
diff --git a/src/fd-cache/Makefile.am b/src/fd-cache/Makefile.am
deleted file mode 100644 (file)
index d3c3251..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-fd-cache.la
-
-libbabeltrace2_fd_cache_la_SOURCES = \
-       fd-cache.c \
-       fd-cache.h
index 944508392752cc37c1e82efb75adfa67dc00e519..f22e7eb569bf10658f21460367a29b085a6f062c 100644 (file)
@@ -82,7 +82,6 @@ void file_key_destroy(gpointer data)
        g_free(fk);
 }
 
-BT_HIDDEN
 int bt_fd_cache_init(struct bt_fd_cache *fdc, int log_level)
 {
        int ret = 0;
@@ -97,7 +96,6 @@ int bt_fd_cache_init(struct bt_fd_cache *fdc, int log_level)
        return ret;
 }
 
-BT_HIDDEN
 void bt_fd_cache_fini(struct bt_fd_cache *fdc)
 {
        if (!fdc->cache) {
@@ -114,7 +112,6 @@ end:
        return;
 }
 
-BT_HIDDEN
 struct bt_fd_cache_handle *bt_fd_cache_get_handle(struct bt_fd_cache *fdc,
                const char *path)
 {
@@ -197,7 +194,6 @@ end:
        return (struct bt_fd_cache_handle *) fd_internal;
 }
 
-BT_HIDDEN
 void bt_fd_cache_put_handle(struct bt_fd_cache *fdc,
                struct bt_fd_cache_handle *handle)
 {
index 3132311cc5a74168ebd12ef1b3ea8c1a71b838c4..9a04bb47330c756256f350884665b702e977728d 100644 (file)
@@ -26,17 +26,13 @@ int bt_fd_cache_handle_get_fd(struct bt_fd_cache_handle *handle)
        return handle->fd;
 }
 
-BT_HIDDEN
 int bt_fd_cache_init(struct bt_fd_cache *fdc, int log_level);
 
-BT_HIDDEN
 void bt_fd_cache_fini(struct bt_fd_cache *fdc);
 
-BT_HIDDEN
 struct bt_fd_cache_handle *bt_fd_cache_get_handle(struct bt_fd_cache *fdc,
                const char *path);
 
-BT_HIDDEN
 void bt_fd_cache_put_handle(struct bt_fd_cache *fdc,
                struct bt_fd_cache_handle *handle);
 
diff --git a/src/gen-version-i.sh b/src/gen-version-i.sh
new file mode 100755 (executable)
index 0000000..e061193
--- /dev/null
@@ -0,0 +1,97 @@
+#!/usr/bin/env sh
+#
+# SPDX-FileCopyrightText: 2023 EfficiOS, Inc.
+# SPDX-License-Identifier: GPL-2.0-only
+
+# This file generates an include file that contains the git version
+# string of the current branch, it must be continuously updated when
+# we build in the git repo and shipped in dist tarballs to reflect the
+# status of the tree when it was generated. If the tree is clean and
+# the current commit is a tag starting with "v", consider this a
+# release version and set an empty git version.
+
+set -o nounset
+set -o errexit
+
+if test "${TOP_SRCDIR:-}" = ""; then
+       echo "$0: TOP_SRCDIR is not set" >&2
+       exit 1
+fi
+
+GREP=${GREP:-grep}
+SED=${SED:-sed}
+
+# Delete any stale "version.i.tmp" file.
+rm -f common/version.i.tmp
+
+if test ! -f common/version.i && test -f "$TOP_SRCDIR/include/version.i"; then
+       cp "$TOP_SRCDIR/include/version.i" common/version.i
+fi
+
+# If "bootstrap" and ".git" exists in the top source directory and the git
+# executable is available, get the current git version string in the form:
+#
+#  "latest_tag"(-"number_of_commits_on_top")(-g"latest_commit_hash")(-dirty)
+#
+# And store it in "version.i.tmp", if the current commit is tagged, the tag
+# starts with "v" and the tree is clean, consider this a release version and
+# overwrite the git version with an empty string in "version.i.tmp".
+#
+# Use an explicit hash abbreviation length, to avoid local `core.abbrev`
+# configurations leading to different results.
+if test -r "$TOP_SRCDIR/bootstrap" && test -r "$TOP_SRCDIR/.git" &&
+               (command -v git > /dev/null 2>&1); then
+       GIT_VERSION_STR="$(cd "$TOP_SRCDIR" && git describe --always --tags --dirty --abbrev=12)"
+       GIT_CURRENT_TAG="$(cd "$TOP_SRCDIR" && (git describe --tags --exact-match --match="v[0-9]*" HEAD || true) 2> /dev/null)"
+       echo "#define BT_VERSION_GIT \"$GIT_VERSION_STR\"" > common/version.i.tmp
+
+       if ! $GREP -- "-dirty" common/version.i.tmp > /dev/null &&
+                       test "x$GIT_CURRENT_TAG" != "x"; then
+               echo "#define BT_VERSION_GIT \"\"" > common/version.i.tmp
+       fi
+fi
+
+# If we don't have a "version.i.tmp" nor a "version.i", generate an empty
+# string as a failover. If a "version.i" is present, for example when building
+# from a distribution tarball, get the git_version using grep.
+if test ! -f common/version.i.tmp; then
+       if test -f common/version.i; then
+               $GREP "^#define \bBT_VERSION_GIT\b.*" common/version.i > common/version.i.tmp
+       else
+               echo '#define BT_VERSION_GIT ""' > common/version.i.tmp
+       fi
+fi
+
+{
+       # Fetch the BT_VERSION_EXTRA_NAME define from "version/extra_version_name" and output it
+       # to "version.i.tmp".
+       echo "#define BT_VERSION_EXTRA_NAME \"$($SED -n '1p' "$TOP_SRCDIR/version/extra_version_name" 2> /dev/null)\""
+
+       # Fetch the BT_VERSION_EXTRA_DESCRIPTION define from "version/extra_version_description",
+       # sanitize and format it with a sed script to replace all non-alpha-numeric values
+       # with "-" and join all lines by replacing "\n" with literal string c-style "\n" and
+       # output it to "version.i.tmp".
+       echo "#define BT_VERSION_EXTRA_DESCRIPTION \"$($SED -E ':a ; N ; $!ba ; s/[^a-zA-Z0-9 \n\t\.,]/-/g ; s/\r{0,1}\n/\\n/g' "$TOP_SRCDIR/version/extra_version_description" 2> /dev/null)\""
+
+       # Repeat the same logic for the "version/extra_patches" directory.
+       # Data fetched from "version/extra_patches" must be sanitized and
+       # formatted.
+       # The data is fetched using "find" with an ignore pattern for the README.adoc file.
+       # The sanitize step uses sed with a script to replace all
+       # non-alpha-numeric values, except " " (space), to "-".
+       # The formatting step uses sed with a script to join all lines
+       # by replacing "\n" with literal string c-style "\n".
+       # shellcheck disable=SC2012
+       echo "#define BT_VERSION_EXTRA_PATCHES \"$(ls -1 "$TOP_SRCDIR/version/extra_patches" | $GREP -v '^README.adoc' | $SED -E ':a ; N ; $!ba ; s/[^a-zA-Z0-9 \n\t\.]/-/g ; s/\r{0,1}\n/\\n/g' 2> /dev/null)\""
+} >> common/version.i.tmp
+
+# If we don't have a "version.i" or we have both files (version.i, version.i.tmp)
+# and they are different, copy "version.i.tmp" over "version.i".
+# This way the dependent targets are only rebuilt when the git version
+# string or either one of extra version string change.
+if test ! -f common/version.i ||
+               test x"$(cat common/version.i.tmp)" != x"$(cat common/version.i)"; then
+       mv -f common/version.i.tmp common/version.i
+fi
+
+rm -f common/version.i.tmp
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
deleted file mode 100644 (file)
index a911381..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = trace-ir prio-heap plugin graph
-
-lib_LTLIBRARIES = libbabeltrace2.la
-
-libbabeltrace2_la_SOURCES = \
-       assert-cond-base.h \
-       assert-cond.h \
-       assert-cond.c \
-       babeltrace2.c \
-       current-thread.c \
-       error.c \
-       error.h \
-       func-status.h \
-       integer-range-set.c \
-       integer-range-set.h \
-       lib-logging.c \
-       logging.c \
-       logging.h \
-       object-pool.c \
-       object-pool.h \
-       object.h \
-       property.h \
-       util.c \
-       value.c \
-       value.h
-
-libbabeltrace2_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LT_NO_UNDEFINED) \
-       -version-info $(BABELTRACE_LIBRARY_VERSION)
-
-libbabeltrace2_la_LIBADD = \
-       prio-heap/libprio-heap.la \
-       graph/libgraph.la \
-       plugin/libplugin.la \
-       trace-ir/libtrace-ir.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/compat/libcompat.la
-
-if ENABLE_BUILT_IN_PYTHON_PLUGIN_SUPPORT
-libbabeltrace2_la_LIBADD += $(top_builddir)/src/python-plugin-provider/babeltrace2-python-plugin-provider.la
-endif
index 271ed02efd37b959d27cf3ab5981498ab21bd6f1..c8d04b63ea6dbc6fbfc61023e64eec7273183700 100644 (file)
@@ -24,9 +24,6 @@
 #endif
 
 #include <stdbool.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include "common/common.h"
 #include "common/macros.h"
 
 /*
@@ -58,7 +55,7 @@
  */
 #define BT_ASSERT_COND_MSG(_fmt, ...)                                  \
        do {                                                            \
-               bt_lib_log(_BT_LOG_SRCLOC_FUNCTION, __FILE__,           \
+               bt_lib_log(__FILE__, __func__,                          \
                        __LINE__, BT_LOG_FATAL, BT_LOG_TAG,             \
                        (_fmt), ##__VA_ARGS__);                         \
        } while (0)
@@ -77,7 +74,6 @@
  *
  * 4. Aborts.
  */
-BT_HIDDEN
 __attribute__((noreturn))
 void bt_lib_assert_cond_failed(const char *cond_type, const char *func,
                const char *id_suffix, const char *fmt, ...);
index c2d23c9e79315deff0d62c0d249793bef4b42af5..b07bedd0c0267540441f4e4f4f2232942eebf026 100644 (file)
@@ -11,7 +11,7 @@
 #include <stdarg.h>
 #include <glib.h>
 #include "common/assert.h"
-#include "common/macros.h"
+#include "common/common.h"
 #include "assert-cond-base.h"
 
 static
@@ -53,7 +53,6 @@ GString *format_cond_id(const char *cond_type, const char *func,
        return id;
 }
 
-BT_HIDDEN
 void bt_lib_assert_cond_failed(const char *cond_type, const char *func,
                const char *id_suffix, const char *fmt, ...)
 {
@@ -70,7 +69,7 @@ void bt_lib_assert_cond_failed(const char *cond_type, const char *func,
        BT_ASSERT_COND_MSG("------------------------------------------------------------------------");
        BT_ASSERT_COND_MSG("Error is:");
        va_start(args, fmt);
-       bt_lib_log_v(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, BT_LOG_FATAL,
+       bt_lib_log_v(__FILE__, __func__, __LINE__, BT_LOG_FATAL,
                BT_LOG_TAG, fmt, &args);
        va_end(args);
        BT_ASSERT_COND_MSG("Aborting...");
index bca924e8c029f22254683c53c79d44171e3a6969..4868c48c07fd3738912b8a1d6afaf3c02a698b86 100644 (file)
@@ -10,6 +10,8 @@
 
 #include "assert-cond-base.h"
 
+#include <inttypes.h>
+
 /*
  * Asserts that a given variable `_obj` named `_obj_name` (capitalized)
  * and having the ID `_obj_id` (within the function's context) is not
index a29cf9dbe7c31a9d157678fec9e8f00e5d9435f2..e478cae4bcb5f9ec1f5d375f090ea429d82d92f5 100644 (file)
@@ -9,55 +9,66 @@
 #include <string.h>
 
 #include "common/version.h"
+#include "common/macros.h"
 
+BT_EXPORT
 unsigned int bt_version_get_major(void)
 {
        return BT_VERSION_MAJOR;
 }
 
+BT_EXPORT
 unsigned int bt_version_get_minor(void)
 {
        return BT_VERSION_MINOR;
 }
 
+BT_EXPORT
 unsigned int bt_version_get_patch(void)
 {
        return BT_VERSION_PATCH;
 }
 
+BT_EXPORT
 const char *bt_version_get_development_stage(void)
 {
        return strlen(BT_VERSION_DEV_STAGE) == 0 ? NULL : BT_VERSION_DEV_STAGE;
 }
 
+BT_EXPORT
 const char *bt_version_get_vcs_revision_description(void)
 {
        return strlen(BT_VERSION_GIT) == 0 ? NULL : BT_VERSION_GIT;
 }
 
+BT_EXPORT
 const char *bt_version_get_name(void)
 {
        return strlen(BT_VERSION_NAME) == 0 ? NULL : BT_VERSION_NAME;
 }
 
+BT_EXPORT
 const char *bt_version_get_name_description(void)
 {
        return strlen(BT_VERSION_DESCRIPTION) == 0 ? NULL :
                BT_VERSION_DESCRIPTION;
 }
 
+BT_EXPORT
 const char *bt_version_get_extra_name(void)
 {
        return strlen(BT_VERSION_EXTRA_NAME) == 0 ? NULL :
                BT_VERSION_EXTRA_NAME;
 }
 
+BT_EXPORT
 const char *bt_version_get_extra_description(void)
 {
        return strlen(BT_VERSION_EXTRA_DESCRIPTION) == 0 ? NULL :
                BT_VERSION_EXTRA_DESCRIPTION;
 }
 
+BT_EXPORT
 const char *bt_version_get_extra_patch_names(void)
 {
        return strlen(BT_VERSION_EXTRA_PATCHES) == 0 ? NULL :
index 9e14e4e70e895920a852707ca98d5464e12cfece..5547a146b5e4b156b422b0767f954c1678db35f7 100644 (file)
@@ -12,7 +12,6 @@
 #include <stdarg.h>
 
 #include "error.h"
-#include "common/assert.h"
 #include "lib/assert-cond.h"
 #include "lib/func-status.h"
 
@@ -28,6 +27,7 @@
  */
 static __thread struct bt_error *thread_error;
 
+BT_EXPORT
 const struct bt_error *bt_current_thread_take_error(void)
 {
        struct bt_error *error = thread_error;
@@ -38,6 +38,7 @@ const struct bt_error *bt_current_thread_take_error(void)
        return error;
 }
 
+BT_EXPORT
 void bt_current_thread_clear_error(void)
 {
        bt_error_destroy(thread_error);
@@ -46,6 +47,7 @@ void bt_current_thread_clear_error(void)
        thread_error = NULL;
 }
 
+BT_EXPORT
 void bt_current_thread_move_error(const struct bt_error *error)
 {
        BT_ASSERT_PRE_ERROR_NON_NULL(error);
@@ -82,6 +84,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_current_thread_error_append_cause_status
 bt_current_thread_error_append_cause_from_unknown(
                const char *module_name, const char *file_name,
@@ -110,6 +113,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_current_thread_error_append_cause_status
 bt_current_thread_error_append_cause_from_component(
                bt_self_component *self_comp, const char *file_name,
@@ -138,6 +142,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_current_thread_error_append_cause_status
 bt_current_thread_error_append_cause_from_component_class(
                bt_self_component_class *self_comp_class, const char *file_name,
@@ -166,6 +171,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_current_thread_error_append_cause_status
 bt_current_thread_error_append_cause_from_message_iterator(
                bt_self_message_iterator *self_iter, const char *file_name,
index 9144bd96ddc528c9f52e47afe88d0c195bd678ed..77064005dc3ddfcc24e3ac1b6830635bbb0a8591 100644 (file)
 #include <babeltrace2/babeltrace.h>
 
 #include "error.h"
-#include "graph/message/iterator.h"
 #include "graph/component.h"
 #include "graph/component-class.h"
+#include "graph/iterator.h"
 #include "common/assert.h"
+#include "common/common.h"
 #include "lib/assert-cond.h"
 #include "lib/func-status.h"
 
@@ -155,8 +156,6 @@ int init_error_cause(struct bt_error_cause *cause,
                goto end;
        }
 
-       BT_LIB_LOGD("Initialized error cause: %!+r", cause);
-
 end:
        return ret;
 }
@@ -300,8 +299,10 @@ struct bt_error_cause_component_actor *create_error_cause_component_actor(
        goto end;
 
 error:
-       destroy_error_cause(&cause->base);
-       cause = NULL;
+       if (cause) {
+               destroy_error_cause(&cause->base);
+               cause = NULL;
+       }
 
 end:
        return cause;
@@ -341,8 +342,10 @@ create_error_cause_component_class_actor(struct bt_component_class *comp_cls,
        goto end;
 
 error:
-       destroy_error_cause(&cause->base);
-       cause = NULL;
+       if (cause) {
+               destroy_error_cause(&cause->base);
+               cause = NULL;
+       }
 
 end:
        return cause;
@@ -407,14 +410,15 @@ create_error_cause_message_iterator_actor(struct bt_message_iterator *iter,
        goto end;
 
 error:
-       destroy_error_cause(&cause->base);
-       cause = NULL;
+       if (cause) {
+               destroy_error_cause(&cause->base);
+               cause = NULL;
+       }
 
 end:
        return cause;
 }
 
-BT_HIDDEN
 struct bt_error *bt_error_create(void)
 {
        struct bt_error *error;
@@ -444,7 +448,6 @@ end:
        return error;
 }
 
-BT_HIDDEN
 void bt_error_destroy(struct bt_error *error)
 {
        if (!error) {
@@ -462,7 +465,6 @@ end:
        return;
 }
 
-BT_HIDDEN
 int bt_error_append_cause_from_unknown(struct bt_error *error,
                const char *module_name, const char *file_name,
                uint64_t line_no, const char *msg_fmt, va_list args)
@@ -490,11 +492,9 @@ int bt_error_append_cause_from_unknown(struct bt_error *error,
        cause = NULL;
 
 end:
-       destroy_error_cause(cause);
        return status;
 }
 
-BT_HIDDEN
 int bt_error_append_cause_from_component(
                struct bt_error *error, bt_self_component *self_comp,
                const char *file_name, uint64_t line_no,
@@ -523,11 +523,9 @@ int bt_error_append_cause_from_component(
        cause = NULL;
 
 end:
-       destroy_error_cause(&cause->base);
        return status;
 }
 
-BT_HIDDEN
 int bt_error_append_cause_from_component_class(
                struct bt_error *error,
                bt_self_component_class *self_comp_class,
@@ -557,11 +555,9 @@ int bt_error_append_cause_from_component_class(
        cause = NULL;
 
 end:
-       destroy_error_cause(&cause->base);
        return status;
 }
 
-BT_HIDDEN
 int bt_error_append_cause_from_message_iterator(
                struct bt_error *error, bt_self_message_iterator *self_iter,
                const char *file_name, uint64_t line_no,
@@ -590,7 +586,6 @@ int bt_error_append_cause_from_message_iterator(
        cause = NULL;
 
 end:
-       destroy_error_cause(&cause->base);
        return status;
 }
 
@@ -600,18 +595,21 @@ uint64_t error_cause_count(const bt_error *error)
        return error->causes ? error->causes->len : 0;
 }
 
+BT_EXPORT
 uint64_t bt_error_get_cause_count(const bt_error *error)
 {
        BT_ASSERT_PRE_ERROR_NON_NULL(error);
        return error_cause_count(error);
 }
 
+BT_EXPORT
 void bt_error_release(const struct bt_error *error)
 {
        BT_ASSERT_PRE_ERROR_NON_NULL(error);
        bt_error_destroy((void *) error);
 }
 
+BT_EXPORT
 const struct bt_error_cause *bt_error_borrow_cause_by_index(
                const bt_error *error, uint64_t index)
 {
@@ -620,6 +618,7 @@ const struct bt_error_cause *bt_error_borrow_cause_by_index(
        return error->causes->pdata[index];
 }
 
+BT_EXPORT
 enum bt_error_cause_actor_type bt_error_cause_get_actor_type(
                const struct bt_error_cause *cause)
 {
@@ -627,30 +626,35 @@ enum bt_error_cause_actor_type bt_error_cause_get_actor_type(
        return cause->actor_type;
 }
 
+BT_EXPORT
 const char *bt_error_cause_get_message(const struct bt_error_cause *cause)
 {
        BT_ASSERT_PRE_ERROR_CAUSE_NON_NULL(cause);
        return cause->message->str;
 }
 
+BT_EXPORT
 const char *bt_error_cause_get_module_name(const struct bt_error_cause *cause)
 {
        BT_ASSERT_PRE_ERROR_CAUSE_NON_NULL(cause);
        return cause->module_name->str;
 }
 
+BT_EXPORT
 const char *bt_error_cause_get_file_name(const struct bt_error_cause *cause)
 {
        BT_ASSERT_PRE_ERROR_CAUSE_NON_NULL(cause);
        return cause->file_name->str;
 }
 
+BT_EXPORT
 uint64_t bt_error_cause_get_line_number(const bt_error_cause *cause)
 {
        BT_ASSERT_PRE_ERROR_CAUSE_NON_NULL(cause);
        return cause->line_no;
 }
 
+BT_EXPORT
 const char *bt_error_cause_component_actor_get_component_name(
                const struct bt_error_cause *cause)
 {
@@ -663,6 +667,7 @@ const char *bt_error_cause_component_actor_get_component_name(
        return spec_cause->comp_name->str;
 }
 
+BT_EXPORT
 bt_component_class_type bt_error_cause_component_actor_get_component_class_type(
                const struct bt_error_cause *cause)
 {
@@ -675,6 +680,7 @@ bt_component_class_type bt_error_cause_component_actor_get_component_class_type(
        return spec_cause->comp_class_id.type;
 }
 
+BT_EXPORT
 const char *bt_error_cause_component_actor_get_component_class_name(
                const struct bt_error_cause *cause)
 {
@@ -687,6 +693,7 @@ const char *bt_error_cause_component_actor_get_component_class_name(
        return spec_cause->comp_class_id.name->str;
 }
 
+BT_EXPORT
 const char *bt_error_cause_component_actor_get_plugin_name(
                const struct bt_error_cause *cause)
 {
@@ -700,6 +707,7 @@ const char *bt_error_cause_component_actor_get_plugin_name(
                spec_cause->comp_class_id.plugin_name->str : NULL;
 }
 
+BT_EXPORT
 bt_component_class_type
 bt_error_cause_component_class_actor_get_component_class_type(
                const struct bt_error_cause *cause)
@@ -713,6 +721,7 @@ bt_error_cause_component_class_actor_get_component_class_type(
        return spec_cause->comp_class_id.type;
 }
 
+BT_EXPORT
 const char *bt_error_cause_component_class_actor_get_component_class_name(
                const struct bt_error_cause *cause)
 {
@@ -725,6 +734,7 @@ const char *bt_error_cause_component_class_actor_get_component_class_name(
        return spec_cause->comp_class_id.name->str;
 }
 
+BT_EXPORT
 const char *bt_error_cause_component_class_actor_get_plugin_name(
                const struct bt_error_cause *cause)
 {
@@ -738,6 +748,7 @@ const char *bt_error_cause_component_class_actor_get_plugin_name(
                spec_cause->comp_class_id.plugin_name->str : NULL;
 }
 
+BT_EXPORT
 const char *bt_error_cause_message_iterator_actor_get_component_name(
                const struct bt_error_cause *cause)
 {
@@ -750,6 +761,7 @@ const char *bt_error_cause_message_iterator_actor_get_component_name(
        return spec_cause->comp_name->str;
 }
 
+BT_EXPORT
 const char *
 bt_error_cause_message_iterator_actor_get_component_output_port_name(
                const struct bt_error_cause *cause)
@@ -763,6 +775,7 @@ bt_error_cause_message_iterator_actor_get_component_output_port_name(
        return spec_cause->output_port_name->str;
 }
 
+BT_EXPORT
 bt_component_class_type
 bt_error_cause_message_iterator_actor_get_component_class_type(
                const struct bt_error_cause *cause)
@@ -776,6 +789,7 @@ bt_error_cause_message_iterator_actor_get_component_class_type(
        return spec_cause->comp_class_id.type;
 }
 
+BT_EXPORT
 const char *bt_error_cause_message_iterator_actor_get_component_class_name(
                const struct bt_error_cause *cause)
 {
@@ -788,6 +802,7 @@ const char *bt_error_cause_message_iterator_actor_get_component_class_name(
        return spec_cause->comp_class_id.name->str;
 }
 
+BT_EXPORT
 const char *bt_error_cause_message_iterator_actor_get_plugin_name(
                const struct bt_error_cause *cause)
 {
index 8095a930c8b62a5317ff02ec3faae662198652f4..39788185629eb90a9df65c1fa69f967765394366 100644 (file)
@@ -10,8 +10,6 @@
 #include <stdarg.h>
 #include <glib.h>
 #include <babeltrace2/babeltrace.h>
-#include "lib/object.h"
-#include "common/macros.h"
 
 struct bt_error_cause {
        enum bt_error_cause_actor_type actor_type;
@@ -71,31 +69,29 @@ const char *bt_error_cause_actor_type_string(
        }
 };
 
-BT_HIDDEN
 struct bt_error *bt_error_create(void);
 
-BT_HIDDEN
 void bt_error_destroy(struct bt_error *error);
 
-BT_HIDDEN __BT_ATTR_FORMAT_PRINTF(5, 0)
+__BT_ATTR_FORMAT_PRINTF(5, 0)
 int bt_error_append_cause_from_unknown(struct bt_error *error,
                const char *module_name, const char *file_name,
                uint64_t line_no, const char *msg_fmt, va_list args);
 
-BT_HIDDEN __BT_ATTR_FORMAT_PRINTF(5, 0)
+__BT_ATTR_FORMAT_PRINTF(5, 0)
 int bt_error_append_cause_from_component(
                struct bt_error *error, bt_self_component *self_comp,
                const char *file_name, uint64_t line_no,
                const char *msg_fmt, va_list args);
 
-BT_HIDDEN __BT_ATTR_FORMAT_PRINTF(5, 0)
+__BT_ATTR_FORMAT_PRINTF(5, 0)
 int bt_error_append_cause_from_component_class(
                struct bt_error *error,
                bt_self_component_class *self_comp_class,
                const char *file_name, uint64_t line_no,
                const char *msg_fmt, va_list args);
 
-BT_HIDDEN __BT_ATTR_FORMAT_PRINTF(5, 0)
+__BT_ATTR_FORMAT_PRINTF(5, 0)
 int bt_error_append_cause_from_message_iterator(
                struct bt_error *error, bt_self_message_iterator *self_iter,
                const char *file_name, uint64_t line_no,
diff --git a/src/lib/graph/Makefile.am b/src/lib/graph/Makefile.am
deleted file mode 100644 (file)
index 69b0531..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = message
-
-noinst_LTLIBRARIES = libgraph.la
-
-# Graph library
-libgraph_la_SOURCES = \
-       component-class-sink-simple.c \
-       component-class-sink-simple.h \
-       component-class.c \
-       component-class.h \
-       component-descriptor-set.c \
-       component-descriptor-set.h \
-       component-filter.c \
-       component-filter.h \
-       component-sink.c \
-       component-sink.h \
-       component-source.c \
-       component-source.h \
-       component.c \
-       component.h \
-       connection.c \
-       connection.h \
-       graph.c \
-       graph.h \
-       interrupter.c \
-       interrupter.h \
-       iterator.c \
-       message-iterator-class.c \
-       message-iterator-class.h \
-       mip.c \
-       port.c \
-       port.h \
-       query-executor.c \
-       query-executor.h
-
-libgraph_la_LIBADD = \
-       message/libgraph-message.la
index 812e3b5fe4ed86e32a6b61506669535f9fe4a7e7..c9a008d323f8e102797f9d1fa44e5243e7af525e 100644 (file)
@@ -9,7 +9,6 @@
 
 #include "common/assert.h"
 #include "common/common.h"
-#include "lib/assert-cond.h"
 #include "lib/object.h"
 #include <babeltrace2/graph/component-class.h>
 #include <babeltrace2/graph/self-component-port.h>
@@ -45,8 +44,9 @@ void simple_sink_data_destroy(struct simple_sink_data *data)
 static
 enum bt_component_class_initialize_method_status simple_sink_init(
                bt_self_component_sink *self_comp,
-               bt_self_component_sink_configuration *config,
-               const struct bt_value *params, void *init_method_data)
+               bt_self_component_sink_configuration *config __attribute__((unused)),
+               const struct bt_value *params __attribute__((unused)),
+               void *init_method_data)
 {
        int status = BT_FUNC_STATUS_OK;
        struct simple_sink_data *data = NULL;
@@ -182,7 +182,6 @@ enum bt_component_class_sink_consume_method_status simple_sink_consume(
        return status;
 }
 
-BT_HIDDEN
 struct bt_component_class_sink *bt_component_class_sink_simple_borrow(void)
 {
        enum bt_component_class_set_method_status set_method_status;
index 5454bd3980a1f51971113cbca97a750327312614..7143da96d84153785655c0ff7788e5c6f0cccf37 100644 (file)
@@ -7,7 +7,6 @@
 #ifndef BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_SIMPLE_H
 #define BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_SIMPLE_H
 
-#include <stdint.h>
 #include <babeltrace2/types.h>
 #include <babeltrace2/graph/message.h>
 
@@ -18,7 +17,6 @@ struct simple_sink_init_method_data {
        void *user_data;
 };
 
-BT_HIDDEN
 struct bt_component_class_sink *bt_component_class_sink_simple_borrow(void);
 
 #endif /* BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_SIMPLE_H */
index 538cd77be1daff6a254ee80bffd03cf3ac087c6d..4c37e617a1ac6242cd9365578a55319d0b019731 100644 (file)
@@ -9,6 +9,7 @@
 #include "lib/logging.h"
 
 #include "common/assert.h"
+#include "common/common.h"
 #include "lib/assert-cond.h"
 #include "compat/compiler.h"
 #include <babeltrace2/graph/component-class.h>
@@ -39,7 +40,7 @@ void destroy_component_class(struct bt_object *obj)
        if (class->destroy_listeners) {
                for (i = class->destroy_listeners->len - 1; i >= 0; i--) {
                        struct bt_component_class_destroy_listener *listener =
-                               &g_array_index(class->destroy_listeners,
+                               &bt_g_array_index(class->destroy_listeners,
                                        struct bt_component_class_destroy_listener,
                                        i);
 
@@ -156,6 +157,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 struct bt_component_class_source *bt_component_class_source_create(
                const char *name,
                struct bt_message_iterator_class *message_iterator_class)
@@ -195,6 +197,7 @@ end:
        return (void *) source_class;
 }
 
+BT_EXPORT
 struct bt_component_class_filter *bt_component_class_filter_create(
                const char *name,
                struct bt_message_iterator_class *message_iterator_class)
@@ -234,6 +237,7 @@ end:
        return (void *) filter_class;
 }
 
+BT_EXPORT
 struct bt_component_class_sink *bt_component_class_sink_create(
                const char *name, bt_component_class_sink_consume_method method)
 {
@@ -273,6 +277,7 @@ end:
        return (void *) sink_class;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_source_set_get_supported_mip_versions_method(
                struct bt_component_class_source *comp_cls,
@@ -288,6 +293,7 @@ bt_component_class_source_set_get_supported_mip_versions_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_filter_set_get_supported_mip_versions_method(
                struct bt_component_class_filter *comp_cls,
@@ -303,6 +309,7 @@ bt_component_class_filter_set_get_supported_mip_versions_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_sink_set_get_supported_mip_versions_method(
                struct bt_component_class_sink *comp_cls,
@@ -318,6 +325,7 @@ bt_component_class_sink_set_get_supported_mip_versions_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_source_set_initialize_method(
                struct bt_component_class_source *comp_cls,
@@ -333,6 +341,7 @@ bt_component_class_source_set_initialize_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_filter_set_initialize_method(
                struct bt_component_class_filter *comp_cls,
@@ -348,6 +357,7 @@ bt_component_class_filter_set_initialize_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_sink_set_initialize_method(
                struct bt_component_class_sink *comp_cls,
@@ -363,6 +373,7 @@ bt_component_class_sink_set_initialize_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_source_set_finalize_method(
                struct bt_component_class_source *comp_cls,
@@ -378,6 +389,7 @@ bt_component_class_source_set_finalize_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_filter_set_finalize_method(
                struct bt_component_class_filter *comp_cls,
@@ -393,6 +405,7 @@ bt_component_class_filter_set_finalize_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_sink_set_finalize_method(
                struct bt_component_class_sink *comp_cls,
@@ -408,6 +421,7 @@ bt_component_class_sink_set_finalize_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_source_set_query_method(
                struct bt_component_class_source *comp_cls,
@@ -423,6 +437,7 @@ bt_component_class_source_set_query_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_filter_set_query_method(
                struct bt_component_class_filter *comp_cls,
@@ -438,6 +453,7 @@ bt_component_class_filter_set_query_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_sink_set_query_method(
                struct bt_component_class_sink *comp_cls,
@@ -453,6 +469,7 @@ bt_component_class_sink_set_query_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_filter_set_input_port_connected_method(
                struct bt_component_class_filter *comp_cls,
@@ -468,6 +485,7 @@ bt_component_class_filter_set_input_port_connected_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_sink_set_input_port_connected_method(
                struct bt_component_class_sink *comp_cls,
@@ -483,6 +501,7 @@ bt_component_class_sink_set_input_port_connected_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_source_set_output_port_connected_method(
                struct bt_component_class_source *comp_cls,
@@ -498,6 +517,7 @@ bt_component_class_source_set_output_port_connected_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_filter_set_output_port_connected_method(
                struct bt_component_class_filter *comp_cls,
@@ -513,6 +533,7 @@ bt_component_class_filter_set_output_port_connected_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_method_status
 bt_component_class_sink_set_graph_is_configured_method(
                struct bt_component_class_sink *comp_cls,
@@ -528,6 +549,7 @@ bt_component_class_sink_set_graph_is_configured_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_description_status
 bt_component_class_set_description(
                struct bt_component_class *comp_cls,
@@ -546,6 +568,7 @@ bt_component_class_set_description(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_component_class_set_help_status bt_component_class_set_help(
                struct bt_component_class *comp_cls,
                const char *help)
@@ -559,12 +582,14 @@ enum bt_component_class_set_help_status bt_component_class_set_help(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 const char *bt_component_class_get_name(const struct bt_component_class *comp_cls)
 {
        BT_ASSERT_PRE_DEV_COMP_CLS_NON_NULL(comp_cls);
        return comp_cls->name->str;
 }
 
+BT_EXPORT
 enum bt_component_class_type bt_component_class_get_type(
                const struct bt_component_class *comp_cls)
 {
@@ -572,6 +597,7 @@ enum bt_component_class_type bt_component_class_get_type(
        return comp_cls->type;
 }
 
+BT_EXPORT
 const char *bt_component_class_get_description(
                const struct bt_component_class *comp_cls)
 {
@@ -581,6 +607,7 @@ const char *bt_component_class_get_description(
                comp_cls->description->str : NULL;
 }
 
+BT_EXPORT
 const char *bt_component_class_get_help(
                const struct bt_component_class *comp_cls)
 {
@@ -589,7 +616,6 @@ const char *bt_component_class_get_help(
                comp_cls->help->str[0] != '\0' ? comp_cls->help->str : NULL;
 }
 
-BT_HIDDEN
 void bt_component_class_add_destroy_listener(
                struct bt_component_class *comp_cls,
                bt_component_class_destroy_listener_func func, void *data)
@@ -605,7 +631,6 @@ void bt_component_class_add_destroy_listener(
                "%![cc-]+C, listener-func-addr=%p", comp_cls, func);
 }
 
-BT_HIDDEN
 void _bt_component_class_freeze(const struct bt_component_class *comp_cls)
 {
        BT_ASSERT(comp_cls);
@@ -613,48 +638,56 @@ void _bt_component_class_freeze(const struct bt_component_class *comp_cls)
        ((struct bt_component_class *) comp_cls)->frozen = true;
 }
 
+BT_EXPORT
 void bt_component_class_get_ref(
                const struct bt_component_class *component_class)
 {
        bt_object_get_ref(component_class);
 }
 
+BT_EXPORT
 void bt_component_class_put_ref(
                const struct bt_component_class *component_class)
 {
        bt_object_put_ref(component_class);
 }
 
+BT_EXPORT
 void bt_component_class_source_get_ref(
                const struct bt_component_class_source *component_class_source)
 {
        bt_object_get_ref(component_class_source);
 }
 
+BT_EXPORT
 void bt_component_class_source_put_ref(
                const struct bt_component_class_source *component_class_source)
 {
        bt_object_put_ref(component_class_source);
 }
 
+BT_EXPORT
 void bt_component_class_filter_get_ref(
                const struct bt_component_class_filter *component_class_filter)
 {
        bt_object_get_ref(component_class_filter);
 }
 
+BT_EXPORT
 void bt_component_class_filter_put_ref(
                const struct bt_component_class_filter *component_class_filter)
 {
        bt_object_put_ref(component_class_filter);
 }
 
+BT_EXPORT
 void bt_component_class_sink_get_ref(
                const struct bt_component_class_sink *component_class_sink)
 {
        bt_object_get_ref(component_class_sink);
 }
 
+BT_EXPORT
 void bt_component_class_sink_put_ref(
                const struct bt_component_class_sink *component_class_sink)
 {
index 17000909e2535f427a873ef1c85bdcae73eb7acd..c6a69e5746514cd0917b4d1834213947907d5c33 100644 (file)
@@ -10,7 +10,6 @@
 
 #include <babeltrace2/graph/component-class.h>
 #include <babeltrace2/graph/component.h>
-#include "common/macros.h"
 #include "lib/object.h"
 #include "common/list.h"
 #include <babeltrace2/types.h>
@@ -84,11 +83,9 @@ struct bt_component_class_sink {
        } methods;
 };
 
-BT_HIDDEN
 void bt_component_class_add_destroy_listener(struct bt_component_class *class,
                bt_component_class_destroy_listener_func func, void *data);
 
-BT_HIDDEN
 void _bt_component_class_freeze(
                const struct bt_component_class *component_class);
 
index 0ef243884ecc1c13e49839646989dc68c3498032..b779f7cce7e106e2548b91c6b51f5c6dfb24b1b7 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "common/assert.h"
 #include "lib/assert-cond.h"
+#include "lib/func-status.h"
 #include "compat/compiler.h"
 #include "common/common.h"
 #include <babeltrace2/types.h>
@@ -19,7 +20,6 @@
 
 #include "component-class.h"
 #include "component-descriptor-set.h"
-#include "component-class-sink-simple.h"
 #include "lib/value.h"
 
 static
@@ -65,6 +65,7 @@ end:
        return;
 }
 
+BT_EXPORT
 struct bt_component_descriptor_set *bt_component_descriptor_set_create(void)
 {
        struct bt_component_descriptor_set *comp_descr_set;
@@ -113,6 +114,7 @@ end:
        return comp_descr_set;
 }
 
+BT_EXPORT
 enum bt_component_descriptor_set_add_descriptor_status
 bt_component_descriptor_set_add_descriptor_with_initialize_method_data(
                struct bt_component_descriptor_set *comp_descr_set,
@@ -193,6 +195,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_component_descriptor_set_add_descriptor_status
 bt_component_descriptor_set_add_descriptor(
                struct bt_component_descriptor_set *comp_descr_set,
@@ -205,12 +208,14 @@ bt_component_descriptor_set_add_descriptor(
                comp_descr_set, comp_cls, params, NULL);
 }
 
+BT_EXPORT
 void bt_component_descriptor_set_get_ref(
                const struct bt_component_descriptor_set *comp_descr_set)
 {
        bt_object_get_ref(comp_descr_set);
 }
 
+BT_EXPORT
 void bt_component_descriptor_set_put_ref(
                const struct bt_component_descriptor_set *comp_descr_set)
 {
index 99499ef22320a1b6c10e7394fd94b57882e993aa..fdc90f461864e50cae204287581eebe51c1fe897 100644 (file)
 
 #include <babeltrace2/graph/graph.h>
 #include <babeltrace2/graph/component-descriptor-set.h>
-#include "common/macros.h"
 #include "lib/object.h"
-#include "common/assert.h"
-#include "common/common.h"
-#include <stdlib.h>
 #include <glib.h>
 
-#include "component.h"
-#include "component-sink.h"
-#include "connection.h"
-#include "lib/func-status.h"
-
 /*
  * This structure describes an eventual component instance.
  */
index 44bf484343c2d8cbc0ff54e3abcaa1a9f39daaec..fe6b6898f166d1594fadaeafb1042aceaf19de3d 100644 (file)
 #include "component-class.h"
 #include "lib/func-status.h"
 
-BT_HIDDEN
-void bt_component_filter_destroy(struct bt_component *component)
-{
-}
-
-BT_HIDDEN
-struct bt_component *bt_component_filter_create(
-               const struct bt_component_class *class)
+struct bt_component *bt_component_filter_create(void)
 {
        struct bt_component_filter *filter = NULL;
 
@@ -43,6 +36,7 @@ end:
        return (void *) filter;
 }
 
+BT_EXPORT
 const bt_component_class_filter *
 bt_component_filter_borrow_class_const(
                const bt_component_filter *component)
@@ -59,6 +53,7 @@ bt_component_filter_borrow_class_const(
        return (bt_component_class_filter *) cls;
 }
 
+BT_EXPORT
 uint64_t bt_component_filter_get_output_port_count(
                const struct bt_component_filter *comp)
 {
@@ -66,6 +61,7 @@ uint64_t bt_component_filter_get_output_port_count(
        return bt_component_get_output_port_count((void *) comp, __func__);
 }
 
+BT_EXPORT
 const struct bt_port_output *
 bt_component_filter_borrow_output_port_by_name_const(
                const struct bt_component_filter *comp, const char *name)
@@ -78,6 +74,7 @@ bt_component_filter_borrow_output_port_by_name_const(
                (void *) comp, name, __func__);
 }
 
+BT_EXPORT
 struct bt_self_component_port_output *
 bt_self_component_filter_borrow_output_port_by_name(
                struct bt_self_component_filter *comp, const char *name)
@@ -90,6 +87,7 @@ bt_self_component_filter_borrow_output_port_by_name(
                (void *) comp, name, __func__);
 }
 
+BT_EXPORT
 const struct bt_port_output *
 bt_component_filter_borrow_output_port_by_index_const(
                const struct bt_component_filter *comp, uint64_t index)
@@ -102,6 +100,7 @@ bt_component_filter_borrow_output_port_by_index_const(
                (void *) comp, index, __func__);
 }
 
+BT_EXPORT
 struct bt_self_component_port_output *
 bt_self_component_filter_borrow_output_port_by_index(
                struct bt_self_component_filter *comp, uint64_t index)
@@ -114,6 +113,7 @@ bt_self_component_filter_borrow_output_port_by_index(
                (void *) comp, index, __func__);
 }
 
+BT_EXPORT
 enum bt_self_component_add_port_status bt_self_component_filter_add_output_port(
                struct bt_self_component_filter *self_comp,
                const char *name, void *user_data,
@@ -123,8 +123,6 @@ enum bt_self_component_add_port_status bt_self_component_filter_add_output_port(
        enum bt_self_component_add_port_status status;
        struct bt_port *port = NULL;
 
-       BT_ASSERT_PRE_NO_ERROR();
-
        /*
         * bt_component_add_output_port() logs details/errors and checks
         * preconditions.
@@ -145,6 +143,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 uint64_t bt_component_filter_get_input_port_count(
                const struct bt_component_filter *component)
 {
@@ -152,6 +151,7 @@ uint64_t bt_component_filter_get_input_port_count(
        return bt_component_get_input_port_count((void *) component, __func__);
 }
 
+BT_EXPORT
 const struct bt_port_input *bt_component_filter_borrow_input_port_by_name_const(
                const struct bt_component_filter *component, const char *name)
 {
@@ -163,6 +163,7 @@ const struct bt_port_input *bt_component_filter_borrow_input_port_by_name_const(
                (void *) component, name, __func__);
 }
 
+BT_EXPORT
 struct bt_self_component_port_input *
 bt_self_component_filter_borrow_input_port_by_name(
                struct bt_self_component_filter *component, const char *name)
@@ -175,6 +176,7 @@ bt_self_component_filter_borrow_input_port_by_name(
                (void *) component, name, __func__);
 }
 
+BT_EXPORT
 const struct bt_port_input *
 bt_component_filter_borrow_input_port_by_index_const(
                const struct bt_component_filter *component, uint64_t index)
@@ -187,6 +189,7 @@ bt_component_filter_borrow_input_port_by_index_const(
                (void *) component, index, __func__);
 }
 
+BT_EXPORT
 struct bt_self_component_port_input *
 bt_self_component_filter_borrow_input_port_by_index(
                struct bt_self_component_filter *component, uint64_t index)
@@ -199,6 +202,7 @@ bt_self_component_filter_borrow_input_port_by_index(
                (void *) component, index, __func__);
 }
 
+BT_EXPORT
 enum bt_self_component_add_port_status bt_self_component_filter_add_input_port(
                struct bt_self_component_filter *self_comp,
                const char *name, void *user_data,
@@ -208,8 +212,6 @@ enum bt_self_component_add_port_status bt_self_component_filter_add_input_port(
        struct bt_port *port = NULL;
        struct bt_component *comp = (void *) self_comp;
 
-       BT_ASSERT_PRE_NO_ERROR();
-
        /*
         * bt_component_add_input_port() logs details/errors and checks
         * preconditions.
@@ -230,12 +232,14 @@ end:
        return status;
 }
 
+BT_EXPORT
 void bt_component_filter_get_ref(
                const struct bt_component_filter *component_filter)
 {
        bt_object_get_ref(component_filter);
 }
 
+BT_EXPORT
 void bt_component_filter_put_ref(
                const struct bt_component_filter *component_filter)
 {
index 1df52b10ec55a8cdc600355f08c5439178424ba9..8308919182c7fba87bd4a5ff578ba65c0cc3b5a3 100644 (file)
@@ -8,21 +8,14 @@
 #ifndef BABELTRACE_GRAPH_COMPONENT_FILTER_INTERNAL_H
 #define BABELTRACE_GRAPH_COMPONENT_FILTER_INTERNAL_H
 
-#include "common/macros.h"
 #include <babeltrace2/graph/component.h>
 
-#include "component-class.h"
 #include "component.h"
 
 struct bt_component_filter {
        struct bt_component parent;
 };
 
-BT_HIDDEN
-struct bt_component *bt_component_filter_create(
-               const struct bt_component_class *class);
-
-BT_HIDDEN
-void bt_component_filter_destroy(struct bt_component *component);
+struct bt_component *bt_component_filter_create(void);
 
 #endif /* BABELTRACE_GRAPH_COMPONENT_FILTER_INTERNAL_H */
index d45532b455c8e89c1740d0b1711e5ab8cd8bb43a..af55ce4290062245211fe15426594dffbee10b83 100644 (file)
 #include "graph.h"
 #include "lib/func-status.h"
 
-BT_HIDDEN
-void bt_component_sink_destroy(struct bt_component *component)
-{
-}
-
-BT_HIDDEN
-struct bt_component *bt_component_sink_create(
-               const struct bt_component_class *class)
+struct bt_component *bt_component_sink_create(void)
 {
        struct bt_component_sink *sink = NULL;
 
@@ -43,6 +36,7 @@ end:
        return (void *) sink;
 }
 
+BT_EXPORT
 const bt_component_class_sink *
 bt_component_sink_borrow_class_const(
                const bt_component_sink *component)
@@ -59,6 +53,7 @@ bt_component_sink_borrow_class_const(
        return (bt_component_class_sink *) cls;
 }
 
+BT_EXPORT
 uint64_t bt_component_sink_get_input_port_count(
                const struct bt_component_sink *component)
 {
@@ -66,6 +61,7 @@ uint64_t bt_component_sink_get_input_port_count(
        return bt_component_get_input_port_count((void *) component, __func__);
 }
 
+BT_EXPORT
 const struct bt_port_input *
 bt_component_sink_borrow_input_port_by_name_const(
                const struct bt_component_sink *component, const char *name)
@@ -78,6 +74,7 @@ bt_component_sink_borrow_input_port_by_name_const(
                __func__);
 }
 
+BT_EXPORT
 struct bt_self_component_port_input *
 bt_self_component_sink_borrow_input_port_by_name(
                struct bt_self_component_sink *component, const char *name)
@@ -90,6 +87,7 @@ bt_self_component_sink_borrow_input_port_by_name(
                (void *) component, name, __func__);
 }
 
+BT_EXPORT
 const struct bt_port_input *bt_component_sink_borrow_input_port_by_index_const(
                const struct bt_component_sink *component, uint64_t index)
 {
@@ -101,6 +99,7 @@ const struct bt_port_input *bt_component_sink_borrow_input_port_by_index_const(
                (void *) component, index, __func__);
 }
 
+BT_EXPORT
 struct bt_self_component_port_input *
 bt_self_component_sink_borrow_input_port_by_index(
                struct bt_self_component_sink *component, uint64_t index)
@@ -113,6 +112,7 @@ bt_self_component_sink_borrow_input_port_by_index(
                (void *) component, index, __func__);
 }
 
+BT_EXPORT
 enum bt_self_component_add_port_status bt_self_component_sink_add_input_port(
                struct bt_self_component_sink *self_comp,
                const char *name, void *user_data,
@@ -122,8 +122,6 @@ enum bt_self_component_add_port_status bt_self_component_sink_add_input_port(
        struct bt_port *port = NULL;
        struct bt_component *comp = (void *) self_comp;
 
-       BT_ASSERT_PRE_NO_ERROR();
-
        /*
         * bt_component_add_input_port() logs details/errors and checks
         * preconditions.
@@ -144,6 +142,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 bt_bool bt_self_component_sink_is_interrupted(
                const struct bt_self_component_sink *self_comp)
 {
@@ -154,12 +153,14 @@ bt_bool bt_self_component_sink_is_interrupted(
                bt_component_borrow_graph(comp));
 }
 
+BT_EXPORT
 void bt_component_sink_get_ref(
                const struct bt_component_sink *component_sink)
 {
        bt_object_get_ref(component_sink);
 }
 
+BT_EXPORT
 void bt_component_sink_put_ref(
                const struct bt_component_sink *component_sink)
 {
index 538a28fdae1c167fd7a5c4397a3a79cfc8ddbb69..2c4569c061d3af6f2e5a4a6f20ba4808590019a6 100644 (file)
 
 #include <stdbool.h>
 
-#include "common/macros.h"
 #include "compat/compiler.h"
 #include <babeltrace2/graph/component.h>
 
-#include "component-class.h"
 #include "component.h"
 
 struct bt_component_sink {
@@ -22,11 +20,6 @@ struct bt_component_sink {
        bool graph_is_configured_method_called;
 };
 
-BT_HIDDEN
-struct bt_component *bt_component_sink_create(
-               const struct bt_component_class *class);
-
-BT_HIDDEN
-void bt_component_sink_destroy(struct bt_component *component);
+struct bt_component *bt_component_sink_create(void);
 
 #endif /* BABELTRACE_GRAPH_COMPONENT_SINK_INTERNAL_H */
index 71d28b2884e1858954c454e88e0baac70ea3abf4..d7315dde2c608d438e41e3069472dfd67170f8d1 100644 (file)
 #include "component-source.h"
 #include "component.h"
 #include "port.h"
-#include "message/iterator.h"
+#include "iterator.h"
 #include "lib/func-status.h"
 
-BT_HIDDEN
-void bt_component_source_destroy(struct bt_component *component)
-{
-}
-
-BT_HIDDEN
-struct bt_component *bt_component_source_create(
-               const struct bt_component_class *class)
+struct bt_component *bt_component_source_create(void)
 {
        struct bt_component_source *source = NULL;
 
@@ -43,6 +36,7 @@ end:
        return (void *) source;
 }
 
+BT_EXPORT
 const bt_component_class_source *
 bt_component_source_borrow_class_const(
                const bt_component_source *component)
@@ -59,6 +53,7 @@ bt_component_source_borrow_class_const(
        return (bt_component_class_source *) cls;
 }
 
+BT_EXPORT
 uint64_t bt_component_source_get_output_port_count(
                const struct bt_component_source *comp)
 {
@@ -66,6 +61,7 @@ uint64_t bt_component_source_get_output_port_count(
        return bt_component_get_output_port_count((void *) comp, __func__);
 }
 
+BT_EXPORT
 const struct bt_port_output *
 bt_component_source_borrow_output_port_by_name_const(
                const struct bt_component_source *comp, const char *name)
@@ -78,6 +74,7 @@ bt_component_source_borrow_output_port_by_name_const(
                __func__);
 }
 
+BT_EXPORT
 struct bt_self_component_port_output *
 bt_self_component_source_borrow_output_port_by_name(
                struct bt_self_component_source *comp, const char *name)
@@ -90,6 +87,7 @@ bt_self_component_source_borrow_output_port_by_name(
                (void *) comp, name, __func__);
 }
 
+BT_EXPORT
 const struct bt_port_output *
 bt_component_source_borrow_output_port_by_index_const(
                const struct bt_component_source *comp, uint64_t index)
@@ -102,6 +100,7 @@ bt_component_source_borrow_output_port_by_index_const(
                __func__);
 }
 
+BT_EXPORT
 struct bt_self_component_port_output *
 bt_self_component_source_borrow_output_port_by_index(
                struct bt_self_component_source *comp, uint64_t index)
@@ -114,6 +113,7 @@ bt_self_component_source_borrow_output_port_by_index(
                (void *) comp, index, __func__);
 }
 
+BT_EXPORT
 enum bt_self_component_add_port_status bt_self_component_source_add_output_port(
                struct bt_self_component_source *self_comp,
                const char *name, void *user_data,
@@ -123,9 +123,6 @@ enum bt_self_component_add_port_status bt_self_component_source_add_output_port(
        enum bt_self_component_add_port_status status;
        struct bt_port *port = NULL;
 
-       BT_ASSERT_PRE_NO_ERROR();
-       BT_ASSERT_PRE_OUTPUT_PORT_NAME_UNIQUE(comp, name);
-
        /*
         * bt_component_add_output_port() logs details/errors and checks
         * preconditions.
@@ -146,12 +143,14 @@ end:
        return status;
 }
 
+BT_EXPORT
 void bt_component_source_get_ref(
                const struct bt_component_source *component_source)
 {
        bt_object_get_ref(component_source);
 }
 
+BT_EXPORT
 void bt_component_source_put_ref(
                const struct bt_component_source *component_source)
 {
index f51aab7e1df1ce43fb87b7a3beaf0af00070ab7c..c91ebda45861c0e964c5cd02e91c036ee5b0efee 100644 (file)
@@ -8,20 +8,12 @@
 #ifndef BABELTRACE_GRAPH_COMPONENT_SOURCE_INTERNAL_H
 #define BABELTRACE_GRAPH_COMPONENT_SOURCE_INTERNAL_H
 
-#include "common/macros.h"
-
-#include "component-class.h"
 #include "component.h"
 
 struct bt_component_source {
        struct bt_component parent;
 };
 
-BT_HIDDEN
-struct bt_component *bt_component_source_create(
-               const struct bt_component_class *class);
-
-BT_HIDDEN
-void bt_component_source_destroy(struct bt_component *component);
+struct bt_component *bt_component_source_create(void);
 
 #endif /* BABELTRACE_GRAPH_COMPONENT_SOURCE_INTERNAL_H */
index fcfdb8b9cba6f403d290a4689adbeb2b31766598..dcda049015af3052f27d30b736e76f6fe175b9fd 100644 (file)
@@ -18,9 +18,7 @@
 #include "compat/compiler.h"
 #include <babeltrace2/types.h>
 #include <babeltrace2/value.h>
-#include "lib/value.h"
 #include <stdint.h>
-#include <inttypes.h>
 
 #include "component.h"
 #include "component-class.h"
 #include "component-sink.h"
 #include "connection.h"
 #include "graph.h"
-#include "message/iterator.h"
+#include "iterator.h"
 #include "port.h"
 #include "lib/func-status.h"
 
 static
-struct bt_component * (* const component_create_funcs[])(
-               const struct bt_component_class *) = {
+struct bt_component * (* const component_create_funcs[])(void) = {
        [BT_COMPONENT_CLASS_TYPE_SOURCE] = bt_component_source_create,
        [BT_COMPONENT_CLASS_TYPE_SINK] = bt_component_sink_create,
        [BT_COMPONENT_CLASS_TYPE_FILTER] = bt_component_filter_create,
 };
 
-static
-void (*component_destroy_funcs[])(struct bt_component *) = {
-       [BT_COMPONENT_CLASS_TYPE_SOURCE] = bt_component_source_destroy,
-       [BT_COMPONENT_CLASS_TYPE_SINK] = bt_component_sink_destroy,
-       [BT_COMPONENT_CLASS_TYPE_FILTER] = bt_component_filter_destroy,
-};
-
 static
 void finalize_component(struct bt_component *comp)
 {
@@ -133,7 +123,7 @@ void destroy_component(struct bt_object *obj)
 
        for (i = component->destroy_listeners->len - 1; i >= 0; i--) {
                struct bt_component_destroy_listener *listener =
-                       &g_array_index(component->destroy_listeners,
+                       &bt_g_array_index(component->destroy_listeners,
                                struct bt_component_destroy_listener, i);
 
                listener->func(component, listener->data);
@@ -148,11 +138,6 @@ void destroy_component(struct bt_object *obj)
                finalize_component(component);
        }
 
-       if (component->destroy) {
-               BT_LOGD_STR("Destroying type-specific data.");
-               component->destroy(component);
-       }
-
        if (component->input_ports) {
                BT_LOGD_STR("Destroying input ports.");
                g_ptr_array_free(component->input_ports, TRUE);
@@ -180,6 +165,7 @@ void destroy_component(struct bt_object *obj)
        g_free(component);
 }
 
+BT_EXPORT
 enum bt_component_class_type bt_component_get_class_type(
                const struct bt_component *component)
 {
@@ -187,18 +173,46 @@ enum bt_component_class_type bt_component_get_class_type(
        return component->class->type;
 }
 
+static
+bool port_name_is_unique(GPtrArray *ports, const char *name)
+{
+       guint i;
+       bool unique;
+
+       for (i = 0; i < ports->len; i++) {
+               struct bt_port *port = g_ptr_array_index(ports, i);
+
+               if (strcmp(port->name->str, name) == 0) {
+                       unique = false;
+                       goto end;
+               }
+       }
+
+       unique = true;
+
+end:
+       return unique;
+}
+
 static
 enum bt_self_component_add_port_status add_port(
                struct bt_component *component, GPtrArray *ports,
                enum bt_port_type port_type, const char *name, void *user_data,
-               struct bt_port **port, const char *api_func)
+               struct bt_port **port, const char *api_func, bool input)
 {
        struct bt_port *new_port = NULL;
        struct bt_graph *graph = NULL;
        enum bt_self_component_add_port_status status;
 
-       BT_ASSERT(component);
-       BT_ASSERT(name);
+       BT_ASSERT_PRE_NO_ERROR_FROM_FUNC(api_func);
+       BT_ASSERT_PRE_COMP_NON_NULL_FROM_FUNC(api_func, component);
+       BT_ASSERT_PRE_NAME_NON_NULL_FROM_FUNC(api_func, name);
+       BT_ASSERT_PRE_FROM_FUNC(api_func,
+               input ? "input" : "output" "-port-name-is-unique",
+               port_name_is_unique(component->output_ports, name),
+               input ? "Input" : "Output"
+                       " port name is not unique: name=\"%s\", %![comp-]c",
+               name, component);
        BT_ASSERT_PRE_FROM_FUNC(api_func, "name-is-not-empty",
                strlen(name) > 0, "Name is empty");
        graph = bt_component_borrow_graph(component);
@@ -207,8 +221,6 @@ enum bt_self_component_add_port_status add_port(
                "Component's graph is already configured: "
                "%![comp-]+c, %![graph-]+g", component, graph);
 
-       // TODO: Validate that the name is not already used.
-
        BT_LIB_LOGI("Adding port to component: %![comp-]+c, "
                "port-type=%s, port-name=\"%s\"", component,
                bt_port_type_string(port_type), name);
@@ -261,7 +273,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 uint64_t bt_component_get_input_port_count(const struct bt_component *comp,
                const char *api_func)
 {
@@ -269,7 +280,6 @@ uint64_t bt_component_get_input_port_count(const struct bt_component *comp,
        return (uint64_t) comp->input_ports->len;
 }
 
-BT_HIDDEN
 uint64_t bt_component_get_output_port_count(const struct bt_component *comp,
                const char *api_func)
 {
@@ -277,7 +287,6 @@ uint64_t bt_component_get_output_port_count(const struct bt_component *comp,
        return (uint64_t) comp->output_ports->len;
 }
 
-BT_HIDDEN
 int bt_component_create(struct bt_component_class *component_class,
                const char *name, bt_logging_level log_level,
                struct bt_component **user_component)
@@ -293,7 +302,7 @@ int bt_component_create(struct bt_component_class *component_class,
        BT_LIB_LOGI("Creating empty component from component class: %![cc-]+C, "
                "comp-name=\"%s\", log-level=%s", component_class, name,
                bt_common_logging_level_string(log_level));
-       component = component_create_funcs[type](component_class);
+       component = component_create_funcs[type]();
        if (!component) {
                BT_LIB_LOGE_APPEND_CAUSE(
                        "Cannot create specific component object.");
@@ -304,7 +313,6 @@ int bt_component_create(struct bt_component_class *component_class,
        bt_object_init_shared_with_parent(&component->base, destroy_component);
        component->class = component_class;
        bt_object_get_ref_no_null_check(component->class);
-       component->destroy = component_destroy_funcs[type];
        component->name = g_string_new(name);
        if (!component->name) {
                BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate one GString.");
@@ -346,12 +354,14 @@ end:
        return ret;
 }
 
+BT_EXPORT
 const char *bt_component_get_name(const struct bt_component *component)
 {
        BT_ASSERT_PRE_DEV_COMP_NON_NULL(component);
        return component->name->str;
 }
 
+BT_EXPORT
 const struct bt_component_class *bt_component_borrow_class_const(
                const struct bt_component *component)
 {
@@ -359,6 +369,7 @@ const struct bt_component_class *bt_component_borrow_class_const(
        return component->class;
 }
 
+BT_EXPORT
 void *bt_self_component_get_data(const struct bt_self_component *self_comp)
 {
        struct bt_component *component = (void *) self_comp;
@@ -367,6 +378,7 @@ void *bt_self_component_get_data(const struct bt_self_component *self_comp)
        return component->user_data;
 }
 
+BT_EXPORT
 void bt_self_component_set_data(struct bt_self_component *self_comp,
                void *data)
 {
@@ -377,7 +389,6 @@ void bt_self_component_set_data(struct bt_self_component *self_comp,
        BT_LIB_LOGD("Set component's user data: %!+c", component);
 }
 
-BT_HIDDEN
 void bt_component_set_graph(struct bt_component *component,
                struct bt_graph *graph)
 {
@@ -406,7 +417,6 @@ struct bt_port *borrow_port_by_name(GPtrArray *ports,
        return ret_port;
 }
 
-BT_HIDDEN
 struct bt_port_input *bt_component_borrow_input_port_by_name(
                struct bt_component *comp, const char *name,
                const char *api_func)
@@ -415,7 +425,6 @@ struct bt_port_input *bt_component_borrow_input_port_by_name(
        return (void *) borrow_port_by_name(comp->input_ports, name, api_func);
 }
 
-BT_HIDDEN
 struct bt_port_output *bt_component_borrow_output_port_by_name(
                struct bt_component *comp, const char *name,
                const char *api_func)
@@ -433,7 +442,6 @@ struct bt_port *borrow_port_by_index(GPtrArray *ports, uint64_t index,
        return g_ptr_array_index(ports, index);
 }
 
-BT_HIDDEN
 struct bt_port_input *bt_component_borrow_input_port_by_index(
                struct bt_component *comp, uint64_t index,
                const char *api_func)
@@ -443,7 +451,6 @@ struct bt_port_input *bt_component_borrow_input_port_by_index(
                borrow_port_by_index(comp->input_ports, index, api_func);
 }
 
-BT_HIDDEN
 struct bt_port_output *bt_component_borrow_output_port_by_index(
                struct bt_component *comp, uint64_t index,
                const char *api_func)
@@ -453,63 +460,25 @@ struct bt_port_output *bt_component_borrow_output_port_by_index(
                borrow_port_by_index(comp->output_ports, index, api_func);
 }
 
-BT_HIDDEN
 enum bt_self_component_add_port_status bt_component_add_input_port(
                struct bt_component *component, const char *name,
                void *user_data, struct bt_port **port, const char *api_func)
 {
-       BT_ASSERT_PRE_COMP_NON_NULL_FROM_FUNC(api_func, component);
-       BT_ASSERT_PRE_NAME_NON_NULL_FROM_FUNC(api_func, name);
-       BT_ASSERT_PRE_FROM_FUNC(api_func, "input-port-name-is-unique",
-               bt_component_port_name_is_unique(component->input_ports, name),
-               "Input port name is not unique: name=\"%s\", %![comp-]c",
-               name, component);
-
        /* add_port() logs details and checks preconditions */
        return add_port(component, component->input_ports,
-               BT_PORT_TYPE_INPUT, name, user_data, port, api_func);
+               BT_PORT_TYPE_INPUT, name, user_data, port, api_func, true);
 }
 
-BT_HIDDEN
 enum bt_self_component_add_port_status bt_component_add_output_port(
                struct bt_component *component, const char *name,
                void *user_data, struct bt_port **port,
                const char *api_func)
 {
-       BT_ASSERT_PRE_COMP_NON_NULL_FROM_FUNC(api_func, component);
-       BT_ASSERT_PRE_NAME_NON_NULL_FROM_FUNC(api_func, name);
-       BT_ASSERT_PRE_FROM_FUNC(api_func, "output-port-name-is-unique",
-               bt_component_port_name_is_unique(component->output_ports, name),
-               "Output port name is not unique: name=\"%s\", %![comp-]c",
-               name, component);
-
        /* add_port() logs details and checks preconditions */
        return add_port(component, component->output_ports,
-               BT_PORT_TYPE_OUTPUT, name, user_data, port, api_func);
-}
-
-BT_HIDDEN
-bool bt_component_port_name_is_unique(GPtrArray *ports, const char *name)
-{
-       guint i;
-       bool unique;
-
-       for (i = 0; i < ports->len; i++) {
-               struct bt_port *port = g_ptr_array_index(ports, i);
-
-               if (strcmp(port->name->str, name) == 0) {
-                       unique = false;
-                       goto end;
-               }
-       }
-
-       unique = true;
-
-end:
-       return unique;
+               BT_PORT_TYPE_OUTPUT, name, user_data, port, api_func, false);
 }
 
-BT_HIDDEN
 enum bt_component_class_port_connected_method_status
 bt_component_port_connected(
                struct bt_component *comp, struct bt_port *self_port,
@@ -600,7 +569,6 @@ bt_component_port_connected(
        return status;
 }
 
-BT_HIDDEN
 void bt_component_add_destroy_listener(struct bt_component *component,
                bt_component_destroy_listener_func func, void *data)
 {
@@ -616,7 +584,6 @@ void bt_component_add_destroy_listener(struct bt_component *component,
                component, func, data);
 }
 
-BT_HIDDEN
 void bt_component_remove_destroy_listener(struct bt_component *component,
                bt_component_destroy_listener_func func, void *data)
 {
@@ -627,7 +594,7 @@ void bt_component_remove_destroy_listener(struct bt_component *component,
 
        for (i = 0; i < component->destroy_listeners->len; i++) {
                struct bt_component_destroy_listener *listener =
-                       &g_array_index(component->destroy_listeners,
+                       &bt_g_array_index(component->destroy_listeners,
                                struct bt_component_destroy_listener, i);
 
                if (listener->func == func && listener->data == data) {
@@ -640,6 +607,7 @@ void bt_component_remove_destroy_listener(struct bt_component *component,
        }
 }
 
+BT_EXPORT
 bt_logging_level bt_component_get_logging_level(
                const struct bt_component *component)
 {
@@ -647,6 +615,7 @@ bt_logging_level bt_component_get_logging_level(
        return component->log_level;
 }
 
+BT_EXPORT
 uint64_t bt_self_component_get_graph_mip_version(
                bt_self_component *self_component)
 {
@@ -656,11 +625,13 @@ uint64_t bt_self_component_get_graph_mip_version(
        return bt_component_borrow_graph(comp)->mip_version;
 }
 
+BT_EXPORT
 void bt_component_get_ref(const struct bt_component *component)
 {
        bt_object_get_ref(component);
 }
 
+BT_EXPORT
 void bt_component_put_ref(const struct bt_component *component)
 {
        bt_object_put_ref(component);
index f12ca3b1aff173de1b623108a56ad31ad7af311f..401cc38591b27873933eeaa3ad03f82bde680bf0 100644 (file)
@@ -8,7 +8,6 @@
 #ifndef BABELTRACE_GRAPH_COMPONENT_INTERNAL_H
 #define BABELTRACE_GRAPH_COMPONENT_INTERNAL_H
 
-#include "common/macros.h"
 #include <babeltrace2/graph/component.h>
 #include <babeltrace2/graph/component-class.h>
 #include "lib/object.h"
 #include "common/assert.h"
 #include <glib.h>
 #include <stdbool.h>
-#include <stdio.h>
 
 #include "component-class.h"
 #include "port.h"
 
-#define BT_ASSERT_PRE_OUTPUT_PORT_NAME_UNIQUE(comp, name)              \
-       BT_ASSERT_PRE("output-port-is-unique",                          \
-               bt_component_port_name_is_unique(comp->output_ports, name), \
-               "Output port name is not unique: name=\"%s\", %![comp-]c", name, comp);
-
 typedef void (*bt_component_destroy_listener_func)(
                struct bt_component *class, void *data);
 
@@ -43,12 +36,6 @@ struct bt_component {
        GString *name;
        bt_logging_level log_level;
 
-       /*
-        * Internal destroy function specific to a source, filter, or
-        * sink component object.
-        */
-       void (*destroy)(struct bt_component *);
-
        /* User-defined data */
        void *user_data;
 
@@ -69,73 +56,56 @@ struct bt_graph *bt_component_borrow_graph(struct bt_component *comp)
        return (void *) bt_object_borrow_parent(&comp->base);
 }
 
-BT_HIDDEN
 int bt_component_create(struct bt_component_class *component_class,
                const char *name, bt_logging_level log_level,
                struct bt_component **component);
 
-BT_HIDDEN
 enum bt_component_class_port_connected_method_status
 bt_component_port_connected(
                struct bt_component *comp,
                struct bt_port *self_port, struct bt_port *other_port);
 
-BT_HIDDEN
 void bt_component_set_graph(struct bt_component *component,
                struct bt_graph *graph);
 
-BT_HIDDEN
 uint64_t bt_component_get_input_port_count(const struct bt_component *comp,
                const char *api_func);
 
-BT_HIDDEN
 uint64_t bt_component_get_output_port_count(const struct bt_component *comp,
                const char *api_func);
 
-BT_HIDDEN
 struct bt_port_input *bt_component_borrow_input_port_by_index(
                struct bt_component *comp, uint64_t index,
                const char *api_func);
 
-BT_HIDDEN
 struct bt_port_output *bt_component_borrow_output_port_by_index(
                struct bt_component *comp, uint64_t index,
                const char *api_func);
 
-BT_HIDDEN
 struct bt_port_input *bt_component_borrow_input_port_by_name(
                struct bt_component *comp, const char *name,
                const char *api_func);
 
-BT_HIDDEN
 struct bt_port_output *bt_component_borrow_output_port_by_name(
                struct bt_component *comp, const char *name,
                const char *api_func);
 
-BT_HIDDEN
 enum bt_self_component_add_port_status bt_component_add_input_port(
                struct bt_component *component, const char *name,
                void *user_data, struct bt_port **port,
                const char *api_func);
 
-BT_HIDDEN
 enum bt_self_component_add_port_status bt_component_add_output_port(
                struct bt_component *component, const char *name,
                void *user_data, struct bt_port **port,
                const char *api_func);
 
-BT_HIDDEN
-bool bt_component_port_name_is_unique(GPtrArray *ports, const char *name);
-
-BT_HIDDEN
 void bt_component_remove_port(struct bt_component *component,
                struct bt_port *port);
 
-BT_HIDDEN
 void bt_component_add_destroy_listener(struct bt_component *component,
                bt_component_destroy_listener_func func, void *data);
 
-BT_HIDDEN
 void bt_component_remove_destroy_listener(struct bt_component *component,
                bt_component_destroy_listener_func func, void *data);
 
index eb84c11b60b88baaa72659b44c0954941811f33b..42accaa45efc1ea8a6b4d105120de34e2968ec97 100644 (file)
@@ -8,7 +8,6 @@
 #define BT_LOG_TAG "LIB/CONNECTION"
 #include "lib/logging.h"
 
-#include "common/assert.h"
 #include "lib/assert-cond.h"
 #include <babeltrace2/graph/connection.h>
 #include "lib/object.h"
@@ -20,7 +19,7 @@
 #include "component.h"
 #include "connection.h"
 #include "graph.h"
-#include "message/iterator.h"
+#include "iterator.h"
 #include "port.h"
 
 static
@@ -97,7 +96,6 @@ void parent_is_owner(struct bt_object *obj)
        try_remove_connection_from_graph(connection);
 }
 
-BT_HIDDEN
 struct bt_connection *bt_connection_create(struct bt_graph *graph,
                struct bt_port *upstream_port,
                struct bt_port *downstream_port)
@@ -139,7 +137,6 @@ end:
        return connection;
 }
 
-BT_HIDDEN
 void bt_connection_end(struct bt_connection *conn, bool try_remove_from_graph)
 {
        struct bt_port *downstream_port = conn->downstream_port;
@@ -212,6 +209,7 @@ void bt_connection_end(struct bt_connection *conn, bool try_remove_from_graph)
        }
 }
 
+BT_EXPORT
 const struct bt_port_output *bt_connection_borrow_upstream_port_const(
                const struct bt_connection *connection)
 {
@@ -219,6 +217,7 @@ const struct bt_port_output *bt_connection_borrow_upstream_port_const(
        return (void *) connection->upstream_port;
 }
 
+BT_EXPORT
 const struct bt_port_input *bt_connection_borrow_downstream_port_const(
                const struct bt_connection *connection)
 {
@@ -226,7 +225,6 @@ const struct bt_port_input *bt_connection_borrow_downstream_port_const(
        return (void *) connection->downstream_port;
 }
 
-BT_HIDDEN
 void bt_connection_remove_iterator(struct bt_connection *conn,
                struct bt_message_iterator *iterator)
 {
@@ -236,11 +234,13 @@ void bt_connection_remove_iterator(struct bt_connection *conn,
        try_remove_connection_from_graph(conn);
 }
 
+BT_EXPORT
 void bt_connection_get_ref(const struct bt_connection *connection)
 {
        bt_object_get_ref(connection);
 }
 
+BT_EXPORT
 void bt_connection_put_ref(const struct bt_connection *connection)
 {
        bt_object_put_ref(connection);
index 87c23f33271a2fc41bd0564a66be7563e90cc804..cc512e1f1361a941d97ff3219bd93ce617332749 100644 (file)
 #include <babeltrace2/graph/connection.h>
 #include "lib/object.h"
 #include "common/assert.h"
-#include "common/macros.h"
 #include <stdbool.h>
 
-#include "message/iterator.h"
+#include "iterator.h"
 
 struct bt_graph;
 
@@ -45,15 +44,12 @@ struct bt_connection {
        bool notified_graph_ports_connected;
 };
 
-BT_HIDDEN
 struct bt_connection *bt_connection_create(struct bt_graph *graph,
                struct bt_port *upstream_port,
                struct bt_port *downstream_port);
 
-BT_HIDDEN
 void bt_connection_end(struct bt_connection *conn, bool try_remove_from_graph);
 
-BT_HIDDEN
 void bt_connection_remove_iterator(struct bt_connection *conn,
                struct bt_message_iterator *iterator);
 
index 37c239946cdeb6d17735b8a5a74d97ba1fbda42f..4290039f0c3179590bb183c21a94328f14fc3aa7 100644 (file)
@@ -145,21 +145,21 @@ void destroy_graph(struct bt_object *obj)
 
 static
 void destroy_message_event(struct bt_message *msg,
-               struct bt_graph *graph)
+               struct bt_graph *graph __attribute__((unused)))
 {
        bt_message_event_destroy(msg);
 }
 
 static
 void destroy_message_packet_begin(struct bt_message *msg,
-               struct bt_graph *graph)
+               struct bt_graph *graph __attribute__((unused)))
 {
        bt_message_packet_destroy(msg);
 }
 
 static
 void destroy_message_packet_end(struct bt_message *msg,
-               struct bt_graph *graph)
+               struct bt_graph *graph __attribute__((unused)))
 {
        bt_message_packet_destroy(msg);
 }
@@ -170,6 +170,7 @@ void notify_message_graph_is_destroyed(struct bt_message *msg)
        bt_message_unlink_graph(msg);
 }
 
+BT_EXPORT
 struct bt_graph *bt_graph_create(uint64_t mip_version)
 {
        struct bt_graph *graph;
@@ -297,6 +298,7 @@ error:
        goto end;
 }
 
+BT_EXPORT
 enum bt_graph_connect_ports_status bt_graph_connect_ports(
                struct bt_graph *graph,
                const struct bt_port_output *upstream_port_out,
@@ -509,7 +511,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 int bt_graph_consume_sink_no_check(struct bt_graph *graph,
                struct bt_component_sink *sink)
 {
@@ -648,6 +649,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_graph_run_once_status bt_graph_run_once(struct bt_graph *graph)
 {
        enum bt_graph_run_once_status status;
@@ -673,6 +675,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_graph_run_status bt_graph_run(struct bt_graph *graph)
 {
        enum bt_graph_run_status status;
@@ -744,6 +747,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_graph_add_listener_status
 bt_graph_add_source_component_output_port_added_listener(
                struct bt_graph *graph,
@@ -772,6 +776,7 @@ bt_graph_add_source_component_output_port_added_listener(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_graph_add_listener_status
 bt_graph_add_filter_component_output_port_added_listener(
                struct bt_graph *graph,
@@ -800,6 +805,7 @@ bt_graph_add_filter_component_output_port_added_listener(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_graph_add_listener_status
 bt_graph_add_filter_component_input_port_added_listener(
                struct bt_graph *graph,
@@ -828,6 +834,7 @@ bt_graph_add_filter_component_input_port_added_listener(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_graph_add_listener_status
 bt_graph_add_sink_component_input_port_added_listener(
                struct bt_graph *graph,
@@ -856,7 +863,6 @@ bt_graph_add_sink_component_input_port_added_listener(
        return BT_FUNC_STATUS_OK;
 }
 
-BT_HIDDEN
 enum bt_graph_listener_func_status bt_graph_notify_port_added(
                struct bt_graph *graph, struct bt_port *port)
 {
@@ -923,7 +929,7 @@ enum bt_graph_listener_func_status bt_graph_notify_port_added(
 
        for (i = 0; i < listeners->len; i++) {
                struct bt_graph_listener_port_added *listener =
-                       &g_array_index(listeners,
+                       &bt_g_array_index(listeners,
                                struct bt_graph_listener_port_added, i);
 
 
@@ -939,7 +945,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 void bt_graph_remove_connection(struct bt_graph *graph,
                struct bt_connection *connection)
 {
@@ -1130,6 +1135,7 @@ add_source_component_with_initialize_method_data(
                api_func, "bt_component_class_source_initialize_method");
 }
 
+BT_EXPORT
 enum bt_graph_add_component_status
 bt_graph_add_source_component_with_initialize_method_data(
                struct bt_graph *graph,
@@ -1142,6 +1148,7 @@ bt_graph_add_source_component_with_initialize_method_data(
                name, params, init_method_data, log_level, component, __func__);
 }
 
+BT_EXPORT
 enum bt_graph_add_component_status bt_graph_add_source_component(
                struct bt_graph *graph,
                const struct bt_component_class_source *comp_cls,
@@ -1171,6 +1178,7 @@ add_filter_component_with_initialize_method_data(
                api_func, "bt_component_class_filter_initialize_method");
 }
 
+BT_EXPORT
 enum bt_graph_add_component_status
 bt_graph_add_filter_component_with_initialize_method_data(
                struct bt_graph *graph,
@@ -1183,6 +1191,7 @@ bt_graph_add_filter_component_with_initialize_method_data(
                name, params, init_method_data, log_level, component, __func__);
 }
 
+BT_EXPORT
 enum bt_graph_add_component_status bt_graph_add_filter_component(
                struct bt_graph *graph,
                const struct bt_component_class_filter *comp_cls,
@@ -1212,6 +1221,7 @@ add_sink_component_with_initialize_method_data(
                api_func, "bt_component_class_sink_initialize_method");
 }
 
+BT_EXPORT
 enum bt_graph_add_component_status
 bt_graph_add_sink_component_with_initialize_method_data(
                struct bt_graph *graph,
@@ -1225,6 +1235,7 @@ bt_graph_add_sink_component_with_initialize_method_data(
                __func__);
 }
 
+BT_EXPORT
 enum bt_graph_add_component_status bt_graph_add_sink_component(
                struct bt_graph *graph,
                const struct bt_component_class_sink *comp_cls,
@@ -1236,6 +1247,7 @@ enum bt_graph_add_component_status bt_graph_add_sink_component(
                name, params, NULL, log_level, component, __func__);
 }
 
+BT_EXPORT
 enum bt_graph_add_component_status
 bt_graph_add_simple_sink_component(struct bt_graph *graph, const char *name,
                bt_graph_simple_sink_component_initialize_func init_func,
@@ -1277,7 +1289,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 void bt_graph_add_message(struct bt_graph *graph,
                struct bt_message *msg)
 {
@@ -1295,13 +1306,13 @@ void bt_graph_add_message(struct bt_graph *graph,
        g_ptr_array_add(graph->messages, msg);
 }
 
-BT_HIDDEN
 bool bt_graph_is_interrupted(const struct bt_graph *graph)
 {
        BT_ASSERT_DBG(graph);
        return bt_interrupter_array_any_is_set(graph->interrupters);
 }
 
+BT_EXPORT
 enum bt_graph_add_interrupter_status bt_graph_add_interrupter(
                struct bt_graph *graph, const struct bt_interrupter *intr)
 {
@@ -1315,17 +1326,20 @@ enum bt_graph_add_interrupter_status bt_graph_add_interrupter(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 struct bt_interrupter *bt_graph_borrow_default_interrupter(bt_graph *graph)
 {
        BT_ASSERT_PRE_GRAPH_NON_NULL(graph);
        return graph->default_interrupter;
 }
 
+BT_EXPORT
 void bt_graph_get_ref(const struct bt_graph *graph)
 {
        bt_object_get_ref(graph);
 }
 
+BT_EXPORT
 void bt_graph_put_ref(const struct bt_graph *graph)
 {
        bt_object_put_ref(graph);
index 066f723399ba21634b1d48f2781dac78f59d93e0..01dfaec6a8df300cccde917a897560cb2fd31d4e 100644 (file)
 
 #include <babeltrace2/graph/graph.h>
 #include <babeltrace2/graph/message.h>
-#include "common/macros.h"
 #include "lib/object.h"
 #include "lib/object-pool.h"
 #include "common/assert.h"
-#include "common/common.h"
 #include <stdbool.h>
-#include <stdlib.h>
 #include <glib.h>
 
 #include "component.h"
 #include "component-sink.h"
 #include "connection.h"
-#include "lib/func-status.h"
 
 /* Protection: this file uses BT_LIB_LOG*() macros directly */
 #ifndef BT_LIB_LOG_SUPPORTED
 # error Please include "lib/logging.h" before including this file.
 #endif
 
-/*
- * Protection: this file uses precondition and postcondition assertion
- * macros directly.
- */
-#ifndef BT_ASSERT_COND_SUPPORTED
-# error Please include "lib/assert-cond.h" before including this file.
-#endif
-
 struct bt_component;
 struct bt_port;
 
@@ -60,7 +48,7 @@ struct bt_graph {
         *
         * In terms of ownership:
         * 1) The graph is the components' parent,
-        * 2) The graph is the connnections' parent,
+        * 2) The graph is the connections' parent,
         * 3) Components share the ownership of their connections,
         * 4) A connection holds weak references to its two component endpoints.
         */
@@ -143,23 +131,18 @@ void bt_graph_set_can_consume(struct bt_graph *graph, bool can_consume)
        graph->can_consume = can_consume;
 }
 
-BT_HIDDEN
 int bt_graph_consume_sink_no_check(struct bt_graph *graph,
                struct bt_component_sink *sink);
 
-BT_HIDDEN
 enum bt_graph_listener_func_status bt_graph_notify_port_added(struct bt_graph *graph,
                struct bt_port *port);
 
-BT_HIDDEN
 void bt_graph_remove_connection(struct bt_graph *graph,
                struct bt_connection *connection);
 
-BT_HIDDEN
 void bt_graph_add_message(struct bt_graph *graph,
                struct bt_message *msg);
 
-BT_HIDDEN
 bool bt_graph_is_interrupted(const struct bt_graph *graph);
 
 static inline
index 75237d3b0550a4ca2198d9e8fa5341caaf4bf046..f52b0733fd2f71407c623d2e9cbd1d6441f5bd88 100644 (file)
@@ -7,8 +7,6 @@
 #define BT_LOG_TAG "LIB/INTERRUPTER"
 #include "lib/logging.h"
 
-#include <stdlib.h>
-#include <stdint.h>
 #include <babeltrace2/babeltrace.h>
 
 #include "interrupter.h"
@@ -20,7 +18,8 @@ void destroy_interrupter(struct bt_object *obj)
        g_free(obj);
 }
 
-extern struct bt_interrupter *bt_interrupter_create(void)
+BT_EXPORT
+struct bt_interrupter *bt_interrupter_create(void)
 {
        struct bt_interrupter *intr = g_new0(struct bt_interrupter, 1);
 
@@ -41,29 +40,34 @@ end:
        return intr;
 }
 
+BT_EXPORT
 void bt_interrupter_set(struct bt_interrupter *intr)
 {
        BT_ASSERT_PRE_INTR_NON_NULL(intr);
        intr->is_set = true;
 }
 
+BT_EXPORT
 void bt_interrupter_reset(struct bt_interrupter *intr)
 {
        BT_ASSERT_PRE_INTR_NON_NULL(intr);
        intr->is_set = false;
 }
 
+BT_EXPORT
 bt_bool bt_interrupter_is_set(const struct bt_interrupter *intr)
 {
        BT_ASSERT_PRE_INTR_NON_NULL(intr);
        return (bt_bool) intr->is_set;
 }
 
+BT_EXPORT
 void bt_interrupter_get_ref(const struct bt_interrupter *intr)
 {
        bt_object_get_ref(intr);
 }
 
+BT_EXPORT
 void bt_interrupter_put_ref(const struct bt_interrupter *intr)
 {
        bt_object_put_ref(intr);
index fd49c27ef15ebb03511b65a65d68781f74a3b174..f7ecc47c3c1636e98ab08cede13111977e94ef77 100644 (file)
@@ -18,6 +18,7 @@
 #include <babeltrace2/trace-ir/packet.h>
 #include "lib/trace-ir/packet.h"
 #include "lib/trace-ir/stream.h"
+#include "lib/trace-ir/stream-class.h"
 #include <babeltrace2/trace-ir/clock-class.h>
 #include <babeltrace2/trace-ir/stream-class.h>
 #include <babeltrace2/trace-ir/stream.h>
 
 #include "component-class.h"
 #include "component.h"
-#include "component-sink.h"
-#include "component-source.h"
 #include "connection.h"
 #include "graph.h"
+#include "iterator.h"
 #include "message-iterator-class.h"
 #include "message/discarded-items.h"
 #include "message/event.h"
-#include "message/iterator.h"
 #include "message/message.h"
 #include "message/message-iterator-inactivity.h"
 #include "message/stream.h"
 #include "message/packet.h"
 #include "lib/func-status.h"
+#include "clock-correlation-validator/clock-correlation-validator.h"
 
 /*
  * TODO: Use graph's state (number of active iterators, etc.) and
                (_iter)->state == BT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR, \
                "Message iterator is in the wrong state: %!+i", (_iter))
 
+BT_IF_DEV_MODE(
+struct per_stream_state
+{
+       bt_packet *cur_packet;
+
+       /* Bit mask of expected message types. */
+       guint expected_msg_types;
+};
+)
+
+static void
+clear_per_stream_state (struct bt_message_iterator *iterator)
+{
+#ifdef BT_DEV_MODE
+       g_hash_table_remove_all(iterator->per_stream_state);
+#else
+       BT_USE_EXPR(iterator);
+#endif
+}
+
 static inline
 void set_msg_iterator_state(struct bt_message_iterator *iterator,
                enum bt_message_iterator_state state)
@@ -135,10 +155,13 @@ void bt_message_iterator_destroy(struct bt_object *obj)
                iterator->msgs = NULL;
        }
 
+       BT_IF_DEV_MODE(bt_clock_correlation_validator_destroy(
+               iterator->correlation_validator));
+       BT_IF_DEV_MODE(g_hash_table_destroy(iterator->per_stream_state));
+
        g_free(iterator);
 }
 
-BT_HIDDEN
 void bt_message_iterator_try_finalize(
                struct bt_message_iterator *iterator)
 {
@@ -240,7 +263,6 @@ end:
        return;
 }
 
-BT_HIDDEN
 void bt_message_iterator_set_connection(
                struct bt_message_iterator *iterator,
                struct bt_connection *connection)
@@ -253,8 +275,9 @@ void bt_message_iterator_set_connection(
 
 static
 enum bt_message_iterator_can_seek_beginning_status can_seek_ns_from_origin_true(
-               struct bt_message_iterator *iterator,
-               int64_t ns_from_origin, bt_bool *can_seek)
+               struct bt_message_iterator *iterator __attribute__((unused)),
+               int64_t ns_from_origin __attribute__((unused)),
+               bt_bool *can_seek)
 {
        *can_seek = BT_TRUE;
 
@@ -263,7 +286,7 @@ enum bt_message_iterator_can_seek_beginning_status can_seek_ns_from_origin_true(
 
 static
 enum bt_message_iterator_can_seek_beginning_status can_seek_beginning_true(
-               struct bt_message_iterator *iterator,
+               struct bt_message_iterator *iterator __attribute__((unused)),
                bt_bool *can_seek)
 {
        *can_seek = BT_TRUE;
@@ -340,8 +363,27 @@ int create_self_component_input_port_message_iterator(
                goto error;
        }
 
+       BT_IF_DEV_MODE(
+               iterator->correlation_validator =
+                       bt_clock_correlation_validator_create();
+               if (!iterator->correlation_validator) {
+                       BT_LIB_LOGE_APPEND_CAUSE(
+                               "Failed to allocate a clock correlation validator.");
+                       status = BT_FUNC_STATUS_MEMORY_ERROR;
+                       goto error;
+               }
+       );
+
        g_ptr_array_set_size(iterator->msgs, MSG_BATCH_SIZE);
        iterator->last_ns_from_origin = INT64_MIN;
+
+       /* The per-stream state is only used for dev assertions right now. */
+       BT_IF_DEV_MODE(iterator->per_stream_state = g_hash_table_new_full(
+               g_direct_hash,
+               g_direct_equal,
+               NULL,
+               g_free));
+
        iterator->auto_seek.msgs = g_queue_new();
        if (!iterator->auto_seek.msgs) {
                BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate a GQueue.");
@@ -457,6 +499,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 bt_message_iterator_create_from_message_iterator_status
 bt_message_iterator_create_from_message_iterator(
                struct bt_self_message_iterator *self_msg_iter,
@@ -469,6 +512,7 @@ bt_message_iterator_create_from_message_iterator(
                input_port, message_iterator, __func__);
 }
 
+BT_EXPORT
 bt_message_iterator_create_from_sink_component_status
 bt_message_iterator_create_from_sink_component(
                struct bt_self_component_sink *self_comp,
@@ -481,6 +525,7 @@ bt_message_iterator_create_from_sink_component(
                input_port, message_iterator, __func__);
 }
 
+BT_EXPORT
 void *bt_self_message_iterator_get_data(
                const struct bt_self_message_iterator *self_iterator)
 {
@@ -491,6 +536,7 @@ void *bt_self_message_iterator_get_data(
        return iterator->user_data;
 }
 
+BT_EXPORT
 void bt_self_message_iterator_set_data(
                struct bt_self_message_iterator *self_iterator, void *data)
 {
@@ -503,6 +549,7 @@ void bt_self_message_iterator_set_data(
                "%!+i, user-data-addr=%p", iterator, data);
 }
 
+BT_EXPORT
 void bt_self_message_iterator_configuration_set_can_seek_forward(
                bt_self_message_iterator_configuration *config,
                bt_bool can_seek_forward)
@@ -624,173 +671,429 @@ end:
        return result;
 }
 
+#define NEXT_METHOD_NAME       "bt_message_iterator_class_next_method"
+
+#ifdef BT_DEV_MODE
+
 /*
  * When a new stream begins, verify that the clock class tied to this
  * stream is compatible with what we've seen before.
  */
 
-BT_ASSERT_COND_DEV_FUNC
 static
-bool clock_classes_are_compatible_one(struct bt_message_iterator *iterator,
+void assert_post_dev_clock_classes_are_compatible_one(
+               struct bt_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;
+       enum bt_clock_correlation_validator_error_type type;
+       bt_uuid expected_uuid;
+       const bt_clock_class *actual_clock_cls;
+       const bt_clock_class *expected_clock_cls;
+
+       if (!bt_clock_correlation_validator_validate_message(
+                       iterator->correlation_validator, msg, &type,
+                       &expected_uuid, &actual_clock_cls,
+                       &expected_clock_cls)) {
+               switch (type) {
+               case BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_NO_CLOCK_CLASS_GOT_ONE:
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "stream-class-has-no-clock-class", false,
+                               "Expecting no clock class, got one.");
+               case BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_NONE:
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "stream-class-has-clock-class-with-unix-epoch-origin", false,
+                               "Expecting a clock class, got none.");
+               case BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_OTHER:
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "clock-class-has-unix-epoch-origin", false,
+                               "Expecting a clock class with Unix epoch origin: %![cc-]+K",
+                               actual_clock_cls);
+               case BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NONE:
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "stream-class-has-clock-class-with-uuid", false,
+                               "Expecting a clock class, got none.");
+               case BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_UNIX:
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "clock-class-has-non-unix-epoch-origin", false,
+                               "Expecting a clock class without Unix epoch origin: %![cc-]+K",
+                               actual_clock_cls);
+               case BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NO_UUID:
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "clock-class-has-uuid", false,
+                               "Expecting a clock class with UUID:  %![cc-]+K",
+                               actual_clock_cls);
+               case BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_OTHER_UUID:
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "clock-class-has-expected-uuid", false,
+                               "Expecting a clock class with UUID, got one with a different UUID: %![cc-]+K, expected-uuid=%!u",
+                               actual_clock_cls, expected_uuid);
 
-               if (clock_class) {
-                       clock_class_uuid = bt_clock_class_get_uuid(clock_class);
+               case BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_NONE:
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "stream-class-has-clock-class", false,
+                               "Expecting a clock class, got none.");
+               case BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_OTHER:
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "clock-class-is-expected", false,
+                               "Unexpected clock class: %![expected-cc-]+K, %![actual-cc-]+K",
+                               expected_clock_cls, actual_clock_cls);
                }
 
-               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.
-                        */
+               bt_common_abort();
+       }
+}
 
-                       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;
+static
+void assert_post_dev_clock_classes_are_compatible(
+               struct bt_message_iterator *iterator,
+               bt_message_array_const msgs, uint64_t msg_count)
+{
+       uint64_t i;
 
-               case CLOCK_EXPECTATION_NONE:
-                       if (clock_class) {
-                               BT_ASSERT_COND_DEV_MSG(
-                                       "Expecting no clock class, got one: %![cc-]+K",
-                                       clock_class);
-                               result = false;
-                               goto end;
-                       }
+       for (i = 0; i < msg_count; i++) {
+               assert_post_dev_clock_classes_are_compatible_one(iterator, msgs[i]);
+       }
+}
 
-                       break;
+static
+const bt_stream *get_stream_from_msg(const struct bt_message *msg)
+{
+       struct bt_stream *stream;
 
-               case CLOCK_EXPECTATION_ORIGIN_UNIX:
-                       if (!clock_class) {
-                               BT_ASSERT_COND_DEV_MSG(
-                                       "Expecting a clock class, got none.");
-                               result = false;
-                               goto end;
-                       }
+       switch (msg->type) {
+       case BT_MESSAGE_TYPE_STREAM_BEGINNING:
+       case BT_MESSAGE_TYPE_STREAM_END:
+       {
+               struct bt_message_stream *msg_stream =
+                       (struct bt_message_stream *) msg;
+               stream = msg_stream->stream;
+               break;
+       }
+       case BT_MESSAGE_TYPE_EVENT:
+       {
+               struct bt_message_event *msg_event =
+                       (struct bt_message_event *) msg;
+               stream = msg_event->event->stream;
+               break;
+       }
+       case BT_MESSAGE_TYPE_PACKET_BEGINNING:
+       case BT_MESSAGE_TYPE_PACKET_END:
+       {
+               struct bt_message_packet *msg_packet =
+                       (struct bt_message_packet *) msg;
+               stream = msg_packet->packet->stream;
+               break;
+       }
+       case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
+       case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
+       {
+               struct bt_message_discarded_items *msg_discarded =
+                       (struct bt_message_discarded_items *) msg;
+               stream = msg_discarded->stream;
+               break;
+       }
+       case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
+               stream = NULL;
+               break;
+       default:
+               bt_common_abort();
+       }
 
-                       if (!bt_clock_class_origin_is_unix_epoch(clock_class)) {
-                               BT_ASSERT_COND_DEV_MSG(
-                                       "Expecting a clock class with Unix epoch origin: %![cc-]+K",
-                                       clock_class);
-                               result = false;
-                               goto end;
-                       }
-                       break;
+       return stream;
+}
 
-               case CLOCK_EXPECTATION_ORIGIN_OTHER_UUID:
-                       if (!clock_class) {
-                               BT_ASSERT_COND_DEV_MSG(
-                                       "Expecting a clock class, got none.");
-                               result = false;
-                               goto end;
-                       }
+static
+GString *message_types_to_string(guint msg_types)
+{
+       GString *str = g_string_new("");
 
-                       if (bt_clock_class_origin_is_unix_epoch(clock_class)) {
-                               BT_ASSERT_COND_DEV_MSG(
-                                       "Expecting a clock class without Unix epoch origin: %![cc-]+K",
-                                       clock_class);
-                               result = false;
-                               goto end;
+       for (int msg_type = 1; msg_type <= BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY;
+                       msg_type <<= 1) {
+               if (msg_type & msg_types) {
+                       if (str->len > 0) {
+                               g_string_append_c(str, '|');
                        }
 
-                       if (!clock_class_uuid) {
-                               BT_ASSERT_COND_DEV_MSG(
-                                       "Expecting a clock class with UUID: %![cc-]+K",
-                                       clock_class);
-                               result = false;
-                               goto end;
-                       }
+                       g_string_append(str,
+                               bt_common_message_type_string(msg_type));
+               }
+       }
 
-                       if (bt_uuid_compare(iterator->clock_expectation.uuid, clock_class_uuid)) {
-                               BT_ASSERT_COND_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;
+       return str;
+}
 
-               case CLOCK_EXPECTATION_ORIGIN_OTHER_NO_UUID:
-                       if (!clock_class) {
-                               BT_ASSERT_COND_DEV_MSG(
-                                       "Expecting a clock class, got none.");
-                               result = false;
-                               goto end;
-                       }
+static
+void update_expected_msg_type(const struct bt_stream *stream,
+               struct per_stream_state *state,
+               const struct bt_message *msg)
+{
+       switch (msg->type) {
+       case BT_MESSAGE_TYPE_STREAM_BEGINNING:
+               state->expected_msg_types = BT_MESSAGE_TYPE_STREAM_END;
 
-                       if (bt_clock_class_origin_is_unix_epoch(clock_class)) {
-                               BT_ASSERT_COND_DEV_MSG(
-                                       "Expecting a clock class without Unix epoch origin: %![cc-]+K",
-                                       clock_class);
-                               result = false;
-                               goto end;
+               if (stream->class->supports_packets) {
+                       state->expected_msg_types |=
+                               BT_MESSAGE_TYPE_PACKET_BEGINNING;
+
+                       if (stream->class->supports_discarded_packets) {
+                               state->expected_msg_types |=
+                                       BT_MESSAGE_TYPE_DISCARDED_PACKETS;
                        }
+               } else {
+                       state->expected_msg_types |= BT_MESSAGE_TYPE_EVENT;
+               }
 
-                       if (clock_class_uuid) {
-                               BT_ASSERT_COND_DEV_MSG(
-                                       "Expecting a clock class without UUID: %![cc-]+K",
-                                       clock_class);
-                               result = false;
-                               goto end;
+               if (stream->class->supports_discarded_events) {
+                       state->expected_msg_types |=
+                               BT_MESSAGE_TYPE_DISCARDED_EVENTS;
+               }
+
+               break;
+       case BT_MESSAGE_TYPE_STREAM_END:
+               state->expected_msg_types = 0;
+               break;
+       case BT_MESSAGE_TYPE_EVENT:
+       {
+               state->expected_msg_types = BT_MESSAGE_TYPE_EVENT;
+
+               if (stream->class->supports_packets) {
+                       state->expected_msg_types |= BT_MESSAGE_TYPE_PACKET_END;
+               } else {
+                       state->expected_msg_types |= BT_MESSAGE_TYPE_STREAM_END;
+               }
+
+               if (stream->class->supports_discarded_events) {
+                       state->expected_msg_types |=
+                               BT_MESSAGE_TYPE_DISCARDED_EVENTS;
+               }
+
+               break;
+       }
+       case BT_MESSAGE_TYPE_PACKET_BEGINNING:
+       {
+               state->expected_msg_types = BT_MESSAGE_TYPE_EVENT |
+                       BT_MESSAGE_TYPE_PACKET_END;
+
+               if (stream->class->supports_discarded_events) {
+                       state->expected_msg_types |=
+                               BT_MESSAGE_TYPE_DISCARDED_EVENTS;
+               }
+
+               break;
+       }
+       case BT_MESSAGE_TYPE_PACKET_END:
+       {
+               state->expected_msg_types = BT_MESSAGE_TYPE_PACKET_BEGINNING |
+                       BT_MESSAGE_TYPE_STREAM_END;
+
+               if (stream->class->supports_discarded_events) {
+                       state->expected_msg_types |=
+                               BT_MESSAGE_TYPE_DISCARDED_EVENTS;
+               }
+
+               if (stream->class->supports_discarded_packets) {
+                       state->expected_msg_types |=
+                               BT_MESSAGE_TYPE_DISCARDED_PACKETS;
+               }
+
+               break;
+       }
+       case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
+               state->expected_msg_types = BT_MESSAGE_TYPE_DISCARDED_EVENTS;
+
+               if (state->cur_packet) {
+                       state->expected_msg_types |= BT_MESSAGE_TYPE_EVENT |
+                               BT_MESSAGE_TYPE_PACKET_END;
+               } else {
+                       state->expected_msg_types |= BT_MESSAGE_TYPE_STREAM_END;
+
+                       if (stream->class->supports_packets) {
+                               state->expected_msg_types |=
+                                       BT_MESSAGE_TYPE_PACKET_BEGINNING;
+
+                               if (stream->class->supports_discarded_packets) {
+                                       state->expected_msg_types |=
+                                               BT_MESSAGE_TYPE_DISCARDED_PACKETS;
+                               }
+                       } else {
+                               state->expected_msg_types |=
+                                       BT_MESSAGE_TYPE_EVENT;
                        }
-                       break;
                }
+
+               break;
+       case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
+               state->expected_msg_types = BT_MESSAGE_TYPE_DISCARDED_PACKETS |
+                       BT_MESSAGE_TYPE_PACKET_BEGINNING |
+                       BT_MESSAGE_TYPE_STREAM_END;
+
+               if (stream->class->supports_discarded_events) {
+                       state->expected_msg_types |=
+                               BT_MESSAGE_TYPE_DISCARDED_EVENTS;
+               }
+               break;
+       default:
+               /*
+                * Other message types are not associated to a stream, so we
+                * should not get them here.
+                */
+               bt_common_abort();
        }
+}
 
-       result = true;
+static
+struct per_stream_state *get_per_stream_state(
+               struct bt_message_iterator *iterator,
+               const struct bt_stream *stream)
+{
+       struct per_stream_state *state = g_hash_table_lookup(
+               iterator->per_stream_state, stream);
+
+       if (!state) {
+               state = g_new0(struct per_stream_state, 1);
+               state->expected_msg_types = BT_MESSAGE_TYPE_STREAM_BEGINNING;
+               g_hash_table_insert(iterator->per_stream_state,
+                       (gpointer) stream, state);
+       }
+
+       return state;
+}
+
+static
+void assert_post_dev_expected_sequence(struct bt_message_iterator *iterator,
+               const struct bt_message *msg)
+{
+       const bt_stream *stream = get_stream_from_msg(msg);
+       struct per_stream_state *state;
+
+       if (!stream) {
+               goto end;
+       }
+
+       state = get_per_stream_state(iterator, stream);
+
+       /*
+        * We don't free the return value of message_types_to_string(), but
+        * that's because we know the program is going to abort anyway, and
+        * we don't want to call it if the assertion holds.
+        */
+       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+               "message-type-is-expected",
+               msg->type & state->expected_msg_types,
+               "Unexpected message type: %![stream-]s, %![iterator-]i, "
+               "%![message-]n, expected-msg-types=%s",
+               stream, iterator, msg,
+               message_types_to_string(state->expected_msg_types)->str);
+
+       update_expected_msg_type(stream, state, msg);
 
 end:
-       return result;
+       return;
+}
+
+static
+void assert_post_dev_expected_packet(struct bt_message_iterator *iterator,
+               const struct bt_message *msg)
+{
+       const bt_stream *stream = get_stream_from_msg(msg);
+       struct per_stream_state *state;
+       const bt_packet *actual_packet = NULL;
+       const bt_packet *expected_packet = NULL;
+
+       if (!stream) {
+               goto end;
+       }
+
+       state = get_per_stream_state(iterator, stream);
+
+       switch (msg->type) {
+       case BT_MESSAGE_TYPE_EVENT:
+       {
+               const struct bt_message_event *msg_event =
+                       (const struct bt_message_event *) msg;
+
+               actual_packet = msg_event->event->packet;
+               expected_packet = state->cur_packet;
+               break;
+       }
+       case BT_MESSAGE_TYPE_PACKET_BEGINNING:
+       {
+               const struct bt_message_packet *msg_packet =
+                       (const struct bt_message_packet *) msg;
+
+               BT_ASSERT(!state->cur_packet);
+               state->cur_packet = msg_packet->packet;
+               break;
+       }
+       case BT_MESSAGE_TYPE_PACKET_END:
+       {
+               const struct bt_message_packet *msg_packet =
+                       (const struct bt_message_packet *) msg;
+
+               actual_packet = msg_packet->packet;
+               expected_packet = state->cur_packet;
+               BT_ASSERT(state->cur_packet);
+               state->cur_packet = NULL;
+               break;
+       }
+       default:
+               break;
+       }
+
+       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+               "message-packet-is-expected",
+               actual_packet == expected_packet,
+               "Message's packet is not expected: %![stream-]s, %![iterator-]i, "
+               "%![message-]n, %![received-packet-]a, %![expected-packet-]a",
+               stream, iterator, msg, actual_packet, expected_packet);
+
+end:
+       return;
 }
 
-BT_ASSERT_COND_DEV_FUNC
 static
-bool clock_classes_are_compatible(
+void assert_post_dev_next(
                struct bt_message_iterator *iterator,
+               bt_message_iterator_class_next_method_status status,
                bt_message_array_const msgs, uint64_t msg_count)
 {
-       uint64_t i;
-       bool result;
+       if (status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK) {
+               uint64_t i;
 
-       for (i = 0; i < msg_count; i++) {
-               if (!clock_classes_are_compatible_one(iterator, msgs[i])) {
-                       result = false;
-                       goto end;
+               for (i = 0; i < msg_count; i++) {
+                       assert_post_dev_expected_sequence(iterator, msgs[i]);
+                       assert_post_dev_expected_packet(iterator, msgs[i]);
                }
-       }
+       } else if (status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END) {
+               GHashTableIter iter;
 
-       result = true;
+               gpointer stream_v, stream_state_v;
 
-end:
-       return result;
+               g_hash_table_iter_init(&iter, iterator->per_stream_state);
+               while (g_hash_table_iter_next(&iter, &stream_v,
+                               &stream_state_v)) {
+                       struct bt_stream *stream = stream_v;
+                       struct per_stream_state *stream_state = stream_state_v;
+
+                       BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
+                               "stream-is-ended",
+                               stream_state->expected_msg_types == 0,
+                               "Stream is not ended: %![stream-]s, "
+                               "%![iterator-]i, expected-msg-types=%s",
+                               stream, iterator,
+                               message_types_to_string(
+                                       stream_state->expected_msg_types)->str);
+               }
+
+       }
 }
+#endif
 
 /*
  * Call the `next` method of the iterator.  Do some validation on the returned
  * messages.
  */
 
-#define NEXT_METHOD_NAME       "bt_message_iterator_class_next_method"
-
 static
 enum bt_message_iterator_class_next_method_status
 call_iterator_next_method(
@@ -806,11 +1109,9 @@ call_iterator_next_method(
                bt_common_func_status_string(status), *user_count);
 
        if (status == BT_FUNC_STATUS_OK) {
-               BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
-                       "message-clock-classes-are-compatible",
-                       clock_classes_are_compatible(iterator, msgs,
-                               *user_count),
-                       "Clocks are not compatible");
+               BT_IF_DEV_MODE(assert_post_dev_clock_classes_are_compatible(
+                       iterator, msgs, *user_count));
+
                BT_ASSERT_POST_DEV(NEXT_METHOD_NAME,
                        "message-clock-snapshots-are-monotonic",
                        clock_snapshots_are_monotonic(iterator, msgs,
@@ -818,12 +1119,16 @@ call_iterator_next_method(
                        "Clock snapshots are not monotonic");
        }
 
+       BT_IF_DEV_MODE(assert_post_dev_next(iterator, status, msgs,
+               *user_count));
+
        BT_ASSERT_POST_DEV_NO_ERROR_IF_NO_ERROR_STATUS(NEXT_METHOD_NAME,
                status);
 
        return status;
 }
 
+BT_EXPORT
 enum bt_message_iterator_next_status
 bt_message_iterator_next(
                struct bt_message_iterator *iterator,
@@ -877,7 +1182,7 @@ bt_message_iterator_next(
         * by its downstream owner.
         *
         * For the same reason, there is no way that this iterator could
-        * have seeked (cannot seek a self message iterator).
+        * have sought (cannot seek a self message iterator).
         */
        BT_ASSERT_DBG(iterator->state ==
                BT_MESSAGE_ITERATOR_STATE_ACTIVE);
@@ -906,6 +1211,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 struct bt_component *
 bt_message_iterator_borrow_component(
                struct bt_message_iterator *iterator)
@@ -914,6 +1220,7 @@ bt_message_iterator_borrow_component(
        return iterator->upstream_component;
 }
 
+BT_EXPORT
 struct bt_self_component *bt_self_message_iterator_borrow_component(
                struct bt_self_message_iterator *self_iterator)
 {
@@ -924,6 +1231,7 @@ struct bt_self_component *bt_self_message_iterator_borrow_component(
        return (void *) iterator->upstream_component;
 }
 
+BT_EXPORT
 struct bt_self_component_port_output *bt_self_message_iterator_borrow_port(
                struct bt_self_message_iterator *self_iterator)
 {
@@ -937,6 +1245,7 @@ struct bt_self_component_port_output *bt_self_message_iterator_borrow_port(
 #define CAN_SEEK_NS_FROM_ORIGIN_METHOD_NAME                            \
        "bt_message_iterator_class_can_seek_ns_from_origin_method"
 
+BT_EXPORT
 enum bt_message_iterator_can_seek_ns_from_origin_status
 bt_message_iterator_can_seek_ns_from_origin(
                struct bt_message_iterator *iterator,
@@ -1014,6 +1323,7 @@ end:
 #define CAN_SEEK_BEGINNING_METHOD_NAME                                 \
        "bt_message_iterator_class_can_seek_beginning"
 
+BT_EXPORT
 enum bt_message_iterator_can_seek_beginning_status
 bt_message_iterator_can_seek_beginning(
                struct bt_message_iterator *iterator,
@@ -1091,7 +1401,6 @@ void reset_iterator_expectations(
                struct bt_message_iterator *iterator)
 {
        iterator->last_ns_from_origin = INT64_MIN;
-       iterator->clock_expectation.type = CLOCK_EXPECTATION_UNSET;
 }
 
 static
@@ -1113,6 +1422,7 @@ bool message_iterator_can_seek_beginning(
 #define SEEK_BEGINNING_METHOD_NAME                                     \
        "bt_message_iterator_class_seek_beginning_method"
 
+BT_EXPORT
 enum bt_message_iterator_seek_beginning_status
 bt_message_iterator_seek_beginning(struct bt_message_iterator *iterator)
 {
@@ -1158,10 +1468,13 @@ bt_message_iterator_seek_beginning(struct bt_message_iterator *iterator)
                        iterator, bt_common_func_status_string(status));
        }
 
+       clear_per_stream_state(iterator);
+
        set_iterator_state_after_seeking(iterator, status);
        return status;
 }
 
+BT_EXPORT
 bt_bool
 bt_message_iterator_can_seek_forward(
                bt_message_iterator *iterator)
@@ -1639,7 +1952,7 @@ end:
 
 /*
  * This function is installed as the iterator's next callback after we have
- * auto-seeked (seeked to the beginning and fast-forwarded) to send the
+ * auto-sought (sought to the beginning and fast-forwarded) to send the
  * messages saved in iterator->auto_seek.msgs.  Once this is done, the original
  * next callback is put back.
  */
@@ -1709,6 +2022,7 @@ bool message_iterator_can_seek_ns_from_origin(
        "bt_message_iterator_class_seek_ns_from_origin_method"
 
 
+BT_EXPORT
 enum bt_message_iterator_seek_ns_from_origin_status
 bt_message_iterator_seek_ns_from_origin(
                struct bt_message_iterator *iterator,
@@ -1813,6 +2127,8 @@ bt_message_iterator_seek_ns_from_origin(
                                iterator, bt_common_func_status_string(status));
                }
 
+               clear_per_stream_state(iterator);
+
                switch (status) {
                case BT_FUNC_STATUS_OK:
                        break;
@@ -1969,6 +2285,8 @@ bt_message_iterator_seek_ns_from_origin(
                }
        }
 
+       clear_per_stream_state(iterator);
+
        /*
         * The following messages returned by the next method (including
         * post_auto_seek_next) must be after (or at) `ns_from_origin`.
@@ -1985,6 +2303,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 bt_bool bt_self_message_iterator_is_interrupted(
                const struct bt_self_message_iterator *self_msg_iter)
 {
@@ -1995,12 +2314,14 @@ bt_bool bt_self_message_iterator_is_interrupted(
        return (bt_bool) bt_graph_is_interrupted(iterator->graph);
 }
 
+BT_EXPORT
 void bt_message_iterator_get_ref(
                const struct bt_message_iterator *iterator)
 {
        bt_object_get_ref(iterator);
 }
 
+BT_EXPORT
 void bt_message_iterator_put_ref(
                const struct bt_message_iterator *iterator)
 {
diff --git a/src/lib/graph/iterator.h b/src/lib/graph/iterator.h
new file mode 100644 (file)
index 0000000..e508dc2
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2017-2018 Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ */
+
+#ifndef BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H
+#define BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H
+
+#include "common/macros.h"
+#include "lib/object.h"
+#include <babeltrace2/graph/connection.h>
+#include <babeltrace2/graph/message.h>
+#include <babeltrace2/types.h>
+#include <stdbool.h>
+#include "common/uuid.h"
+
+struct bt_port;
+struct bt_graph;
+
+enum bt_message_iterator_state {
+       /* Iterator is not initialized */
+       BT_MESSAGE_ITERATOR_STATE_NON_INITIALIZED,
+
+       /* Iterator is active, not at the end yet, and not finalized */
+       BT_MESSAGE_ITERATOR_STATE_ACTIVE,
+
+       /*
+        * Iterator is ended, not finalized yet: the "next" method
+        * returns BT_MESSAGE_ITERATOR_STATUS_END.
+        */
+       BT_MESSAGE_ITERATOR_STATE_ENDED,
+
+       /* Iterator is currently being finalized */
+       BT_MESSAGE_ITERATOR_STATE_FINALIZING,
+
+       /* Iterator is finalized */
+       BT_MESSAGE_ITERATOR_STATE_FINALIZED,
+
+       /* Iterator is seeking */
+       BT_MESSAGE_ITERATOR_STATE_SEEKING,
+
+       /* Iterator did seek, but returned `BT_MESSAGE_ITERATOR_STATUS_AGAIN` */
+       BT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN,
+
+       /* Iterator did seek, but returned error status */
+       BT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR,
+};
+
+typedef enum bt_message_iterator_class_next_method_status
+(*bt_message_iterator_next_method)(
+               void *, bt_message_array_const, uint64_t, uint64_t *);
+
+typedef enum bt_message_iterator_class_seek_ns_from_origin_method_status
+(*bt_message_iterator_seek_ns_from_origin_method)(
+               void *, int64_t);
+
+typedef enum bt_message_iterator_class_seek_beginning_method_status
+(*bt_message_iterator_seek_beginning_method)(
+               void *);
+
+typedef enum bt_message_iterator_class_can_seek_ns_from_origin_method_status
+(*bt_message_iterator_can_seek_ns_from_origin_method)(
+               void *, int64_t, bt_bool *);
+
+typedef enum bt_message_iterator_class_can_seek_beginning_method_status
+(*bt_message_iterator_can_seek_beginning_method)(
+               void *, bt_bool *);
+
+struct bt_self_message_iterator_configuration {
+       bool frozen;
+       bool can_seek_forward;
+};
+
+struct bt_message_iterator {
+       struct bt_object base;
+       GPtrArray *msgs;
+       struct bt_component *upstream_component; /* Weak */
+       struct bt_port *upstream_port; /* Weak */
+       struct bt_connection *connection; /* Weak */
+       struct bt_graph *graph; /* Weak */
+       struct bt_self_message_iterator_configuration config;
+
+       /*
+        * Array of
+        * `struct bt_message_iterator *`
+        * (weak).
+        *
+        * This is an array of upstream message iterators on which this
+        * iterator depends. The references are weak: an upstream
+        * message iterator is responsible for removing its entry within
+        * this array on finalization/destruction.
+        */
+       GPtrArray *upstream_msg_iters;
+
+       /*
+        * Downstream message iterator which depends on this message
+        * iterator (weak).
+        *
+        * This can be `NULL` if this message iterator's owner is a sink
+        * component.
+        */
+       struct bt_message_iterator *downstream_msg_iter;
+
+       struct {
+               bt_message_iterator_next_method next;
+
+               /* These two are always both set or both unset. */
+               bt_message_iterator_seek_ns_from_origin_method seek_ns_from_origin;
+               bt_message_iterator_can_seek_ns_from_origin_method can_seek_ns_from_origin;
+
+               /* These two are always both set or both unset. */
+               bt_message_iterator_seek_beginning_method seek_beginning;
+               bt_message_iterator_can_seek_beginning_method can_seek_beginning;
+       } methods;
+
+       enum bt_message_iterator_state state;
+
+       /*
+        * Timestamp of the last received message (or INT64_MIN in the
+        * beginning, or after a seek to beginning).
+        */
+       int64_t last_ns_from_origin;
+
+       BT_IF_DEV_MODE(
+               struct bt_clock_correlation_validator *correlation_validator);
+       BT_IF_DEV_MODE(GHashTable *per_stream_state);
+
+       /*
+        * Data necessary for auto seek (the seek-to-beginning then fast-forward
+        * seek strategy).
+        */
+       struct {
+               /*
+                * Queue of `const bt_message *` (owned by this queue).
+                *
+                * When fast-forwarding, we get the messages from upstream in
+                * batches. Once we have found the first message with timestamp
+                * greater or equal to the seek time, we put it and all of the
+                * following message of the batch in this queue.  They will be
+                * sent on the next "next" call on this iterator.
+                *
+                * The messages are in chronological order (i.e. the first to
+                * send is the first of the queue).
+                */
+               GQueue *msgs;
+
+               /*
+                * After auto-seeking, we replace the iterator's `next` callback
+                * with our own, which returns the contents of the `msgs` queue.
+                * This field is where we save the original callback, so we can
+                * restore it.
+                */
+               void *original_next_callback;
+       } auto_seek;
+
+       void *user_data;
+};
+
+void bt_message_iterator_try_finalize(
+               struct bt_message_iterator *iterator);
+
+void bt_message_iterator_set_connection(
+               struct bt_message_iterator *iterator,
+               struct bt_connection *connection);
+
+static inline
+const char *bt_message_iterator_state_string(
+               enum bt_message_iterator_state state)
+{
+       switch (state) {
+       case BT_MESSAGE_ITERATOR_STATE_ACTIVE:
+               return "ACTIVE";
+       case BT_MESSAGE_ITERATOR_STATE_ENDED:
+               return "ENDED";
+       case BT_MESSAGE_ITERATOR_STATE_FINALIZING:
+               return "FINALIZING";
+       case BT_MESSAGE_ITERATOR_STATE_FINALIZED:
+               return "FINALIZED";
+       case BT_MESSAGE_ITERATOR_STATE_SEEKING:
+               return "SEEKING";
+       case BT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN:
+               return "LAST_SEEKING_RETURNED_AGAIN";
+       case BT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR:
+               return "LAST_SEEKING_RETURNED_ERROR";
+       default:
+               return "(unknown)";
+       }
+};
+
+#endif /* BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H */
index 4322f4fe15c84cfaa88457cea0f5d880715be8bc..9e5e3016d5b99bbd38ed059e4f3083223068122b 100644 (file)
@@ -18,7 +18,6 @@
                (_msg_iter_cls), "Message iterator class",              \
                ": %!+I", (_msg_iter_cls))
 
-BT_HIDDEN
 void _bt_message_iterator_class_freeze(
                const struct bt_message_iterator_class *msg_iter_cls)
 {
@@ -27,12 +26,14 @@ void _bt_message_iterator_class_freeze(
        ((struct bt_message_iterator_class *) msg_iter_cls)->frozen = true;
 }
 
+BT_EXPORT
 void bt_message_iterator_class_get_ref(
                const bt_message_iterator_class *message_iterator_class)
 {
        bt_object_get_ref(message_iterator_class);
 }
 
+BT_EXPORT
 void bt_message_iterator_class_put_ref(
                const bt_message_iterator_class *message_iterator_class)
 {
@@ -52,6 +53,7 @@ void destroy_iterator_class(struct bt_object *obj)
        g_free(class);
 }
 
+BT_EXPORT
 struct bt_message_iterator_class *bt_message_iterator_class_create(
                bt_message_iterator_class_next_method next_method)
 {
@@ -77,6 +79,7 @@ end:
        return message_iterator_class;
 }
 
+BT_EXPORT
 bt_message_iterator_class_set_method_status
 bt_message_iterator_class_set_initialize_method(
        bt_message_iterator_class *message_iterator_class,
@@ -92,6 +95,7 @@ bt_message_iterator_class_set_initialize_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 bt_message_iterator_class_set_method_status
 bt_message_iterator_class_set_finalize_method(
                bt_message_iterator_class *message_iterator_class,
@@ -107,6 +111,7 @@ bt_message_iterator_class_set_finalize_method(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 bt_message_iterator_class_set_method_status
 bt_message_iterator_class_set_seek_ns_from_origin_methods(
                bt_message_iterator_class *message_iterator_class,
@@ -124,6 +129,7 @@ bt_message_iterator_class_set_seek_ns_from_origin_methods(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 bt_message_iterator_class_set_method_status
 bt_message_iterator_class_set_seek_beginning_methods(
                bt_message_iterator_class *message_iterator_class,
index 538f5b4a283fa731b3abb9ee0eb57bfdab358e8f..0606aa72b6fe1dfbc20bfe39286d9314f777e678 100644 (file)
@@ -10,7 +10,6 @@
 
 #include <babeltrace2/graph/message-iterator-class.h>
 #include <babeltrace2/types.h>
-#include "common/macros.h"
 #include "lib/object.h"
 #include <stdbool.h>
 #include <glib.h>
@@ -30,7 +29,6 @@ struct bt_message_iterator_class {
        } methods;
 };
 
-BT_HIDDEN
 void _bt_message_iterator_class_freeze(
                const struct bt_message_iterator_class *message_iterator_class);
 
diff --git a/src/lib/graph/message/Makefile.am b/src/lib/graph/message/Makefile.am
deleted file mode 100644 (file)
index c34530b..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libgraph-message.la
-
-libgraph_message_la_SOURCES = \
-       discarded-items.c \
-       discarded-items.h \
-       event.c \
-       event.h \
-       iterator.h \
-       message.c \
-       message.h \
-       message-iterator-inactivity.c \
-       message-iterator-inactivity.h \
-       packet.c \
-       packet.h \
-       stream.c \
-       stream.h
index 313348c9956aa2221f4bc7e64b41465dc2307482..4a846313a54bca69105653c1fee3a38d2cb165c4 100644 (file)
@@ -224,6 +224,7 @@ borrow_discarded_items_message_end_default_clock_snapshot_const(
 #define SC_SUPPORTS_DISC_PRECOND_ID(_item_type)                                \
        "stream-class-supports-discarded-" _item_type
 
+BT_EXPORT
 struct bt_message *bt_message_discarded_events_create(
                struct bt_self_message_iterator *message_iterator,
                const struct bt_stream *stream)
@@ -236,6 +237,7 @@ struct bt_message *bt_message_discarded_events_create(
                SC_SUPPORTS_DISC_PRECOND_ID("events"));
 }
 
+BT_EXPORT
 struct bt_message *bt_message_discarded_events_create_with_default_clock_snapshots(
                struct bt_self_message_iterator *message_iterator,
                const struct bt_stream *stream, uint64_t beginning_raw_value,
@@ -251,6 +253,7 @@ struct bt_message *bt_message_discarded_events_create_with_default_clock_snapsho
                SC_SUPPORTS_DISC_PRECOND_ID("events"));
 }
 
+BT_EXPORT
 struct bt_stream *bt_message_discarded_events_borrow_stream(
                struct bt_message *message)
 {
@@ -259,6 +262,7 @@ struct bt_stream *bt_message_discarded_events_borrow_stream(
        return borrow_discarded_items_message_stream(message);
 }
 
+BT_EXPORT
 void bt_message_discarded_events_set_count(struct bt_message *message,
                uint64_t count)
 {
@@ -269,6 +273,7 @@ void bt_message_discarded_events_set_count(struct bt_message *message,
        set_discarded_items_message_count(message, count);
 }
 
+BT_EXPORT
 const struct bt_clock_snapshot *
 bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
                const struct bt_message *msg)
@@ -279,6 +284,7 @@ bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
                msg, __func__);
 }
 
+BT_EXPORT
 const struct bt_clock_snapshot *
 bt_message_discarded_events_borrow_end_default_clock_snapshot_const(
                const struct bt_message *msg)
@@ -289,6 +295,7 @@ bt_message_discarded_events_borrow_end_default_clock_snapshot_const(
                msg, __func__);
 }
 
+BT_EXPORT
 const struct bt_stream *
 bt_message_discarded_events_borrow_stream_const(const struct bt_message *message)
 {
@@ -296,6 +303,7 @@ bt_message_discarded_events_borrow_stream_const(const struct bt_message *message
                (void *) message);
 }
 
+BT_EXPORT
 enum bt_property_availability bt_message_discarded_events_get_count(
                const struct bt_message *message, uint64_t *count)
 {
@@ -305,6 +313,7 @@ enum bt_property_availability bt_message_discarded_events_get_count(
        return get_discarded_items_message_count(message, count);
 }
 
+BT_EXPORT
 struct bt_message *bt_message_discarded_packets_create(
                struct bt_self_message_iterator *message_iterator,
                const struct bt_stream *stream)
@@ -316,6 +325,7 @@ struct bt_message *bt_message_discarded_packets_create(
                false, 0, 0, __func__, SC_SUPPORTS_DISC_PRECOND_ID("packets"));
 }
 
+BT_EXPORT
 struct bt_message *bt_message_discarded_packets_create_with_default_clock_snapshots(
                struct bt_self_message_iterator *message_iterator,
                const struct bt_stream *stream, uint64_t beginning_raw_value,
@@ -331,6 +341,7 @@ struct bt_message *bt_message_discarded_packets_create_with_default_clock_snapsh
                SC_SUPPORTS_DISC_PRECOND_ID("packets"));
 }
 
+BT_EXPORT
 struct bt_stream *bt_message_discarded_packets_borrow_stream(
                struct bt_message *message)
 {
@@ -339,6 +350,7 @@ struct bt_stream *bt_message_discarded_packets_borrow_stream(
        return borrow_discarded_items_message_stream(message);
 }
 
+BT_EXPORT
 void bt_message_discarded_packets_set_count(struct bt_message *message,
                uint64_t count)
 {
@@ -349,6 +361,7 @@ void bt_message_discarded_packets_set_count(struct bt_message *message,
        set_discarded_items_message_count(message, count);
 }
 
+BT_EXPORT
 const struct bt_clock_snapshot *
 bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
                const struct bt_message *msg)
@@ -359,6 +372,7 @@ bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
                msg, __func__);
 }
 
+BT_EXPORT
 const struct bt_clock_snapshot *
 bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(
                const struct bt_message *msg)
@@ -369,6 +383,7 @@ bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(
                msg, __func__);
 }
 
+BT_EXPORT
 const struct bt_stream *
 bt_message_discarded_packets_borrow_stream_const(const struct bt_message *message)
 {
@@ -376,6 +391,7 @@ bt_message_discarded_packets_borrow_stream_const(const struct bt_message *messag
                (void *) message);
 }
 
+BT_EXPORT
 enum bt_property_availability bt_message_discarded_packets_get_count(
                const struct bt_message *message, uint64_t *count)
 {
@@ -396,6 +412,7 @@ borrow_discarded_items_message_stream_class_default_clock_class(
        return disc_items_msg->stream->class->default_clock_class;
 }
 
+BT_EXPORT
 const struct bt_clock_class *
 bt_message_discarded_events_borrow_stream_class_default_clock_class_const(
                const struct bt_message *msg)
@@ -406,6 +423,7 @@ bt_message_discarded_events_borrow_stream_class_default_clock_class_const(
                msg);
 }
 
+BT_EXPORT
 const struct bt_clock_class *
 bt_message_discarded_packets_borrow_stream_class_default_clock_class_const(
                const struct bt_message *msg)
index 932a598ebf2216daa3496e30d1061822e340b2a0..bbd99b7f1a9e379e9527706a317783e610017bc4 100644 (file)
@@ -22,7 +22,6 @@
 #include <babeltrace2/graph/message.h>
 #include <babeltrace2/types.h>
 #include <stdbool.h>
-#include <inttypes.h>
 
 #include "event.h"
 
@@ -30,7 +29,6 @@
        BT_ASSERT_PRE_DEV_MSG_HAS_TYPE("message", (_msg), "event",      \
                BT_MESSAGE_TYPE_EVENT)
 
-BT_HIDDEN
 struct bt_message *bt_message_event_new(
                struct bt_graph *graph)
 {
@@ -202,6 +200,7 @@ end:
        return (void *) message;
 }
 
+BT_EXPORT
 struct bt_message *bt_message_event_create(
                struct bt_self_message_iterator *msg_iter,
                const struct bt_event_class *event_class,
@@ -213,6 +212,7 @@ struct bt_message *bt_message_event_create(
                false, 0, __func__);
 }
 
+BT_EXPORT
 struct bt_message *bt_message_event_create_with_packet(
                struct bt_self_message_iterator *msg_iter,
                const struct bt_event_class *event_class,
@@ -224,6 +224,7 @@ struct bt_message *bt_message_event_create_with_packet(
                packet->stream, false, 0, __func__);
 }
 
+BT_EXPORT
 struct bt_message *bt_message_event_create_with_default_clock_snapshot(
                struct bt_self_message_iterator *msg_iter,
                const struct bt_event_class *event_class,
@@ -236,6 +237,7 @@ struct bt_message *bt_message_event_create_with_default_clock_snapshot(
                true, raw_value, __func__);
 }
 
+BT_EXPORT
 struct bt_message *
 bt_message_event_create_with_packet_and_default_clock_snapshot(
                struct bt_self_message_iterator *msg_iter,
@@ -249,7 +251,6 @@ bt_message_event_create_with_packet_and_default_clock_snapshot(
                packet->stream, true, raw_value, __func__);
 }
 
-BT_HIDDEN
 void bt_message_event_destroy(struct bt_message *msg)
 {
        struct bt_message_event *event_msg = (void *) msg;
@@ -270,7 +271,6 @@ void bt_message_event_destroy(struct bt_message *msg)
        g_free(msg);
 }
 
-BT_HIDDEN
 void bt_message_event_recycle(struct bt_message *msg)
 {
        struct bt_message_event *event_msg = (void *) msg;
@@ -316,6 +316,7 @@ struct bt_event *borrow_event(struct bt_message *message)
        return event_message->event;
 }
 
+BT_EXPORT
 struct bt_event *bt_message_event_borrow_event(
                struct bt_message *message)
 {
@@ -323,6 +324,7 @@ struct bt_event *bt_message_event_borrow_event(
        return borrow_event(message);
 }
 
+BT_EXPORT
 const struct bt_event *bt_message_event_borrow_event_const(
                const struct bt_message *message)
 {
@@ -330,6 +332,7 @@ const struct bt_event *bt_message_event_borrow_event_const(
        return borrow_event((void *) message);
 }
 
+BT_EXPORT
 const struct bt_clock_snapshot *
 bt_message_event_borrow_default_clock_snapshot_const(
                const struct bt_message *msg)
@@ -346,6 +349,7 @@ bt_message_event_borrow_default_clock_snapshot_const(
        return event_msg->default_cs;
 }
 
+BT_EXPORT
 const bt_clock_class *
 bt_message_event_borrow_stream_class_default_clock_class_const(
                const bt_message *msg)
index ee366e320ef69e2aefe046e75c25ac44f780fd83..98d4acf1d6211315d435c1e61306380b9520f2e6 100644 (file)
@@ -11,8 +11,6 @@
 #include "compat/compiler.h"
 #include <babeltrace2/trace-ir/event-class.h>
 #include <babeltrace2/trace-ir/event.h>
-#include "common/assert.h"
-#include "common/macros.h"
 
 #include "message.h"
 
@@ -26,13 +24,10 @@ struct bt_message_event {
        struct bt_clock_snapshot *default_cs;
 };
 
-BT_HIDDEN
 struct bt_message *bt_message_event_new(struct bt_graph *graph);
 
-BT_HIDDEN
 void bt_message_event_recycle(struct bt_message *msg);
 
-BT_HIDDEN
 void bt_message_event_destroy(struct bt_message *msg);
 
 #ifdef __cplusplus
diff --git a/src/lib/graph/message/iterator.h b/src/lib/graph/message/iterator.h
deleted file mode 100644 (file)
index b587735..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2017-2018 Philippe Proulx <pproulx@efficios.com>
- * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- */
-
-#ifndef BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H
-#define BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H
-
-#include "common/macros.h"
-#include "lib/object.h"
-#include <babeltrace2/graph/connection.h>
-#include <babeltrace2/graph/message.h>
-#include <babeltrace2/types.h>
-#include "common/assert.h"
-#include <stdbool.h>
-#include "common/uuid.h"
-
-struct bt_port;
-struct bt_graph;
-
-enum bt_message_iterator_state {
-       /* Iterator is not initialized */
-       BT_MESSAGE_ITERATOR_STATE_NON_INITIALIZED,
-
-       /* Iterator is active, not at the end yet, and not finalized */
-       BT_MESSAGE_ITERATOR_STATE_ACTIVE,
-
-       /*
-        * Iterator is ended, not finalized yet: the "next" method
-        * returns BT_MESSAGE_ITERATOR_STATUS_END.
-        */
-       BT_MESSAGE_ITERATOR_STATE_ENDED,
-
-       /* Iterator is currently being finalized */
-       BT_MESSAGE_ITERATOR_STATE_FINALIZING,
-
-       /* Iterator is finalized */
-       BT_MESSAGE_ITERATOR_STATE_FINALIZED,
-
-       /* Iterator is seeking */
-       BT_MESSAGE_ITERATOR_STATE_SEEKING,
-
-       /* Iterator did seek, but returned `BT_MESSAGE_ITERATOR_STATUS_AGAIN` */
-       BT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN,
-
-       /* Iterator did seek, but returned error status */
-       BT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR,
-};
-
-typedef enum bt_message_iterator_class_next_method_status
-(*bt_message_iterator_next_method)(
-               void *, bt_message_array_const, uint64_t, uint64_t *);
-
-typedef enum bt_message_iterator_class_seek_ns_from_origin_method_status
-(*bt_message_iterator_seek_ns_from_origin_method)(
-               void *, int64_t);
-
-typedef enum bt_message_iterator_class_seek_beginning_method_status
-(*bt_message_iterator_seek_beginning_method)(
-               void *);
-
-typedef enum bt_message_iterator_class_can_seek_ns_from_origin_method_status
-(*bt_message_iterator_can_seek_ns_from_origin_method)(
-               void *, int64_t, bt_bool *);
-
-typedef enum bt_message_iterator_class_can_seek_beginning_method_status
-(*bt_message_iterator_can_seek_beginning_method)(
-               void *, bt_bool *);
-
-struct bt_self_message_iterator_configuration {
-       bool frozen;
-       bool can_seek_forward;
-};
-
-struct bt_message_iterator {
-       struct bt_object base;
-       GPtrArray *msgs;
-       struct bt_component *upstream_component; /* Weak */
-       struct bt_port *upstream_port; /* Weak */
-       struct bt_connection *connection; /* Weak */
-       struct bt_graph *graph; /* Weak */
-       struct bt_self_message_iterator_configuration config;
-
-       /*
-        * Array of
-        * `struct bt_message_iterator *`
-        * (weak).
-        *
-        * This is an array of upstream message iterators on which this
-        * iterator depends. The references are weak: an upstream
-        * message iterator is responsible for removing its entry within
-        * this array on finalization/destruction.
-        */
-       GPtrArray *upstream_msg_iters;
-
-       /*
-        * Downstream message iterator which depends on this message
-        * iterator (weak).
-        *
-        * This can be `NULL` if this message iterator's owner is a sink
-        * component.
-        */
-       struct bt_message_iterator *downstream_msg_iter;
-
-       struct {
-               bt_message_iterator_next_method next;
-
-               /* These two are always both set or both unset. */
-               bt_message_iterator_seek_ns_from_origin_method seek_ns_from_origin;
-               bt_message_iterator_can_seek_ns_from_origin_method can_seek_ns_from_origin;
-
-               /* These two are always both set or both unset. */
-               bt_message_iterator_seek_beginning_method seek_beginning;
-               bt_message_iterator_can_seek_beginning_method can_seek_beginning;
-       } methods;
-
-       enum bt_message_iterator_state state;
-
-       /*
-        * Timestamp of the last received message (or INT64_MIN in the
-        * beginning, or after a seek to beginning).
-        */
-       int64_t last_ns_from_origin;
-
-       struct {
-               enum {
-                       /* We haven't recorded clock properties yet. */
-                       CLOCK_EXPECTATION_UNSET,
-
-                       /* Expect to have no clock. */
-                       CLOCK_EXPECTATION_NONE,
-
-                       /* Clock with origin_is_unix_epoch true.*/
-                       CLOCK_EXPECTATION_ORIGIN_UNIX,
-
-                       /* Clock with origin_is_unix_epoch false, with a UUID.*/
-                       CLOCK_EXPECTATION_ORIGIN_OTHER_UUID,
-
-                       /* Clock with origin_is_unix_epoch false, without a UUID.*/
-                       CLOCK_EXPECTATION_ORIGIN_OTHER_NO_UUID,
-               } type;
-
-               /*
-                * Expected UUID of the clock, if `type`is CLOCK_EXPECTATION_ORIGIN_OTHER_UUID.
-                *
-                * If the clock's origin is the unix epoch, the UUID is
-                * irrelevant (as the clock will be correlatable with other
-                * clocks having the same origin).
-                */
-               bt_uuid_t uuid;
-       } clock_expectation;
-
-       /*
-        * Data necessary for auto seek (the seek-to-beginning then fast-forward
-        * seek strategy).
-        */
-       struct {
-               /*
-                * Queue of `const bt_message *` (owned by this queue).
-                *
-                * When fast-forwarding, we get the messages from upstream in
-                * batches. Once we have found the first message with timestamp
-                * greater or equal to the seek time, we put it and all of the
-                * following message of the batch in this queue.  They will be
-                * sent on the next "next" call on this iterator.
-                *
-                * The messages are in chronological order (i.e. the first to
-                * send is the first of the queue).
-                */
-               GQueue *msgs;
-
-               /*
-                * After auto-seeking, we replace the iterator's `next` callback
-                * with our own, which returns the contents of the `msgs` queue.
-                * This field is where we save the original callback, so we can
-                * restore it.
-                */
-               void *original_next_callback;
-       } auto_seek;
-
-       void *user_data;
-};
-
-BT_HIDDEN
-void bt_message_iterator_try_finalize(
-               struct bt_message_iterator *iterator);
-
-BT_HIDDEN
-void bt_message_iterator_set_connection(
-               struct bt_message_iterator *iterator,
-               struct bt_connection *connection);
-
-static inline
-const char *bt_message_iterator_state_string(
-               enum bt_message_iterator_state state)
-{
-       switch (state) {
-       case BT_MESSAGE_ITERATOR_STATE_ACTIVE:
-               return "ACTIVE";
-       case BT_MESSAGE_ITERATOR_STATE_ENDED:
-               return "ENDED";
-       case BT_MESSAGE_ITERATOR_STATE_FINALIZING:
-               return "FINALIZING";
-       case BT_MESSAGE_ITERATOR_STATE_FINALIZED:
-               return "FINALIZED";
-       case BT_MESSAGE_ITERATOR_STATE_SEEKING:
-               return "SEEKING";
-       case BT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN:
-               return "LAST_SEEKING_RETURNED_AGAIN";
-       case BT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR:
-               return "LAST_SEEKING_RETURNED_ERROR";
-       default:
-               return "(unknown)";
-       }
-};
-
-#endif /* BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H */
index 89302ff23cf7fd65adabcb0be18c8a5e67f954a8..3006811fb515acd33818e314471b09279c36db68 100644 (file)
@@ -34,6 +34,7 @@ void bt_message_message_iterator_inactivity_destroy(struct bt_object *obj)
        g_free(message);
 }
 
+BT_EXPORT
 struct bt_message *bt_message_message_iterator_inactivity_create(
                struct bt_self_message_iterator *self_msg_iter,
                const struct bt_clock_class *clock_class,
@@ -79,7 +80,8 @@ end:
        return (void *) ret_msg;
 }
 
-extern const struct bt_clock_snapshot *
+BT_EXPORT
+const struct bt_clock_snapshot *
 bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(
                const bt_message *msg)
 {
index f5e21c3ea95a0cfce5b1e04653caceac94f200fe..02c3e64b3e61852598c2f10b0d00542966e36d7c 100644 (file)
@@ -14,7 +14,6 @@
 #include "lib/graph/message/message.h"
 #include "lib/graph/graph.h"
 
-BT_HIDDEN
 void bt_message_init(struct bt_message *message,
                enum bt_message_type type,
                bt_object_release_func release,
@@ -29,6 +28,7 @@ void bt_message_init(struct bt_message *message,
        }
 }
 
+BT_EXPORT
 enum bt_message_type bt_message_get_type(
                const struct bt_message *message)
 {
@@ -36,18 +36,19 @@ enum bt_message_type bt_message_get_type(
        return message->type;
 }
 
-BT_HIDDEN
 void bt_message_unlink_graph(struct bt_message *msg)
 {
        BT_ASSERT(msg);
        msg->graph = NULL;
 }
 
+BT_EXPORT
 void bt_message_get_ref(const struct bt_message *message)
 {
        bt_object_get_ref(message);
 }
 
+BT_EXPORT
 void bt_message_put_ref(const struct bt_message *message)
 {
        bt_object_put_ref(message);
index 791ac9aeb08815bda876ea0e8f319cb4157a93d5..d6ba358a945c05b06720b2755c1723c0b781afae 100644 (file)
@@ -13,7 +13,6 @@
 # error Please include "lib/logging.h" before including this file.
 #endif
 
-#include "common/macros.h"
 #include "lib/object.h"
 #include "common/assert.h"
 #include <babeltrace2/graph/graph.h>
@@ -39,7 +38,6 @@ struct bt_message {
        struct bt_graph *graph;
 };
 
-BT_HIDDEN
 void bt_message_init(struct bt_message *message,
                enum bt_message_type type,
                bt_object_release_func release,
@@ -86,7 +84,6 @@ static inline void _bt_message_freeze(struct bt_message *message)
        message->frozen = BT_TRUE;
 }
 
-BT_HIDDEN
 void bt_message_unlink_graph(struct bt_message *msg);
 
 #ifdef BT_DEV_MODE
index e27c766c29ff5b18be7cbafbf421681902076399..b956b106b28ff7be73458110915fb68a2d69f602 100644 (file)
@@ -73,14 +73,12 @@ end:
        return (void *) message;
 }
 
-BT_HIDDEN
 struct bt_message *bt_message_packet_beginning_new(struct bt_graph *graph)
 {
        return new_packet_message(graph, BT_MESSAGE_TYPE_PACKET_BEGINNING,
                (bt_object_release_func) bt_message_packet_beginning_recycle);
 }
 
-BT_HIDDEN
 struct bt_message *bt_message_packet_end_new(struct bt_graph *graph)
 {
        return new_packet_message(graph, BT_MESSAGE_TYPE_PACKET_END,
@@ -173,6 +171,7 @@ end:
        return (void *) message;
 }
 
+BT_EXPORT
 struct bt_message *bt_message_packet_beginning_create(
                struct bt_self_message_iterator *self_msg_iter,
                const struct bt_packet *packet)
@@ -186,6 +185,7 @@ struct bt_message *bt_message_packet_beginning_create(
                &msg_iter->graph->packet_begin_msg_pool, false, 0, __func__);
 }
 
+BT_EXPORT
 struct bt_message *bt_message_packet_beginning_create_with_default_clock_snapshot(
                struct bt_self_message_iterator *self_msg_iter,
                const struct bt_packet *packet, uint64_t raw_value)
@@ -200,6 +200,7 @@ struct bt_message *bt_message_packet_beginning_create_with_default_clock_snapsho
                __func__);
 }
 
+BT_EXPORT
 struct bt_message *bt_message_packet_end_create(
                struct bt_self_message_iterator *self_msg_iter,
                const struct bt_packet *packet)
@@ -213,6 +214,7 @@ struct bt_message *bt_message_packet_end_create(
                &msg_iter->graph->packet_end_msg_pool, false, 0, __func__);
 }
 
+BT_EXPORT
 struct bt_message *bt_message_packet_end_create_with_default_clock_snapshot(
                struct bt_self_message_iterator *self_msg_iter,
                const struct bt_packet *packet, uint64_t raw_value)
@@ -227,7 +229,6 @@ struct bt_message *bt_message_packet_end_create_with_default_clock_snapshot(
                __func__);
 }
 
-BT_HIDDEN
 void bt_message_packet_destroy(struct bt_message *msg)
 {
        struct bt_message_packet *packet_msg = (void *) msg;
@@ -263,7 +264,6 @@ void recycle_packet_message(struct bt_message *msg, struct bt_object_pool *pool)
        bt_object_pool_recycle_object(pool, msg);
 }
 
-BT_HIDDEN
 void bt_message_packet_beginning_recycle(struct bt_message *msg)
 {
        BT_ASSERT(msg);
@@ -276,7 +276,6 @@ void bt_message_packet_beginning_recycle(struct bt_message *msg)
        recycle_packet_message(msg, &msg->graph->packet_begin_msg_pool);
 }
 
-BT_HIDDEN
 void bt_message_packet_end_recycle(struct bt_message *msg)
 {
        BT_ASSERT(msg);
@@ -289,6 +288,7 @@ void bt_message_packet_end_recycle(struct bt_message *msg)
        recycle_packet_message(msg, &msg->graph->packet_end_msg_pool);
 }
 
+BT_EXPORT
 struct bt_packet *bt_message_packet_beginning_borrow_packet(
                struct bt_message *message)
 {
@@ -299,6 +299,7 @@ struct bt_packet *bt_message_packet_beginning_borrow_packet(
        return packet_msg->packet;
 }
 
+BT_EXPORT
 const struct bt_packet *bt_message_packet_beginning_borrow_packet_const(
                const struct bt_message *message)
 {
@@ -306,6 +307,7 @@ const struct bt_packet *bt_message_packet_beginning_borrow_packet_const(
                (void *) message);
 }
 
+BT_EXPORT
 struct bt_packet *bt_message_packet_end_borrow_packet(
                struct bt_message *message)
 {
@@ -316,6 +318,7 @@ struct bt_packet *bt_message_packet_end_borrow_packet(
        return packet_msg->packet;
 }
 
+BT_EXPORT
 const struct bt_packet *bt_message_packet_end_borrow_packet_const(
                const struct bt_message *message)
 {
@@ -338,6 +341,7 @@ borrow_packet_message_default_clock_snapshot_const(
        return packet_msg->default_cs;
 }
 
+BT_EXPORT
 const struct bt_clock_snapshot *
 bt_message_packet_beginning_borrow_default_clock_snapshot_const(
                const struct bt_message *msg)
@@ -349,6 +353,7 @@ bt_message_packet_beginning_borrow_default_clock_snapshot_const(
        return borrow_packet_message_default_clock_snapshot_const(msg);
 }
 
+BT_EXPORT
 const struct bt_clock_snapshot *
 bt_message_packet_end_borrow_default_clock_snapshot_const(
                const struct bt_message *msg)
@@ -371,6 +376,7 @@ borrow_packet_message_stream_class_default_clock_class(
        return packet_msg->packet->stream->class->default_clock_class;
 }
 
+BT_EXPORT
 const struct bt_clock_class *
 bt_message_packet_beginning_borrow_stream_class_default_clock_class_const(
                const struct bt_message *msg)
@@ -380,6 +386,7 @@ bt_message_packet_beginning_borrow_stream_class_default_clock_class_const(
        return borrow_packet_message_stream_class_default_clock_class(msg);
 }
 
+BT_EXPORT
 const struct bt_clock_class *
 bt_message_packet_end_borrow_stream_class_default_clock_class_const(
                const struct bt_message *msg)
index 4a13d794928d7467349429c970bb26a29f3efe4c..632aaeea9bde57491d1276a67022f026095029d6 100644 (file)
@@ -11,8 +11,6 @@
 #include "compat/compiler.h"
 #include <babeltrace2/trace-ir/packet.h>
 #include "lib/trace-ir/clock-snapshot.h"
-#include "common/assert.h"
-#include "common/macros.h"
 
 #include "message.h"
 
@@ -22,19 +20,14 @@ struct bt_message_packet {
        struct bt_clock_snapshot *default_cs;
 };
 
-BT_HIDDEN
 void bt_message_packet_destroy(struct bt_message *msg);
 
-BT_HIDDEN
 struct bt_message *bt_message_packet_beginning_new(
                struct bt_graph *graph);
-BT_HIDDEN
 void bt_message_packet_beginning_recycle(struct bt_message *msg);
 
-BT_HIDDEN
 struct bt_message *bt_message_packet_end_new(struct bt_graph *graph);
 
-BT_HIDDEN
 void bt_message_packet_end_recycle(struct bt_message *msg);
 
 #endif /* BABELTRACE_GRAPH_MESSAGE_PACKET_INTERNAL_H */
index a3723e4ac8f485179284d0ed7c857d7604f66850..86ec69eb69ca3aa997d6c593188b6117455908fd 100644 (file)
@@ -99,6 +99,7 @@ end:
        return &message->parent;
 }
 
+BT_EXPORT
 struct bt_message *bt_message_stream_beginning_create(
                struct bt_self_message_iterator *self_msg_iter,
                const struct bt_stream *stream)
@@ -109,6 +110,7 @@ struct bt_message *bt_message_stream_beginning_create(
                BT_MESSAGE_TYPE_STREAM_BEGINNING, __func__);
 }
 
+BT_EXPORT
 struct bt_message *bt_message_stream_end_create(
                struct bt_self_message_iterator *self_msg_iter,
                const struct bt_stream *stream)
@@ -129,6 +131,7 @@ struct bt_stream *borrow_stream_message_stream(struct bt_message *message)
        return stream_msg->stream;
 }
 
+BT_EXPORT
 struct bt_stream *bt_message_stream_beginning_borrow_stream(
                struct bt_message *message)
 {
@@ -137,6 +140,7 @@ struct bt_stream *bt_message_stream_beginning_borrow_stream(
        return borrow_stream_message_stream(message);
 }
 
+BT_EXPORT
 struct bt_stream *bt_message_stream_end_borrow_stream(
                struct bt_message *message)
 {
@@ -145,6 +149,7 @@ struct bt_stream *bt_message_stream_end_borrow_stream(
        return borrow_stream_message_stream(message);
 }
 
+BT_EXPORT
 const struct bt_stream *bt_message_stream_beginning_borrow_stream_const(
                const struct bt_message *message)
 {
@@ -152,6 +157,7 @@ const struct bt_stream *bt_message_stream_beginning_borrow_stream_const(
                (void *) message);
 }
 
+BT_EXPORT
 const struct bt_stream *bt_message_stream_end_borrow_stream_const(
                const struct bt_message *message)
 {
@@ -180,6 +186,7 @@ void set_stream_default_clock_snapshot(
                "%![msg-]+n, value=%" PRIu64, msg, raw_value);
 }
 
+BT_EXPORT
 void bt_message_stream_beginning_set_default_clock_snapshot(
                struct bt_message *message, uint64_t raw_value)
 {
@@ -188,6 +195,7 @@ void bt_message_stream_beginning_set_default_clock_snapshot(
        set_stream_default_clock_snapshot(message, raw_value, __func__);
 }
 
+BT_EXPORT
 void bt_message_stream_end_set_default_clock_snapshot(
                struct bt_message *message, uint64_t raw_value)
 {
@@ -215,6 +223,7 @@ borrow_stream_message_default_clock_snapshot_const(
        return stream_msg->default_cs_state;
 }
 
+BT_EXPORT
 enum bt_message_stream_clock_snapshot_state
 bt_message_stream_beginning_borrow_default_clock_snapshot_const(
                const bt_message *message, const bt_clock_snapshot **snapshot)
@@ -225,6 +234,7 @@ bt_message_stream_beginning_borrow_default_clock_snapshot_const(
                message, snapshot, __func__);
 }
 
+BT_EXPORT
 enum bt_message_stream_clock_snapshot_state
 bt_message_stream_end_borrow_default_clock_snapshot_const(
                const bt_message *message, const bt_clock_snapshot **snapshot)
@@ -246,6 +256,7 @@ borrow_stream_message_stream_class_default_clock_class(
        return stream_msg->stream->class->default_clock_class;
 }
 
+BT_EXPORT
 const struct bt_clock_class *
 bt_message_stream_beginning_borrow_stream_class_default_clock_class_const(
                const struct bt_message *msg)
@@ -255,6 +266,7 @@ bt_message_stream_beginning_borrow_stream_class_default_clock_class_const(
        return borrow_stream_message_stream_class_default_clock_class(msg);
 }
 
+BT_EXPORT
 const struct bt_clock_class *
 bt_message_stream_end_borrow_stream_class_default_clock_class_const(
                const struct bt_message *msg)
index cbc36ae0cc843167083859e279910ee6ea16f435..c90476db06919e671a9e484e6bbf1a6f95112cde 100644 (file)
@@ -13,7 +13,6 @@
 #include "compat/compiler.h"
 #include "lib/trace-ir/stream.h"
 #include "lib/trace-ir/clock-snapshot.h"
-#include "common/assert.h"
 
 #include "message.h"
 
index 19635a6acbbdb6245dcdec15829af14f225a6f6a..57bf7ef07cfa6de2746b1302121fb6b47106ecb8 100644 (file)
@@ -16,6 +16,8 @@
 #include "common/assert.h"
 #include "compat/compiler.h"
 #include "common/common.h"
+#include "lib/func-status.h"
+#include "lib/graph/component-class.h"
 #include "lib/value.h"
 #include "component-descriptor-set.h"
 #include "lib/integer-range-set.h"
@@ -176,6 +178,7 @@ end:
  * When any component descriptor does not support MIP version 0, this
  * function returns `BT_FUNC_STATUS_NO_MATCH`.
  */
+BT_EXPORT
 enum bt_get_greatest_operative_mip_version_status
 bt_get_greatest_operative_mip_version(
                const struct bt_component_descriptor_set *comp_descr_set,
@@ -218,6 +221,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 uint64_t bt_get_maximal_mip_version(void)
 {
        return 0;
index 291615a81955e98a15a0e9c5a08f5df1880332b4..596510ce2bfe2b2e5af66abbf1f68fa76cc7a111 100644 (file)
@@ -34,7 +34,6 @@ void destroy_port(struct bt_object *obj)
        g_free(port);
 }
 
-BT_HIDDEN
 struct bt_port *bt_port_create(struct bt_component *parent_component,
                enum bt_port_type type, const char *name, void *user_data)
 {
@@ -71,18 +70,21 @@ end:
        return port;
 }
 
+BT_EXPORT
 const char *bt_port_get_name(const struct bt_port *port)
 {
        BT_ASSERT_PRE_DEV_PORT_NON_NULL(port);
        return port->name->str;
 }
 
+BT_EXPORT
 enum bt_port_type bt_port_get_type(const struct bt_port *port)
 {
        BT_ASSERT_PRE_DEV_PORT_NON_NULL(port);
        return port->type;
 }
 
+BT_EXPORT
 const struct bt_connection *bt_port_borrow_connection_const(
                const struct bt_port *port)
 {
@@ -90,6 +92,7 @@ const struct bt_connection *bt_port_borrow_connection_const(
        return port->connection;
 }
 
+BT_EXPORT
 const struct bt_component *bt_port_borrow_component_const(
                const struct bt_port *port)
 {
@@ -97,6 +100,7 @@ const struct bt_component *bt_port_borrow_component_const(
        return bt_port_borrow_component_inline(port);
 }
 
+BT_EXPORT
 struct bt_self_component *bt_self_component_port_borrow_component(
                struct bt_self_component_port *port)
 {
@@ -104,7 +108,6 @@ struct bt_self_component *bt_self_component_port_borrow_component(
        return (void *) bt_object_borrow_parent((void *) port);
 }
 
-BT_HIDDEN
 void bt_port_set_connection(struct bt_port *port,
                struct bt_connection *connection)
 {
@@ -118,43 +121,51 @@ void bt_port_set_connection(struct bt_port *port,
                connection);
 }
 
+BT_EXPORT
 bt_bool bt_port_is_connected(const struct bt_port *port)
 {
        BT_ASSERT_PRE_DEV_PORT_NON_NULL(port);
        return port->connection ? BT_TRUE : BT_FALSE;
 }
 
+BT_EXPORT
 void *bt_self_component_port_get_data(const struct bt_self_component_port *port)
 {
        BT_ASSERT_PRE_DEV_PORT_NON_NULL(port);
        return ((struct bt_port *) port)->user_data;
 }
 
+BT_EXPORT
 void bt_port_get_ref(const struct bt_port *port)
 {
        bt_object_get_ref(port);
 }
 
+BT_EXPORT
 void bt_port_put_ref(const struct bt_port *port)
 {
        bt_object_put_ref(port);
 }
 
+BT_EXPORT
 void bt_port_input_get_ref(const struct bt_port_input *port_input)
 {
        bt_object_get_ref(port_input);
 }
 
+BT_EXPORT
 void bt_port_input_put_ref(const struct bt_port_input *port_input)
 {
        bt_object_put_ref(port_input);
 }
 
+BT_EXPORT
 void bt_port_output_get_ref(const struct bt_port_output *port_output)
 {
        bt_object_get_ref(port_output);
 }
 
+BT_EXPORT
 void bt_port_output_put_ref(const struct bt_port_output *port_output)
 {
        bt_object_put_ref(port_output);
index 674ca5bd309cf2bf88536be67f85070b40162d7e..4a84db1c6803911bd1aca2d6f5bad9d98ef844a3 100644 (file)
@@ -9,7 +9,6 @@
 #define BABELTRACE_GRAPH_PORT_INTERNAL_H
 
 #include <babeltrace2/graph/port.h>
-#include "common/macros.h"
 
 struct bt_port {
        struct bt_object base;
@@ -21,11 +20,9 @@ struct bt_port {
 
 struct bt_component;
 
-BT_HIDDEN
 struct bt_port *bt_port_create(struct bt_component *parent_component,
                enum bt_port_type type, const char *name, void *user_data);
 
-BT_HIDDEN
 void bt_port_set_connection(struct bt_port *port,
                struct bt_connection *connection);
 
index e68d1e978470cb7c535546e8fe7549d373d91e4d..e331fcd431b13daa7a196c2115b83a3ca1044718 100644 (file)
@@ -7,7 +7,6 @@
 #define BT_LOG_TAG "LIB/QUERY-EXECUTOR"
 #include "lib/logging.h"
 
-#include "common/assert.h"
 #include "common/common.h"
 #include "lib/assert-cond.h"
 #include <babeltrace2/graph/query-executor.h>
@@ -50,6 +49,7 @@ void bt_query_executor_destroy(struct bt_object *obj)
        g_free(query_exec);
 }
 
+BT_EXPORT
 struct bt_query_executor *bt_query_executor_create_with_method_data(
                const bt_component_class *comp_cls, const char *object,
                const bt_value *params, void *method_data)
@@ -116,6 +116,7 @@ end:
        return (void *) query_exec;
 }
 
+BT_EXPORT
 struct bt_query_executor *bt_query_executor_create(
                const bt_component_class *comp_cls, const char *object,
                const bt_value *params)
@@ -125,6 +126,7 @@ struct bt_query_executor *bt_query_executor_create(
                object, params, NULL);
 }
 
+BT_EXPORT
 enum bt_query_executor_query_status bt_query_executor_query(
                struct bt_query_executor *query_exec,
                const struct bt_value **user_result)
@@ -242,6 +244,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_query_executor_add_interrupter_status bt_query_executor_add_interrupter(
                struct bt_query_executor *query_exec,
                const struct bt_interrupter *intr)
@@ -257,6 +260,7 @@ enum bt_query_executor_add_interrupter_status bt_query_executor_add_interrupter(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 bt_bool bt_query_executor_is_interrupted(const struct bt_query_executor *query_exec)
 {
        BT_ASSERT_PRE_QUERY_EXEC_NON_NULL(query_exec);
@@ -264,6 +268,7 @@ bt_bool bt_query_executor_is_interrupted(const struct bt_query_executor *query_e
                query_exec->interrupters);
 }
 
+BT_EXPORT
 struct bt_interrupter *bt_query_executor_borrow_default_interrupter(
                struct bt_query_executor *query_exec)
 {
@@ -271,6 +276,7 @@ struct bt_interrupter *bt_query_executor_borrow_default_interrupter(
        return query_exec->default_interrupter;
 }
 
+BT_EXPORT
 enum bt_query_executor_set_logging_level_status
 bt_query_executor_set_logging_level(struct bt_query_executor *query_exec,
                enum bt_logging_level log_level)
@@ -280,6 +286,7 @@ bt_query_executor_set_logging_level(struct bt_query_executor *query_exec,
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_logging_level bt_query_executor_get_logging_level(
                const struct bt_query_executor *query_exec)
 {
@@ -287,11 +294,13 @@ enum bt_logging_level bt_query_executor_get_logging_level(
        return query_exec->log_level;
 }
 
+BT_EXPORT
 void bt_query_executor_get_ref(const struct bt_query_executor *query_executor)
 {
        bt_object_get_ref(query_executor);
 }
 
+BT_EXPORT
 void bt_query_executor_put_ref(const struct bt_query_executor *query_executor)
 {
        bt_object_put_ref(query_executor);
index 2f2fea7e93db1e8a600eebffee5b0cc02055c2c2..1e27e1895655f15bae8dad1a78406bcdfc07076c 100644 (file)
@@ -16,6 +16,7 @@
 #include "func-status.h"
 #include "integer-range-set.h"
 
+BT_EXPORT
 uint64_t bt_integer_range_unsigned_get_lower(
                const struct bt_integer_range_unsigned *u_range)
 {
@@ -25,6 +26,7 @@ uint64_t bt_integer_range_unsigned_get_lower(
        return range->lower.u;
 }
 
+BT_EXPORT
 uint64_t bt_integer_range_unsigned_get_upper(
                const struct bt_integer_range_unsigned *u_range)
 {
@@ -34,6 +36,7 @@ uint64_t bt_integer_range_unsigned_get_upper(
        return range->upper.u;
 }
 
+BT_EXPORT
 int64_t bt_integer_range_signed_get_lower(
                const struct bt_integer_range_signed *i_range)
 {
@@ -43,6 +46,7 @@ int64_t bt_integer_range_signed_get_lower(
        return range->lower.i;
 }
 
+BT_EXPORT
 int64_t bt_integer_range_signed_get_upper(
                const struct bt_integer_range_signed *i_range)
 {
@@ -60,6 +64,7 @@ bool compare_ranges(const struct bt_integer_range *range_a,
                range_a->upper.u == range_b->upper.u;
 }
 
+BT_EXPORT
 bt_bool bt_integer_range_unsigned_is_equal(
                const struct bt_integer_range_unsigned *range_a,
                const struct bt_integer_range_unsigned *range_b)
@@ -72,6 +77,7 @@ bt_bool bt_integer_range_unsigned_is_equal(
                (const void *) range_b);
 }
 
+BT_EXPORT
 bt_bool bt_integer_range_signed_is_equal(
                const struct bt_integer_range_signed *range_a,
                const struct bt_integer_range_signed *range_b)
@@ -84,6 +90,7 @@ bt_bool bt_integer_range_signed_is_equal(
                (const void *) range_b);
 }
 
+BT_EXPORT
 uint64_t bt_integer_range_set_get_range_count(
                const bt_integer_range_set *range_set)
 {
@@ -91,6 +98,7 @@ uint64_t bt_integer_range_set_get_range_count(
        return (uint64_t) range_set->ranges->len;
 }
 
+BT_EXPORT
 const struct bt_integer_range_unsigned *
 bt_integer_range_set_unsigned_borrow_range_by_index_const(
                const bt_integer_range_set_unsigned *u_range_set,
@@ -105,6 +113,7 @@ bt_integer_range_set_unsigned_borrow_range_by_index_const(
                index);
 }
 
+BT_EXPORT
 const struct bt_integer_range_signed *
 bt_integer_range_set_signed_borrow_range_by_index_const(
                const bt_integer_range_set_signed *i_range_set, uint64_t index)
@@ -166,6 +175,7 @@ end:
        return range_set;
 }
 
+BT_EXPORT
 struct bt_integer_range_set_unsigned *bt_integer_range_set_unsigned_create(void)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -173,6 +183,7 @@ struct bt_integer_range_set_unsigned *bt_integer_range_set_unsigned_create(void)
        return (void *) create_range_set();
 }
 
+BT_EXPORT
 struct bt_integer_range_set_signed *bt_integer_range_set_signed_create(void)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -198,6 +209,7 @@ void add_range_to_range_set(struct bt_integer_range_set *range_set,
                "upper-unsigned=%" PRIu64, range_set, u_lower, u_upper);
 }
 
+BT_EXPORT
 enum bt_integer_range_set_add_range_status
 bt_integer_range_set_unsigned_add_range(
                struct bt_integer_range_set_unsigned *range_set,
@@ -211,6 +223,7 @@ bt_integer_range_set_unsigned_add_range(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_integer_range_set_add_range_status
 bt_integer_range_set_signed_add_range(
                struct bt_integer_range_set_signed *range_set,
@@ -225,7 +238,6 @@ bt_integer_range_set_signed_add_range(
        return BT_FUNC_STATUS_OK;
 }
 
-BT_HIDDEN
 void _bt_integer_range_set_freeze(const struct bt_integer_range_set *range_set)
 {
        BT_ASSERT(range_set);
@@ -233,7 +245,6 @@ void _bt_integer_range_set_freeze(const struct bt_integer_range_set *range_set)
        ((struct bt_integer_range_set *) range_set)->frozen = true;
 }
 
-BT_HIDDEN
 bool bt_integer_range_set_unsigned_has_overlaps(
                const struct bt_integer_range_set *range_set)
 {
@@ -267,7 +278,6 @@ end:
        return has_overlap;
 }
 
-BT_HIDDEN
 bool bt_integer_range_set_signed_has_overlaps(
                const struct bt_integer_range_set *range_set)
 {
@@ -351,6 +361,7 @@ end:
        return is_equal;
 }
 
+BT_EXPORT
 bt_bool bt_integer_range_set_unsigned_is_equal(
                const struct bt_integer_range_set_unsigned *range_set_a,
                const struct bt_integer_range_set_unsigned *range_set_b)
@@ -363,6 +374,7 @@ bt_bool bt_integer_range_set_unsigned_is_equal(
                (const void *) range_set_b);
 }
 
+BT_EXPORT
 bt_bool bt_integer_range_set_signed_is_equal(
                const struct bt_integer_range_set_signed *range_set_a,
                const struct bt_integer_range_set_signed *range_set_b)
@@ -375,24 +387,28 @@ bt_bool bt_integer_range_set_signed_is_equal(
                (const void *) range_set_b);
 }
 
+BT_EXPORT
 void bt_integer_range_set_unsigned_get_ref(
                const struct bt_integer_range_set_unsigned *range_set)
 {
        bt_object_get_ref(range_set);
 }
 
+BT_EXPORT
 void bt_integer_range_set_unsigned_put_ref(
                const struct bt_integer_range_set_unsigned *range_set)
 {
        bt_object_put_ref(range_set);
 }
 
+BT_EXPORT
 void bt_integer_range_set_signed_get_ref(
                const struct bt_integer_range_set_signed *range_set)
 {
        bt_object_get_ref(range_set);
 }
 
+BT_EXPORT
 void bt_integer_range_set_signed_put_ref(
                const struct bt_integer_range_set_signed *range_set)
 {
index 666f82e6e042c6b1450393916b973eb55955e8bf..c53a784976b92bdcc9181ba65cd8008b9b44c6a2 100644 (file)
@@ -17,7 +17,7 @@
 #include "object.h"
 
 #define BT_INTEGER_RANGE_SET_RANGE_AT_INDEX(_rs, _index)               \
-       (&g_array_index((_rs)->ranges, struct bt_integer_range, (_index)))
+       (&bt_g_array_index((_rs)->ranges, struct bt_integer_range, (_index)))
 
 struct bt_integer_range {
        union {
@@ -39,7 +39,6 @@ struct bt_integer_range_set {
        bool frozen;
 };
 
-BT_HIDDEN
 void _bt_integer_range_set_freeze(const struct bt_integer_range_set *range_set);
 
 #ifdef BT_DEV_MODE
@@ -48,11 +47,9 @@ void _bt_integer_range_set_freeze(const struct bt_integer_range_set *range_set);
 # define bt_integer_range_set_freeze(_sc)
 #endif
 
-BT_HIDDEN
 bool bt_integer_range_set_unsigned_has_overlaps(
                const struct bt_integer_range_set *range_set);
 
-BT_HIDDEN
 bool bt_integer_range_set_signed_has_overlaps(
                const struct bt_integer_range_set *range_set);
 
index df73244b22e969978054b679d7330c97357d1f6e..a22e000f0ee3f56ca0b6140e1446768c5cc29600 100644 (file)
 #include <babeltrace2/babeltrace.h>
 
 #include "logging.h"
-#include "assert-cond.h"
 #include "value.h"
 #include "integer-range-set.h"
 #include "object-pool.h"
 #include "graph/interrupter.h"
 #include "graph/component-class.h"
-#include "graph/component-filter.h"
 #include "graph/component.h"
-#include "graph/component-sink.h"
-#include "graph/component-source.h"
 #include "graph/connection.h"
 #include "graph/graph.h"
+#include "graph/iterator.h"
 #include "graph/message/discarded-items.h"
 #include "graph/message/event.h"
-#include "graph/message/iterator.h"
 #include "graph/message/message.h"
-#include "graph/message/message-iterator-inactivity.h"
 #include "graph/message/packet.h"
 #include "graph/message/stream.h"
 #include "graph/port.h"
@@ -55,7 +50,6 @@
 #include "trace-ir/stream.h"
 #include "trace-ir/trace-class.h"
 #include "trace-ir/trace.h"
-#include "trace-ir/utils.h"
 #include "error.h"
 
 #define LIB_LOGGING_BUF_SIZE   (4096 * 4)
@@ -114,8 +108,8 @@ static inline void format_clock_snapshot(char **buf_ch, bool extended,
 static inline void format_field_path(char **buf_ch, bool extended,
                const char *prefix, const struct bt_field_path *field_path);
 
-static inline void format_object(char **buf_ch, bool extended,
-               const char *prefix, const struct bt_object *obj)
+static inline void format_object(char **buf_ch, const char *prefix,
+               const struct bt_object *obj)
 {
        BUF_APPEND(", %sref-count=%llu", prefix, obj->ref_count);
 }
@@ -125,8 +119,8 @@ static inline void format_uuid(char **buf_ch, bt_uuid uuid)
        BUF_APPEND("\"" BT_UUID_FMT "\"", BT_UUID_FMT_VALUES(uuid));
 }
 
-static inline void format_object_pool(char **buf_ch, bool extended,
-               const char *prefix, const struct bt_object_pool *pool)
+static inline void format_object_pool(char **buf_ch, const char *prefix,
+               const struct bt_object_pool *pool)
 {
        BUF_APPEND(", %ssize=%zu", PRFIELD(pool->size));
 
@@ -135,8 +129,7 @@ static inline void format_object_pool(char **buf_ch, bool extended,
        }
 }
 
-static inline void format_integer_field_class(char **buf_ch,
-               bool extended, const char *prefix,
+static inline void format_integer_field_class(char **buf_ch, const char *prefix,
                const struct bt_field_class *field_class)
 {
        const struct bt_field_class_integer *int_fc =
@@ -147,8 +140,7 @@ static inline void format_integer_field_class(char **buf_ch,
                PRFIELD(bt_common_field_class_integer_preferred_display_base_string(int_fc->base)));
 }
 
-static inline void format_array_field_class(char **buf_ch,
-               bool extended, const char *prefix,
+static inline void format_array_field_class(char **buf_ch, const char *prefix,
                const struct bt_field_class *field_class)
 {
        const struct bt_field_class_array *array_fc =
@@ -187,7 +179,7 @@ static inline void format_field_class(char **buf_ch, bool extended,
        case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER:
        case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER:
        {
-               format_integer_field_class(buf_ch, extended, prefix, field_class);
+               format_integer_field_class(buf_ch, prefix, field_class);
                break;
        }
        case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
@@ -196,7 +188,7 @@ static inline void format_field_class(char **buf_ch, bool extended,
                const struct bt_field_class_enumeration *enum_fc =
                        (const void *) field_class;
 
-               format_integer_field_class(buf_ch, extended, prefix, field_class);
+               format_integer_field_class(buf_ch, prefix, field_class);
                BUF_APPEND(", %smapping-count=%u",
                        PRFIELD(enum_fc->mappings->len));
                break;
@@ -218,7 +210,7 @@ static inline void format_field_class(char **buf_ch, bool extended,
                const struct bt_field_class_array_static *array_fc =
                        (const void *) field_class;
 
-               format_array_field_class(buf_ch, extended, prefix, field_class);
+               format_array_field_class(buf_ch, prefix, field_class);
                BUF_APPEND(", %slength=%" PRIu64, PRFIELD(array_fc->length));
                break;
        }
@@ -228,7 +220,7 @@ static inline void format_field_class(char **buf_ch, bool extended,
                const struct bt_field_class_array_dynamic *array_fc =
                        (const void *) field_class;
 
-               format_array_field_class(buf_ch, extended, prefix, field_class);
+               format_array_field_class(buf_ch, prefix, field_class);
 
                if (array_fc->length_fc) {
                        SET_TMP_PREFIX("length-fc-");
@@ -588,7 +580,7 @@ static inline void format_stream_class(char **buf_ch, bool extended,
        SET_TMP_PREFIX("trace-class-");
        format_trace_class(buf_ch, false, tmp_prefix, trace_class);
        SET_TMP_PREFIX("pcf-pool-");
-       format_object_pool(buf_ch, extended, tmp_prefix,
+       format_object_pool(buf_ch, tmp_prefix,
                &stream_class->packet_context_field_pool);
 }
 
@@ -644,8 +636,7 @@ static inline void format_event_class(char **buf_ch, bool extended,
        SET_TMP_PREFIX("trace-class-");
        format_trace_class(buf_ch, false, tmp_prefix, trace_class);
        SET_TMP_PREFIX("event-pool-");
-       format_object_pool(buf_ch, extended, tmp_prefix,
-               &event_class->event_pool);
+       format_object_pool(buf_ch, tmp_prefix, &event_class->event_pool);
 }
 
 static inline void format_stream(char **buf_ch, bool extended,
@@ -688,7 +679,7 @@ static inline void format_stream(char **buf_ch, bool extended,
        }
 
        SET_TMP_PREFIX("packet-pool-");
-       format_object_pool(buf_ch, extended, tmp_prefix, &stream->packet_pool);
+       format_object_pool(buf_ch, tmp_prefix, &stream->packet_pool);
 }
 
 static inline void format_packet(char **buf_ch, bool extended,
@@ -816,8 +807,7 @@ static inline void format_clock_class(char **buf_ch, bool extended,
                PRFIELD(clock_class->base_offset.value_ns));
 
        SET_TMP_PREFIX("cs-pool-");
-       format_object_pool(buf_ch, extended, tmp_prefix,
-               &clock_class->cs_pool);
+       format_object_pool(buf_ch, tmp_prefix, &clock_class->cs_pool);
 }
 
 static inline void format_clock_snapshot(char **buf_ch, bool extended,
@@ -843,8 +833,8 @@ static inline void format_clock_snapshot(char **buf_ch, bool extended,
        }
 }
 
-static inline void format_interrupter(char **buf_ch, bool extended,
-               const char *prefix, const struct bt_interrupter *intr)
+static inline void format_interrupter(char **buf_ch, const char *prefix,
+               const struct bt_interrupter *intr)
 {
        BUF_APPEND(", %sis-set=%d", PRFIELD(intr->is_set));
 }
@@ -1173,21 +1163,11 @@ static inline void format_graph(char **buf_ch, bool extended,
        }
 
        SET_TMP_PREFIX("en-pool-");
-       format_object_pool(buf_ch, extended, tmp_prefix,
-               &graph->event_msg_pool);
+       format_object_pool(buf_ch, tmp_prefix, &graph->event_msg_pool);
        SET_TMP_PREFIX("pbn-pool-");
-       format_object_pool(buf_ch, extended, tmp_prefix,
-               &graph->packet_begin_msg_pool);
+       format_object_pool(buf_ch, tmp_prefix, &graph->packet_begin_msg_pool);
        SET_TMP_PREFIX("pen-pool-");
-       format_object_pool(buf_ch, extended, tmp_prefix,
-               &graph->packet_end_msg_pool);
-}
-
-static inline void format_message_iterator_class(char **buf_ch,
-               bool extended, const char *prefix,
-               const struct bt_message_iterator_class *iterator_class)
-{
-       /* Empty, the address is automatically printed. */
+       format_object_pool(buf_ch, tmp_prefix, &graph->packet_end_msg_pool);
 }
 
 static inline void format_message_iterator(char **buf_ch,
@@ -1342,8 +1322,10 @@ static inline void format_error_cause(char **buf_ch, bool extended,
        }
 }
 
-static inline void handle_conversion_specifier_bt(void *priv_data,
-               char **buf_ch, size_t avail_size,
+static inline void handle_conversion_specifier_bt(
+               void *priv_data __attribute__((unused)),
+               char **buf_ch,
+               size_t avail_size __attribute__((unused)),
                const char **out_fmt_ch, va_list *args)
 {
        const char *fmt_ch = *out_fmt_ch;
@@ -1440,7 +1422,7 @@ static inline void handle_conversion_specifier_bt(void *priv_data,
                format_message(buf_ch, extended, prefix, obj);
                break;
        case 'I':
-               format_message_iterator_class(buf_ch, extended, prefix, obj);
+               /* Empty, the address is automatically printed. */
                break;
        case 'i':
                format_message_iterator(buf_ch, extended, prefix, obj);
@@ -1464,13 +1446,13 @@ static inline void handle_conversion_specifier_bt(void *priv_data,
                format_graph(buf_ch, extended, prefix, obj);
                break;
        case 'z':
-               format_interrupter(buf_ch, extended, prefix, obj);
+               format_interrupter(buf_ch, prefix, obj);
                break;
        case 'o':
-               format_object_pool(buf_ch, extended, prefix, obj);
+               format_object_pool(buf_ch, prefix, obj);
                break;
        case 'O':
-               format_object(buf_ch, extended, prefix, obj);
+               format_object(buf_ch, prefix, obj);
                break;
        case 'r':
                format_error_cause(buf_ch, extended, prefix, obj);
@@ -1484,26 +1466,47 @@ update_fmt:
        *out_fmt_ch = fmt_ch;
 }
 
-void bt_lib_log_v(const char *func, const char *file, unsigned line,
+/*
+ * This function would normally not be BT_EXPORTed, but it is used by the
+ * Python plugin provider, which is conceptually part of libbabeltrace2, but
+ * implemented as a separate shared object, for modularity.  It is therefore
+ * exposed, but not part of the public ABI.
+ */
+BT_EXPORT
+void bt_lib_log_v(const char *file, const char *func, unsigned line,
                int lvl, const char *tag, const char *fmt, va_list *args)
 {
        BT_ASSERT(fmt);
        bt_common_custom_vsnprintf(lib_logging_buf, LIB_LOGGING_BUF_SIZE, '!',
                handle_conversion_specifier_bt, NULL, fmt, args);
-       _bt_log_write_d(func, file, line, lvl, tag, "%s", lib_logging_buf);
+       bt_log_write(file, func, line, lvl, tag, lib_logging_buf);
 }
 
-void bt_lib_log(const char *func, const char *file, unsigned line,
+/*
+ * This function would normally not be BT_EXPORTed, but it is used by the
+ * Python plugin provider, which is conceptually part of libbabeltrace2, but
+ * implemented as a separate shared object, for modularity.  It is therefore
+ * exposed, but not part of the public ABI.
+ */
+BT_EXPORT
+void bt_lib_log(const char *file, const char *func, unsigned line,
                int lvl, const char *tag, const char *fmt, ...)
 {
        va_list args;
 
        BT_ASSERT(fmt);
        va_start(args, fmt);
-       bt_lib_log_v(func, file, line, lvl, tag, fmt, &args);
+       bt_lib_log_v(file, func, line, lvl, tag, fmt, &args);
        va_end(args);
 }
 
+/*
+ * This function would normally not be BT_EXPORTed, but it is used by the
+ * Python plugin provider, which is conceptually part of libbabeltrace2, but
+ * implemented as a separate shared object, for modularity.  It is therefore
+ * exposed, but not part of the ABI.
+ */
+BT_EXPORT
 void bt_lib_maybe_log_and_append_cause(const char *func, const char *file,
                unsigned line, int lvl, const char *tag,
                const char *fmt, ...)
@@ -1519,8 +1522,7 @@ void bt_lib_maybe_log_and_append_cause(const char *func, const char *file,
 
        /* Log conditionally, but always append the error cause */
        if (BT_LOG_ON(lvl)) {
-               _bt_log_write_d(func, file, line, lvl, tag, "%s",
-                       lib_logging_buf);
+               bt_log_write(file, func, line, lvl, tag, lib_logging_buf);
        }
 
        status = bt_current_thread_error_append_cause_from_unknown(
index 5502610fac09c31221e46499f92967cfa1335c3a..1e1cdf746dc44926c0a1147bb7f136789a3cbfdf 100644 (file)
@@ -4,7 +4,6 @@
  * Copyright 2017-2018 Philippe Proulx <pproulx@efficios.com>
  */
 
-#include <stdlib.h>
 #include <babeltrace2/babeltrace.h>
 
 #define BT_LOG_TAG "LIB/LOGGING"
  * `LIBBABELTRACE2_INIT_LOG_LEVEL` environment variable to enable
  * logging.
  */
+BT_EXPORT
 int bt_lib_log_level = BT_LOG_NONE;
 
+BT_EXPORT
 enum bt_logging_level bt_logging_get_minimal_level(void)
 {
-       return BT_MINIMAL_LOG_LEVEL;
+       return BT_LOG_MINIMAL_LEVEL;
 }
 
+BT_EXPORT
 enum bt_logging_level bt_logging_get_global_level(void)
 {
        return bt_lib_log_level;
 }
 
+BT_EXPORT
 void bt_logging_set_global_level(enum bt_logging_level log_level)
 {
        bt_lib_log_level = log_level;
@@ -46,7 +49,7 @@ void __attribute__((constructor)) bt_logging_ctor(void)
                bt_version_get_development_stage() : "";
 
        bt_logging_set_global_level(
-               bt_log_get_level_from_env("LIBBABELTRACE2_INIT_LOG_LEVEL"));
+               (int) bt_log_get_level_from_env("LIBBABELTRACE2_INIT_LOG_LEVEL"));
        BT_LOGI("Babeltrace %u.%u.%u%s library loaded: "
                "major=%u, minor=%u, patch=%u, extra=\"%s\"",
                bt_version_get_major(), bt_version_get_minor(),
index 8462e47cb782787868d02ad9f04c286cb7f8bbb7..aa4fc5e501e7748ae6b578d2bf0268907b3aed58 100644 (file)
@@ -7,7 +7,6 @@
 #ifndef BABELTRACE_LIB_LOGGING_INTERNAL_H
 #define BABELTRACE_LIB_LOGGING_INTERNAL_H
 
-#include "common/macros.h"
 #include <stdarg.h>
 
 #ifndef BT_LOG_TAG
@@ -26,7 +25,7 @@ int bt_lib_log_level;
 #define BT_LIB_LOG(_lvl, _fmt, ...)                                    \
        do {                                                            \
                if (BT_LOG_ON(_lvl)) {                                  \
-                       bt_lib_log(_BT_LOG_SRCLOC_FUNCTION, __FILE__,   \
+                       bt_lib_log(__FILE__, __func__,                  \
                                __LINE__, _lvl, _BT_LOG_TAG,            \
                                (_fmt), ##__VA_ARGS__);                 \
                }                                                       \
@@ -48,22 +47,17 @@ int bt_lib_log_level;
  *
  * Use one of the BT_LIB_LOG*() macros above instead of calling this
  * function directly.
- *
- * This function would normally be BT_HIDDEN, but it is used by the Python
- * plugin provider, which is conceptually part of libbabeltrace2, but
- * implemented as a separate shared object, for modularity.  It is therefore
- * exposed, but not part of the public ABI.
  */
-void bt_lib_log(const char *func, const char *file, unsigned line,
+void bt_lib_log(const char *file, const char *func, unsigned line,
                int lvl, const char *tag, const char *fmt, ...);
 
-void bt_lib_log_v(const char *func, const char *file, unsigned line,
+void bt_lib_log_v(const char *file, const char *func, unsigned line,
                int lvl, const char *tag, const char *fmt, va_list *args);
 
 #define BT_LIB_LOG_AND_APPEND(_lvl, _fmt, ...)                         \
        do {                                                            \
                bt_lib_maybe_log_and_append_cause(                      \
-                       _BT_LOG_SRCLOC_FUNCTION, __FILE__,              \
+                       __func__, __FILE__,                             \
                        __LINE__, _lvl, _BT_LOG_TAG,                    \
                        (_fmt), ##__VA_ARGS__);                         \
        } while (0)
@@ -85,11 +79,6 @@ void bt_lib_log_v(const char *func, const char *file, unsigned line,
  *
  * Use one of the BT_LIB_LOG*_APPEND_CAUSE() macros above instead of
  * calling this function directly.
- *
- * This function would normally be BT_HIDDEN, but it is used by the Python
- * plugin provider, which is conceptually part of libbabeltrace2, but
- * implemented as a separate shared object, for modularity.  It is therefore
- * exposed, but not part of the ABI.
  */
 void bt_lib_maybe_log_and_append_cause(const char *func, const char *file,
                unsigned line, int lvl, const char *tag,
index 3b48dee9dd71c1749376c33ae88a9b93bebae5e6..cde131345442a2290c85f299393ddde53fcaab55 100644 (file)
@@ -9,7 +9,6 @@
 
 #include <stdint.h>
 #include "common/assert.h"
-#include "lib/assert-cond.h"
 #include "lib/object-pool.h"
 
 int bt_object_pool_initialize(struct bt_object_pool *pool,
index 5f65c59dcdc945d4f3e11446ef62c10886efd410..e0501daf70b47a075695c4939f3e8b28965b4708 100644 (file)
@@ -72,7 +72,6 @@ struct bt_object_pool {
 /*
  * Initializes an object pool which is already allocated.
  */
-BT_HIDDEN
 int bt_object_pool_initialize(struct bt_object_pool *pool,
                bt_object_pool_new_object_func new_object_func,
                bt_object_pool_destroy_object_func destroy_object_func,
@@ -81,7 +80,6 @@ int bt_object_pool_initialize(struct bt_object_pool *pool,
 /*
  * Finalizes an object pool without deallocating it.
  */
-BT_HIDDEN
 void bt_object_pool_finalize(struct bt_object_pool *pool);
 
 /*
index 2fbe7f02a2d275f0cdcaa9f373ff008e31dbd0b6..6531394a831484c13ce9a521e31a4e56bd89e61b 100644 (file)
@@ -8,7 +8,6 @@
 #ifndef BABELTRACE_OBJECT_INTERNAL_H
 #define BABELTRACE_OBJECT_INTERNAL_H
 
-#include "common/macros.h"
 #include "common/assert.h"
 #include <stdbool.h>
 
diff --git a/src/lib/plugin/Makefile.am b/src/lib/plugin/Makefile.am
deleted file mode 100644 (file)
index e25fd70..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-AM_CPPFLAGS += '-DBABELTRACE_PLUGIN_PROVIDERS_DIR="$(BABELTRACE_PLUGIN_PROVIDERS_DIR)"'
-
-noinst_LTLIBRARIES = libplugin.la
-
-# Plug-in system library
-libplugin_la_SOURCES = \
-       plugin.c \
-       plugin.h \
-       plugin-so.c \
-       plugin-so.h
index 3017e6c0af167d5850b134cf6321f2e1c675b62c..698d03d0a0acb61ab667bcddcc6fb8b0a6d83697 100644 (file)
@@ -9,7 +9,6 @@
 #include "lib/logging.h"
 
 #include "common/assert.h"
-#include "lib/assert-cond.h"
 #include "compat/compiler.h"
 #include <babeltrace2/plugin/plugin-dev.h>
 #include "lib/graph/component-class.h"
@@ -460,7 +459,7 @@ int bt_plugin_so_init(struct bt_plugin *plugin,
                /* Find the corresponding component class descriptor entry */
                for (i = 0; i < comp_class_full_descriptors->len; i++) {
                        struct comp_class_full_descriptor *cc_full_descr =
-                               &g_array_index(comp_class_full_descriptors,
+                               &bt_g_array_index(comp_class_full_descriptors,
                                        struct comp_class_full_descriptor, i);
 
                        if (cur_cc_descr_attr->comp_class_descriptor !=
@@ -735,6 +734,12 @@ int bt_plugin_so_init(struct bt_plugin *plugin,
                                status = init_status;
                                goto end;
                        } else {
+                               /*
+                                * Since we don't return an error,
+                                * there's no way to communicate this
+                                * error to the caller.
+                                */
+                               bt_current_thread_clear_error();
                                BT_LIB_LOGW(
                                        "User's plugin initialization function failed: "
                                        "status=%s",
@@ -751,7 +756,7 @@ int bt_plugin_so_init(struct bt_plugin *plugin,
        /* Add described component classes to plugin */
        for (i = 0; i < comp_class_full_descriptors->len; i++) {
                struct comp_class_full_descriptor *cc_full_descr =
-                       &g_array_index(comp_class_full_descriptors,
+                       &bt_g_array_index(comp_class_full_descriptors,
                                struct comp_class_full_descriptor, i);
                struct bt_component_class *comp_class = NULL;
                struct bt_component_class_source *src_comp_class = NULL;
@@ -1336,6 +1341,12 @@ int bt_plugin_so_create_all_from_sections(
                        /* Add to plugin set */
                        bt_plugin_set_add_plugin(*plugin_set_out, plugin);
                        BT_OBJECT_PUT_REF_AND_RESET(plugin);
+               } else if (status == BT_FUNC_STATUS_NOT_FOUND) {
+                       /*
+                        * There was an error initializing the plugin,
+                        * but `fail_on_load_error` is false.
+                        */
+                       BT_OBJECT_PUT_REF_AND_RESET(plugin);
                } else if (status < 0) {
                        /*
                         * bt_plugin_so_init() handles
@@ -1368,7 +1379,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 int bt_plugin_so_create_all_from_static(bool fail_on_load_error,
                struct bt_plugin_set **plugin_set_out)
 {
@@ -1405,7 +1415,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 int bt_plugin_so_create_all_from_file(const char *path,
                bool fail_on_load_error, struct bt_plugin_set **plugin_set_out)
 {
@@ -1664,7 +1673,7 @@ end:
 
 static
 void plugin_comp_class_destroy_listener(struct bt_component_class *comp_class,
-               void *data)
+               void *data __attribute__((unused)))
 {
        bt_list_del(&comp_class->node);
        BT_OBJECT_PUT_REF_AND_RESET(comp_class->so_handle);
@@ -1672,6 +1681,13 @@ void plugin_comp_class_destroy_listener(struct bt_component_class *comp_class,
                "comp-cls-addr=%p", comp_class);
 }
 
+/*
+ * This function would normally not be BT_EXPORTed, but it is used by the
+ * Python plugin provider, which is conceptually part of libbabeltrace2, but
+ * implemented as a separate shared object, for modularity.  It is therefore
+ * exposed, but not part of the public ABI.
+ */
+BT_EXPORT
 void bt_plugin_so_on_add_component_class(struct bt_plugin *plugin,
                struct bt_component_class *comp_class)
 {
index c341749bc9e38441f54ef4331f5b2054ab11354f..7d663e2061be3321cf09c43e91a25209abb94a16 100644 (file)
@@ -12,7 +12,6 @@
 #include <gmodule.h>
 #include <stdbool.h>
 #include <babeltrace2/types.h>
-#include "common/macros.h"
 
 struct bt_plugin;
 struct bt_component_class;
@@ -37,20 +36,12 @@ struct bt_plugin_so_spec_data {
        const struct __bt_plugin_descriptor_version *version;
 };
 
-BT_HIDDEN
 int bt_plugin_so_create_all_from_file(const char *path,
                bool fail_on_load_error, struct bt_plugin_set **plugin_set_out);
 
-BT_HIDDEN
 int bt_plugin_so_create_all_from_static(bool fail_on_load_error,
                struct bt_plugin_set **plugin_set_out);
 
-/*
- * This function would normally be BT_HIDDEN, but it is used by the Python
- * plugin provider, which is conceptually part of libbabeltrace2, but
- * implemented as a separate shared object, for modularity.  It is therefore
- * exposed, but not part of the public ABI.
- */
 void bt_plugin_so_on_add_component_class(struct bt_plugin *plugin,
                struct bt_component_class *comp_class);
 
index 434bb3ea5a0d43fa510d894c909a09f0ef8fba14..8ee34e7fa6c7c4be37c0909184d9913336f9e9cf 100644 (file)
@@ -24,7 +24,6 @@
 #include <stdbool.h>
 #include <stdlib.h>
 #include <stdint.h>
-#include <inttypes.h>
 #include <sys/stat.h>
 #include <ftw.h>
 #include <pthread.h>
@@ -153,12 +152,14 @@ void fini_python_plugin_provider(void) {
 }
 #endif
 
+BT_EXPORT
 uint64_t bt_plugin_set_get_plugin_count(const struct bt_plugin_set *plugin_set)
 {
        BT_ASSERT_PRE_DEV_PLUGIN_SET_NON_NULL(plugin_set);
        return (uint64_t) plugin_set->plugins->len;
 }
 
+BT_EXPORT
 const struct bt_plugin *bt_plugin_set_borrow_plugin_by_index_const(
                const struct bt_plugin_set *plugin_set, uint64_t index)
 {
@@ -167,6 +168,7 @@ const struct bt_plugin *bt_plugin_set_borrow_plugin_by_index_const(
        return g_ptr_array_index(plugin_set->plugins, index);
 }
 
+BT_EXPORT
 enum bt_plugin_find_all_from_static_status bt_plugin_find_all_from_static(
                bt_bool fail_on_load_error,
                const struct bt_plugin_set **plugin_set_out)
@@ -178,6 +180,7 @@ enum bt_plugin_find_all_from_static_status bt_plugin_find_all_from_static(
                (void *) plugin_set_out);
 }
 
+BT_EXPORT
 enum bt_plugin_find_all_from_file_status bt_plugin_find_all_from_file(
                const char *path, bt_bool fail_on_load_error,
                const struct bt_plugin_set **plugin_set_out)
@@ -186,7 +189,7 @@ enum bt_plugin_find_all_from_file_status bt_plugin_find_all_from_file(
 
        BT_ASSERT_PRE_NO_ERROR();
        BT_ASSERT_PRE_NON_NULL("path", path, "Path");
-       BT_ASSERT_PRE_PLUGIN_SET_OUT_NON_NULL(path);
+       BT_ASSERT_PRE_PLUGIN_SET_OUT_NON_NULL(plugin_set_out);
        BT_LOGI("Creating plugins from file: path=\"%s\"", path);
 
        /* Try shared object plugins */
@@ -256,6 +259,7 @@ void destroy_gstring(void *data)
        g_string_free(data, TRUE);
 }
 
+BT_EXPORT
 enum bt_plugin_find_all_status bt_plugin_find_all(bt_bool find_in_std_env_var,
                bt_bool find_in_user_dir, bt_bool find_in_sys_dir,
                bt_bool find_in_static, bt_bool fail_on_load_error,
@@ -443,6 +447,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_plugin_find_status bt_plugin_find(const char *plugin_name,
                bt_bool find_in_std_env_var, bt_bool find_in_user_dir,
                bt_bool find_in_sys_dir, bt_bool find_in_static,
@@ -509,7 +514,8 @@ static struct {
 
 static
 int nftw_append_all_from_dir(const char *file,
-               const struct stat *sb, int flag, struct FTW *s)
+               const struct stat *sb __attribute__((unused)),
+               int flag, struct FTW *s)
 {
        int ret = 0;
        const char *name = file + s->base;
@@ -647,6 +653,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_plugin_find_all_from_dir_status bt_plugin_find_all_from_dir(
                const char *path, bt_bool recurse, bt_bool fail_on_load_error,
                const struct bt_plugin_set **plugin_set_out)
@@ -705,30 +712,35 @@ end:
        return status;
 }
 
+BT_EXPORT
 const char *bt_plugin_get_name(const struct bt_plugin *plugin)
 {
        BT_ASSERT_PRE_DEV_PLUGIN_NON_NULL(plugin);
        return plugin->info.name_set ? plugin->info.name->str : NULL;
 }
 
+BT_EXPORT
 const char *bt_plugin_get_author(const struct bt_plugin *plugin)
 {
        BT_ASSERT_PRE_DEV_PLUGIN_NON_NULL(plugin);
        return plugin->info.author_set ? plugin->info.author->str : NULL;
 }
 
+BT_EXPORT
 const char *bt_plugin_get_license(const struct bt_plugin *plugin)
 {
        BT_ASSERT_PRE_DEV_PLUGIN_NON_NULL(plugin);
        return plugin->info.license_set ? plugin->info.license->str : NULL;
 }
 
+BT_EXPORT
 const char *bt_plugin_get_path(const struct bt_plugin *plugin)
 {
        BT_ASSERT_PRE_DEV_PLUGIN_NON_NULL(plugin);
        return plugin->info.path_set ? plugin->info.path->str : NULL;
 }
 
+BT_EXPORT
 const char *bt_plugin_get_description(const struct bt_plugin *plugin)
 {
        BT_ASSERT_PRE_DEV_PLUGIN_NON_NULL(plugin);
@@ -736,6 +748,7 @@ const char *bt_plugin_get_description(const struct bt_plugin *plugin)
                plugin->info.description->str : NULL;
 }
 
+BT_EXPORT
 enum bt_property_availability bt_plugin_get_version(const struct bt_plugin *plugin,
                unsigned int *major, unsigned int *minor, unsigned int *patch,
                const char **extra)
@@ -771,18 +784,21 @@ end:
        return avail;
 }
 
+BT_EXPORT
 uint64_t bt_plugin_get_source_component_class_count(const struct bt_plugin *plugin)
 {
        BT_ASSERT_PRE_DEV_PLUGIN_NON_NULL(plugin);
        return (uint64_t) plugin->src_comp_classes->len;
 }
 
+BT_EXPORT
 uint64_t bt_plugin_get_filter_component_class_count(const struct bt_plugin *plugin)
 {
        BT_ASSERT_PRE_DEV_PLUGIN_NON_NULL(plugin);
        return (uint64_t) plugin->flt_comp_classes->len;
 }
 
+BT_EXPORT
 uint64_t bt_plugin_get_sink_component_class_count(const struct bt_plugin *plugin)
 {
        BT_ASSERT_PRE_DEV_PLUGIN_NON_NULL(plugin);
@@ -799,6 +815,7 @@ struct bt_component_class *borrow_component_class_by_index(
        return g_ptr_array_index(comp_classes, index);
 }
 
+BT_EXPORT
 const struct bt_component_class_source *
 bt_plugin_borrow_source_component_class_by_index_const(
                const struct bt_plugin *plugin, uint64_t index)
@@ -807,6 +824,7 @@ bt_plugin_borrow_source_component_class_by_index_const(
                plugin->src_comp_classes, index);
 }
 
+BT_EXPORT
 const struct bt_component_class_filter *
 bt_plugin_borrow_filter_component_class_by_index_const(
                const struct bt_plugin *plugin, uint64_t index)
@@ -815,6 +833,7 @@ bt_plugin_borrow_filter_component_class_by_index_const(
                plugin->flt_comp_classes, index);
 }
 
+BT_EXPORT
 const struct bt_component_class_sink *
 bt_plugin_borrow_sink_component_class_by_index_const(
                const struct bt_plugin *plugin, uint64_t index)
@@ -851,6 +870,7 @@ struct bt_component_class *borrow_component_class_by_name(
        return comp_class;
 }
 
+BT_EXPORT
 const struct bt_component_class_source *
 bt_plugin_borrow_source_component_class_by_name_const(
                const struct bt_plugin *plugin, const char *name)
@@ -859,6 +879,7 @@ bt_plugin_borrow_source_component_class_by_name_const(
                plugin->src_comp_classes, name);
 }
 
+BT_EXPORT
 const struct bt_component_class_filter *
 bt_plugin_borrow_filter_component_class_by_name_const(
                const struct bt_plugin *plugin, const char *name)
@@ -867,6 +888,7 @@ bt_plugin_borrow_filter_component_class_by_name_const(
                plugin->flt_comp_classes, name);
 }
 
+BT_EXPORT
 const struct bt_component_class_sink *
 bt_plugin_borrow_sink_component_class_by_name_const(
                const struct bt_plugin *plugin, const char *name)
@@ -875,21 +897,25 @@ bt_plugin_borrow_sink_component_class_by_name_const(
                plugin->sink_comp_classes, name);
 }
 
+BT_EXPORT
 void bt_plugin_get_ref(const struct bt_plugin *plugin)
 {
        bt_object_get_ref(plugin);
 }
 
+BT_EXPORT
 void bt_plugin_put_ref(const struct bt_plugin *plugin)
 {
        bt_object_put_ref(plugin);
 }
 
+BT_EXPORT
 void bt_plugin_set_get_ref(const struct bt_plugin_set *plugin_set)
 {
        bt_object_get_ref(plugin_set);
 }
 
+BT_EXPORT
 void bt_plugin_set_put_ref(const struct bt_plugin_set *plugin_set)
 {
        bt_object_put_ref(plugin_set);
index 7ab4170dfa97e445dc8c87f0d811db22b8715135..60db3aaaf2d22b2ee69c676441dc04671815cf3d 100644 (file)
@@ -8,7 +8,6 @@
 #ifndef BABELTRACE_PLUGIN_PLUGIN_INTERNAL_H
 #define BABELTRACE_PLUGIN_PLUGIN_INTERNAL_H
 
-#include "common/macros.h"
 #include "common/common.h"
 #include "lib/graph/component-class.h"
 #include <babeltrace2/plugin/plugin-loading.h>
diff --git a/src/lib/prio-heap/Makefile.am b/src/lib/prio-heap/Makefile.am
deleted file mode 100644 (file)
index 5a2652c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libprio-heap.la
-
-libprio_heap_la_SOURCES = \
-       prio-heap.c \
-       prio-heap.h
diff --git a/src/lib/prio-heap/prio-heap.c b/src/lib/prio-heap/prio-heap.c
deleted file mode 100644 (file)
index d137aed..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Static-sized priority heap containing pointers. Based on CLRS,
- * chapter 6.
- */
-
-#include "common/macros.h"
-#include "common/assert.h"
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "prio-heap.h"
-
-#ifdef DEBUG_HEAP
-void check_heap(const struct ptr_heap *heap)
-{
-       size_t i;
-
-       if (!heap->len)
-               return;
-
-       for (i = 1; i < heap->len; i++)
-               BT_ASSERT_DBG(!heap->gt(heap->ptrs[i], heap->ptrs[0]));
-}
-#endif
-
-static
-size_t parent(size_t i)
-{
-       return (i - 1) >> 1;
-}
-
-static
-size_t left(size_t i)
-{
-       return (i << 1) + 1;
-}
-
-static
-size_t right(size_t i)
-{
-       return (i << 1) + 2;
-}
-
-/*
- * Copy of heap->ptrs pointer is invalid after heap_grow.
- */
-static
-int heap_grow(struct ptr_heap *heap, size_t new_len)
-{
-       void **new_ptrs;
-
-       if (G_LIKELY(heap->alloc_len >= new_len))
-               return 0;
-
-       heap->alloc_len = bt_max_t(size_t, new_len, heap->alloc_len << 1);
-       new_ptrs = calloc(heap->alloc_len, sizeof(void *));
-       if (G_UNLIKELY(!new_ptrs))
-               return -ENOMEM;
-       if (G_LIKELY(heap->ptrs))
-               memcpy(new_ptrs, heap->ptrs, heap->len * sizeof(void *));
-       free(heap->ptrs);
-       heap->ptrs = new_ptrs;
-       return 0;
-}
-
-static
-int heap_set_len(struct ptr_heap *heap, size_t new_len)
-{
-       int ret;
-
-       ret = heap_grow(heap, new_len);
-       if (G_UNLIKELY(ret))
-               return ret;
-       heap->len = new_len;
-       return 0;
-}
-
-int bt_heap_init(struct ptr_heap *heap, size_t alloc_len,
-             int gt(void *a, void *b))
-{
-       heap->ptrs = NULL;
-       heap->len = 0;
-       heap->alloc_len = 0;
-       heap->gt = gt;
-       /*
-        * Minimum size allocated is 1 entry to ensure memory allocation
-        * never fails within bt_heap_replace_max.
-        */
-       return heap_grow(heap, bt_max_t(size_t, 1, alloc_len));
-}
-
-void bt_heap_free(struct ptr_heap *heap)
-{
-       free(heap->ptrs);
-}
-
-static void heapify(struct ptr_heap *heap, size_t i)
-{
-       void **ptrs = heap->ptrs;
-       size_t l, r, largest;
-
-       for (;;) {
-               void *tmp;
-
-               l = left(i);
-               r = right(i);
-               if (l < heap->len && heap->gt(ptrs[l], ptrs[i]))
-                       largest = l;
-               else
-                       largest = i;
-               if (r < heap->len && heap->gt(ptrs[r], ptrs[largest]))
-                       largest = r;
-               if (G_UNLIKELY(largest == i))
-                       break;
-               tmp = ptrs[i];
-               ptrs[i] = ptrs[largest];
-               ptrs[largest] = tmp;
-               i = largest;
-       }
-       check_heap(heap);
-}
-
-void *bt_heap_replace_max(struct ptr_heap *heap, void *p)
-{
-       void *res;
-
-       if (G_UNLIKELY(!heap->len)) {
-               (void) heap_set_len(heap, 1);
-               heap->ptrs[0] = p;
-               check_heap(heap);
-               return NULL;
-       }
-
-       /* Replace the current max and heapify */
-       res = heap->ptrs[0];
-       heap->ptrs[0] = p;
-       heapify(heap, 0);
-       return res;
-}
-
-int bt_heap_insert(struct ptr_heap *heap, void *p)
-{
-       void **ptrs;
-       size_t pos;
-       int ret;
-
-       ret = heap_set_len(heap, heap->len + 1);
-       if (G_UNLIKELY(ret))
-               return ret;
-       ptrs = heap->ptrs;
-       pos = heap->len - 1;
-       while (pos > 0 && heap->gt(p, ptrs[parent(pos)])) {
-               /* Move parent down until we find the right spot */
-               ptrs[pos] = ptrs[parent(pos)];
-               pos = parent(pos);
-       }
-       ptrs[pos] = p;
-       check_heap(heap);
-       return 0;
-}
-
-void *bt_heap_remove(struct ptr_heap *heap)
-{
-       switch (heap->len) {
-       case 0:
-               return NULL;
-       case 1:
-               (void) heap_set_len(heap, 0);
-               return heap->ptrs[0];
-       }
-       /* Shrink, replace the current max by previous last entry and heapify */
-       heap_set_len(heap, heap->len - 1);
-       /* len changed. previous last entry is at heap->len */
-       return bt_heap_replace_max(heap, heap->ptrs[heap->len]);
-}
-
-void *bt_heap_cherrypick(struct ptr_heap *heap, void *p)
-{
-       size_t pos, len = heap->len;
-
-       for (pos = 0; pos < len; pos++)
-               if (G_UNLIKELY(heap->ptrs[pos] == p))
-                       goto found;
-       return NULL;
-found:
-       if (G_UNLIKELY(heap->len == 1)) {
-               (void) heap_set_len(heap, 0);
-               check_heap(heap);
-               return heap->ptrs[0];
-       }
-       /* Replace p with previous last entry and heapify. */
-       heap_set_len(heap, heap->len - 1);
-       /* len changed. previous last entry is at heap->len */
-       heap->ptrs[pos] = heap->ptrs[heap->len];
-       heapify(heap, pos);
-       return p;
-}
-
-int bt_heap_copy(struct ptr_heap *dst, struct ptr_heap *src)
-{
-       int ret;
-
-       ret = bt_heap_init(dst, src->alloc_len, src->gt);
-       if (ret < 0)
-               goto end;
-
-       ret = heap_set_len(dst, src->len);
-       if (ret < 0)
-               goto end;
-
-       memcpy(dst->ptrs, src->ptrs, src->len * sizeof(void *));
-
-end:
-       return ret;
-}
diff --git a/src/lib/prio-heap/prio-heap.h b/src/lib/prio-heap/prio-heap.h
deleted file mode 100644 (file)
index 88d1871..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Static-sized priority heap containing pointers. Based on CLRS,
- * chapter 6.
- */
-
-#ifndef _BABELTRACE_PRIO_HEAP_H
-#define _BABELTRACE_PRIO_HEAP_H
-
-#include <unistd.h>
-#include "common/macros.h"
-
-struct ptr_heap {
-       size_t len, alloc_len;
-       void **ptrs;
-       int (*gt)(void *a, void *b);
-};
-
-#ifdef DEBUG_HEAP
-void check_heap(const struct ptr_heap *heap);
-#else
-static inline
-void check_heap(const struct ptr_heap *heap)
-{
-}
-#endif
-
-/**
- * bt_heap_maximum - return the largest element in the heap
- * @heap: the heap to be operated on
- *
- * Returns the largest element in the heap, without performing any modification
- * to the heap structure. Returns NULL if the heap is empty.
- */
-static inline void *bt_heap_maximum(const struct ptr_heap *heap)
-{
-       check_heap(heap);
-       return G_LIKELY(heap->len) ? heap->ptrs[0] : NULL;
-}
-
-/**
- * bt_heap_init - initialize the heap
- * @heap: the heap to initialize
- * @alloc_len: number of elements initially allocated
- * @gt: function to compare the elements
- *
- * Returns -ENOMEM if out of memory.
- */
-BT_HIDDEN
-extern int bt_heap_init(struct ptr_heap *heap,
-                    size_t alloc_len,
-                    int gt(void *a, void *b));
-
-/**
- * bt_heap_free - free the heap
- * @heap: the heap to free
- */
-BT_HIDDEN
-extern void bt_heap_free(struct ptr_heap *heap);
-
-/**
- * bt_heap_insert - insert an element into the heap
- * @heap: the heap to be operated on
- * @p: the element to add
- *
- * Insert an element into the heap.
- *
- * Returns -ENOMEM if out of memory.
- */
-BT_HIDDEN
-extern int bt_heap_insert(struct ptr_heap *heap, void *p);
-
-/**
- * bt_heap_remove - remove the largest element from the heap
- * @heap: the heap to be operated on
- *
- * Returns the largest element in the heap. It removes this element from the
- * heap. Returns NULL if the heap is empty.
- */
-BT_HIDDEN
-extern void *bt_heap_remove(struct ptr_heap *heap);
-
-/**
- * bt_heap_cherrypick - remove a given element from the heap
- * @heap: the heap to be operated on
- * @p: the element
- *
- * Remove the given element from the heap. Return the element if present, else
- * return NULL. This algorithm has a complexity of O(n), which is higher than
- * O(log(n)) provided by the rest of this API.
- */
-BT_HIDDEN
-extern void *bt_heap_cherrypick(struct ptr_heap *heap, void *p);
-
-/**
- * bt_heap_replace_max - replace the the largest element from the heap
- * @heap: the heap to be operated on
- * @p: the pointer to be inserted as topmost element replacement
- *
- * Returns the largest element in the heap. It removes this element from the
- * heap. The heap is rebalanced only once after the insertion. Returns NULL if
- * the heap is empty.
- *
- * This is the equivalent of calling bt_heap_remove() and then bt_heap_insert(), but
- * it only rebalances the heap once. It never allocates memory.
- */
-BT_HIDDEN
-extern void *bt_heap_replace_max(struct ptr_heap *heap, void *p);
-
-/**
- * bt_heap_copy - copy a heap
- * @dst: the destination heap (must be allocated)
- * @src: the source heap
- *
- * Returns -ENOMEM if out of memory.
- */
-BT_HIDDEN
-extern int bt_heap_copy(struct ptr_heap *dst, struct ptr_heap *src);
-
-#endif /* _BABELTRACE_PRIO_HEAP_H */
index a1807728c0dbe9c1ec9eea8f48b67a08e4504025..685568c79ca927dee11c91457616bff537848779 100644 (file)
@@ -11,7 +11,6 @@
 #include <babeltrace2/babeltrace.h>
 #include <glib.h>
 #include <stdint.h>
-#include <string.h>
 
 struct bt_property {
        enum bt_property_availability avail;
diff --git a/src/lib/trace-ir/Makefile.am b/src/lib/trace-ir/Makefile.am
deleted file mode 100644 (file)
index 1a79d06..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libtrace-ir.la
-
-libtrace_ir_la_SOURCES = \
-       attributes.c \
-       attributes.h \
-       clock-class.c \
-       clock-class.h \
-       clock-snapshot.c \
-       clock-snapshot.h \
-       event.c \
-       event-class.c \
-       event-class.h \
-       event.h \
-       field.c \
-       field-class.c \
-       field-class.h \
-       field.h \
-       field-path.c \
-       field-path.h \
-       field-wrapper.c \
-       field-wrapper.h \
-       packet.c \
-       packet.h \
-       resolve-field-path.c \
-       resolve-field-path.h \
-       stream.c \
-       stream-class.c \
-       stream-class.h \
-       stream.h \
-       trace.c \
-       trace-class.c \
-       trace-class.h \
-       trace.h \
-       utils.c \
-       utils.h
index 68f54b8627bf54da58cf008c62ee99b532c869cf..ca47167221cd8f9ebd7a453b69fa2671a7fcc00f 100644 (file)
@@ -8,21 +8,17 @@
 #define BT_LOG_TAG "LIB/ATTRS"
 #include "lib/logging.h"
 
-#include "common/macros.h"
 #include <babeltrace2/value.h>
-#include "lib/assert-cond.h"
 #include "lib/object.h"
 #include <babeltrace2/value.h>
 #include "lib/value.h"
 #include "attributes.h"
 #include <inttypes.h>
-#include "compat/string.h"
 #include "common/assert.h"
 
 #define BT_ATTR_NAME_INDEX             0
 #define BT_ATTR_VALUE_INDEX            1
 
-BT_HIDDEN
 struct bt_value *bt_attributes_create(void)
 {
        struct bt_value *attr_obj;
@@ -53,20 +49,17 @@ struct bt_value *bt_attributes_create(void)
        return attr_obj;
 }
 
-BT_HIDDEN
 void bt_attributes_destroy(struct bt_value *attr_obj)
 {
        BT_LOGD("Destroying attributes object: addr=%p", attr_obj);
        BT_OBJECT_PUT_REF_AND_RESET(attr_obj);
 }
 
-BT_HIDDEN
 uint64_t bt_attributes_get_count(const struct bt_value *attr_obj)
 {
        return bt_value_array_get_length(attr_obj);
 }
 
-BT_HIDDEN
 const char *bt_attributes_get_field_name(const struct bt_value *attr_obj,
                uint64_t index)
 {
@@ -85,7 +78,6 @@ const char *bt_attributes_get_field_name(const struct bt_value *attr_obj,
        return bt_value_string_get(attr_field_name_obj);
 }
 
-BT_HIDDEN
 struct bt_value *bt_attributes_borrow_field_value(
                struct bt_value *attr_obj, uint64_t index)
 {
@@ -132,7 +124,6 @@ struct bt_value *bt_attributes_borrow_field_by_name(
        return value_obj;
 }
 
-BT_HIDDEN
 int bt_attributes_set_field_value(struct bt_value *attr_obj,
                const char *name, struct bt_value *value_obj)
 {
@@ -183,7 +174,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_value *bt_attributes_borrow_field_value_by_name(
                struct bt_value *attr_obj, const char *name)
 {
@@ -206,7 +196,6 @@ end:
        return value_obj;
 }
 
-BT_HIDDEN
 int bt_attributes_freeze(const struct bt_value *attr_obj)
 {
        uint64_t i, count;
index b4487e00c1784baab24e26b16bb0b72db36dd18c..2920f383fc394b3d2792bc3d154c74b22b0d12af 100644 (file)
@@ -13,36 +13,27 @@ extern "C" {
 #endif
 
 #include <stdint.h>
-#include "common/macros.h"
 #include <babeltrace2/value.h>
 
-BT_HIDDEN
 struct bt_value *bt_attributes_create(void);
 
-BT_HIDDEN
 void bt_attributes_destroy(struct bt_value *attr_obj);
 
-BT_HIDDEN
 uint64_t bt_attributes_get_count(const struct bt_value *attr_obj);
 
-BT_HIDDEN
 const char *bt_attributes_get_field_name(const struct bt_value *attr_obj,
                uint64_t index);
 
-BT_HIDDEN
 struct bt_value *bt_attributes_borrow_field_value(
                struct bt_value *attr_obj,
                uint64_t index);
 
-BT_HIDDEN
 int bt_attributes_set_field_value(struct bt_value *attr_obj,
                const char *name, struct bt_value *value_obj);
 
-BT_HIDDEN
 struct bt_value *bt_attributes_borrow_field_value_by_name(
                struct bt_value *attr_obj, const char *name);
 
-BT_HIDDEN
 int bt_attributes_freeze(const struct bt_value *attr_obj);
 
 #ifdef __cplusplus
index bb50730609b5036985b94cc0644bb87e78ec51ba..10030155b5c1ece5ad791f803bfae618c0d13f99 100644 (file)
@@ -16,7 +16,6 @@
 #include "utils.h"
 #include "compat/compiler.h"
 #include <babeltrace2/types.h>
-#include "compat/string.h"
 #include <inttypes.h>
 #include <stdbool.h>
 #include "lib/object.h"
@@ -54,7 +53,7 @@ void destroy_clock_class(struct bt_object *obj)
 
 static
 void free_clock_snapshot(struct bt_clock_snapshot *clock_snapshot,
-               struct bt_clock_class *clock_class)
+               struct bt_clock_class *clock_class __attribute__((unused)))
 {
        bt_clock_snapshot_destroy(clock_snapshot);
 }
@@ -67,6 +66,7 @@ void set_base_offset(struct bt_clock_class *clock_class)
                clock_class->frequency, &clock_class->base_offset.value_ns);
 }
 
+BT_EXPORT
 struct bt_clock_class *bt_clock_class_create(bt_self_component *self_comp)
 {
        int ret;
@@ -128,12 +128,14 @@ end:
        return clock_class;
 }
 
+BT_EXPORT
 const char *bt_clock_class_get_name(const struct bt_clock_class *clock_class)
 {
        BT_ASSERT_PRE_DEV_CLK_CLS_NON_NULL(clock_class);
        return clock_class->name.value;
 }
 
+BT_EXPORT
 enum bt_clock_class_set_name_status bt_clock_class_set_name(
                struct bt_clock_class *clock_class, const char *name)
 {
@@ -147,6 +149,7 @@ enum bt_clock_class_set_name_status bt_clock_class_set_name(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 const char *bt_clock_class_get_description(
                const struct bt_clock_class *clock_class)
 {
@@ -154,6 +157,7 @@ const char *bt_clock_class_get_description(
        return clock_class->description.value;
 }
 
+BT_EXPORT
 enum bt_clock_class_set_description_status bt_clock_class_set_description(
                struct bt_clock_class *clock_class, const char *descr)
 {
@@ -168,12 +172,14 @@ enum bt_clock_class_set_description_status bt_clock_class_set_description(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 uint64_t bt_clock_class_get_frequency(const struct bt_clock_class *clock_class)
 {
        BT_ASSERT_PRE_DEV_CLK_CLS_NON_NULL(clock_class);
        return clock_class->frequency;
 }
 
+BT_EXPORT
 void bt_clock_class_set_frequency(struct bt_clock_class *clock_class,
                uint64_t frequency)
 {
@@ -192,12 +198,14 @@ void bt_clock_class_set_frequency(struct bt_clock_class *clock_class,
        BT_LIB_LOGD("Set clock class's frequency: %!+K", clock_class);
 }
 
+BT_EXPORT
 uint64_t bt_clock_class_get_precision(const struct bt_clock_class *clock_class)
 {
        BT_ASSERT_PRE_DEV_CLK_CLS_NON_NULL(clock_class);
        return clock_class->precision;
 }
 
+BT_EXPORT
 void bt_clock_class_set_precision(struct bt_clock_class *clock_class,
                uint64_t precision)
 {
@@ -210,6 +218,7 @@ void bt_clock_class_set_precision(struct bt_clock_class *clock_class,
        BT_LIB_LOGD("Set clock class's precision: %!+K", clock_class);
 }
 
+BT_EXPORT
 void bt_clock_class_get_offset(const struct bt_clock_class *clock_class,
                int64_t *seconds, uint64_t *cycles)
 {
@@ -221,6 +230,7 @@ void bt_clock_class_get_offset(const struct bt_clock_class *clock_class,
        *cycles = clock_class->offset_cycles;
 }
 
+BT_EXPORT
 void bt_clock_class_set_offset(struct bt_clock_class *clock_class,
                int64_t seconds, uint64_t cycles)
 {
@@ -236,12 +246,14 @@ void bt_clock_class_set_offset(struct bt_clock_class *clock_class,
        BT_LIB_LOGD("Set clock class's offset: %!+K", clock_class);
 }
 
+BT_EXPORT
 bt_bool bt_clock_class_origin_is_unix_epoch(const struct bt_clock_class *clock_class)
 {
        BT_ASSERT_PRE_DEV_CLK_CLS_NON_NULL(clock_class);
        return (bool) clock_class->origin_is_unix_epoch;
 }
 
+BT_EXPORT
 void bt_clock_class_set_origin_is_unix_epoch(struct bt_clock_class *clock_class,
                bt_bool origin_is_unix_epoch)
 {
@@ -252,12 +264,14 @@ void bt_clock_class_set_origin_is_unix_epoch(struct bt_clock_class *clock_class,
                clock_class);
 }
 
+BT_EXPORT
 bt_uuid bt_clock_class_get_uuid(const struct bt_clock_class *clock_class)
 {
        BT_ASSERT_PRE_DEV_CLK_CLS_NON_NULL(clock_class);
        return clock_class->uuid.value;
 }
 
+BT_EXPORT
 void bt_clock_class_set_uuid(struct bt_clock_class *clock_class,
                bt_uuid uuid)
 {
@@ -269,7 +283,6 @@ void bt_clock_class_set_uuid(struct bt_clock_class *clock_class,
        BT_LIB_LOGD("Set clock class's UUID: %!+K", clock_class);
 }
 
-BT_HIDDEN
 void _bt_clock_class_freeze(const struct bt_clock_class *clock_class)
 {
        BT_ASSERT(clock_class);
@@ -285,6 +298,7 @@ void _bt_clock_class_freeze(const struct bt_clock_class *clock_class)
        ((struct bt_clock_class *) clock_class)->frozen = 1;
 }
 
+BT_EXPORT
 enum bt_clock_class_cycles_to_ns_from_origin_status
 bt_clock_class_cycles_to_ns_from_origin(
                const struct bt_clock_class *clock_class,
@@ -309,6 +323,7 @@ bt_clock_class_cycles_to_ns_from_origin(
        return ret;
 }
 
+BT_EXPORT
 const struct bt_value *bt_clock_class_borrow_user_attributes_const(
                const struct bt_clock_class *clock_class)
 {
@@ -316,6 +331,7 @@ const struct bt_value *bt_clock_class_borrow_user_attributes_const(
        return clock_class->user_attributes;
 }
 
+BT_EXPORT
 struct bt_value *bt_clock_class_borrow_user_attributes(
                struct bt_clock_class *clock_class)
 {
@@ -323,6 +339,7 @@ struct bt_value *bt_clock_class_borrow_user_attributes(
                (void *) clock_class);
 }
 
+BT_EXPORT
 void bt_clock_class_set_user_attributes(
                struct bt_clock_class *clock_class,
                const struct bt_value *user_attributes)
@@ -336,11 +353,13 @@ void bt_clock_class_set_user_attributes(
        bt_object_get_ref_no_null_check(clock_class->user_attributes);
 }
 
+BT_EXPORT
 void bt_clock_class_get_ref(const struct bt_clock_class *clock_class)
 {
        bt_object_get_ref(clock_class);
 }
 
+BT_EXPORT
 void bt_clock_class_put_ref(const struct bt_clock_class *clock_class)
 {
        bt_object_put_ref(clock_class);
index 7c5b76af7d18204c0321595e9af170247f164b30..e2e90412c797f53e01cb5f279a8ac2f83b673b21 100644 (file)
 
 #include <babeltrace2/trace-ir/clock-class.h>
 #include "lib/object.h"
-#include "common/macros.h"
 #include "common/common.h"
 #include "lib/object-pool.h"
 #include "common/uuid.h"
 #include <babeltrace2/types.h>
-#include "lib/property.h"
 #include "common/assert.h"
 #include <stdbool.h>
 #include <stdint.h>
@@ -81,7 +79,6 @@ struct bt_clock_class {
        bool frozen;
 };
 
-BT_HIDDEN
 void _bt_clock_class_freeze(const struct bt_clock_class *clock_class);
 
 #ifdef BT_DEV_MODE
@@ -90,7 +87,6 @@ void _bt_clock_class_freeze(const struct bt_clock_class *clock_class);
 # define bt_clock_class_freeze(_cc)
 #endif
 
-BT_HIDDEN
 bt_bool bt_clock_class_is_valid(struct bt_clock_class *clock_class);
 
 static inline
index 55e3777b2f098ae123eaedf069959820fca14994..a64a3b0b34ddcf951303b159017c0a8a5245dbb2 100644 (file)
@@ -8,19 +8,15 @@
 #include "lib/logging.h"
 
 #include "lib/assert-cond.h"
-#include "common/uuid.h"
 #include "clock-class.h"
 #include "clock-snapshot.h"
 #include <babeltrace2/trace-ir/clock-snapshot.h>
 #include "compat/compiler.h"
 #include <babeltrace2/types.h>
-#include "compat/string.h"
-#include <inttypes.h>
 #include "lib/object.h"
 #include "common/assert.h"
 #include "lib/func-status.h"
 
-BT_HIDDEN
 void bt_clock_snapshot_destroy(struct bt_clock_snapshot *clock_snapshot)
 {
        BT_ASSERT(clock_snapshot);
@@ -29,7 +25,6 @@ void bt_clock_snapshot_destroy(struct bt_clock_snapshot *clock_snapshot)
        g_free(clock_snapshot);
 }
 
-BT_HIDDEN
 struct bt_clock_snapshot *bt_clock_snapshot_new(
                struct bt_clock_class *clock_class)
 {
@@ -55,7 +50,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_clock_snapshot *bt_clock_snapshot_create(
                struct bt_clock_class *clock_class)
 {
@@ -79,7 +73,6 @@ end:
        return clock_snapshot;
 }
 
-BT_HIDDEN
 void bt_clock_snapshot_recycle(struct bt_clock_snapshot *clock_snapshot)
 {
        struct bt_clock_class *clock_class;
@@ -117,6 +110,7 @@ void bt_clock_snapshot_recycle(struct bt_clock_snapshot *clock_snapshot)
        bt_object_put_ref(clock_class);
 }
 
+BT_EXPORT
 uint64_t bt_clock_snapshot_get_value(
                const struct bt_clock_snapshot *clock_snapshot)
 {
@@ -125,6 +119,7 @@ uint64_t bt_clock_snapshot_get_value(
        return clock_snapshot->value_cycles;
 }
 
+BT_EXPORT
 enum bt_clock_snapshot_get_ns_from_origin_status
 bt_clock_snapshot_get_ns_from_origin(
                const struct bt_clock_snapshot *clock_snapshot,
@@ -153,6 +148,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 const struct bt_clock_class *bt_clock_snapshot_borrow_clock_class_const(
                const struct bt_clock_snapshot *clock_snapshot)
 {
index 29de3b96e09a229a1e8b5e3d9533f19df4e60942..860b4200d4e205ec11e7df7ba9643c7b05f25bd3 100644 (file)
@@ -7,7 +7,6 @@
 #ifndef BABELTRACE_TRACE_IR_CLOCK_SNAPSHOT_INTERNAL_H
 #define BABELTRACE_TRACE_IR_CLOCK_SNAPSHOT_INTERNAL_H
 
-#include "common/macros.h"
 #include "lib/object.h"
 #include <stdbool.h>
 #include <stdint.h>
@@ -60,17 +59,13 @@ void bt_clock_snapshot_set_raw_value(struct bt_clock_snapshot *clock_snapshot,
        bt_clock_snapshot_set(clock_snapshot);
 }
 
-BT_HIDDEN
 void bt_clock_snapshot_destroy(struct bt_clock_snapshot *clock_snapshot);
 
-BT_HIDDEN
 struct bt_clock_snapshot *bt_clock_snapshot_new(struct bt_clock_class *clock_class);
 
-BT_HIDDEN
 struct bt_clock_snapshot *bt_clock_snapshot_create(
                struct bt_clock_class *clock_class);
 
-BT_HIDDEN
 void bt_clock_snapshot_recycle(struct bt_clock_snapshot *clock_snapshot);
 
 #endif /* BABELTRACE_TRACE_IR_CLOCK_SNAPSHOT_INTERNAL_H */
index 871f40cbb13eeaa15fdc00183954142870c52d14..0d90717561ca9727a5eddcce141450de73edc87b 100644 (file)
 #include <stdbool.h>
 #include <stdlib.h>
 
-#include "attributes.h"
-#include "clock-snapshot.h"
 #include "event-class.h"
 #include "event.h"
 #include "field-class.h"
-#include "field.h"
 #include "resolve-field-path.h"
 #include "stream-class.h"
-#include "trace.h"
-#include "utils.h"
 #include "lib/func-status.h"
 
 #define BT_ASSERT_PRE_DEV_EVENT_CLASS_HOT(_ec)                         \
@@ -66,7 +61,7 @@ void destroy_event_class(struct bt_object *obj)
 
 static
 void free_event(struct bt_event *event,
-               struct bt_event_class *event_class)
+               struct bt_event_class *event_class __attribute__((unused)))
 {
        bt_event_destroy(event);
 }
@@ -160,6 +155,7 @@ end:
        return event_class;
 }
 
+BT_EXPORT
 struct bt_event_class *bt_event_class_create(
                struct bt_stream_class *stream_class)
 {
@@ -174,6 +170,7 @@ struct bt_event_class *bt_event_class_create(
                (uint64_t) stream_class->event_classes->len);
 }
 
+BT_EXPORT
 struct bt_event_class *bt_event_class_create_with_id(
                struct bt_stream_class *stream_class, uint64_t id)
 {
@@ -186,12 +183,14 @@ struct bt_event_class *bt_event_class_create_with_id(
        return create_event_class_with_id(stream_class, id);
 }
 
+BT_EXPORT
 const char *bt_event_class_get_name(const struct bt_event_class *event_class)
 {
        BT_ASSERT_PRE_DEV_EC_NON_NULL(event_class);
        return event_class->name.value;
 }
 
+BT_EXPORT
 enum bt_event_class_set_name_status bt_event_class_set_name(
                struct bt_event_class *event_class, const char *name)
 {
@@ -205,12 +204,14 @@ enum bt_event_class_set_name_status bt_event_class_set_name(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 uint64_t bt_event_class_get_id(const struct bt_event_class *event_class)
 {
        BT_ASSERT_PRE_DEV_EC_NON_NULL(event_class);
        return event_class->id;
 }
 
+BT_EXPORT
 enum bt_property_availability bt_event_class_get_log_level(
                const struct bt_event_class *event_class,
                enum bt_event_class_log_level *log_level)
@@ -223,6 +224,7 @@ enum bt_property_availability bt_event_class_get_log_level(
        return event_class->log_level.base.avail;
 }
 
+BT_EXPORT
 void bt_event_class_set_log_level(
                struct bt_event_class *event_class,
                enum bt_event_class_log_level log_level)
@@ -234,12 +236,14 @@ void bt_event_class_set_log_level(
        BT_LIB_LOGD("Set event class's log level: %!+E", event_class);
 }
 
+BT_EXPORT
 const char *bt_event_class_get_emf_uri(const struct bt_event_class *event_class)
 {
        BT_ASSERT_PRE_DEV_EC_NON_NULL(event_class);
        return event_class->emf_uri.value;
 }
 
+BT_EXPORT
 enum bt_event_class_set_emf_uri_status bt_event_class_set_emf_uri(
                struct bt_event_class *event_class,
                const char *emf_uri)
@@ -254,6 +258,7 @@ enum bt_event_class_set_emf_uri_status bt_event_class_set_emf_uri(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 struct bt_stream_class *bt_event_class_borrow_stream_class(
                struct bt_event_class *event_class)
 {
@@ -261,6 +266,7 @@ struct bt_stream_class *bt_event_class_borrow_stream_class(
        return bt_event_class_borrow_stream_class_inline(event_class);
 }
 
+BT_EXPORT
 const struct bt_stream_class *
 bt_event_class_borrow_stream_class_const(
                const struct bt_event_class *event_class)
@@ -268,6 +274,7 @@ bt_event_class_borrow_stream_class_const(
        return bt_event_class_borrow_stream_class((void *) event_class);
 }
 
+BT_EXPORT
 const struct bt_field_class *
 bt_event_class_borrow_specific_context_field_class_const(
                const struct bt_event_class *event_class)
@@ -276,6 +283,7 @@ bt_event_class_borrow_specific_context_field_class_const(
        return event_class->specific_context_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *
 bt_event_class_borrow_specific_context_field_class(
                struct bt_event_class *event_class)
@@ -284,6 +292,7 @@ bt_event_class_borrow_specific_context_field_class(
        return event_class->specific_context_fc;
 }
 
+BT_EXPORT
 enum bt_event_class_set_field_class_status
 bt_event_class_set_specific_context_field_class(
                struct bt_event_class *event_class,
@@ -333,6 +342,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 const struct bt_field_class *bt_event_class_borrow_payload_field_class_const(
                const struct bt_event_class *event_class)
 {
@@ -340,6 +350,7 @@ const struct bt_field_class *bt_event_class_borrow_payload_field_class_const(
        return event_class->payload_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *bt_event_class_borrow_payload_field_class(
                struct bt_event_class *event_class)
 {
@@ -347,6 +358,7 @@ struct bt_field_class *bt_event_class_borrow_payload_field_class(
        return event_class->payload_fc;
 }
 
+BT_EXPORT
 enum bt_event_class_set_field_class_status
 bt_event_class_set_payload_field_class(
                struct bt_event_class *event_class,
@@ -395,7 +407,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void _bt_event_class_freeze(const struct bt_event_class *event_class)
 {
        /* The field classes are already frozen */
@@ -407,6 +418,7 @@ void _bt_event_class_freeze(const struct bt_event_class *event_class)
        ((struct bt_event_class *) event_class)->frozen = true;
 }
 
+BT_EXPORT
 const struct bt_value *bt_event_class_borrow_user_attributes_const(
                const struct bt_event_class *event_class)
 {
@@ -414,6 +426,7 @@ const struct bt_value *bt_event_class_borrow_user_attributes_const(
        return event_class->user_attributes;
 }
 
+BT_EXPORT
 struct bt_value *bt_event_class_borrow_user_attributes(
                struct bt_event_class *event_class)
 {
@@ -421,6 +434,7 @@ struct bt_value *bt_event_class_borrow_user_attributes(
                (void *) event_class);
 }
 
+BT_EXPORT
 void bt_event_class_set_user_attributes(
                struct bt_event_class *event_class,
                const struct bt_value *user_attributes)
@@ -434,11 +448,13 @@ void bt_event_class_set_user_attributes(
        bt_object_get_ref_no_null_check(event_class->user_attributes);
 }
 
+BT_EXPORT
 void bt_event_class_get_ref(const struct bt_event_class *event_class)
 {
        bt_object_get_ref(event_class);
 }
 
+BT_EXPORT
 void bt_event_class_put_ref(const struct bt_event_class *event_class)
 {
        bt_object_put_ref(event_class);
index 00b2e23c1816b97a6c21a6ba0b3cc365d84d6521..67d93695e84711c271c1a5fd012396fb8651ca19 100644 (file)
@@ -8,10 +8,8 @@
 #ifndef BABELTRACE_TRACE_IR_EVENT_CLASS_INTERNAL_H
 #define BABELTRACE_TRACE_IR_EVENT_CLASS_INTERNAL_H
 
-#include "lib/assert-cond.h"
 #include <babeltrace2/trace-ir/field-class.h>
 #include <babeltrace2/trace-ir/field.h>
-#include "common/macros.h"
 #include <babeltrace2/value.h>
 #include <babeltrace2/trace-ir/stream-class.h>
 #include <babeltrace2/trace-ir/stream.h>
@@ -23,8 +21,6 @@
 #include <glib.h>
 #include <stdbool.h>
 
-#include "trace.h"
-
 struct bt_event_class {
        struct bt_object base;
        struct bt_field_class *specific_context_fc;
@@ -56,7 +52,6 @@ struct bt_event_class {
        bool frozen;
 };
 
-BT_HIDDEN
 void _bt_event_class_freeze(const struct bt_event_class *event_class);
 
 #ifdef BT_DEV_MODE
index fe2c5805cb7ead5d39ce3dced85b12d81220ea8f..a8ea1b38e13941d24672d4a7760df7ece9246303 100644 (file)
@@ -16,7 +16,6 @@
 #include <babeltrace2/trace-ir/trace.h>
 #include "common/assert.h"
 #include "compat/compiler.h"
-#include <inttypes.h>
 #include <stdbool.h>
 
 #include "field.h"
 #include "stream-class.h"
 #include "stream.h"
 #include "packet.h"
-#include "trace.h"
 #include "packet.h"
-#include "attributes.h"
 #include "event-class.h"
 
-BT_HIDDEN
 void _bt_event_set_is_frozen(const struct bt_event *event, bool is_frozen)
 {
        BT_ASSERT_DBG(event);
@@ -63,7 +59,6 @@ void _bt_event_set_is_frozen(const struct bt_event *event, bool is_frozen)
        }
 }
 
-BT_HIDDEN
 struct bt_event *bt_event_new(struct bt_event_class *event_class)
 {
        struct bt_event *event = NULL;
@@ -119,36 +114,42 @@ end:
        return event;
 }
 
+BT_EXPORT
 struct bt_event_class *bt_event_borrow_class(struct bt_event *event)
 {
        BT_ASSERT_PRE_DEV_EVENT_NON_NULL(event);
        return event->class;
 }
 
+BT_EXPORT
 const struct bt_event_class *bt_event_borrow_class_const(
                const struct bt_event *event)
 {
        return bt_event_borrow_class((void *) event);
 }
 
+BT_EXPORT
 struct bt_stream *bt_event_borrow_stream(struct bt_event *event)
 {
        BT_ASSERT_PRE_DEV_EVENT_NON_NULL(event);
        return event->stream;
 }
 
+BT_EXPORT
 const struct bt_stream *bt_event_borrow_stream_const(
                const struct bt_event *event)
 {
        return bt_event_borrow_stream((void *) event);
 }
 
+BT_EXPORT
 struct bt_field *bt_event_borrow_common_context_field(struct bt_event *event)
 {
        BT_ASSERT_PRE_DEV_EVENT_NON_NULL(event);
        return event->common_context_field;
 }
 
+BT_EXPORT
 const struct bt_field *bt_event_borrow_common_context_field_const(
                const struct bt_event *event)
 {
@@ -156,12 +157,14 @@ const struct bt_field *bt_event_borrow_common_context_field_const(
        return event->common_context_field;
 }
 
+BT_EXPORT
 struct bt_field *bt_event_borrow_specific_context_field(struct bt_event *event)
 {
        BT_ASSERT_PRE_DEV_EVENT_NON_NULL(event);
        return event->specific_context_field;
 }
 
+BT_EXPORT
 const struct bt_field *bt_event_borrow_specific_context_field_const(
                const struct bt_event *event)
 {
@@ -169,12 +172,14 @@ const struct bt_field *bt_event_borrow_specific_context_field_const(
        return event->specific_context_field;
 }
 
+BT_EXPORT
 struct bt_field *bt_event_borrow_payload_field(struct bt_event *event)
 {
        BT_ASSERT_PRE_DEV_EVENT_NON_NULL(event);
        return event->payload_field;
 }
 
+BT_EXPORT
 const struct bt_field *bt_event_borrow_payload_field_const(
                const struct bt_event *event)
 {
@@ -182,7 +187,6 @@ const struct bt_field *bt_event_borrow_payload_field_const(
        return event->payload_field;
 }
 
-BT_HIDDEN
 void bt_event_destroy(struct bt_event *event)
 {
        BT_ASSERT(event);
@@ -215,12 +219,14 @@ void bt_event_destroy(struct bt_event *event)
        g_free(event);
 }
 
+BT_EXPORT
 struct bt_packet *bt_event_borrow_packet(struct bt_event *event)
 {
        BT_ASSERT_PRE_DEV_EVENT_NON_NULL(event);
        return event->packet;
 }
 
+BT_EXPORT
 const struct bt_packet *bt_event_borrow_packet_const(
                const struct bt_event *event)
 {
index 6ef182917c114ce66964fe27be5db82256dde67a..53130c4ba52c5dfb9157b592d9118485b514b1cd 100644 (file)
@@ -14,7 +14,6 @@
 #endif
 
 #include "lib/assert-cond.h"
-#include "common/macros.h"
 #include <babeltrace2/value.h>
 #include <babeltrace2/trace-ir/stream-class.h>
 #include <babeltrace2/trace-ir/stream.h>
@@ -27,7 +26,6 @@
 
 #include "event-class.h"
 #include "field.h"
-#include "field-wrapper.h"
 #include "packet.h"
 #include "stream.h"
 
@@ -53,13 +51,10 @@ struct bt_event {
        bool frozen;
 };
 
-BT_HIDDEN
 void bt_event_destroy(struct bt_event *event);
 
-BT_HIDDEN
 struct bt_event *bt_event_new(struct bt_event_class *event_class);
 
-BT_HIDDEN
 void _bt_event_set_is_frozen(const struct bt_event *event, bool is_frozen);
 
 #ifdef BT_DEV_MODE
index 2a9230252243ba1aec674cf23078ceea8f99b055..e862d3252a292b732bf875421d2434f31718bb97 100644 (file)
 #include "compat/compiler.h"
 #include "compat/endian.h"
 #include "common/assert.h"
+#include "common/common.h"
 #include "compat/glib.h"
-#include <float.h>
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stdlib.h>
 
-#include "clock-class.h"
 #include "field-class.h"
-#include "field.h"
 #include "field-path.h"
-#include "utils.h"
 #include "lib/func-status.h"
 #include "lib/integer-range-set.h"
 #include "lib/value.h"
 
+BT_EXPORT
 enum bt_field_class_type bt_field_class_get_type(
                const struct bt_field_class *fc)
 {
@@ -75,6 +73,7 @@ void destroy_bit_array_field_class(struct bt_object *obj)
        g_free(obj);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_bit_array_create(
                struct bt_trace_class *trace_class, uint64_t length)
 {
@@ -109,6 +108,7 @@ end:
        return (void *) ba_fc;
 }
 
+BT_EXPORT
 uint64_t bt_field_class_bit_array_get_length(const struct bt_field_class *fc)
 {
        const struct bt_field_class_bit_array *ba_fc = (const void *) fc;
@@ -128,6 +128,7 @@ void destroy_bool_field_class(struct bt_object *obj)
        g_free(obj);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_bool_create(
                bt_trace_class *trace_class)
 {
@@ -217,6 +218,7 @@ end:
        return (void *) int_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_integer_unsigned_create(
                bt_trace_class *trace_class)
 {
@@ -226,6 +228,7 @@ struct bt_field_class *bt_field_class_integer_unsigned_create(
                BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_integer_signed_create(
                bt_trace_class *trace_class)
 {
@@ -235,6 +238,7 @@ struct bt_field_class *bt_field_class_integer_signed_create(
                BT_FIELD_CLASS_TYPE_SIGNED_INTEGER);
 }
 
+BT_EXPORT
 uint64_t bt_field_class_integer_get_field_value_range(
                const struct bt_field_class *fc)
 {
@@ -246,13 +250,15 @@ uint64_t bt_field_class_integer_get_field_value_range(
 }
 
 static
-bool size_is_valid_for_enumeration_field_class(struct bt_field_class *fc,
-               uint64_t size)
+bool size_is_valid_for_enumeration_field_class(
+               struct bt_field_class *fc __attribute__((unused)),
+               uint64_t size __attribute__((unused)))
 {
        // TODO
        return true;
 }
 
+BT_EXPORT
 void bt_field_class_integer_set_field_value_range(
                struct bt_field_class *fc, uint64_t size)
 {
@@ -276,6 +282,7 @@ void bt_field_class_integer_set_field_value_range(
        BT_LIB_LOGD("Set integer field class's field value range: %!+F", fc);
 }
 
+BT_EXPORT
 enum bt_field_class_integer_preferred_display_base
 bt_field_class_integer_get_preferred_display_base(const struct bt_field_class *fc)
 {
@@ -286,6 +293,7 @@ bt_field_class_integer_get_preferred_display_base(const struct bt_field_class *f
        return int_fc->base;
 }
 
+BT_EXPORT
 void bt_field_class_integer_set_preferred_display_base(
                struct bt_field_class *fc,
                enum bt_field_class_integer_preferred_display_base base)
@@ -386,6 +394,7 @@ end:
        return (void *) enum_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_enumeration_unsigned_create(
                bt_trace_class *trace_class)
 {
@@ -395,6 +404,7 @@ struct bt_field_class *bt_field_class_enumeration_unsigned_create(
                BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_enumeration_signed_create(
                bt_trace_class *trace_class)
 {
@@ -404,6 +414,7 @@ struct bt_field_class *bt_field_class_enumeration_signed_create(
                BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION);
 }
 
+BT_EXPORT
 uint64_t bt_field_class_enumeration_get_mapping_count(
                const struct bt_field_class *fc)
 {
@@ -414,6 +425,7 @@ uint64_t bt_field_class_enumeration_get_mapping_count(
        return (uint64_t) enum_fc->mappings->len;
 }
 
+BT_EXPORT
 const struct bt_field_class_enumeration_unsigned_mapping *
 bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
                const struct bt_field_class *fc, uint64_t index)
@@ -427,6 +439,7 @@ bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
        return (const void *) BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(fc, index);
 }
 
+BT_EXPORT
 const struct bt_field_class_enumeration_signed_mapping *
 bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
                const struct bt_field_class *fc, uint64_t index)
@@ -467,6 +480,7 @@ end:
        return mapping;
 }
 
+BT_EXPORT
 const struct bt_field_class_enumeration_signed_mapping *
 bt_field_class_enumeration_signed_borrow_mapping_by_label_const(
                const struct bt_field_class *fc, const char *label)
@@ -478,6 +492,7 @@ bt_field_class_enumeration_signed_borrow_mapping_by_label_const(
                (const void *) fc, label, __func__);
 }
 
+BT_EXPORT
 const struct bt_field_class_enumeration_unsigned_mapping *
 bt_field_class_enumeration_unsigned_borrow_mapping_by_label_const(
                const struct bt_field_class *fc, const char *label)
@@ -489,6 +504,7 @@ bt_field_class_enumeration_unsigned_borrow_mapping_by_label_const(
                (const void *) fc, label, __func__);
 }
 
+BT_EXPORT
 const char *bt_field_class_enumeration_mapping_get_label(
                const struct bt_field_class_enumeration_mapping *mapping)
 {
@@ -497,6 +513,7 @@ const char *bt_field_class_enumeration_mapping_get_label(
        return mapping->label->str;
 }
 
+BT_EXPORT
 const struct bt_integer_range_set_unsigned *
 bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
                const struct bt_field_class_enumeration_unsigned_mapping *u_mapping)
@@ -509,6 +526,7 @@ bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
        return (const void *) mapping->range_set;
 }
 
+BT_EXPORT
 const struct bt_integer_range_set_signed *
 bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
                const struct bt_field_class_enumeration_signed_mapping *s_mapping)
@@ -521,6 +539,7 @@ bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
        return (const void *) mapping->range_set;
 }
 
+BT_EXPORT
 enum bt_field_class_enumeration_get_mapping_labels_for_value_status
 bt_field_class_enumeration_unsigned_get_mapping_labels_for_value(
                const struct bt_field_class *fc, uint64_t value,
@@ -563,6 +582,7 @@ bt_field_class_enumeration_unsigned_get_mapping_labels_for_value(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_field_class_enumeration_get_mapping_labels_for_value_status
 bt_field_class_enumeration_signed_get_mapping_labels_for_value(
                const struct bt_field_class *fc, int64_t value,
@@ -668,6 +688,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_field_class_enumeration_add_mapping_status
 bt_field_class_enumeration_unsigned_add_mapping(
                struct bt_field_class *fc, const char *label,
@@ -682,6 +703,7 @@ bt_field_class_enumeration_unsigned_add_mapping(
                (const void *) range_set, __func__);
 }
 
+BT_EXPORT
 enum bt_field_class_enumeration_add_mapping_status
 bt_field_class_enumeration_signed_add_mapping(
                struct bt_field_class *fc, const char *label,
@@ -734,6 +756,7 @@ end:
        return (void *) real_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_real_single_precision_create(
        bt_trace_class *trace_class)
 {
@@ -743,6 +766,7 @@ struct bt_field_class *bt_field_class_real_single_precision_create(
                BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_real_double_precision_create(
        bt_trace_class *trace_class)
 {
@@ -855,6 +879,7 @@ void destroy_structure_field_class(struct bt_object *obj)
        g_free(obj);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_structure_create(
                bt_trace_class *trace_class)
 {
@@ -1010,6 +1035,7 @@ int append_named_field_class_to_container_field_class(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_field_class_structure_append_member_status
 bt_field_class_structure_append_member(
                struct bt_field_class *fc, const char *name,
@@ -1040,13 +1066,14 @@ end:
        return status;
 }
 
+BT_EXPORT
 uint64_t bt_field_class_structure_get_member_count(
                const struct bt_field_class *fc)
 {
        struct bt_field_class_structure *struct_fc = (void *) fc;
 
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_IS_STRUCT("field-class", fc, "Field class");
+       BT_ASSERT_PRE_DEV_FC_IS_STRUCT("field-class", fc, "Field class");
        return (uint64_t) struct_fc->common.named_fcs->len;
 }
 
@@ -1062,23 +1089,25 @@ borrow_named_field_class_from_container_field_class_at_index(
        return fc->named_fcs->pdata[index];
 }
 
+BT_EXPORT
 const struct bt_field_class_structure_member *
 bt_field_class_structure_borrow_member_by_index_const(
                const struct bt_field_class *fc, uint64_t index)
 {
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_IS_STRUCT("field-class", fc, "Field class");
+       BT_ASSERT_PRE_DEV_FC_IS_STRUCT("field-class", fc, "Field class");
        return (const void *)
                borrow_named_field_class_from_container_field_class_at_index(
                        (void *) fc, index, __func__);
 }
 
+BT_EXPORT
 struct bt_field_class_structure_member *
 bt_field_class_structure_borrow_member_by_index(
                struct bt_field_class *fc, uint64_t index)
 {
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_IS_STRUCT("field-class", fc, "Field class");
+       BT_ASSERT_PRE_DEV_FC_IS_STRUCT("field-class", fc, "Field class");
        return (void *)
                borrow_named_field_class_from_container_field_class_at_index(
                        (void *) fc, index, __func__);
@@ -1107,28 +1136,31 @@ end:
        return named_fc;
 }
 
+BT_EXPORT
 const struct bt_field_class_structure_member *
 bt_field_class_structure_borrow_member_by_name_const(
                const struct bt_field_class *fc, const char *name)
 {
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_IS_STRUCT("field-class", fc, "Field class");
+       BT_ASSERT_PRE_DEV_FC_IS_STRUCT("field-class", fc, "Field class");
        return (const void *)
                borrow_named_field_class_from_container_field_class_by_name(
                        (void *) fc, name, __func__);
 }
 
+BT_EXPORT
 struct bt_field_class_structure_member *
 bt_field_class_structure_borrow_member_by_name(
                struct bt_field_class *fc, const char *name)
 {
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_IS_STRUCT("field-class", fc, "Field class");
+       BT_ASSERT_PRE_DEV_FC_IS_STRUCT("field-class", fc, "Field class");
        return (void *)
                borrow_named_field_class_from_container_field_class_by_name(
                        (void *) fc, name, __func__);
 }
 
+BT_EXPORT
 const char *bt_field_class_structure_member_get_name(
                const struct bt_field_class_structure_member *member)
 {
@@ -1138,6 +1170,7 @@ const char *bt_field_class_structure_member_get_name(
        return named_fc->name->str;
 }
 
+BT_EXPORT
 const struct bt_field_class *
 bt_field_class_structure_member_borrow_field_class_const(
                const struct bt_field_class_structure_member *member)
@@ -1148,6 +1181,7 @@ bt_field_class_structure_member_borrow_field_class_const(
        return named_fc->fc;
 }
 
+BT_EXPORT
 struct bt_field_class *
 bt_field_class_structure_member_borrow_field_class(
                struct bt_field_class_structure_member *member)
@@ -1275,6 +1309,7 @@ end:
        return (void *) opt_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_option_without_selector_create(
                struct bt_trace_class *trace_class,
                struct bt_field_class *content_fc)
@@ -1284,6 +1319,7 @@ struct bt_field_class *bt_field_class_option_without_selector_create(
                content_fc, NULL, __func__);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_option_with_selector_field_bool_create(
                struct bt_trace_class *trace_class,
                struct bt_field_class *content_fc,
@@ -1296,6 +1332,7 @@ struct bt_field_class *bt_field_class_option_with_selector_field_bool_create(
                content_fc, selector_fc, __func__);
 }
 
+BT_EXPORT
 struct bt_field_class *
 bt_field_class_option_with_selector_field_integer_unsigned_create(
                struct bt_trace_class *trace_class,
@@ -1326,6 +1363,7 @@ end:
        return (void *) fc;
 }
 
+BT_EXPORT
 struct bt_field_class *
 bt_field_class_option_with_selector_field_integer_signed_create(
                struct bt_trace_class *trace_class,
@@ -1356,6 +1394,7 @@ end:
        return (void *) fc;
 }
 
+BT_EXPORT
 const struct bt_field_class *bt_field_class_option_borrow_field_class_const(
                        const struct bt_field_class *fc)
 {
@@ -1366,6 +1405,7 @@ const struct bt_field_class *bt_field_class_option_borrow_field_class_const(
        return opt_fc->content_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_option_borrow_field_class(
                        struct bt_field_class *fc)
 {
@@ -1376,6 +1416,7 @@ struct bt_field_class *bt_field_class_option_borrow_field_class(
        return opt_fc->content_fc;
 }
 
+BT_EXPORT
 const struct bt_field_path *
 bt_field_class_option_with_selector_field_borrow_selector_field_path_const(
                const struct bt_field_class *fc)
@@ -1388,6 +1429,7 @@ bt_field_class_option_with_selector_field_borrow_selector_field_path_const(
        return opt_fc->selector_field_path;
 }
 
+BT_EXPORT
 void bt_field_class_option_with_selector_field_bool_set_selector_is_reversed(
                struct bt_field_class *fc, bt_bool sel_is_reversed)
 {
@@ -1402,6 +1444,7 @@ void bt_field_class_option_with_selector_field_bool_set_selector_is_reversed(
        opt_fc->sel_is_reversed = sel_is_reversed;
 }
 
+BT_EXPORT
 bt_bool bt_field_class_option_with_selector_field_bool_selector_is_reversed(
                const struct bt_field_class *fc)
 {
@@ -1415,6 +1458,7 @@ bt_bool bt_field_class_option_with_selector_field_bool_selector_is_reversed(
        return opt_fc->sel_is_reversed;
 }
 
+BT_EXPORT
 const struct bt_integer_range_set_unsigned *
 bt_field_class_option_with_selector_field_integer_unsigned_borrow_selector_ranges_const(
                const struct bt_field_class *fc)
@@ -1428,6 +1472,7 @@ bt_field_class_option_with_selector_field_integer_unsigned_borrow_selector_range
        return (const void *) opt_fc->range_set;
 }
 
+BT_EXPORT
 const struct bt_integer_range_set_signed *
 bt_field_class_option_with_selector_field_integer_signed_borrow_selector_ranges_const(
                const struct bt_field_class *fc)
@@ -1474,6 +1519,7 @@ void destroy_variant_with_selector_field_field_class(struct bt_object *obj)
        g_free(fc);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_variant_create(
                bt_trace_class *trace_class, bt_field_class *selector_fc)
 {
@@ -1557,6 +1603,7 @@ end:
 #define VAR_FC_OPT_NAME_IS_UNIQUE_ID                                   \
        "variant-field-class-option-name-is-unique"
 
+BT_EXPORT
 enum bt_field_class_variant_without_selector_append_option_status
 bt_field_class_variant_without_selector_append_option(struct bt_field_class *fc,
                const char *name, struct bt_field_class *option_fc)
@@ -1733,6 +1780,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_field_class_variant_with_selector_field_integer_append_option_status
 bt_field_class_variant_with_selector_field_integer_unsigned_append_option(
                struct bt_field_class *fc, const char *name,
@@ -1751,6 +1799,7 @@ bt_field_class_variant_with_selector_field_integer_unsigned_append_option(
                __func__);
 }
 
+BT_EXPORT
 enum bt_field_class_variant_with_selector_field_integer_append_option_status
 bt_field_class_variant_with_selector_field_integer_signed_append_option(
                struct bt_field_class *fc, const char *name,
@@ -1769,6 +1818,7 @@ bt_field_class_variant_with_selector_field_integer_signed_append_option(
                __func__);
 }
 
+BT_EXPORT
 uint64_t bt_field_class_variant_get_option_count(const struct bt_field_class *fc)
 {
        const struct bt_field_class_variant *var_fc = (const void *) fc;
@@ -1778,6 +1828,7 @@ uint64_t bt_field_class_variant_get_option_count(const struct bt_field_class *fc
        return (uint64_t) var_fc->common.named_fcs->len;
 }
 
+BT_EXPORT
 const struct bt_field_class_variant_option *
 bt_field_class_variant_borrow_option_by_name_const(
                const struct bt_field_class *fc, const char *name)
@@ -1789,6 +1840,7 @@ bt_field_class_variant_borrow_option_by_name_const(
                        (void *) fc, name, __func__);
 }
 
+BT_EXPORT
 const struct bt_field_class_variant_option *
 bt_field_class_variant_borrow_option_by_index_const(
                const struct bt_field_class *fc, uint64_t index)
@@ -1800,6 +1852,7 @@ bt_field_class_variant_borrow_option_by_index_const(
                        (void *) fc, index, __func__);
 }
 
+BT_EXPORT
 struct bt_field_class_variant_option *
 bt_field_class_variant_borrow_option_by_name(
                struct bt_field_class *fc, const char *name)
@@ -1811,6 +1864,7 @@ bt_field_class_variant_borrow_option_by_name(
                        (void *) fc, name, __func__);
 }
 
+BT_EXPORT
 struct bt_field_class_variant_option *
 bt_field_class_variant_borrow_option_by_index(
                struct bt_field_class *fc, uint64_t index)
@@ -1822,12 +1876,13 @@ bt_field_class_variant_borrow_option_by_index(
                        (void *) fc, index, __func__);
 }
 
+BT_EXPORT
 const struct bt_field_class_variant_with_selector_field_integer_unsigned_option *
 bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_name_const(
                const struct bt_field_class *fc, const char *name)
 {
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_HAS_TYPE("field-class", fc,
+       BT_ASSERT_PRE_DEV_FC_HAS_TYPE("field-class", fc,
                "variant-field-class-with-unsigned-integer-selector-field",
                BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD,
                "Field class");
@@ -1836,12 +1891,13 @@ bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_nam
                        (void *) fc, name, __func__);
 }
 
+BT_EXPORT
 const struct bt_field_class_variant_with_selector_field_integer_unsigned_option *
 bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_index_const(
                const struct bt_field_class *fc, uint64_t index)
 {
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_HAS_TYPE("field-class", fc,
+       BT_ASSERT_PRE_DEV_FC_HAS_TYPE("field-class", fc,
                "variant-field-class-with-unsigned-integer-selector-field",
                BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD,
                "Field class");
@@ -1850,12 +1906,13 @@ bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_ind
                        (void *) fc, index, __func__);
 }
 
+BT_EXPORT
 const struct bt_field_class_variant_with_selector_field_integer_signed_option *
 bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_name_const(
                const struct bt_field_class *fc, const char *name)
 {
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_HAS_TYPE("field-class", fc,
+       BT_ASSERT_PRE_DEV_FC_HAS_TYPE("field-class", fc,
                "variant-field-class-with-signed-integer-selector-field",
                BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD,
                "Field class");
@@ -1864,12 +1921,13 @@ bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_name_
                        (void *) fc, name, __func__);
 }
 
+BT_EXPORT
 const struct bt_field_class_variant_with_selector_field_integer_signed_option *
 bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_index_const(
                const struct bt_field_class *fc, uint64_t index)
 {
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_HAS_TYPE("field-class", fc,
+       BT_ASSERT_PRE_DEV_FC_HAS_TYPE("field-class", fc,
                "variant-field-class-with-signed-integer-selector-field",
                BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD,
                "Field class");
@@ -1878,6 +1936,7 @@ bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_index
                        (void *) fc, index, __func__);
 }
 
+BT_EXPORT
 const char *bt_field_class_variant_option_get_name(
                const struct bt_field_class_variant_option *option)
 {
@@ -1887,6 +1946,7 @@ const char *bt_field_class_variant_option_get_name(
        return named_fc->name->str;
 }
 
+BT_EXPORT
 const struct bt_field_class *
 bt_field_class_variant_option_borrow_field_class_const(
                const struct bt_field_class_variant_option *option)
@@ -1897,6 +1957,7 @@ bt_field_class_variant_option_borrow_field_class_const(
        return named_fc->fc;
 }
 
+BT_EXPORT
 struct bt_field_class *
 bt_field_class_variant_option_borrow_field_class(
                struct bt_field_class_variant_option *option)
@@ -1907,6 +1968,7 @@ bt_field_class_variant_option_borrow_field_class(
        return named_fc->fc;
 }
 
+BT_EXPORT
 const struct bt_integer_range_set_unsigned *
 bt_field_class_variant_with_selector_field_integer_unsigned_option_borrow_ranges_const(
                const struct bt_field_class_variant_with_selector_field_integer_unsigned_option *option)
@@ -1918,6 +1980,7 @@ bt_field_class_variant_with_selector_field_integer_unsigned_option_borrow_ranges
        return (const void *) opt->range_set;
 }
 
+BT_EXPORT
 const struct bt_integer_range_set_signed *
 bt_field_class_variant_with_selector_field_integer_signed_option_borrow_ranges_const(
                const struct bt_field_class_variant_with_selector_field_integer_signed_option *option)
@@ -1929,6 +1992,7 @@ bt_field_class_variant_with_selector_field_integer_signed_option_borrow_ranges_c
        return (const void *) opt->range_set;
 }
 
+BT_EXPORT
 const struct bt_field_path *
 bt_field_class_variant_with_selector_field_borrow_selector_field_path_const(
                const struct bt_field_class *fc)
@@ -1981,6 +2045,7 @@ void destroy_static_array_field_class(struct bt_object *obj)
        g_free(obj);
 }
 
+BT_EXPORT
 struct bt_field_class *
 bt_field_class_array_static_create(bt_trace_class *trace_class,
                struct bt_field_class *element_fc, uint64_t length)
@@ -2016,6 +2081,7 @@ end:
        return (void *) array_fc;
 }
 
+BT_EXPORT
 const struct bt_field_class *
 bt_field_class_array_borrow_element_field_class_const(
                const struct bt_field_class *fc)
@@ -2027,6 +2093,7 @@ bt_field_class_array_borrow_element_field_class_const(
        return array_fc->element_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *
 bt_field_class_array_borrow_element_field_class(struct bt_field_class *fc)
 {
@@ -2037,12 +2104,13 @@ bt_field_class_array_borrow_element_field_class(struct bt_field_class *fc)
        return array_fc->element_fc;
 }
 
+BT_EXPORT
 uint64_t bt_field_class_array_static_get_length(const struct bt_field_class *fc)
 {
        const struct bt_field_class_array_static *array_fc = (const void *) fc;
 
        BT_ASSERT_PRE_DEV_FC_NON_NULL(fc);
-       BT_ASSERT_PRE_FC_HAS_TYPE("field-class", fc,
+       BT_ASSERT_PRE_DEV_FC_HAS_TYPE("field-class", fc,
                "static-array-field-class", BT_FIELD_CLASS_TYPE_STATIC_ARRAY,
                "Field class");
        return (uint64_t) array_fc->length;
@@ -2063,6 +2131,7 @@ void destroy_dynamic_array_field_class(struct bt_object *obj)
        g_free(fc);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_array_dynamic_create(
                struct bt_trace_class *trace_class,
                struct bt_field_class *element_fc,
@@ -2108,6 +2177,7 @@ end:
        return (void *) array_fc;
 }
 
+BT_EXPORT
 const struct bt_field_path *
 bt_field_class_array_dynamic_with_length_field_borrow_length_field_path_const(
                const struct bt_field_class *fc)
@@ -2132,6 +2202,7 @@ void destroy_string_field_class(struct bt_object *obj)
        g_free(obj);
 }
 
+BT_EXPORT
 struct bt_field_class *bt_field_class_string_create(bt_trace_class *trace_class)
 {
        struct bt_field_class_string *string_fc = NULL;
@@ -2161,7 +2232,6 @@ end:
        return (void *) string_fc;
 }
 
-BT_HIDDEN
 void _bt_field_class_freeze(const struct bt_field_class *c_fc)
 {
        struct bt_field_class *fc = (void *) c_fc;
@@ -2188,7 +2258,6 @@ void _bt_field_class_freeze(const struct bt_field_class *c_fc)
        }
 }
 
-BT_HIDDEN
 void _bt_named_field_class_freeze(const struct bt_named_field_class *named_fc)
 {
        BT_ASSERT(named_fc);
@@ -2199,7 +2268,6 @@ void _bt_named_field_class_freeze(const struct bt_named_field_class *named_fc)
        ((struct bt_named_field_class *) named_fc)->frozen = true;
 }
 
-BT_HIDDEN
 void bt_field_class_make_part_of_trace_class(const struct bt_field_class *c_fc)
 {
        struct bt_field_class *fc = (void *) c_fc;
@@ -2231,6 +2299,7 @@ void bt_field_class_make_part_of_trace_class(const struct bt_field_class *c_fc)
        }
 }
 
+BT_EXPORT
 const struct bt_value *bt_field_class_borrow_user_attributes_const(
                const struct bt_field_class *fc)
 {
@@ -2238,6 +2307,7 @@ const struct bt_value *bt_field_class_borrow_user_attributes_const(
        return fc->user_attributes;
 }
 
+BT_EXPORT
 struct bt_value *bt_field_class_borrow_user_attributes(
                struct bt_field_class *field_class)
 {
@@ -2246,6 +2316,7 @@ struct bt_value *bt_field_class_borrow_user_attributes(
 }
 
 
+BT_EXPORT
 void bt_field_class_set_user_attributes(
                struct bt_field_class *fc,
                const struct bt_value *user_attributes)
@@ -2278,6 +2349,7 @@ void set_named_field_class_user_attributes(
        bt_object_get_ref_no_null_check(named_fc->user_attributes);
 }
 
+BT_EXPORT
 const struct bt_value *
 bt_field_class_structure_member_borrow_user_attributes_const(
                const struct bt_field_class_structure_member *member)
@@ -2287,6 +2359,7 @@ bt_field_class_structure_member_borrow_user_attributes_const(
                (const void *) member);
 }
 
+BT_EXPORT
 struct bt_value *
 bt_field_class_structure_member_borrow_user_attributes(
                struct bt_field_class_structure_member *member)
@@ -2296,6 +2369,7 @@ bt_field_class_structure_member_borrow_user_attributes(
                (void *) member);
 }
 
+BT_EXPORT
 void bt_field_class_structure_member_set_user_attributes(
                struct bt_field_class_structure_member *member,
                const struct bt_value *user_attributes)
@@ -2308,6 +2382,7 @@ void bt_field_class_structure_member_set_user_attributes(
                user_attributes, __func__);
 }
 
+BT_EXPORT
 const struct bt_value *bt_field_class_variant_option_borrow_user_attributes_const(
                const struct bt_field_class_variant_option *option)
 {
@@ -2316,6 +2391,7 @@ const struct bt_value *bt_field_class_variant_option_borrow_user_attributes_cons
                (const void *) option);
 }
 
+BT_EXPORT
 struct bt_value *bt_field_class_variant_option_borrow_user_attributes(
                struct bt_field_class_variant_option *option)
 {
@@ -2324,6 +2400,7 @@ struct bt_value *bt_field_class_variant_option_borrow_user_attributes(
                (void *) option);
 }
 
+BT_EXPORT
 void bt_field_class_variant_option_set_user_attributes(
                struct bt_field_class_variant_option *option,
                const struct bt_value *user_attributes)
@@ -2336,11 +2413,13 @@ void bt_field_class_variant_option_set_user_attributes(
                user_attributes, __func__);
 }
 
+BT_EXPORT
 void bt_field_class_get_ref(const struct bt_field_class *field_class)
 {
        bt_object_get_ref(field_class);
 }
 
+BT_EXPORT
 void bt_field_class_put_ref(const struct bt_field_class *field_class)
 {
        bt_object_put_ref(field_class);
index 464dd286c98e23e2a4dba8f80ff0a629e26ca560..289f3b0978bd752484ba94c2d160437cdae3632a 100644 (file)
@@ -8,11 +8,9 @@
 #ifndef BABELTRACE_TRACE_IR_FIELD_CLASSES_INTERNAL_H
 #define BABELTRACE_TRACE_IR_FIELD_CLASSES_INTERNAL_H
 
-#include "lib/assert-cond.h"
 #include <babeltrace2/trace-ir/clock-class.h>
 #include <babeltrace2/trace-ir/field-class.h>
 #include "common/macros.h"
-#include "common/common.h"
 #include "lib/object.h"
 #include <babeltrace2/types.h>
 #include <stdbool.h>
 #include <glib.h>
 
 #define BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(_fc, _index)              \
-       (&g_array_index(((struct bt_field_class_enumeration *) (_fc))->mappings, \
+       (&bt_g_array_index(((struct bt_field_class_enumeration *) (_fc))->mappings, \
                struct bt_field_class_enumeration_mapping, (_index)))
 
 #define BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX(_mapping, _index)   \
-       (&g_array_index((_mapping)->ranges,                             \
+       (&bt_g_array_index((_mapping)->ranges,                          \
                struct bt_field_class_enumeration_mapping_range, (_index)))
 
-struct bt_field;
-struct bt_field_class;
-
 struct bt_field_class {
        struct bt_object base;
        enum bt_field_class_type type;
@@ -227,7 +222,6 @@ struct bt_field_class_variant_with_selector_field {
        struct bt_field_path *selector_field_path;
 };
 
-BT_HIDDEN
 void _bt_field_class_freeze(const struct bt_field_class *field_class);
 
 #ifdef BT_DEV_MODE
@@ -236,7 +230,6 @@ void _bt_field_class_freeze(const struct bt_field_class *field_class);
 # define bt_field_class_freeze(_fc)    ((void) _fc)
 #endif
 
-BT_HIDDEN
 void _bt_named_field_class_freeze(const struct bt_named_field_class *named_fc);
 
 #ifdef BT_DEV_MODE
@@ -251,7 +244,6 @@ void _bt_named_field_class_freeze(const struct bt_named_field_class *named_fc);
  * are used at a single location within trace objects even if they are
  * shared objects for other purposes.
  */
-BT_HIDDEN
 void bt_field_class_make_part_of_trace_class(
                const struct bt_field_class *field_class);
 
index f7d7551b0e052f34d5ce8ca408d8853d031eb0fa..92bca3f15c6403be130fc20b41bcefcbc4075ea4 100644 (file)
 #include "lib/assert-cond.h"
 #include <babeltrace2/trace-ir/field-class.h>
 #include <babeltrace2/trace-ir/field-path.h>
-#include <limits.h>
 #include <stdint.h>
-#include <inttypes.h>
 #include "common/assert.h"
 #include <glib.h>
 
-#include "field-class.h"
 #include "field-path.h"
 
 static
@@ -32,7 +29,6 @@ void destroy_field_path(struct bt_object *obj)
        g_free(field_path);
 }
 
-BT_HIDDEN
 struct bt_field_path *bt_field_path_create(void)
 {
        struct bt_field_path *field_path = NULL;
@@ -63,6 +59,7 @@ end:
        return field_path;
 }
 
+BT_EXPORT
 enum bt_field_path_scope bt_field_path_get_root_scope(
                const struct bt_field_path *field_path)
 {
@@ -70,12 +67,14 @@ enum bt_field_path_scope bt_field_path_get_root_scope(
        return field_path->root;
 }
 
+BT_EXPORT
 uint64_t bt_field_path_get_item_count(const struct bt_field_path *field_path)
 {
        BT_ASSERT_PRE_DEV_FP_NON_NULL(field_path);
        return (uint64_t) field_path->items->len;
 }
 
+BT_EXPORT
 const struct bt_field_path_item *bt_field_path_borrow_item_by_index_const(
                const struct bt_field_path *field_path, uint64_t index)
 {
@@ -84,6 +83,7 @@ const struct bt_field_path_item *bt_field_path_borrow_item_by_index_const(
        return bt_field_path_borrow_item_by_index_inline(field_path, index);
 }
 
+BT_EXPORT
 enum bt_field_path_item_type bt_field_path_item_get_type(
                const struct bt_field_path_item *field_path_item)
 {
@@ -92,6 +92,7 @@ enum bt_field_path_item_type bt_field_path_item_get_type(
        return field_path_item->type;
 }
 
+BT_EXPORT
 uint64_t bt_field_path_item_index_get_index(
                const struct bt_field_path_item *field_path_item)
 {
@@ -105,11 +106,13 @@ uint64_t bt_field_path_item_index_get_index(
        return  field_path_item->index;
 }
 
+BT_EXPORT
 void bt_field_path_get_ref(const struct bt_field_path *field_path)
 {
        bt_object_get_ref(field_path);
 }
 
+BT_EXPORT
 void bt_field_path_put_ref(const struct bt_field_path *field_path)
 {
        bt_object_put_ref(field_path);
index cae07b8c66e36237e58ede233ce59d7ea25ab438..79e9d8f17b26d091ba2bea736191759afd7b9d2c 100644 (file)
@@ -29,7 +29,6 @@ struct bt_field_path {
        GArray *items;
 };
 
-BT_HIDDEN
 struct bt_field_path *bt_field_path_create(void);
 
 static inline
@@ -38,7 +37,7 @@ struct bt_field_path_item *bt_field_path_borrow_item_by_index_inline(
 {
        BT_ASSERT_DBG(field_path);
        BT_ASSERT_DBG(index < field_path->items->len);
-       return &g_array_index(field_path->items, struct bt_field_path_item,
+       return &bt_g_array_index(field_path->items, struct bt_field_path_item,
                index);
 }
 
index a26958a72291d22b8ecb482990d1c7236651149d..b9eee6708a649fc33c6165c522b9cce38db305e6 100644 (file)
@@ -14,8 +14,7 @@
 #include "field-wrapper.h"
 #include "field.h"
 
-BT_HIDDEN
-struct bt_field_wrapper *bt_field_wrapper_new(void *data)
+struct bt_field_wrapper *bt_field_wrapper_new(void *data __attribute__((unused)))
 {
        struct bt_field_wrapper *field_wrapper =
                g_new0(struct bt_field_wrapper, 1);
@@ -36,7 +35,6 @@ end:
        return field_wrapper;
 }
 
-BT_HIDDEN
 void bt_field_wrapper_destroy(struct bt_field_wrapper *field_wrapper)
 {
        BT_LOGD("Destroying field wrapper: addr=%p", field_wrapper);
@@ -51,7 +49,6 @@ void bt_field_wrapper_destroy(struct bt_field_wrapper *field_wrapper)
        g_free(field_wrapper);
 }
 
-BT_HIDDEN
 struct bt_field_wrapper *bt_field_wrapper_create(
                struct bt_object_pool *pool, struct bt_field_class *fc)
 {
index f3fdee3c7de7e7d574b6a03463b7e978087ccb20..c80d6a30a84094f48b529fc4387313cafe010cee 100644 (file)
@@ -7,7 +7,6 @@
 #ifndef BABELTRACE_TRACE_IR_FIELD_WRAPPER_INTERNAL_H
 #define BABELTRACE_TRACE_IR_FIELD_WRAPPER_INTERNAL_H
 
-#include "common/macros.h"
 #include "lib/object-pool.h"
 #include "lib/object.h"
 
@@ -20,13 +19,10 @@ struct bt_field_wrapper {
        struct bt_field *field;
 };
 
-BT_HIDDEN
 struct bt_field_wrapper *bt_field_wrapper_new(void *data);
 
-BT_HIDDEN
 void bt_field_wrapper_destroy(struct bt_field_wrapper *field);
 
-BT_HIDDEN
 struct bt_field_wrapper *bt_field_wrapper_create(
                struct bt_object_pool *pool, struct bt_field_class *fc);
 
index 4d6db1337eda5679ba05fd9f5a025745c5e89a26..8110e58acc80749adfb8352acec7768fd87860a2 100644 (file)
@@ -13,7 +13,6 @@
 #include "lib/object.h"
 #include "compat/compiler.h"
 #include "compat/fcntl.h"
-#include "common/align.h"
 #include "common/assert.h"
 #include <inttypes.h>
 #include <stdbool.h>
@@ -21,6 +20,7 @@
 #include "field.h"
 #include "field-class.h"
 #include "lib/func-status.h"
+#include "utils.h"
 
 #define BT_ASSERT_PRE_DEV_FIELD_HOT(_field)                            \
        BT_ASSERT_PRE_DEV_HOT("field",                                  \
@@ -191,12 +191,14 @@ void destroy_option_field(struct bt_field *field);
 static
 void destroy_variant_field(struct bt_field *field);
 
+BT_EXPORT
 struct bt_field_class *bt_field_borrow_class(struct bt_field *field)
 {
        BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
        return field->class;
 }
 
+BT_EXPORT
 const struct bt_field_class *bt_field_borrow_class_const(
                const struct bt_field *field)
 {
@@ -204,13 +206,13 @@ const struct bt_field_class *bt_field_borrow_class_const(
        return field->class;
 }
 
+BT_EXPORT
 enum bt_field_class_type bt_field_get_class_type(const struct bt_field *field)
 {
        BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
        return field->class->type;
 }
 
-BT_HIDDEN
 struct bt_field *bt_field_create(struct bt_field_class *fc)
 {
        struct bt_field *field = NULL;
@@ -386,7 +388,8 @@ struct bt_field *create_string_field(struct bt_field_class *fc)
                goto end;
        }
 
-       g_array_index(string_field->buf, char, 0) = '\0';
+       g_array_set_size(string_field->buf, 1);
+       bt_g_array_index(string_field->buf, char, 0) = '\0';
        BT_LIB_LOGD("Created string field object: %!+f", string_field);
 
 end:
@@ -620,6 +623,7 @@ end:
        return (void *) array_field;
 }
 
+BT_EXPORT
 bt_bool bt_field_bool_get_value(const struct bt_field *field)
 {
        const struct bt_field_bool *bool_field = (const void *) field;
@@ -631,6 +635,7 @@ bt_bool bt_field_bool_get_value(const struct bt_field *field)
        return (bt_bool) bool_field->value;
 }
 
+BT_EXPORT
 void bt_field_bool_set_value(struct bt_field *field, bt_bool value)
 {
        struct bt_field_bool *bool_field = (void *) field;
@@ -643,6 +648,7 @@ void bt_field_bool_set_value(struct bt_field *field, bt_bool value)
        bt_field_set_single(field, true);
 }
 
+BT_EXPORT
 uint64_t bt_field_bit_array_get_value_as_integer(const struct bt_field *field)
 {
        const struct bt_field_bit_array *ba_field = (const void *) field;
@@ -654,6 +660,7 @@ uint64_t bt_field_bit_array_get_value_as_integer(const struct bt_field *field)
        return ba_field->value_as_int;
 }
 
+BT_EXPORT
 void bt_field_bit_array_set_value_as_integer(struct bt_field *field,
                uint64_t value)
 {
@@ -675,6 +682,7 @@ void bt_field_bit_array_set_value_as_integer(struct bt_field *field,
        bt_field_set_single(field, true);
 }
 
+BT_EXPORT
 int64_t bt_field_integer_signed_get_value(const struct bt_field *field)
 {
        const struct bt_field_integer *int_field = (const void *) field;
@@ -685,6 +693,7 @@ int64_t bt_field_integer_signed_get_value(const struct bt_field *field)
        return int_field->value.i;
 }
 
+BT_EXPORT
 void bt_field_integer_signed_set_value(struct bt_field *field, int64_t value)
 {
        struct bt_field_integer *int_field = (void *) field;
@@ -702,6 +711,7 @@ void bt_field_integer_signed_set_value(struct bt_field *field, int64_t value)
        bt_field_set_single(field, true);
 }
 
+BT_EXPORT
 uint64_t bt_field_integer_unsigned_get_value(const struct bt_field *field)
 {
        const struct bt_field_integer *int_field = (const void *) field;
@@ -712,6 +722,7 @@ uint64_t bt_field_integer_unsigned_get_value(const struct bt_field *field)
        return int_field->value.u;
 }
 
+BT_EXPORT
 void bt_field_integer_unsigned_set_value(struct bt_field *field, uint64_t value)
 {
        struct bt_field_integer *int_field = (void *) field;
@@ -729,6 +740,7 @@ void bt_field_integer_unsigned_set_value(struct bt_field *field, uint64_t value)
        bt_field_set_single(field, true);
 }
 
+BT_EXPORT
 float bt_field_real_single_precision_get_value(const struct bt_field *field)
 {
        const struct bt_field_real *real_field = (const void *) field;
@@ -741,6 +753,7 @@ float bt_field_real_single_precision_get_value(const struct bt_field *field)
        return (float) real_field->value;
 }
 
+BT_EXPORT
 double bt_field_real_double_precision_get_value(const struct bt_field *field)
 {
        const struct bt_field_real *real_field = (const void *) field;
@@ -753,6 +766,7 @@ double bt_field_real_double_precision_get_value(const struct bt_field *field)
        return real_field->value;
 }
 
+BT_EXPORT
 void bt_field_real_single_precision_set_value(struct bt_field *field,
                float value)
 {
@@ -768,6 +782,7 @@ void bt_field_real_single_precision_set_value(struct bt_field *field,
        bt_field_set_single(field, true);
 }
 
+BT_EXPORT
 void bt_field_real_double_precision_set_value(struct bt_field *field,
                double value)
 {
@@ -783,6 +798,7 @@ void bt_field_real_double_precision_set_value(struct bt_field *field,
        bt_field_set_single(field, true);
 }
 
+BT_EXPORT
 enum bt_field_enumeration_get_mapping_labels_status
 bt_field_enumeration_unsigned_get_mapping_labels(
                const struct bt_field *field,
@@ -805,6 +821,7 @@ bt_field_enumeration_unsigned_get_mapping_labels(
                        field->class, int_field->value.u, label_array, count);
 }
 
+BT_EXPORT
 enum bt_field_enumeration_get_mapping_labels_status
 bt_field_enumeration_signed_get_mapping_labels(
                const struct bt_field *field,
@@ -827,6 +844,7 @@ bt_field_enumeration_signed_get_mapping_labels(
                        field->class, int_field->value.i, label_array, count);
 }
 
+BT_EXPORT
 const char *bt_field_string_get_value(const struct bt_field *field)
 {
        const struct bt_field_string *string_field = (const void *) field;
@@ -838,6 +856,7 @@ const char *bt_field_string_get_value(const struct bt_field *field)
        return (const char *) string_field->buf->data;
 }
 
+BT_EXPORT
 uint64_t bt_field_string_get_length(const struct bt_field *field)
 {
        const struct bt_field_string *string_field = (const void *) field;
@@ -856,10 +875,11 @@ void clear_string_field(struct bt_field *field)
 
        BT_ASSERT_DBG(field);
        string_field->length = 0;
-       g_array_index(string_field->buf, char, 0) = '\0';
+       bt_g_array_index(string_field->buf, char, 0) = '\0';
        bt_field_set_single(field, true);
 }
 
+BT_EXPORT
 enum bt_field_string_set_value_status bt_field_string_set_value(
                struct bt_field *field, const char *value)
 {
@@ -914,6 +934,7 @@ enum bt_field_string_append_status append_to_string_field_with_length(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_field_string_append_status bt_field_string_append_with_length(
                struct bt_field *field, const char *value, uint64_t length)
 {
@@ -922,6 +943,7 @@ enum bt_field_string_append_status bt_field_string_append_with_length(
        return append_to_string_field_with_length(field, value, length);
 }
 
+BT_EXPORT
 enum bt_field_string_append_status bt_field_string_append(
                struct bt_field *field, const char *value)
 {
@@ -932,6 +954,7 @@ enum bt_field_string_append_status bt_field_string_append(
        return append_to_string_field_with_length(field, value, length);
 }
 
+BT_EXPORT
 void bt_field_string_clear(struct bt_field *field)
 {
        BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
@@ -941,6 +964,7 @@ void bt_field_string_clear(struct bt_field *field)
        clear_string_field(field);
 }
 
+BT_EXPORT
 uint64_t bt_field_array_get_length(const struct bt_field *field)
 {
        const struct bt_field_array *array_field = (const void *) field;
@@ -950,6 +974,7 @@ uint64_t bt_field_array_get_length(const struct bt_field *field)
        return array_field->length;
 }
 
+BT_EXPORT
 enum bt_field_array_dynamic_set_length_status bt_field_array_dynamic_set_length(
                struct bt_field *field, uint64_t length)
 {
@@ -1009,6 +1034,7 @@ struct bt_field *borrow_array_field_element_field_by_index(
        return array_field->fields->pdata[index];
 }
 
+BT_EXPORT
 struct bt_field *bt_field_array_borrow_element_field_by_index(
                struct bt_field *field, uint64_t index)
 {
@@ -1016,6 +1042,7 @@ struct bt_field *bt_field_array_borrow_element_field_by_index(
                __func__);
 }
 
+BT_EXPORT
 const struct bt_field *
 bt_field_array_borrow_element_field_by_index_const(
                const struct bt_field *field, uint64_t index)
@@ -1039,6 +1066,7 @@ struct bt_field *borrow_structure_field_member_field_by_index(
        return struct_field->fields->pdata[index];
 }
 
+BT_EXPORT
 struct bt_field *bt_field_structure_borrow_member_field_by_index(
                struct bt_field *field, uint64_t index)
 {
@@ -1046,6 +1074,7 @@ struct bt_field *bt_field_structure_borrow_member_field_by_index(
                index, __func__);
 }
 
+BT_EXPORT
 const struct bt_field *
 bt_field_structure_borrow_member_field_by_index_const(
                const struct bt_field *field, uint64_t index)
@@ -1084,6 +1113,7 @@ end:
        return ret_field;
 }
 
+BT_EXPORT
 struct bt_field *bt_field_structure_borrow_member_field_by_name(
                struct bt_field *field, const char *name)
 {
@@ -1091,6 +1121,7 @@ struct bt_field *bt_field_structure_borrow_member_field_by_name(
                __func__);
 }
 
+BT_EXPORT
 const struct bt_field *bt_field_structure_borrow_member_field_by_name_const(
                const struct bt_field *field, const char *name)
 {
@@ -1098,6 +1129,7 @@ const struct bt_field *bt_field_structure_borrow_member_field_by_name_const(
                (void *) field, name, __func__);
 }
 
+BT_EXPORT
 void bt_field_option_set_has_field(struct bt_field *field, bt_bool has_field)
 {
        struct bt_field_option *opt_field = (void *) field;
@@ -1113,6 +1145,7 @@ void bt_field_option_set_has_field(struct bt_field *field, bt_bool has_field)
        }
 }
 
+BT_EXPORT
 struct bt_field *bt_field_option_borrow_field(struct bt_field *field)
 {
        struct bt_field_option *opt_field = (void *) field;
@@ -1122,6 +1155,7 @@ struct bt_field *bt_field_option_borrow_field(struct bt_field *field)
        return opt_field->selected_field;
 }
 
+BT_EXPORT
 const struct bt_field *bt_field_option_borrow_field_const(
                const struct bt_field *field)
 {
@@ -1150,6 +1184,7 @@ struct bt_field *borrow_variant_field_selected_option_field(
        return var_field->selected_field;
 }
 
+BT_EXPORT
 struct bt_field *bt_field_variant_borrow_selected_option_field(
                struct bt_field *field)
 {
@@ -1157,6 +1192,7 @@ struct bt_field *bt_field_variant_borrow_selected_option_field(
        return borrow_variant_field_selected_option_field(field);
 }
 
+BT_EXPORT
 const struct bt_field *bt_field_variant_borrow_selected_option_field_const(
                const struct bt_field *field)
 {
@@ -1185,6 +1221,7 @@ borrow_variant_field_selected_class_option(const struct bt_field *field)
        return container_fc->named_fcs->pdata[var_field->selected_index];
 }
 
+BT_EXPORT
 const struct bt_field_class_variant_option *
 bt_field_variant_borrow_selected_option_class_const(
                const struct bt_field *field)
@@ -1195,6 +1232,7 @@ bt_field_variant_borrow_selected_option_class_const(
        return borrow_variant_field_selected_class_option(field);
 }
 
+BT_EXPORT
 const struct bt_field_class_variant_with_selector_field_integer_unsigned_option *
 bt_field_variant_with_selector_field_integer_unsigned_borrow_selected_option_class_const(
                const struct bt_field *field)
@@ -1208,6 +1246,7 @@ bt_field_variant_with_selector_field_integer_unsigned_borrow_selected_option_cla
        return (const void *) borrow_variant_field_selected_class_option(field);
 }
 
+BT_EXPORT
 const struct bt_field_class_variant_with_selector_field_integer_signed_option *
 bt_field_variant_with_selector_field_integer_signed_borrow_selected_option_class_const(
                const struct bt_field *field)
@@ -1221,6 +1260,7 @@ bt_field_variant_with_selector_field_integer_signed_borrow_selected_option_class
        return (const void *) borrow_variant_field_selected_class_option(field);
 }
 
+BT_EXPORT
 enum bt_field_variant_select_option_by_index_status
 bt_field_variant_select_option_by_index(
                struct bt_field *field, uint64_t index)
@@ -1237,6 +1277,7 @@ bt_field_variant_select_option_by_index(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 uint64_t bt_field_variant_get_selected_option_index(
                const struct bt_field *field)
 {
@@ -1377,7 +1418,6 @@ void destroy_string_field(struct bt_field *field)
        g_free(field);
 }
 
-BT_HIDDEN
 void bt_field_destroy(struct bt_field *field)
 {
        BT_ASSERT(field);
@@ -1562,7 +1602,6 @@ void set_array_field_is_frozen(struct bt_field *field, bool is_frozen)
        set_single_field_is_frozen(field, is_frozen);
 }
 
-BT_HIDDEN
 void _bt_field_set_is_frozen(const struct bt_field *field,
                bool is_frozen)
 {
index df303b662e379993eec73b6c923c49d4fa5a5acc..87446b26fc1b782baa1572ebb5234daae7372344 100644 (file)
@@ -8,19 +8,13 @@
 #ifndef BABELTRACE_TRACE_IR_FIELDS_INTERNAL_H
 #define BABELTRACE_TRACE_IR_FIELDS_INTERNAL_H
 
-#include "lib/assert-cond.h"
-#include "common/common.h"
 #include "lib/object.h"
-#include "common/macros.h"
 #include <babeltrace2/types.h>
 #include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
 #include <stdbool.h>
 #include <glib.h>
 
 #include "field-class.h"
-#include "utils.h"
 
 struct bt_field;
 
@@ -130,7 +124,6 @@ struct bt_field_string {
 # define bt_field_set_single(_field, _val)
 #endif
 
-BT_HIDDEN
 void _bt_field_set_is_frozen(const struct bt_field *field, bool is_frozen);
 
 static inline
@@ -164,10 +157,8 @@ end:
        return is_set;
 }
 
-BT_HIDDEN
 struct bt_field *bt_field_create(struct bt_field_class *class);
 
-BT_HIDDEN
 void bt_field_destroy(struct bt_field *field);
 
 #endif /* BABELTRACE_TRACE_IR_FIELDS_INTERNAL_H */
index 96cbae019e54d11dda7dd80c427d560b7538fa49..3d8ab27aecafb1ca0d249707f0ed7d321b6e8024 100644 (file)
@@ -14,7 +14,6 @@
 #include <babeltrace2/trace-ir/stream.h>
 #include "lib/object.h"
 #include "common/assert.h"
-#include <inttypes.h>
 #include <stdbool.h>
 
 #include "field.h"
 #include "packet.h"
 #include "stream-class.h"
 #include "stream.h"
-#include "trace.h"
-#include "lib/func-status.h"
 
+BT_EXPORT
 struct bt_stream *bt_packet_borrow_stream(struct bt_packet *packet)
 {
        BT_ASSERT_PRE_DEV_PACKET_NON_NULL(packet);
        return packet->stream;
 }
 
+BT_EXPORT
 const struct bt_stream *bt_packet_borrow_stream_const(
                const struct bt_packet *packet)
 {
        return bt_packet_borrow_stream((void *) packet);
 }
 
+BT_EXPORT
 struct bt_field *bt_packet_borrow_context_field(struct bt_packet *packet)
 {
        BT_ASSERT_PRE_DEV_PACKET_NON_NULL(packet);
        return packet->context_field ? packet->context_field->field : NULL;
 }
 
+BT_EXPORT
 const struct bt_field *bt_packet_borrow_context_field_const(
                const struct bt_packet *packet)
 {
        return bt_packet_borrow_context_field((void *) packet);
 }
 
-BT_HIDDEN
 void _bt_packet_set_is_frozen(const struct bt_packet *packet, bool is_frozen)
 {
        if (!packet) {
@@ -93,7 +93,6 @@ void recycle_context_field(struct bt_field_wrapper *context_field,
                context_field);
 }
 
-BT_HIDDEN
 void bt_packet_recycle(struct bt_packet *packet)
 {
        struct bt_stream *stream;
@@ -131,7 +130,6 @@ void bt_packet_recycle(struct bt_packet *packet)
        bt_object_put_ref_no_null_check(&stream->base);
 }
 
-BT_HIDDEN
 void bt_packet_destroy(struct bt_packet *packet)
 {
        BT_LIB_LOGD("Destroying packet: %!+a", packet);
@@ -153,7 +151,6 @@ void bt_packet_destroy(struct bt_packet *packet)
        g_free(packet);
 }
 
-BT_HIDDEN
 struct bt_packet *bt_packet_new(struct bt_stream *stream)
 {
        struct bt_packet *packet = NULL;
@@ -197,6 +194,7 @@ end:
        return packet;
 }
 
+BT_EXPORT
 struct bt_packet *bt_packet_create(const struct bt_stream *c_stream)
 {
        struct bt_packet *packet = NULL;
@@ -226,11 +224,13 @@ end:
        return (void *) packet;
 }
 
+BT_EXPORT
 void bt_packet_get_ref(const struct bt_packet *packet)
 {
        bt_object_get_ref(packet);
 }
 
+BT_EXPORT
 void bt_packet_put_ref(const struct bt_packet *packet)
 {
        bt_object_put_ref(packet);
index 752611a06f137ea669397302c3122f61c2559e6e..c22e7c9f47beaf4083cea89b870a3b7e53e24247 100644 (file)
@@ -8,14 +8,11 @@
 #define BABELTRACE_TRACE_IR_PACKET_INTERNAL_H
 
 #include <stdbool.h>
-#include "common/assert.h"
 #include <babeltrace2/trace-ir/clock-snapshot.h>
 #include <babeltrace2/trace-ir/packet.h>
 #include <babeltrace2/trace-ir/field.h>
 #include <babeltrace2/trace-ir/stream.h>
 #include "lib/object.h"
-#include "common/macros.h"
-#include "lib/property.h"
 
 #include "field-wrapper.h"
 
@@ -26,7 +23,6 @@ struct bt_packet {
        bool frozen;
 };
 
-BT_HIDDEN
 void _bt_packet_set_is_frozen(const struct bt_packet *packet, bool is_frozen);
 
 #ifdef BT_DEV_MODE
@@ -35,13 +31,10 @@ void _bt_packet_set_is_frozen(const struct bt_packet *packet, bool is_frozen);
 # define bt_packet_set_is_frozen(_packet, _is_frozen)
 #endif /* BT_DEV_MODE */
 
-BT_HIDDEN
 struct bt_packet *bt_packet_new(struct bt_stream *stream);
 
-BT_HIDDEN
 void bt_packet_recycle(struct bt_packet *packet);
 
-BT_HIDDEN
 void bt_packet_destroy(struct bt_packet *packet);
 
 #endif /* BABELTRACE_TRACE_IR_PACKET_INTERNAL_H */
index 51719387e97624cc3d0af638254653d87243b5be..6c60a57e7c4dfeedf76f204b60c7a168f4490ad5 100644 (file)
 #include "lib/assert-cond.h"
 #include "common/assert.h"
 #include <babeltrace2/trace-ir/field-path.h>
-#include <limits.h>
 #include <stdbool.h>
 #include <stdint.h>
-#include <inttypes.h>
 #include <glib.h>
 
 #include "field-class.h"
 #include "field-path.h"
 #include "resolve-field-path.h"
+#include "common/common.h"
 
 static
 bool find_field_class_recursive(struct bt_field_class *fc,
@@ -506,7 +505,6 @@ struct bt_field_path *resolve_field_path(struct bt_field_class *src_fc,
        return find_field_class_in_ctx(tgt_fc, ctx);
 }
 
-BT_HIDDEN
 int bt_resolve_field_paths(struct bt_field_class *fc,
                struct bt_resolve_field_path_context *ctx,
                const char *api_func)
index 4cd436a326ced1dda982b897b23811ecb912ee1a..a0df1cd5d47e7ae5409272db48fa14f6eb3481d7 100644 (file)
@@ -10,8 +10,6 @@
 #ifndef BABELTRACE_TRACE_IR_RESOLVE_FIELD_PATH_INTERNAL
 #define BABELTRACE_TRACE_IR_RESOLVE_FIELD_PATH_INTERNAL
 
-#include "common/macros.h"
-#include "lib/object.h"
 #include <babeltrace2/trace-ir/field-class.h>
 #include <babeltrace2/trace-ir/field-path.h>
 #include <glib.h>
@@ -23,7 +21,6 @@ struct bt_resolve_field_path_context {
        struct bt_field_class *event_payload;
 };
 
-BT_HIDDEN
 int bt_resolve_field_paths(struct bt_field_class *field_class,
                struct bt_resolve_field_path_context *ctx,
                const char *api_func);
index 73d96f0e8a67e9b4284072caaea7080abfbc4d4f..08395849b03c9331fde5cfe8b7422d00ccb6f077 100644 (file)
 #include "lib/assert-cond.h"
 #include <babeltrace2/trace-ir/trace.h>
 #include "compat/compiler.h"
-#include "common/align.h"
 #include "compat/endian.h"
 #include "common/assert.h"
-#include "lib/property.h"
 #include <inttypes.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include "clock-class.h"
 #include "event-class.h"
 #include "field-class.h"
-#include "field.h"
 #include "field-wrapper.h"
 #include "resolve-field-path.h"
 #include "stream-class.h"
-#include "trace.h"
-#include "utils.h"
 #include "lib/value.h"
 #include "lib/func-status.h"
+#include "trace-class.h"
 
 #define BT_ASSERT_PRE_DEV_STREAM_CLASS_HOT(_sc)                                \
        BT_ASSERT_PRE_DEV_HOT("stream-class", (_sc), "Stream class",    \
@@ -67,7 +63,7 @@ void destroy_stream_class(struct bt_object *obj)
 
 static
 void free_field_wrapper(struct bt_field_wrapper *field_wrapper,
-               struct bt_stream_class *stream_class)
+               struct bt_stream_class *stream_class __attribute__((unused)))
 {
        bt_field_wrapper_destroy((void *) field_wrapper);
 }
@@ -161,6 +157,7 @@ end:
        return stream_class;
 }
 
+BT_EXPORT
 struct bt_stream_class *bt_stream_class_create(struct bt_trace_class *tc)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -173,6 +170,7 @@ struct bt_stream_class *bt_stream_class_create(struct bt_trace_class *tc)
                (uint64_t) tc->stream_classes->len);
 }
 
+BT_EXPORT
 struct bt_stream_class *bt_stream_class_create_with_id(
                struct bt_trace_class *tc, uint64_t id)
 {
@@ -186,6 +184,7 @@ struct bt_stream_class *bt_stream_class_create_with_id(
        return create_stream_class_with_id(tc, id);
 }
 
+BT_EXPORT
 struct bt_trace_class *bt_stream_class_borrow_trace_class(
                struct bt_stream_class *stream_class)
 {
@@ -193,18 +192,21 @@ struct bt_trace_class *bt_stream_class_borrow_trace_class(
        return bt_stream_class_borrow_trace_class_inline(stream_class);
 }
 
+BT_EXPORT
 const struct bt_trace_class *bt_stream_class_borrow_trace_class_const(
                const struct bt_stream_class *stream_class)
 {
        return bt_stream_class_borrow_trace_class((void *) stream_class);
 }
 
+BT_EXPORT
 const char *bt_stream_class_get_name(const struct bt_stream_class *stream_class)
 {
        BT_ASSERT_PRE_DEV_SC_NON_NULL(stream_class);
        return stream_class->name.value;
 }
 
+BT_EXPORT
 enum bt_stream_class_set_name_status bt_stream_class_set_name(
                struct bt_stream_class *stream_class,
                const char *name)
@@ -219,12 +221,14 @@ enum bt_stream_class_set_name_status bt_stream_class_set_name(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 uint64_t bt_stream_class_get_id(const struct bt_stream_class *stream_class)
 {
        BT_ASSERT_PRE_DEV_SC_NON_NULL(stream_class);
        return stream_class->id;
 }
 
+BT_EXPORT
 uint64_t bt_stream_class_get_event_class_count(
                const struct bt_stream_class *stream_class)
 {
@@ -232,6 +236,7 @@ uint64_t bt_stream_class_get_event_class_count(
        return (uint64_t) stream_class->event_classes->len;
 }
 
+BT_EXPORT
 struct bt_event_class *bt_stream_class_borrow_event_class_by_index(
                struct bt_stream_class *stream_class, uint64_t index)
 {
@@ -240,6 +245,7 @@ struct bt_event_class *bt_stream_class_borrow_event_class_by_index(
        return g_ptr_array_index(stream_class->event_classes, index);
 }
 
+BT_EXPORT
 const struct bt_event_class *
 bt_stream_class_borrow_event_class_by_index_const(
                const struct bt_stream_class *stream_class, uint64_t index)
@@ -248,6 +254,7 @@ bt_stream_class_borrow_event_class_by_index_const(
                (void *) stream_class, index);
 }
 
+BT_EXPORT
 struct bt_event_class *bt_stream_class_borrow_event_class_by_id(
                struct bt_stream_class *stream_class, uint64_t id)
 {
@@ -270,6 +277,7 @@ end:
        return event_class;
 }
 
+BT_EXPORT
 const struct bt_event_class *
 bt_stream_class_borrow_event_class_by_id_const(
                const struct bt_stream_class *stream_class, uint64_t id)
@@ -278,6 +286,7 @@ bt_stream_class_borrow_event_class_by_id_const(
                (void *) stream_class, id);
 }
 
+BT_EXPORT
 const struct bt_field_class *
 bt_stream_class_borrow_packet_context_field_class_const(
                const struct bt_stream_class *stream_class)
@@ -286,6 +295,7 @@ bt_stream_class_borrow_packet_context_field_class_const(
        return stream_class->packet_context_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *
 bt_stream_class_borrow_packet_context_field_class(
                struct bt_stream_class *stream_class)
@@ -294,6 +304,7 @@ bt_stream_class_borrow_packet_context_field_class(
        return stream_class->packet_context_fc;
 }
 
+BT_EXPORT
 enum bt_stream_class_set_field_class_status
 bt_stream_class_set_packet_context_field_class(
                struct bt_stream_class *stream_class,
@@ -340,6 +351,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 const struct bt_field_class *
 bt_stream_class_borrow_event_common_context_field_class_const(
                const struct bt_stream_class *stream_class)
@@ -348,6 +360,7 @@ bt_stream_class_borrow_event_common_context_field_class_const(
        return stream_class->event_common_context_fc;
 }
 
+BT_EXPORT
 struct bt_field_class *
 bt_stream_class_borrow_event_common_context_field_class(
                struct bt_stream_class *stream_class)
@@ -356,6 +369,7 @@ bt_stream_class_borrow_event_common_context_field_class(
        return stream_class->event_common_context_fc;
 }
 
+BT_EXPORT
 enum bt_stream_class_set_field_class_status
 bt_stream_class_set_event_common_context_field_class(
                struct bt_stream_class *stream_class,
@@ -399,7 +413,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void _bt_stream_class_freeze(const struct bt_stream_class *stream_class)
 {
        /* The field classes and default clock class are already frozen */
@@ -411,6 +424,7 @@ void _bt_stream_class_freeze(const struct bt_stream_class *stream_class)
        ((struct bt_stream_class *) stream_class)->frozen = true;
 }
 
+BT_EXPORT
 enum bt_stream_class_set_default_clock_class_status
 bt_stream_class_set_default_clock_class(
                struct bt_stream_class *stream_class,
@@ -429,6 +443,7 @@ bt_stream_class_set_default_clock_class(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 struct bt_clock_class *bt_stream_class_borrow_default_clock_class(
                struct bt_stream_class *stream_class)
 {
@@ -436,6 +451,7 @@ struct bt_clock_class *bt_stream_class_borrow_default_clock_class(
        return stream_class->default_clock_class;
 }
 
+BT_EXPORT
 const struct bt_clock_class *bt_stream_class_borrow_default_clock_class_const(
                const struct bt_stream_class *stream_class)
 {
@@ -443,6 +459,7 @@ const struct bt_clock_class *bt_stream_class_borrow_default_clock_class_const(
        return stream_class->default_clock_class;
 }
 
+BT_EXPORT
 bt_bool bt_stream_class_assigns_automatic_event_class_id(
                const struct bt_stream_class *stream_class)
 {
@@ -450,6 +467,7 @@ bt_bool bt_stream_class_assigns_automatic_event_class_id(
        return (bt_bool) stream_class->assigns_automatic_event_class_id;
 }
 
+BT_EXPORT
 void bt_stream_class_set_assigns_automatic_event_class_id(
                struct bt_stream_class *stream_class,
                bt_bool value)
@@ -461,6 +479,7 @@ void bt_stream_class_set_assigns_automatic_event_class_id(
                "assignment property: %!+S", stream_class);
 }
 
+BT_EXPORT
 bt_bool bt_stream_class_assigns_automatic_stream_id(
                const struct bt_stream_class *stream_class)
 {
@@ -468,6 +487,7 @@ bt_bool bt_stream_class_assigns_automatic_stream_id(
        return (bt_bool) stream_class->assigns_automatic_stream_id;
 }
 
+BT_EXPORT
 void bt_stream_class_set_supports_discarded_events(
                struct bt_stream_class *stream_class,
                bt_bool supports_discarded_events,
@@ -492,6 +512,7 @@ void bt_stream_class_set_supports_discarded_events(
                "%!+S", stream_class);
 }
 
+BT_EXPORT
 bt_bool bt_stream_class_supports_discarded_events(
                const struct bt_stream_class *stream_class)
 {
@@ -499,6 +520,7 @@ bt_bool bt_stream_class_supports_discarded_events(
        return (bt_bool) stream_class->supports_discarded_events;
 }
 
+BT_EXPORT
 bt_bool bt_stream_class_discarded_events_have_default_clock_snapshots(
                const struct bt_stream_class *stream_class)
 {
@@ -506,6 +528,7 @@ bt_bool bt_stream_class_discarded_events_have_default_clock_snapshots(
        return (bt_bool) stream_class->discarded_events_have_default_clock_snapshots;
 }
 
+BT_EXPORT
 void bt_stream_class_set_supports_discarded_packets(
                struct bt_stream_class *stream_class,
                bt_bool supports_discarded_packets,
@@ -535,6 +558,7 @@ void bt_stream_class_set_supports_discarded_packets(
                "%!+S", stream_class);
 }
 
+BT_EXPORT
 bt_bool bt_stream_class_supports_discarded_packets(
                const struct bt_stream_class *stream_class)
 {
@@ -542,6 +566,7 @@ bt_bool bt_stream_class_supports_discarded_packets(
        return (bt_bool) stream_class->supports_discarded_packets;
 }
 
+BT_EXPORT
 bt_bool bt_stream_class_discarded_packets_have_default_clock_snapshots(
                const struct bt_stream_class *stream_class)
 {
@@ -549,6 +574,7 @@ bt_bool bt_stream_class_discarded_packets_have_default_clock_snapshots(
        return (bt_bool) stream_class->discarded_packets_have_default_clock_snapshots;
 }
 
+BT_EXPORT
 void bt_stream_class_set_supports_packets(
                struct bt_stream_class *stream_class,
                bt_bool supports_packets,
@@ -586,6 +612,7 @@ void bt_stream_class_set_supports_packets(
                stream_class);
 }
 
+BT_EXPORT
 bt_bool bt_stream_class_supports_packets(
                const struct bt_stream_class *stream_class)
 {
@@ -593,6 +620,7 @@ bt_bool bt_stream_class_supports_packets(
        return (bt_bool) stream_class->supports_packets;
 }
 
+BT_EXPORT
 bt_bool bt_stream_class_packets_have_beginning_default_clock_snapshot(
                const struct bt_stream_class *stream_class)
 {
@@ -600,6 +628,7 @@ bt_bool bt_stream_class_packets_have_beginning_default_clock_snapshot(
        return (bt_bool) stream_class->packets_have_beginning_default_clock_snapshot;
 }
 
+BT_EXPORT
 bt_bool bt_stream_class_packets_have_end_default_clock_snapshot(
                const struct bt_stream_class *stream_class)
 {
@@ -607,6 +636,7 @@ bt_bool bt_stream_class_packets_have_end_default_clock_snapshot(
        return (bt_bool) stream_class->packets_have_end_default_clock_snapshot;
 }
 
+BT_EXPORT
 void bt_stream_class_set_assigns_automatic_stream_id(
                struct bt_stream_class *stream_class,
                bt_bool value)
@@ -618,6 +648,7 @@ void bt_stream_class_set_assigns_automatic_stream_id(
                "assignment property: %!+S", stream_class);
 }
 
+BT_EXPORT
 const struct bt_value *bt_stream_class_borrow_user_attributes_const(
                const struct bt_stream_class *stream_class)
 {
@@ -625,6 +656,7 @@ const struct bt_value *bt_stream_class_borrow_user_attributes_const(
        return stream_class->user_attributes;
 }
 
+BT_EXPORT
 struct bt_value *bt_stream_class_borrow_user_attributes(
                struct bt_stream_class *stream_class)
 {
@@ -632,6 +664,7 @@ struct bt_value *bt_stream_class_borrow_user_attributes(
                (void *) stream_class);
 }
 
+BT_EXPORT
 void bt_stream_class_set_user_attributes(
                struct bt_stream_class *stream_class,
                const struct bt_value *user_attributes)
@@ -645,11 +678,13 @@ void bt_stream_class_set_user_attributes(
        bt_object_get_ref_no_null_check(stream_class->user_attributes);
 }
 
+BT_EXPORT
 void bt_stream_class_get_ref(const struct bt_stream_class *stream_class)
 {
        bt_object_get_ref(stream_class);
 }
 
+BT_EXPORT
 void bt_stream_class_put_ref(const struct bt_stream_class *stream_class)
 {
        bt_object_put_ref(stream_class);
index 16573ff939329cde5625bd2c85ff65925ef56910..771a5899197d22161e0098f7f95b4e4de05f25c5 100644 (file)
@@ -9,17 +9,13 @@
 #define BABELTRACE_TRACE_IR_STREAM_CLASS_INTERNAL_H
 
 #include "common/assert.h"
-#include "common/common.h"
 #include <babeltrace2/trace-ir/stream-class.h>
 #include "lib/object.h"
 #include "lib/object-pool.h"
-#include "common/macros.h"
 #include <glib.h>
-#include <inttypes.h>
 #include <stdbool.h>
 
 #include "field-class.h"
-#include "utils.h"
 
 struct bt_stream_class {
        struct bt_object base;
@@ -57,7 +53,6 @@ struct bt_stream_class {
        bool frozen;
 };
 
-BT_HIDDEN
 void _bt_stream_class_freeze(const struct bt_stream_class *stream_class);
 
 #ifdef BT_DEV_MODE
index fcfe3d4b7ec58bdac76c0e38da9a6dfb5136832f..7cb124f425bfe5661436d94a38169952e582f297 100644 (file)
@@ -13,9 +13,7 @@
 #include <babeltrace2/trace-ir/stream-class.h>
 #include <babeltrace2/trace-ir/trace.h>
 #include "compat/compiler.h"
-#include "common/align.h"
 #include "common/assert.h"
-#include "lib/property.h"
 #include <inttypes.h>
 #include <stdbool.h>
 #include <unistd.h>
@@ -52,7 +50,8 @@ void destroy_stream(struct bt_object *obj)
 }
 
 static
-void bt_stream_free_packet(struct bt_packet *packet, struct bt_stream *stream)
+void bt_stream_free_packet(struct bt_packet *packet,
+               struct bt_stream *stream __attribute__((unused)))
 {
        bt_packet_destroy(packet);
 }
@@ -149,6 +148,7 @@ end:
        return stream;
 }
 
+BT_EXPORT
 struct bt_stream *bt_stream_create(struct bt_stream_class *stream_class,
                struct bt_trace *trace)
 {
@@ -165,6 +165,7 @@ struct bt_stream *bt_stream_create(struct bt_stream_class *stream_class,
        return create_stream_with_id(stream_class, trace, id, __func__);
 }
 
+BT_EXPORT
 struct bt_stream *bt_stream_create_with_id(struct bt_stream_class *stream_class,
                struct bt_trace *trace, uint64_t id)
 {
@@ -178,36 +179,42 @@ struct bt_stream *bt_stream_create_with_id(struct bt_stream_class *stream_class,
        return create_stream_with_id(stream_class, trace, id, __func__);
 }
 
+BT_EXPORT
 struct bt_stream_class *bt_stream_borrow_class(struct bt_stream *stream)
 {
        BT_ASSERT_PRE_DEV_STREAM_NON_NULL(stream);
        return stream->class;
 }
 
+BT_EXPORT
 const struct bt_stream_class *bt_stream_borrow_class_const(
                const struct bt_stream *stream)
 {
        return bt_stream_borrow_class((void *) stream);
 }
 
+BT_EXPORT
 struct bt_trace *bt_stream_borrow_trace(struct bt_stream *stream)
 {
        BT_ASSERT_PRE_DEV_STREAM_NON_NULL(stream);
        return bt_stream_borrow_trace_inline(stream);
 }
 
+BT_EXPORT
 const struct bt_trace *bt_stream_borrow_trace_const(
                const struct bt_stream *stream)
 {
        return bt_stream_borrow_trace((void *) stream);
 }
 
+BT_EXPORT
 const char *bt_stream_get_name(const struct bt_stream *stream)
 {
        BT_ASSERT_PRE_DEV_STREAM_NON_NULL(stream);
        return stream->name.value;
 }
 
+BT_EXPORT
 enum bt_stream_set_name_status bt_stream_set_name(struct bt_stream *stream,
                const char *name)
 {
@@ -221,13 +228,13 @@ enum bt_stream_set_name_status bt_stream_set_name(struct bt_stream *stream,
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 uint64_t bt_stream_get_id(const struct bt_stream *stream)
 {
        BT_ASSERT_PRE_DEV_SC_NON_NULL(stream);
        return stream->id;
 }
 
-BT_HIDDEN
 void _bt_stream_freeze(const struct bt_stream *stream)
 {
        BT_ASSERT(stream);
@@ -238,6 +245,7 @@ void _bt_stream_freeze(const struct bt_stream *stream)
        ((struct bt_stream *) stream)->frozen = true;
 }
 
+BT_EXPORT
 const struct bt_value *bt_stream_borrow_user_attributes_const(
                const struct bt_stream *stream)
 {
@@ -245,11 +253,13 @@ const struct bt_value *bt_stream_borrow_user_attributes_const(
        return stream->user_attributes;
 }
 
+BT_EXPORT
 struct bt_value *bt_stream_borrow_user_attributes(struct bt_stream *stream)
 {
        return (void *) bt_stream_borrow_user_attributes_const((void *) stream);
 }
 
+BT_EXPORT
 void bt_stream_set_user_attributes(struct bt_stream *stream,
                const struct bt_value *user_attributes)
 {
@@ -262,11 +272,13 @@ void bt_stream_set_user_attributes(struct bt_stream *stream,
        bt_object_get_ref_no_null_check(stream->user_attributes);
 }
 
+BT_EXPORT
 void bt_stream_get_ref(const struct bt_stream *stream)
 {
        bt_object_get_ref(stream);
 }
 
+BT_EXPORT
 void bt_stream_put_ref(const struct bt_stream *stream)
 {
        bt_object_put_ref(stream);
index 68043887f7e354a5349407340664c3800187da2d..89d7bf73afd47e3018e16d2290981f69f82abc10 100644 (file)
 #include <babeltrace2/trace-ir/stream.h>
 #include "lib/object.h"
 #include "lib/object-pool.h"
-#include "common/macros.h"
 #include <glib.h>
 #include <stdbool.h>
 
-#include "utils.h"
-
 struct bt_stream_class;
 struct bt_stream;
 
@@ -44,7 +41,6 @@ struct bt_stream {
        bool frozen;
 };
 
-BT_HIDDEN
 void _bt_stream_freeze(const struct bt_stream *stream);
 
 #ifdef BT_DEV_MODE
index 642fa9060086618f790e177f21c9ed1330a49ec1..648a2af8095c3d1fcdbfe7162cc7597ff571551b 100644 (file)
 #include "lib/assert-cond.h"
 #include <babeltrace2/trace-ir/trace-class.h>
 #include <babeltrace2/trace-ir/event-class.h>
-#include "ctf-writer/functor.h"
-#include "ctf-writer/clock.h"
 #include "compat/compiler.h"
 #include <babeltrace2/value.h>
 #include "lib/value.h"
 #include <babeltrace2/types.h>
 #include "compat/endian.h"
 #include "common/assert.h"
-#include "compat/glib.h"
 #include <inttypes.h>
 #include <stdint.h>
 #include <string.h>
 #include <stdbool.h>
 #include <stdlib.h>
 
-#include "clock-class.h"
-#include "event-class.h"
-#include "event.h"
-#include "field-class.h"
-#include "field-wrapper.h"
-#include "resolve-field-path.h"
+#include "trace-class.h"
 #include "stream-class.h"
 #include "stream.h"
-#include "trace.h"
-#include "utils.h"
 #include "lib/value.h"
 #include "lib/func-status.h"
 
@@ -85,7 +75,7 @@ void destroy_trace_class(struct bt_object *obj)
                /* Call all the trace class destruction listeners */
                for (i = 0; i < tc->destruction_listeners->len; i++) {
                        struct bt_trace_class_destruction_listener_elem elem =
-                               g_array_index(tc->destruction_listeners,
+                               bt_g_array_index(tc->destruction_listeners,
                                                struct bt_trace_class_destruction_listener_elem, i);
 
                        if (elem.func) {
@@ -121,6 +111,7 @@ void destroy_trace_class(struct bt_object *obj)
        g_free(tc);
 }
 
+BT_EXPORT
 struct bt_trace_class *bt_trace_class_create(bt_self_component *self_comp)
 {
        struct bt_trace_class *tc = NULL;
@@ -167,6 +158,7 @@ end:
        return tc;
 }
 
+BT_EXPORT
 enum bt_trace_class_add_listener_status bt_trace_class_add_destruction_listener(
                const struct bt_trace_class *_tc,
                bt_trace_class_destruction_listener_func listener,
@@ -186,7 +178,7 @@ enum bt_trace_class_add_listener_status bt_trace_class_add_destruction_listener(
        /* Find the next available spot */
        for (i = 0; i < tc->destruction_listeners->len; i++) {
                struct bt_trace_class_destruction_listener_elem elem =
-                       g_array_index(tc->destruction_listeners,
+                       bt_g_array_index(tc->destruction_listeners,
                                struct bt_trace_class_destruction_listener_elem, i);
 
                if (!elem.func) {
@@ -213,11 +205,12 @@ static
 bool has_listener_id(const struct bt_trace_class *tc, uint64_t listener_id)
 {
        BT_ASSERT(listener_id < tc->destruction_listeners->len);
-       return (&g_array_index(tc->destruction_listeners,
+       return (&bt_g_array_index(tc->destruction_listeners,
                        struct bt_trace_class_destruction_listener_elem,
                        listener_id))->func;
 }
 
+BT_EXPORT
 enum bt_trace_class_remove_listener_status bt_trace_class_remove_destruction_listener(
                const struct bt_trace_class *_tc, bt_listener_id listener_id)
 {
@@ -230,7 +223,7 @@ enum bt_trace_class_remove_listener_status bt_trace_class_remove_destruction_lis
                has_listener_id(tc, listener_id),
                "Trace class has no such trace class destruction listener ID: "
                "%![tc-]+T, %" PRIu64, tc, listener_id);
-       elem = &g_array_index(tc->destruction_listeners,
+       elem = &bt_g_array_index(tc->destruction_listeners,
                        struct bt_trace_class_destruction_listener_elem,
                        listener_id);
        BT_ASSERT(elem->func);
@@ -243,12 +236,14 @@ enum bt_trace_class_remove_listener_status bt_trace_class_remove_destruction_lis
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 uint64_t bt_trace_class_get_stream_class_count(const struct bt_trace_class *tc)
 {
        BT_ASSERT_PRE_DEV_TC_NON_NULL(tc);
        return (uint64_t) tc->stream_classes->len;
 }
 
+BT_EXPORT
 struct bt_stream_class *bt_trace_class_borrow_stream_class_by_index(
                struct bt_trace_class *tc, uint64_t index)
 {
@@ -257,6 +252,7 @@ struct bt_stream_class *bt_trace_class_borrow_stream_class_by_index(
        return g_ptr_array_index(tc->stream_classes, index);
 }
 
+BT_EXPORT
 const struct bt_stream_class *
 bt_trace_class_borrow_stream_class_by_index_const(
                const struct bt_trace_class *tc, uint64_t index)
@@ -265,6 +261,7 @@ bt_trace_class_borrow_stream_class_by_index_const(
                (void *) tc, index);
 }
 
+BT_EXPORT
 struct bt_stream_class *bt_trace_class_borrow_stream_class_by_id(
                struct bt_trace_class *tc, uint64_t id)
 {
@@ -287,6 +284,7 @@ end:
        return stream_class;
 }
 
+BT_EXPORT
 const struct bt_stream_class *
 bt_trace_class_borrow_stream_class_by_id_const(
                const struct bt_trace_class *tc, uint64_t id)
@@ -294,7 +292,6 @@ bt_trace_class_borrow_stream_class_by_id_const(
        return bt_trace_class_borrow_stream_class_by_id((void *) tc, id);
 }
 
-BT_HIDDEN
 void _bt_trace_class_freeze(const struct bt_trace_class *tc)
 {
        BT_ASSERT(tc);
@@ -302,12 +299,14 @@ void _bt_trace_class_freeze(const struct bt_trace_class *tc)
        ((struct bt_trace_class *) tc)->frozen = true;
 }
 
+BT_EXPORT
 bt_bool bt_trace_class_assigns_automatic_stream_class_id(const struct bt_trace_class *tc)
 {
        BT_ASSERT_PRE_DEV_TC_NON_NULL(tc);
        return (bt_bool) tc->assigns_automatic_stream_class_id;
 }
 
+BT_EXPORT
 void bt_trace_class_set_assigns_automatic_stream_class_id(struct bt_trace_class *tc,
                bt_bool value)
 {
@@ -318,6 +317,7 @@ void bt_trace_class_set_assigns_automatic_stream_class_id(struct bt_trace_class
                "assignment property: %!+T", tc);
 }
 
+BT_EXPORT
 const struct bt_value *bt_trace_class_borrow_user_attributes_const(
                const struct bt_trace_class *trace_class)
 {
@@ -325,6 +325,7 @@ const struct bt_value *bt_trace_class_borrow_user_attributes_const(
        return trace_class->user_attributes;
 }
 
+BT_EXPORT
 struct bt_value *bt_trace_class_borrow_user_attributes(
                struct bt_trace_class *trace_class)
 {
@@ -332,6 +333,7 @@ struct bt_value *bt_trace_class_borrow_user_attributes(
                (void *) trace_class);
 }
 
+BT_EXPORT
 void bt_trace_class_set_user_attributes(struct bt_trace_class *trace_class,
                const struct bt_value *user_attributes)
 {
@@ -344,11 +346,13 @@ void bt_trace_class_set_user_attributes(struct bt_trace_class *trace_class,
        bt_object_get_ref_no_null_check(trace_class->user_attributes);
 }
 
+BT_EXPORT
 void bt_trace_class_get_ref(const struct bt_trace_class *trace_class)
 {
        bt_object_get_ref(trace_class);
 }
 
+BT_EXPORT
 void bt_trace_class_put_ref(const struct bt_trace_class *trace_class)
 {
        bt_object_put_ref(trace_class);
index 0a09dfbe285cbff397491f3fcc426ccf9d982d6a..8df87e970629ca458a3a8ebf5a4ee171185ccb39 100644 (file)
@@ -8,23 +8,16 @@
 #ifndef BABELTRACE_TRACE_IR_TRACE_CLASS_INTERNAL_H
 #define BABELTRACE_TRACE_IR_TRACE_CLASS_INTERNAL_H
 
-#include "lib/assert-cond.h"
 #include <babeltrace2/trace-ir/trace-class.h>
 #include <babeltrace2/trace-ir/field-class.h>
 #include <babeltrace2/trace-ir/field.h>
 #include "lib/object.h"
-#include "lib/object-pool.h"
-#include "common/macros.h"
 #include <babeltrace2/value.h>
 #include <babeltrace2/types.h>
 #include <glib.h>
 #include <sys/types.h>
 #include <stdbool.h>
 
-#include "stream-class.h"
-#include "attributes.h"
-#include "clock-class.h"
-
 struct bt_trace_class {
        struct bt_object base;
 
@@ -39,7 +32,6 @@ struct bt_trace_class {
        bool frozen;
 };
 
-BT_HIDDEN
 void _bt_trace_class_freeze(const struct bt_trace_class *trace_class);
 
 #ifdef BT_DEV_MODE
index bd20f9fbda343e48689edf140573574d02eaa8da..8cc005a785530654984cd837d5e2a7e7107212c4 100644 (file)
@@ -11,8 +11,6 @@
 #include "lib/assert-cond.h"
 #include <babeltrace2/trace-ir/trace.h>
 #include <babeltrace2/trace-ir/event-class.h>
-#include "ctf-writer/functor.h"
-#include "ctf-writer/clock.h"
 #include "compat/compiler.h"
 #include <babeltrace2/value.h>
 #include "lib/value.h"
 #include <stdlib.h>
 
 #include "attributes.h"
-#include "clock-class.h"
-#include "event-class.h"
-#include "event.h"
-#include "field-class.h"
-#include "field-wrapper.h"
-#include "resolve-field-path.h"
 #include "stream-class.h"
 #include "stream.h"
 #include "trace-class.h"
 #include "trace.h"
-#include "utils.h"
 #include "lib/value.h"
 #include "lib/func-status.h"
 
@@ -87,7 +78,7 @@ void destroy_trace(struct bt_object *obj)
                /* Call all the trace destruction listeners */
                for (i = 0; i < trace->destruction_listeners->len; i++) {
                        struct bt_trace_destruction_listener_elem elem =
-                               g_array_index(trace->destruction_listeners,
+                               bt_g_array_index(trace->destruction_listeners,
                                        struct bt_trace_destruction_listener_elem, i);
 
                        if (elem.func) {
@@ -143,6 +134,7 @@ void destroy_trace(struct bt_object *obj)
        g_free(trace);
 }
 
+BT_EXPORT
 struct bt_trace *bt_trace_create(struct bt_trace_class *tc)
 {
        struct bt_trace *trace = NULL;
@@ -209,12 +201,14 @@ end:
        return trace;
 }
 
+BT_EXPORT
 const char *bt_trace_get_name(const struct bt_trace *trace)
 {
        BT_ASSERT_PRE_DEV_TRACE_NON_NULL(trace);
        return trace->name.value;
 }
 
+BT_EXPORT
 enum bt_trace_set_name_status bt_trace_set_name(struct bt_trace *trace,
                const char *name)
 {
@@ -228,12 +222,14 @@ enum bt_trace_set_name_status bt_trace_set_name(struct bt_trace *trace,
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 bt_uuid bt_trace_get_uuid(const struct bt_trace *trace)
 {
        BT_ASSERT_PRE_DEV_TRACE_NON_NULL(trace);
        return trace->uuid.value;
 }
 
+BT_EXPORT
 void bt_trace_set_uuid(struct bt_trace *trace, bt_uuid uuid)
 {
        BT_ASSERT_PRE_TRACE_NON_NULL(trace);
@@ -284,6 +280,7 @@ enum bt_trace_set_environment_entry_status set_environment_entry(
        return ret;
 }
 
+BT_EXPORT
 enum bt_trace_set_environment_entry_status
 bt_trace_set_environment_entry_string(
                struct bt_trace *trace, const char *name, const char *value)
@@ -312,6 +309,7 @@ end:
        return ret;
 }
 
+BT_EXPORT
 enum bt_trace_set_environment_entry_status
 bt_trace_set_environment_entry_integer(
                struct bt_trace *trace, const char *name, int64_t value)
@@ -339,12 +337,14 @@ end:
        return ret;
 }
 
+BT_EXPORT
 uint64_t bt_trace_get_environment_entry_count(const struct bt_trace *trace)
 {
        BT_ASSERT_PRE_DEV_TRACE_NON_NULL(trace);
        return bt_attributes_get_count(trace->environment);
 }
 
+BT_EXPORT
 void bt_trace_borrow_environment_entry_by_index_const(
                const struct bt_trace *trace, uint64_t index,
                const char **name, const struct bt_value **value)
@@ -361,6 +361,7 @@ void bt_trace_borrow_environment_entry_by_index_const(
        BT_ASSERT(*name);
 }
 
+BT_EXPORT
 const struct bt_value *bt_trace_borrow_environment_entry_value_by_name_const(
                const struct bt_trace *trace, const char *name)
 {
@@ -370,12 +371,14 @@ const struct bt_value *bt_trace_borrow_environment_entry_value_by_name_const(
                name);
 }
 
+BT_EXPORT
 uint64_t bt_trace_get_stream_count(const struct bt_trace *trace)
 {
        BT_ASSERT_PRE_DEV_TRACE_NON_NULL(trace);
        return (uint64_t) trace->streams->len;
 }
 
+BT_EXPORT
 struct bt_stream *bt_trace_borrow_stream_by_index(
                struct bt_trace *trace, uint64_t index)
 {
@@ -384,12 +387,14 @@ struct bt_stream *bt_trace_borrow_stream_by_index(
        return g_ptr_array_index(trace->streams, index);
 }
 
+BT_EXPORT
 const struct bt_stream *bt_trace_borrow_stream_by_index_const(
                const struct bt_trace *trace, uint64_t index)
 {
        return bt_trace_borrow_stream_by_index((void *) trace, index);
 }
 
+BT_EXPORT
 struct bt_stream *bt_trace_borrow_stream_by_id(struct bt_trace *trace,
                uint64_t id)
 {
@@ -412,12 +417,14 @@ end:
        return stream;
 }
 
+BT_EXPORT
 const struct bt_stream *bt_trace_borrow_stream_by_id_const(
                const struct bt_trace *trace, uint64_t id)
 {
        return bt_trace_borrow_stream_by_id((void *) trace, id);
 }
 
+BT_EXPORT
 enum bt_trace_add_listener_status bt_trace_add_destruction_listener(
                const struct bt_trace *c_trace,
                bt_trace_destruction_listener_func listener,
@@ -437,7 +444,7 @@ enum bt_trace_add_listener_status bt_trace_add_destruction_listener(
        /* Find the next available spot */
        for (i = 0; i < trace->destruction_listeners->len; i++) {
                struct bt_trace_destruction_listener_elem elem =
-                       g_array_index(trace->destruction_listeners,
+                       bt_g_array_index(trace->destruction_listeners,
                                struct bt_trace_destruction_listener_elem, i);
 
                if (!elem.func) {
@@ -464,11 +471,12 @@ static
 bool has_listener_id(const struct bt_trace *trace, uint64_t listener_id)
 {
        BT_ASSERT(listener_id < trace->destruction_listeners->len);
-       return (&g_array_index(trace->destruction_listeners,
+       return (&bt_g_array_index(trace->destruction_listeners,
                        struct bt_trace_destruction_listener_elem,
                        listener_id))->func;
 }
 
+BT_EXPORT
 enum bt_trace_remove_listener_status bt_trace_remove_destruction_listener(
                const struct bt_trace *c_trace, bt_listener_id listener_id)
 {
@@ -481,7 +489,7 @@ enum bt_trace_remove_listener_status bt_trace_remove_destruction_listener(
                has_listener_id(trace, listener_id),
                "Trace has no such trace destruction listener ID: "
                "%![trace-]+t, %" PRIu64, trace, listener_id);
-       elem = &g_array_index(trace->destruction_listeners,
+       elem = &bt_g_array_index(trace->destruction_listeners,
                        struct bt_trace_destruction_listener_elem,
                        listener_id);
        BT_ASSERT(elem->func);
@@ -494,7 +502,6 @@ enum bt_trace_remove_listener_status bt_trace_remove_destruction_listener(
        return BT_FUNC_STATUS_OK;
 }
 
-BT_HIDDEN
 void _bt_trace_freeze(const struct bt_trace *trace)
 {
        BT_ASSERT(trace);
@@ -507,7 +514,6 @@ void _bt_trace_freeze(const struct bt_trace *trace)
        ((struct bt_trace *) trace)->frozen = true;
 }
 
-BT_HIDDEN
 void bt_trace_add_stream(struct bt_trace *trace, struct bt_stream *stream)
 {
        guint count = 0;
@@ -526,7 +532,6 @@ void bt_trace_add_stream(struct bt_trace *trace, struct bt_stream *stream)
                stream->class, GUINT_TO_POINTER(count + 1));
 }
 
-BT_HIDDEN
 uint64_t bt_trace_get_automatic_stream_id(const struct bt_trace *trace,
                const struct bt_stream_class *stream_class)
 {
@@ -544,18 +549,21 @@ uint64_t bt_trace_get_automatic_stream_id(const struct bt_trace *trace,
        return id;
 }
 
+BT_EXPORT
 struct bt_trace_class *bt_trace_borrow_class(struct bt_trace *trace)
 {
        BT_ASSERT_PRE_DEV_TRACE_NON_NULL(trace);
        return trace->class;
 }
 
+BT_EXPORT
 const struct bt_trace_class *bt_trace_borrow_class_const(
                const struct bt_trace *trace)
 {
        return bt_trace_borrow_class((void *) trace);
 }
 
+BT_EXPORT
 const struct bt_value *bt_trace_borrow_user_attributes_const(
                const struct bt_trace *trace)
 {
@@ -563,11 +571,13 @@ const struct bt_value *bt_trace_borrow_user_attributes_const(
        return trace->user_attributes;
 }
 
+BT_EXPORT
 struct bt_value *bt_trace_borrow_user_attributes(struct bt_trace *trace)
 {
        return (void *) bt_trace_borrow_user_attributes_const((void *) trace);
 }
 
+BT_EXPORT
 void bt_trace_set_user_attributes(
                struct bt_trace *trace,
                const struct bt_value *user_attributes)
@@ -581,11 +591,13 @@ void bt_trace_set_user_attributes(
        bt_object_get_ref_no_null_check(trace->user_attributes);
 }
 
+BT_EXPORT
 void bt_trace_get_ref(const struct bt_trace *trace)
 {
        bt_object_get_ref(trace);
 }
 
+BT_EXPORT
 void bt_trace_put_ref(const struct bt_trace *trace)
 {
        bt_object_put_ref(trace);
index c62087787000f984a80103ef17d466d457ec8898..294e8943cd2ffb5933cb7a14888092e435f2b7a7 100644 (file)
@@ -8,13 +8,10 @@
 #ifndef BABELTRACE_TRACE_IR_TRACE_INTERNAL_H
 #define BABELTRACE_TRACE_IR_TRACE_INTERNAL_H
 
-#include "lib/assert-cond.h"
 #include <babeltrace2/trace-ir/trace.h>
 #include <babeltrace2/trace-ir/field-class.h>
 #include <babeltrace2/trace-ir/field.h>
 #include "lib/object.h"
-#include "lib/object-pool.h"
-#include "common/macros.h"
 #include <babeltrace2/value.h>
 #include <babeltrace2/types.h>
 #include <glib.h>
@@ -22,8 +19,6 @@
 #include <sys/types.h>
 #include "common/uuid.h"
 
-#include "attributes.h"
-#include "clock-class.h"
 #include "stream-class.h"
 #include "trace-class.h"
 
@@ -66,7 +61,6 @@ struct bt_trace {
        bool frozen;
 };
 
-BT_HIDDEN
 void _bt_trace_freeze(const struct bt_trace *trace);
 
 #ifdef BT_DEV_MODE
@@ -75,10 +69,8 @@ void _bt_trace_freeze(const struct bt_trace *trace);
 # define bt_trace_freeze(_trace)
 #endif
 
-BT_HIDDEN
 void bt_trace_add_stream(struct bt_trace *trace, struct bt_stream *stream);
 
-BT_HIDDEN
 uint64_t bt_trace_get_automatic_stream_id(const struct bt_trace *trace,
                const struct bt_stream_class *stream_class);
 
index 45a795aa9c7f3c25c9c885d1a8693b6085a23c07..c37111f280fe87aa5e7249864d91036f061151ba 100644 (file)
@@ -8,9 +8,5 @@
 #define BT_LOG_TAG "LIB/TRACE-IR-UTILS"
 #include "lib/logging.h"
 
-#include <stdlib.h>
 #include <glib.h>
 #include <babeltrace2/trace-ir/clock-class.h>
-#include "common/assert.h"
-
-#include "field-class.h"
index f37d1a828d6a2d81eb5078a2431bb5630163966b..f9c2f6e3a4ba4ba8f4a700a244676e8b949addd7 100644 (file)
@@ -7,7 +7,6 @@
 #ifndef BABELTRACE_TRACE_IR_UTILS_INTERNAL_H
 #define BABELTRACE_TRACE_IR_UTILS_INTERNAL_H
 
-#include "common/macros.h"
 #include <babeltrace2/trace-ir/field-class.h>
 #include <stdbool.h>
 #include <stdint.h>
@@ -79,7 +78,6 @@ end:
 
 static inline
 int bt_util_ns_from_origin_inline(int64_t base_offset_ns,
-               int64_t offset_seconds, uint64_t offset_cycles,
                uint64_t frequency, uint64_t value, int64_t *ns_from_origin)
 {
        int ret = 0;
@@ -135,7 +133,6 @@ int bt_util_ns_from_origin_clock_class(const struct bt_clock_class *clock_class,
        }
 
        ret = bt_util_ns_from_origin_inline(clock_class->base_offset.value_ns,
-               clock_class->offset_seconds, clock_class->offset_cycles,
                clock_class->frequency, value, ns_from_origin);
 
 end:
index 49d39f0b183a2352899f7f838d3befac1ac3ddbf..f635624ca15b7d757f8a2e76657f5c484d8d7c67 100644 (file)
@@ -9,12 +9,11 @@
 
 #include "lib/assert-cond.h"
 #include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
 #include <inttypes.h>
 #include <babeltrace2/babeltrace.h>
 #include "lib/trace-ir/utils.h"
 
+BT_EXPORT
 bt_util_clock_cycles_to_ns_from_origin_status
 bt_util_clock_cycles_to_ns_from_origin(uint64_t cycles,
                uint64_t frequency, int64_t offset_seconds,
@@ -45,10 +44,8 @@ bt_util_clock_cycles_to_ns_from_origin(uint64_t cycles,
                goto end;
        }
 
-       ret = bt_util_ns_from_origin_inline(base_offset_ns,
-               offset_seconds, offset_cycles,
-               frequency, cycles, ns);
-       if (ret) {
+        ret = bt_util_ns_from_origin_inline(base_offset_ns, frequency, cycles, ns);
+        if (ret) {
                status = BT_FUNC_STATUS_OVERFLOW_ERROR;
        }
 
index e570b005b4168448712f9bd34797ad56b800cf6c..c4e38f9b023f1b0ef65fece06179790191e47107 100644 (file)
@@ -56,6 +56,7 @@ struct bt_value bt_value_null_instance = {
        .frozen = BT_TRUE,
 };
 
+BT_EXPORT
 struct bt_value *const bt_value_null = &bt_value_null_instance;
 
 static
@@ -266,8 +267,9 @@ struct bt_value *(* const copy_funcs[])(const struct bt_value *) = {
 };
 
 static
-bt_bool bt_value_null_is_equal(const struct bt_value *object_a,
-               const struct bt_value *object_b)
+bt_bool bt_value_null_is_equal(
+               const struct bt_value *object_a __attribute__((unused)),
+               const struct bt_value *object_b __attribute__((unused)))
 {
        /*
         * Always BT_TRUE since bt_value_is_equal() already checks if both
@@ -449,7 +451,7 @@ bt_bool (* const is_equal_funcs[])(const struct bt_value *,
 };
 
 static
-void bt_value_null_freeze(struct bt_value *object)
+void bt_value_null_freeze(struct bt_value *object __attribute__((unused)))
 {
 }
 
@@ -521,7 +523,6 @@ void bt_value_destroy(struct bt_object *obj)
        g_free(value);
 }
 
-BT_HIDDEN
 void _bt_value_freeze(const struct bt_value *c_object)
 {
        const struct bt_value *object = (void *) c_object;
@@ -539,6 +540,7 @@ end:
        return;
 }
 
+BT_EXPORT
 enum bt_value_type bt_value_get_type(const struct bt_value *object)
 {
        BT_ASSERT_PRE_DEV_VALUE_NON_NULL(object);
@@ -556,6 +558,7 @@ struct bt_value bt_value_create_base(enum bt_value_type type)
        return value;
 }
 
+BT_EXPORT
 struct bt_value *bt_value_bool_create_init(bt_bool val)
 {
        struct bt_value_bool *bool_obj;
@@ -578,6 +581,7 @@ end:
        return (void *) bool_obj;
 }
 
+BT_EXPORT
 struct bt_value *bt_value_bool_create(void)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -619,6 +623,7 @@ end:
        return (void *) integer_obj;
 }
 
+BT_EXPORT
 struct bt_value *bt_value_integer_unsigned_create_init(uint64_t val)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -627,6 +632,7 @@ struct bt_value *bt_value_integer_unsigned_create_init(uint64_t val)
                val);
 }
 
+BT_EXPORT
 struct bt_value *bt_value_integer_unsigned_create(void)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -634,6 +640,7 @@ struct bt_value *bt_value_integer_unsigned_create(void)
        return bt_value_integer_unsigned_create_init(0);
 }
 
+BT_EXPORT
 struct bt_value *bt_value_integer_signed_create_init(int64_t val)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -642,6 +649,7 @@ struct bt_value *bt_value_integer_signed_create_init(int64_t val)
                (uint64_t) val);
 }
 
+BT_EXPORT
 struct bt_value *bt_value_integer_signed_create(void)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -649,6 +657,7 @@ struct bt_value *bt_value_integer_signed_create(void)
        return bt_value_integer_signed_create_init(0);
 }
 
+BT_EXPORT
 struct bt_value *bt_value_real_create_init(double val)
 {
        struct bt_value_real *real_obj;
@@ -672,6 +681,7 @@ end:
        return (void *) real_obj;
 }
 
+BT_EXPORT
 struct bt_value *bt_value_real_create(void)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -679,6 +689,7 @@ struct bt_value *bt_value_real_create(void)
        return bt_value_real_create_init(0.);
 }
 
+BT_EXPORT
 struct bt_value *bt_value_string_create_init(const char *val)
 {
        struct bt_value_string *string_obj = NULL;
@@ -711,6 +722,7 @@ end:
        return (void *) string_obj;
 }
 
+BT_EXPORT
 struct bt_value *bt_value_string_create(void)
 {
        BT_ASSERT_PRE_NO_ERROR();
@@ -718,6 +730,7 @@ struct bt_value *bt_value_string_create(void)
        return bt_value_string_create_init("");
 }
 
+BT_EXPORT
 struct bt_value *bt_value_array_create(void)
 {
        struct bt_value_array *array_obj;
@@ -749,6 +762,7 @@ end:
        return (void *) array_obj;
 }
 
+BT_EXPORT
 struct bt_value *bt_value_map_create(void)
 {
        struct bt_value_map *map_obj;
@@ -779,6 +793,7 @@ end:
        return (void *) map_obj;
 }
 
+BT_EXPORT
 bt_bool bt_value_bool_get(const struct bt_value *bool_obj)
 {
        BT_ASSERT_PRE_DEV_VALUE_NON_NULL(bool_obj);
@@ -786,6 +801,7 @@ bt_bool bt_value_bool_get(const struct bt_value *bool_obj)
        return BT_VALUE_TO_BOOL(bool_obj)->value;
 }
 
+BT_EXPORT
 void bt_value_bool_set(struct bt_value *bool_obj, bt_bool val)
 {
        BT_ASSERT_PRE_VALUE_NON_NULL(bool_obj);
@@ -795,6 +811,7 @@ void bt_value_bool_set(struct bt_value *bool_obj, bt_bool val)
                bool_obj, val);
 }
 
+BT_EXPORT
 uint64_t bt_value_integer_unsigned_get(const struct bt_value *integer_obj)
 {
        BT_ASSERT_PRE_DEV_VALUE_NON_NULL(integer_obj);
@@ -802,6 +819,7 @@ uint64_t bt_value_integer_unsigned_get(const struct bt_value *integer_obj)
        return BT_VALUE_TO_INTEGER(integer_obj)->value.u;
 }
 
+BT_EXPORT
 int64_t bt_value_integer_signed_get(const struct bt_value *integer_obj)
 {
        BT_ASSERT_PRE_DEV_VALUE_NON_NULL(integer_obj);
@@ -811,35 +829,36 @@ int64_t bt_value_integer_signed_get(const struct bt_value *integer_obj)
 
 static inline
 void set_integer_value(struct bt_value *integer_obj,
-               enum bt_value_type expected_type, uint64_t uval,
+               uint64_t uval,
                const char *api_func)
 {
        BT_ASSERT_PRE_DEV_VALUE_HOT_FROM_FUNC(api_func, integer_obj);
        BT_VALUE_TO_INTEGER(integer_obj)->value.u = uval;
 }
 
+BT_EXPORT
 void bt_value_integer_unsigned_set(struct bt_value *integer_obj,
                uint64_t val)
 {
        BT_ASSERT_PRE_VALUE_NON_NULL(integer_obj);
        BT_ASSERT_PRE_VALUE_IS_UNSIGNED_INT(integer_obj);
-       set_integer_value(integer_obj, BT_VALUE_TYPE_UNSIGNED_INTEGER, val,
-               __func__);
+       set_integer_value(integer_obj, val, __func__);
        BT_LOGT("Set unsigned integer value's raw value: "
                "value-addr=%p, value=%" PRIu64, integer_obj, val);
 }
 
+BT_EXPORT
 void bt_value_integer_signed_set(struct bt_value *integer_obj,
                int64_t val)
 {
        BT_ASSERT_PRE_VALUE_NON_NULL(integer_obj);
        BT_ASSERT_PRE_VALUE_IS_SIGNED_INT(integer_obj);
-       set_integer_value(integer_obj, BT_VALUE_TYPE_SIGNED_INTEGER,
-               (uint64_t) val, __func__);
+       set_integer_value(integer_obj, (uint64_t) val, __func__);
        BT_LOGT("Set signed integer value's raw value: "
                "value-addr=%p, value=%" PRId64, integer_obj, val);
 }
 
+BT_EXPORT
 double bt_value_real_get(const struct bt_value *real_obj)
 {
        BT_ASSERT_PRE_DEV_VALUE_NON_NULL(real_obj);
@@ -847,6 +866,7 @@ double bt_value_real_get(const struct bt_value *real_obj)
        return BT_VALUE_TO_REAL(real_obj)->value;
 }
 
+BT_EXPORT
 void bt_value_real_set(struct bt_value *real_obj, double val)
 {
        BT_ASSERT_PRE_VALUE_NON_NULL(real_obj);
@@ -857,6 +877,7 @@ void bt_value_real_set(struct bt_value *real_obj, double val)
                real_obj, val);
 }
 
+BT_EXPORT
 const char *bt_value_string_get(const struct bt_value *string_obj)
 {
        BT_ASSERT_PRE_DEV_VALUE_NON_NULL(string_obj);
@@ -864,6 +885,7 @@ const char *bt_value_string_get(const struct bt_value *string_obj)
        return BT_VALUE_TO_STRING(string_obj)->gstr->str;
 }
 
+BT_EXPORT
 enum bt_value_string_set_status bt_value_string_set(
                struct bt_value *string_obj, const char *val)
 {
@@ -877,6 +899,7 @@ enum bt_value_string_set_status bt_value_string_set(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 uint64_t bt_value_array_get_length(const struct bt_value *array_obj)
 {
        BT_ASSERT_PRE_DEV_VALUE_NON_NULL(array_obj);
@@ -884,6 +907,7 @@ uint64_t bt_value_array_get_length(const struct bt_value *array_obj)
        return (uint64_t) BT_VALUE_TO_ARRAY(array_obj)->garray->len;
 }
 
+BT_EXPORT
 struct bt_value *bt_value_array_borrow_element_by_index(
                struct bt_value *array_obj, uint64_t index)
 {
@@ -896,6 +920,7 @@ struct bt_value *bt_value_array_borrow_element_by_index(
        return g_ptr_array_index(typed_array_obj->garray, index);
 }
 
+BT_EXPORT
 const struct bt_value *bt_value_array_borrow_element_by_index_const(
                const struct bt_value *array_obj,
                uint64_t index)
@@ -928,6 +953,7 @@ enum bt_value_array_append_element_status append_array_element(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_value_array_append_element_status bt_value_array_append_element(
                struct bt_value *array_obj,
                struct bt_value *element_obj)
@@ -935,6 +961,7 @@ enum bt_value_array_append_element_status bt_value_array_append_element(
        return append_array_element(array_obj, element_obj, __func__);
 }
 
+BT_EXPORT
 enum bt_value_array_append_element_status
 bt_value_array_append_bool_element(struct bt_value *array_obj, bt_bool val)
 {
@@ -950,6 +977,7 @@ bt_value_array_append_bool_element(struct bt_value *array_obj, bt_bool val)
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_array_append_element_status
 bt_value_array_append_unsigned_integer_element(struct bt_value *array_obj,
                uint64_t val)
@@ -966,6 +994,7 @@ bt_value_array_append_unsigned_integer_element(struct bt_value *array_obj,
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_array_append_element_status
 bt_value_array_append_signed_integer_element(struct bt_value *array_obj,
                int64_t val)
@@ -982,6 +1011,7 @@ bt_value_array_append_signed_integer_element(struct bt_value *array_obj,
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_array_append_element_status
 bt_value_array_append_real_element(struct bt_value *array_obj, double val)
 {
@@ -997,6 +1027,7 @@ bt_value_array_append_real_element(struct bt_value *array_obj, double val)
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_array_append_element_status
 bt_value_array_append_string_element(struct bt_value *array_obj,
                const char *val)
@@ -1013,6 +1044,7 @@ bt_value_array_append_string_element(struct bt_value *array_obj,
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_array_append_element_status
 bt_value_array_append_empty_array_element(struct bt_value *array_obj,
                struct bt_value **element_obj)
@@ -1034,6 +1066,7 @@ bt_value_array_append_empty_array_element(struct bt_value *array_obj,
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_array_append_element_status
 bt_value_array_append_empty_map_element(struct bt_value *array_obj,
                struct bt_value **element_obj)
@@ -1055,6 +1088,7 @@ bt_value_array_append_empty_map_element(struct bt_value *array_obj,
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_array_set_element_by_index_status
 bt_value_array_set_element_by_index(struct bt_value *array_obj, uint64_t index,
                struct bt_value *element_obj)
@@ -1079,6 +1113,7 @@ bt_value_array_set_element_by_index(struct bt_value *array_obj, uint64_t index,
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 uint64_t bt_value_map_get_size(const struct bt_value *map_obj)
 {
        BT_ASSERT_PRE_DEV_VALUE_NON_NULL(map_obj);
@@ -1086,6 +1121,7 @@ uint64_t bt_value_map_get_size(const struct bt_value *map_obj)
        return (uint64_t) g_hash_table_size(BT_VALUE_TO_MAP(map_obj)->ght);
 }
 
+BT_EXPORT
 struct bt_value *bt_value_map_borrow_entry_value(struct bt_value *map_obj,
                const char *key)
 {
@@ -1096,12 +1132,14 @@ struct bt_value *bt_value_map_borrow_entry_value(struct bt_value *map_obj,
                GUINT_TO_POINTER(g_quark_from_string(key)));
 }
 
+BT_EXPORT
 const struct bt_value *bt_value_map_borrow_entry_value_const(
                const struct bt_value *map_obj, const char *key)
 {
        return bt_value_map_borrow_entry_value((void *) map_obj, key);
 }
 
+BT_EXPORT
 bt_bool bt_value_map_has_entry(const struct bt_value *map_obj, const char *key)
 {
        BT_ASSERT_PRE_DEV_VALUE_NON_NULL(map_obj);
@@ -1134,6 +1172,7 @@ enum bt_value_map_insert_entry_status insert_map_value_entry(
        return BT_FUNC_STATUS_OK;
 }
 
+BT_EXPORT
 enum bt_value_map_insert_entry_status bt_value_map_insert_entry(
                struct bt_value *map_obj, const char *key,
                struct bt_value *element_obj)
@@ -1141,6 +1180,7 @@ enum bt_value_map_insert_entry_status bt_value_map_insert_entry(
        return insert_map_value_entry(map_obj, key, element_obj, __func__);
 }
 
+BT_EXPORT
 enum bt_value_map_insert_entry_status bt_value_map_insert_bool_entry(
                struct bt_value *map_obj, const char *key, bt_bool val)
 {
@@ -1156,6 +1196,7 @@ enum bt_value_map_insert_entry_status bt_value_map_insert_bool_entry(
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_map_insert_entry_status
 bt_value_map_insert_unsigned_integer_entry(struct bt_value *map_obj,
                const char *key, uint64_t val)
@@ -1172,6 +1213,7 @@ bt_value_map_insert_unsigned_integer_entry(struct bt_value *map_obj,
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_map_insert_entry_status
 bt_value_map_insert_signed_integer_entry(struct bt_value *map_obj,
                const char *key, int64_t val)
@@ -1188,6 +1230,7 @@ bt_value_map_insert_signed_integer_entry(struct bt_value *map_obj,
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_map_insert_entry_status bt_value_map_insert_real_entry(
                struct bt_value *map_obj, const char *key, double val)
 {
@@ -1203,6 +1246,7 @@ enum bt_value_map_insert_entry_status bt_value_map_insert_real_entry(
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_map_insert_entry_status bt_value_map_insert_string_entry(
                struct bt_value *map_obj, const char *key,
                const char *val)
@@ -1219,6 +1263,7 @@ enum bt_value_map_insert_entry_status bt_value_map_insert_string_entry(
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_map_insert_entry_status
 bt_value_map_insert_empty_array_entry(
                struct bt_value *map_obj, const char *key,
@@ -1241,6 +1286,7 @@ bt_value_map_insert_empty_array_entry(
        return ret;
 }
 
+BT_EXPORT
 enum bt_value_map_insert_entry_status
 bt_value_map_insert_empty_map_entry(struct bt_value *map_obj, const char *key,
                bt_value **entry_obj)
@@ -1321,6 +1367,7 @@ enum bt_value_map_foreach_entry_status foreach_map_entry(
        return status;
 }
 
+BT_EXPORT
 enum bt_value_map_foreach_entry_status bt_value_map_foreach_entry(
                struct bt_value *map_obj, bt_value_map_foreach_entry_func func,
                void *data)
@@ -1329,6 +1376,7 @@ enum bt_value_map_foreach_entry_status bt_value_map_foreach_entry(
                "bt_value_map_foreach_entry_func");
 }
 
+BT_EXPORT
 enum bt_value_map_foreach_entry_const_status bt_value_map_foreach_entry_const(
                const struct bt_value *map_obj,
                bt_value_map_foreach_entry_const_func func, void *data)
@@ -1384,6 +1432,7 @@ end:
        return status;
 }
 
+BT_EXPORT
 enum bt_value_map_extend_status bt_value_map_extend(
                struct bt_value *base_map_obj,
                const struct bt_value *extension_obj)
@@ -1423,6 +1472,7 @@ enum bt_value_map_extend_status bt_value_map_extend(
        return status;
 }
 
+BT_EXPORT
 enum bt_value_copy_status bt_value_copy(const struct bt_value *object,
                struct bt_value **copy_obj)
 {
@@ -1446,6 +1496,7 @@ enum bt_value_copy_status bt_value_copy(const struct bt_value *object,
        return status;
 }
 
+BT_EXPORT
 bt_bool bt_value_is_equal(const struct bt_value *object_a,
        const struct bt_value *object_b)
 {
@@ -1472,11 +1523,13 @@ end:
        return ret;
 }
 
+BT_EXPORT
 void bt_value_get_ref(const struct bt_value *value)
 {
        bt_object_get_ref(value);
 }
 
+BT_EXPORT
 void bt_value_put_ref(const struct bt_value *value)
 {
        bt_object_put_ref(value);
index 518012a52df95c59756ff9523935a7ca5a1fd151..34b7a40ad89957ff411b062dab1db35dfaaf5cd2 100644 (file)
@@ -11,7 +11,6 @@
 #include <babeltrace2/babeltrace.h>
 
 #include "lib/object.h"
-#include "common/macros.h"
 
 struct bt_value {
        struct bt_object base;
@@ -52,7 +51,6 @@ struct bt_value_map {
        GHashTable *ght;
 };
 
-BT_HIDDEN
 void _bt_value_freeze(const struct bt_value *object);
 
 #ifdef BT_DEV_MODE
diff --git a/src/logging/Makefile.am b/src/logging/Makefile.am
deleted file mode 100644 (file)
index dd45f6d..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-logging.la
-
-libbabeltrace2_logging_la_SOURCES = log.c log.h comp-logging.h
index d4ce06086498021790db6ff971baace0036997d0..00bce0473a5ac2ea5025bb30d6e4f7eebd0b2354 100644 (file)
@@ -20,7 +20,9 @@
 
 /* Logs with level `_lvl` for self component `_self_comp` */
 #define BT_COMP_LOG(_lvl, _self_comp, _fmt, ...)                       \
-       BT_LOG_WRITE((_lvl), BT_LOG_TAG, _BT_COMP_LOG_COMP_PREFIX _fmt, \
+       BT_LOG_WRITE_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),         \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL), BT_LOG_TAG,  \
+               _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
                                bt_self_component_as_component(_self_comp)) : \
 
 /* Logs with level `_lvl` for self component class `_self_comp_class` */
 #define BT_COMP_CLASS_LOG(_lvl, _self_comp_class, _fmt, ...)           \
-       BT_LOG_WRITE((_lvl), BT_LOG_TAG, _BT_COMP_LOG_COMP_PREFIX _fmt, \
+       BT_LOG_WRITE_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),         \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL), BT_LOG_TAG,  \
+               _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                bt_component_class_get_name(                            \
                        bt_self_component_class_as_component_class(     \
                                _self_comp_class)), ##__VA_ARGS__)
 
 #define BT_COMP_LOG_CUR_LVL(_lvl, _cur_lvl, _self_comp, _fmt, ...)     \
-       BT_LOG_WRITE_CUR_LVL((_lvl), (_cur_lvl), BT_LOG_TAG,            \
+       BT_LOG_WRITE_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),         \
+               (enum bt_log_level) (_cur_lvl), BT_LOG_TAG,             \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
@@ -44,7 +49,9 @@
                ##__VA_ARGS__)
 
 #define BT_COMP_LOG_ERRNO(_lvl, _self_comp, _msg, _fmt, ...)           \
-       BT_LOG_WRITE_ERRNO((_lvl), BT_LOG_TAG, _msg,                    \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),   \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL),              \
+               BT_LOG_TAG, _msg,                                       \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
@@ -53,7 +60,8 @@
                ##__VA_ARGS__)
 
 #define BT_COMP_LOG_ERRNO_CUR_LVL(_lvl, _cur_lvl, _self_comp, _msg, _fmt, ...) \
-       BT_LOG_WRITE_ERRNO_CUR_LVL((_lvl), (_cur_lvl), BT_LOG_TAG, _msg, \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),   \
+               (enum bt_log_level) (_cur_lvl), BT_LOG_TAG, _msg,       \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
@@ -62,7 +70,9 @@
                ##__VA_ARGS__)
 
 #define BT_COMP_LOG_MEM(_lvl, _self_comp, _data_ptr, _data_sz, _fmt, ...) \
-       BT_LOG_WRITE_MEM((_lvl), BT_LOG_TAG, (_data_ptr), (_data_sz),   \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),     \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL), BT_LOG_TAG,  \
+               (_data_ptr), (_data_sz),                                \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
 
 /* Logs error and errno string from component class context. */
 #define BT_COMP_CLASS_LOG_ERRNO(_lvl, _self_comp_class, _msg, _fmt, ...)               \
-       BT_LOG_WRITE_ERRNO((_lvl), BT_LOG_TAG, _msg,                                    \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),                   \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL), BT_LOG_TAG, _msg,            \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                                          \
                bt_component_class_get_name(                                            \
                        bt_self_component_class_as_component_class(_self_comp_class))   \
diff --git a/src/logging/log-api.c b/src/logging/log-api.c
new file mode 100644 (file)
index 0000000..2d2499c
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2016 wonder-mice
+ * Copyright (c) 2016-2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * This is very inspired by zf_log.c (see
+ * <https://github.com/wonder-mice/zf_log/>), but modified (mostly
+ * stripped down) for the use cases of Babeltrace.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <glib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#if defined(_WIN32) || defined(_WIN64)
+# include <windows.h>
+#endif
+
+#ifdef __linux__
+# include <sys/prctl.h>
+# include <sys/syscall.h>
+#endif
+
+#ifdef __MACH__
+# include <pthread.h>
+#endif
+
+#ifdef __GNU__
+# include <mach.h>
+#endif
+
+#ifdef __FreeBSD__
+# include <sys/thr.h>
+#endif
+
+#include "common/assert.h"
+#include "common/common.h"
+#include "common/macros.h"
+#include "compat/time.h"
+
+#include "log-api.h"
+
+#ifdef __CYGWIN__
+extern unsigned long pthread_getsequence_np(pthread_t *);
+#endif
+
+/*
+ * Thread-local logging message buffer to put the next message and write
+ * it with a single system call.
+ */
+static __thread char msg_buf[4 * 4096];
+
+/*
+ * Returns the number of available bytes in `msg_buf` considering the
+ * writing position `at`.
+ */
+static inline
+size_t avail_msg_buf_bytes(char *at)
+{
+       return msg_buf + sizeof(msg_buf) - at;
+}
+
+/*
+ * Appends the character `ch` to `msg_buf`.
+ */
+static inline
+void append_char_to_msg_buf(char ** const at, const char ch)
+{
+       **at = ch;
+       (*at)++;
+}
+
+/*
+ * Appends a space character to `msg_buf`.
+ */
+static inline
+void append_sp_to_msg_buf(char ** const at)
+{
+       append_char_to_msg_buf(at, ' ');
+}
+
+/*
+ * Appends the null-terminated string `str` to `msg_buf`.
+ */
+static inline
+void append_str_to_msg_buf(char ** const at, const char * const str)
+{
+       const size_t len = strlen(str);
+
+       memcpy(*at, str, len);
+       *at += len;
+}
+
+/*
+ * Formats the unsigned integer `val` with sprintf() using `fmt` and
+ * appends the resulting string to `msg_buf`.
+ */
+static inline
+void append_uint_to_msg_buf(char ** const at, const char * const fmt,
+               const unsigned int val)
+{
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+       const int written_len = sprintf(*at, fmt, val);
+#pragma GCC diagnostic pop
+
+       BT_ASSERT_DBG(written_len > 0);
+       *at += (size_t) written_len;
+}
+
+/*
+ * Thread-local cache of seconds and milliseconds to formatted date/time
+ * string (null terminated).
+ *
+ * This is often useful because many log messages may happen during the
+ * same millisecond.
+ *
+ * Does not need any kind of protection since it's TLS.
+ */
+struct date_time_cache {
+       uint64_t s;
+       uint32_t ms;
+       char str[128];
+};
+
+static __thread struct date_time_cache date_time_cache = {0};
+
+static
+const char *date_time_cache_get(const struct timeval tv)
+{
+       const uint64_t s = (uint64_t) tv.tv_sec;
+       const uint32_t ms = (uint32_t) (tv.tv_usec / 1000);
+
+       if (date_time_cache.s != s || date_time_cache.ms != ms) {
+               /* Add to cache now */
+               struct tm tm;
+               const time_t time_s = (time_t) tv.tv_sec;
+
+               date_time_cache.s = (uint64_t) tv.tv_sec;
+               date_time_cache.ms = (uint32_t) (tv.tv_usec / 1000);
+               (void) bt_localtime_r(&time_s, &tm);
+               (void) sprintf(date_time_cache.str, "%02u-%02u %02u:%02u:%02u.%03u",
+                       tm.tm_mon + 1, tm.tm_mday,
+                       tm.tm_hour, tm.tm_min, tm.tm_sec, date_time_cache.ms);
+       }
+
+       return date_time_cache.str;
+}
+
+/*
+ * Appends a formatted date/time for `tv` to `msg_buf`.
+ */
+static inline
+void append_date_time_to_msg_buf(char ** const at, const struct timeval tv)
+{
+       const char *str = date_time_cache_get(tv);
+
+       append_str_to_msg_buf(at, str);
+}
+
+/*
+ * Appends the PID and TID to `msg_buf`.
+ */
+static inline
+void append_pid_tid_to_msg_buf(char ** const at)
+{
+       const unsigned int pid = (unsigned int) getpid();
+       unsigned int tid;
+
+       /* Look at this beautiful portability */
+#if defined(_WIN32) || defined(_WIN64)
+       tid = (unsigned int) GetCurrentThreadId();
+#elif defined(__CYGWIN__)
+       {
+               pthread_t thr = pthread_self();
+
+               tid = (unsigned int) pthread_getsequence_np(&thr);
+       }
+#elif defined(__sun__)
+       tid = (unsigned int) pthread_self();
+#elif defined(__linux__)
+       tid = (unsigned int) syscall(SYS_gettid);
+#elif defined(__APPLE__) && defined(__MACH__)
+       tid = (unsigned int) pthread_mach_thread_np(pthread_self());
+#elif defined(__GNU__)
+       {
+               mach_port_t mach_port = mach_thread_self();
+
+               mach_port_deallocate(mach_task_self(), mach_port);
+               tid = (unsigned int) mach_port;
+       }
+#elif defined(__FreeBSD__)
+       {
+               long ltid;
+
+               thr_self(&ltid);
+
+               /*
+                * The thread ID is an integer in the range from
+                * `PID_MAX + 2` (100001) to `INT_MAX`.
+                */
+               tid = (unsigned int) ltid;
+       }
+#else
+# error "Platform not supported"
+#endif
+
+       /* Append them now */
+       append_uint_to_msg_buf(at, "%u", pid);
+       append_sp_to_msg_buf(at);
+       append_uint_to_msg_buf(at, "%u", tid);
+}
+
+/*
+ * Writes the initial part of the log message to `msg_buf`, without the
+ * message and without resetting the terminal color).
+ */
+static
+void common_write_init(char ** const at, const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag)
+{
+       const char *color_p = "";
+       struct timeval tv;
+
+       /* Get time immediately */
+       gettimeofday(&tv, 0);
+
+       /* Write the terminal color code to use, if any */
+       switch (lvl) {
+       case BT_LOG_INFO:
+               color_p = bt_common_color_fg_blue();
+               break;
+       case BT_LOG_WARNING:
+               color_p = bt_common_color_fg_yellow();
+               break;
+       case BT_LOG_ERROR:
+       case BT_LOG_FATAL:
+               color_p = bt_common_color_fg_red();
+               break;
+       default:
+               break;
+       }
+
+       append_str_to_msg_buf(at, color_p);
+
+       /* Write date/time */
+       append_date_time_to_msg_buf(at, tv);
+       append_sp_to_msg_buf(at);
+
+       /* Write PID/TID */
+       append_pid_tid_to_msg_buf(at);
+       append_sp_to_msg_buf(at);
+
+       /* Write log level letter */
+       append_char_to_msg_buf(at, bt_log_get_letter_from_level(lvl));
+       append_sp_to_msg_buf(at);
+
+       /* Write tag */
+       if (tag) {
+               append_str_to_msg_buf(at, tag);
+               append_sp_to_msg_buf(at);
+       }
+
+       /* Write source location */
+       append_str_to_msg_buf(at, func_name);
+       append_char_to_msg_buf(at, '@');
+       append_str_to_msg_buf(at, file_name);
+       append_uint_to_msg_buf(at, ":%u", line_no);
+       append_sp_to_msg_buf(at);
+}
+
+/*
+ * Writes the final part of the log message to `msg_buf` (resets the
+ * terminal color and appends a newline), and then writes the whole log
+ * message to the standard error.
+ */
+static
+void common_write_fini(char ** const at)
+{
+       append_str_to_msg_buf(at, bt_common_color_reset());
+       append_char_to_msg_buf(at, '\n');
+       (void) write(STDERR_FILENO, msg_buf, *at - msg_buf);
+}
+
+void bt_log_write(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const char * const msg)
+{
+       char *at = msg_buf;
+
+       common_write_init(&at, file_name, func_name, line_no, lvl, tag);
+       append_str_to_msg_buf(&at, msg);
+       common_write_fini(&at);
+}
+
+_BT_LOG_PRINTFLIKE(6, 0)
+static
+void write_vprintf(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const char * const fmt, va_list args)
+{
+       char *at = msg_buf;
+       int written_len;
+
+       common_write_init(&at, file_name, func_name, line_no, lvl, tag);
+       written_len = vsnprintf(at, avail_msg_buf_bytes(at) - 16,
+               fmt, args);
+       if (written_len > 0) {
+               at += (size_t) written_len;
+       }
+
+       common_write_fini(&at);
+}
+
+void bt_log_write_printf(const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const char * const fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       write_vprintf(file_name, func_name, line_no, lvl, tag, fmt, va);
+       va_end(va);
+}
+
+/*
+ * Writes the initial part of the log message to `msg_buf`, including
+ * the initial message and `errno` string, without the message and
+ * without resetting the terminal color).
+ */
+static
+void common_write_errno_init(char ** const at, const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const char * const init_msg)
+{
+       BT_ASSERT_DBG(errno != 0);
+
+       const char * const errno_msg = g_strerror(errno);
+
+       common_write_init(at, file_name, func_name, line_no, lvl, tag);
+       append_str_to_msg_buf(at, init_msg);
+       append_char_to_msg_buf(at, ':');
+       append_sp_to_msg_buf(at);
+       append_str_to_msg_buf(at, errno_msg);
+}
+
+void bt_log_write_errno(const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const char * const init_msg, const char * const msg)
+{
+       char *at = msg_buf;
+
+       common_write_errno_init(&at, file_name, func_name, line_no, lvl,
+               tag, init_msg);
+       append_str_to_msg_buf(&at, msg);
+       common_write_fini(&at);
+}
+
+void bt_log_write_errno_printf(const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const char * const init_msg, const char * const fmt, ...)
+{
+       char *at = msg_buf;
+       int written_len;
+       va_list va;
+
+       common_write_errno_init(&at, file_name, func_name, line_no, lvl,
+               tag, init_msg);
+       va_start(va, fmt);
+       written_len = vsnprintf(at, avail_msg_buf_bytes(at) - 16, fmt, va);
+       va_end(va);
+       if (written_len > 0) {
+               at += (size_t) written_len;
+       }
+
+       common_write_fini(&at);
+}
+
+/*
+ * Logs `mem_len` bytes of `mem_data` on a single line.
+ */
+static
+void write_mem_line(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const uint8_t * const mem_data,
+               const size_t mem_len, const size_t max_mem_line_len)
+{
+       static const char * const hex_chars = "0123456789abcdef";
+       char *at = msg_buf;
+       size_t i;
+
+       common_write_init(&at, file_name, func_name, line_no, lvl, tag);
+
+       /* Write hexadecimal representation */
+       for (i = 0; i < mem_len; i++) {
+               const uint8_t byte = mem_data[i];
+
+               /* Write nibble */
+               append_char_to_msg_buf(&at, hex_chars[byte >> 4]);
+               append_char_to_msg_buf(&at, hex_chars[byte & 0xf]);
+
+               /* Add a space */
+               append_sp_to_msg_buf(&at);
+       }
+
+       /* Insert spaces to align the following ASCII representation */
+       for (i = 0; i < max_mem_line_len - mem_len; i++) {
+               append_sp_to_msg_buf(&at);
+               append_sp_to_msg_buf(&at);
+               append_sp_to_msg_buf(&at);
+       }
+
+       /* Insert a vertical line between the representations */
+       append_str_to_msg_buf(&at, "| ");
+
+       /* Write the ASCII representation */
+       for (i = 0; i < mem_len; i++) {
+               const uint8_t byte = mem_data[i];
+
+               if (isprint(byte)) {
+                       append_char_to_msg_buf(&at, (char) byte);
+               } else {
+                       /* Non-printable character */
+                       append_char_to_msg_buf(&at, '.');
+               }
+       }
+
+       common_write_fini(&at);
+}
+
+/*
+ * Logs `mem_len` bytes of `mem_data` on one or more lines.
+ */
+static
+void write_mem_lines(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const uint8_t * const mem_data,
+               const size_t mem_len)
+{
+       const uint8_t *mem_at = mem_data;
+       size_t rem_mem_len = mem_len;
+
+       if (!mem_data || mem_len == 0) {
+               /* Nothing to write */
+               goto end;
+       }
+
+       while (rem_mem_len > 0) {
+               static const size_t max_mem_line_len = 16;
+
+               /* Number of bytes to write on this line */
+               const size_t mem_line_len = rem_mem_len > max_mem_line_len ?
+                       max_mem_line_len : rem_mem_len;
+
+               /* Log those bytes */
+               write_mem_line(file_name, func_name, line_no, lvl, tag,
+                       mem_at, mem_line_len, max_mem_line_len);
+
+               /* Adjust for next iteration */
+               rem_mem_len -= mem_line_len;
+               mem_at += mem_line_len;
+       }
+
+end:
+       return;
+}
+
+void bt_log_write_mem(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const void * const mem_data,
+               const size_t mem_len, const char * const msg)
+{
+       bt_log_write(file_name, func_name, line_no, lvl, tag, msg);
+       write_mem_lines(file_name, func_name, line_no, lvl, tag,
+               mem_data, mem_len);
+}
+
+void bt_log_write_mem_printf(const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const void * const mem_data, const size_t mem_len,
+               const char * const fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       write_vprintf(file_name, func_name, line_no, lvl, tag, fmt, va);
+       va_end(va);
+
+       write_mem_lines(file_name, func_name, line_no, lvl, tag,
+               mem_data, mem_len);
+}
diff --git a/src/logging/log-api.h b/src/logging/log-api.h
new file mode 100644 (file)
index 0000000..5754f8f
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2016 wonder-mice
+ * Copyright (c) 2016-2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * This is very inspired by zf_log.h (see
+ * <https://github.com/wonder-mice/zf_log/>), but modified (mostly
+ * stripped down) for the use cases of Babeltrace.
+ */
+
+#ifndef BABELTRACE_LOGGING_LOG_API_H
+#define BABELTRACE_LOGGING_LOG_API_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include <babeltrace2/babeltrace.h>
+
+/* Access private `__BT_LOGGING_LEVEL_*` macros. */
+#define __BT_IN_BABELTRACE_H
+#include <babeltrace2/logging-defs.h>
+#undef __BT_IN_BABELTRACE_H
+
+#include "common/macros.h"
+#include "common/assert.h"
+
+/* Log levels */
+enum bt_log_level {
+       BT_LOG_TRACE    = __BT_LOGGING_LEVEL_TRACE,
+       BT_LOG_DEBUG    = __BT_LOGGING_LEVEL_DEBUG,
+       BT_LOG_INFO     = __BT_LOGGING_LEVEL_INFO,
+       BT_LOG_WARNING  = __BT_LOGGING_LEVEL_WARNING,
+       BT_LOG_ERROR    = __BT_LOGGING_LEVEL_ERROR,
+       BT_LOG_FATAL    = __BT_LOGGING_LEVEL_FATAL,
+       BT_LOG_NONE     = __BT_LOGGING_LEVEL_NONE,
+};
+
+/*
+ * `BT_LOG_MINIMAL_LEVEL` (constant integer, mandatory): minimal log
+ * level to completely disable (not build) logging with levels that are
+ * more verbose.
+ */
+#ifndef BT_LOG_MINIMAL_LEVEL
+# error "`BT_LOG_MINIMAL_LEVEL` must to defined"
+#endif
+
+/* Internal: portable printf()-like attribute */
+#if defined(__printflike)
+# define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check)       \
+       __printflike(_str_index, _first_to_check)
+#elif defined(__MINGW_PRINTF_FORMAT)
+# define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check)       \
+       __attribute__((format(__MINGW_PRINTF_FORMAT, _str_index, _first_to_check)))
+#elif defined(__GNUC__)
+# define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check)       \
+       __attribute__((format(__printf__, _str_index, _first_to_check)))
+#else
+# define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check)
+#endif
+
+/*
+ * Runs `_expr` if `_cond` is true.
+ */
+#define BT_LOG_IF(_cond, _expr)        do { if (_cond) { _expr; } } while (0)
+
+/*
+ * Returns whether or not `_lvl` is enabled at build time, that is, it's
+ * equally or less verbose than `BT_LOG_MINIMAL_LEVEL`.
+ *
+ * See `BT_LOG_MINIMAL_LEVEL` to learn more.
+ */
+#define BT_LOG_ENABLED(_lvl)           ((_lvl) >= BT_LOG_MINIMAL_LEVEL)
+#define BT_LOG_ENABLED_TRACE           BT_LOG_ENABLED(__BT_LOGGING_LEVEL_TRACE)
+#define BT_LOG_ENABLED_DEBUG           BT_LOG_ENABLED(__BT_LOGGING_LEVEL_DEBUG)
+#define BT_LOG_ENABLED_INFO            BT_LOG_ENABLED(__BT_LOGGING_LEVEL_INFO)
+#define BT_LOG_ENABLED_WARNING         BT_LOG_ENABLED(__BT_LOGGING_LEVEL_WARNING)
+#define BT_LOG_ENABLED_ERROR           BT_LOG_ENABLED(__BT_LOGGING_LEVEL_ERROR)
+#define BT_LOG_ENABLED_FATAL           BT_LOG_ENABLED(__BT_LOGGING_LEVEL_FATAL)
+
+/*
+ * Returns whether or not `_lvl` is enabled at run time, that is, it's
+ * equally or less verbose than some current (run-time) level
+ * `_cur_lvl`.
+ */
+#define BT_LOG_ON_CUR_LVL(_lvl, _cur_lvl)      \
+       G_UNLIKELY(BT_LOG_ENABLED((_lvl)) && (_lvl) >= (_cur_lvl))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Writes the log message `msg` using the file name `file_name`, the
+ * function name `func_name`, the line number `line_no`, the log level
+ * `lvl`, and the tag `tag`.
+ *
+ * NOTE: This function writes unconditionally, without checking the
+ * current (run-time) log level.
+ */
+void bt_log_write(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const char *msg);
+
+/*
+ * Calls bt_log_write(), formatting the log message through sprintf()
+ * with `fmt` and the following arguments.
+ */
+void bt_log_write_printf(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const char *fmt, ...) _BT_LOG_PRINTFLIKE(6, 7);
+
+/*
+ * Writes the log message `msg` using the file name `file_name`, the
+ * function name `func_name`, the line number `line_no`, the log level
+ * `lvl`, and the tag `tag`, and also dumps `mem_len` bytes of
+ * `mem_data`.
+ *
+ * NOTE: This function writes unconditionally, without checking the
+ * current (run-time) log level.
+ */
+void bt_log_write_mem(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const void *mem_data, size_t mem_len,
+               const char *msg);
+
+/*
+ * Calls bt_log_write_mem(), formatting the log message through
+ * sprintf() with `fmt` and the following arguments.
+ */
+void bt_log_write_mem_printf(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const void *mem_data, size_t mem_len,
+               const char *fmt, ...) _BT_LOG_PRINTFLIKE(8, 9);
+
+/*
+ * Writes:
+ *
+ * 1. `init_msg`
+ * 2. The string `: `
+ * 3. The message corresponding to `errno` (current error number)
+ * 4. `msg`
+ *
+ * This function uses the file name `file_name`, the function name
+ * `func_name`, the line number `line_no`, the log level `lvl`, and the
+ * tag `tag`.
+ *
+ * NOTE: This function writes unconditionally, without checking the
+ * current (run-time) log level.
+ */
+void bt_log_write_errno(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const char *init_msg, const char *msg);
+
+/*
+ * Calls bt_log_write_errno(), formatting the log message through
+ * sprintf() with `fmt` and the following arguments.
+ */
+void bt_log_write_errno_printf(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const char *init_msg,
+               const char *fmt, ...) _BT_LOG_PRINTFLIKE(7, 8);
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * Calls bt_log_write() if logging is enabled at run time for the
+ * current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write().
+ */
+#define BT_LOG_WRITE_CUR_LVL(_lvl, _cur_lvl, _tag, _msg)               \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write(__FILE__, __func__, __LINE__,      \
+                               (_lvl), (_tag), (_msg));                \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_printf() if logging is enabled at run time for the
+ * current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_printf().
+ */
+#define BT_LOG_WRITE_PRINTF_CUR_LVL(_lvl, _cur_lvl, _tag, _fmt, ...)   \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_printf(__FILE__, __func__,         \
+                               __LINE__, (_lvl), (_tag), (_fmt),       \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_mem() if logging is enabled at run time for the
+ * current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_mem().
+ */
+#define BT_LOG_WRITE_MEM_CUR_LVL(_lvl, _cur_lvl, _tag, _mem_data, _mem_len, _msg) \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_mem(__FILE__, __func__, __LINE__,  \
+                               (_lvl), (_tag), (_mem_data),            \
+                               (_mem_len), (_msg));                    \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_mem_printf() if logging is enabled at run time for
+ * the current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_mem_printf().
+ */
+#define BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(_lvl, _cur_lvl, _tag, _mem_data, _mem_len, _fmt, ...) \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_mem_printf(__FILE__, __func__,     \
+                               __LINE__, (_lvl), (_tag), (_mem_data),  \
+                               (_mem_len), (_fmt), ##__VA_ARGS__);     \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_errno() if logging is enabled at run time for the
+ * current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_errno().
+ */
+#define BT_LOG_WRITE_ERRNO_CUR_LVL(_lvl, _cur_lvl, _tag, _init_msg, _msg) \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_errno(__FILE__, __func__,          \
+                               __LINE__, (_lvl), (_tag), (_init_msg),  \
+                               (_msg));                                \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_errno_printf() if logging is enabled at run time
+ * for the current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_errno_printf().
+ */
+#define BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(_lvl, _cur_lvl, _tag, _init_msg, _fmt, ...) \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_errno_printf(__FILE__, __func__,   \
+                               __LINE__, (_lvl), (_tag), (_init_msg),  \
+                               (_fmt), ##__VA_ARGS__);                 \
+               }                                                       \
+       } while (0)
+
+/*
+ * Returns the equivalent letter of the log level `level`.
+ *
+ * `level` must be a valid log level.
+ */
+static inline
+char bt_log_get_letter_from_level(const enum bt_log_level level)
+{
+       char letter;
+
+       switch (level) {
+       case BT_LOG_TRACE:
+               letter = 'T';
+               break;
+       case BT_LOG_DEBUG:
+               letter = 'D';
+               break;
+       case BT_LOG_INFO:
+               letter = 'I';
+               break;
+       case BT_LOG_WARNING:
+               letter = 'W';
+               break;
+       case BT_LOG_ERROR:
+               letter = 'E';
+               break;
+       case BT_LOG_FATAL:
+               letter = 'F';
+               break;
+       case BT_LOG_NONE:
+               letter = 'N';
+               break;
+       default:
+               abort();
+       }
+
+       return letter;
+}
+
+/*
+ * Returns the log level as an integer for the string `str`, or -1 if
+ * `str` is not a valid log level string.
+ */
+static inline
+int bt_log_get_level_from_string(const char * const str)
+{
+       int level = -1;
+
+       BT_ASSERT(str);
+
+       if (strcmp(str, "TRACE") == 0 || strcmp(str, "T") == 0) {
+               level = BT_LOG_TRACE;
+       } else if (strcmp(str, "DEBUG") == 0 || strcmp(str, "D") == 0) {
+               level = BT_LOG_DEBUG;
+       } else if (strcmp(str, "INFO") == 0 || strcmp(str, "I") == 0) {
+               level = BT_LOG_INFO;
+       } else if (strcmp(str, "WARN") == 0 ||
+                       strcmp(str, "WARNING") == 0 ||
+                       strcmp(str, "W") == 0) {
+               level = BT_LOG_WARNING;
+       } else if (strcmp(str, "ERROR") == 0 || strcmp(str, "E") == 0) {
+               level = BT_LOG_ERROR;
+       } else if (strcmp(str, "FATAL") == 0 || strcmp(str, "F") == 0) {
+               level = BT_LOG_FATAL;
+       } else if (strcmp(str, "NONE") == 0 || strcmp(str, "N") == 0) {
+               level = BT_LOG_NONE;
+       } else {
+               /* FIXME: Should we warn here? How? */
+       }
+
+       return level;
+}
+
+/*
+ * Returns the log level as an integer for the letter `letter`, or -1 if
+ * `letter` is not a valid log level string.
+ */
+static inline
+int bt_log_get_level_from_letter(const char letter)
+{
+       const char str[] = {letter, '\0'};
+
+       return bt_log_get_level_from_string(str);
+}
+
+/*
+ * Returns the log level for the value of the environment variable named
+ * `env_var_name`, or `BT_LOG_NONE` if not a valid log level string.
+ */
+static inline
+enum bt_log_level bt_log_get_level_from_env(const char *env_var_name)
+{
+       const char * const varval = getenv(env_var_name);
+       enum bt_log_level level = BT_LOG_NONE;
+       int int_level;
+
+       if (!varval) {
+               goto end;
+       }
+
+       int_level = bt_log_get_level_from_string(varval);
+       if (int_level < 0) {
+               /* FIXME: Should we warn here? How? */
+               int_level = BT_LOG_NONE;
+       }
+
+       level = (enum bt_log_level) int_level;
+
+end:
+       return level;
+}
+
+/*
+ * Declares the variable named `_level_sym` as an external symbol
+ * containing a log level.
+ */
+#define BT_LOG_LEVEL_EXTERN_SYMBOL(_level_sym)                         \
+       extern enum bt_log_level _level_sym
+
+/*
+ * 1. Defines the log level variable `_level_sym`, initializing it to
+ *    `BT_LOG_NONE` (logging disabled).
+ *
+ * 2. Defines a library constructor named _bt_log_level_ctor() which
+ *    initializes the log level variable `_level_sym` from the value of
+ *    the environment variable named `_env_var_name`.
+ */
+#define BT_LOG_INIT_LOG_LEVEL(_level_sym, _env_var_name)               \
+       enum bt_log_level _level_sym = BT_LOG_NONE;                                     \
+       static                                                          \
+       void __attribute__((constructor)) _bt_log_level_ctor(void)      \
+       {                                                               \
+               _level_sym = bt_log_get_level_from_env(_env_var_name);  \
+       }
+
+#endif /* BABELTRACE_LOGGING_LOG_API_H */
diff --git a/src/logging/log.c b/src/logging/log.c
deleted file mode 100644 (file)
index 613e056..0000000
+++ /dev/null
@@ -1,1443 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2016 wonder-mice
- *
- * This is zf_log.c, modified with Babeltrace prefixes.
- * See <https://github.com/wonder-mice/zf_log/>.
- */
-
-#include "common/macros.h"
-#include "common/common.h"
-#include <pthread.h>
-#include "common/assert.h"
-
-#ifdef __CYGWIN__
-extern unsigned long pthread_getsequence_np(pthread_t *);
-#endif
-
-/* When defined, Android log (android/log.h) will be used by default instead of
- * stderr (ignored on non-Android platforms). Date, time, pid and tid (context)
- * will be provided by Android log. Android log features will be used to output
- * log level and tag.
- */
-#ifdef BT_LOG_USE_ANDROID_LOG
-       #undef BT_LOG_USE_ANDROID_LOG
-       #if defined(__ANDROID__)
-               #define BT_LOG_USE_ANDROID_LOG 1
-       #else
-               #define BT_LOG_USE_ANDROID_LOG 0
-       #endif
-#else
-       #define BT_LOG_USE_ANDROID_LOG 0
-#endif
-/* When defined, NSLog (uses Apple System Log) will be used instead of stderr
- * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be
- * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on
- * non-public CFLog() function. Both use Apple System Log internally, but it's
- * easier to call CFLog() from C than NSLog(). Current implementation doesn't
- * support "%@" format specifier.
- */
-#ifdef BT_LOG_USE_NSLOG
-       #undef BT_LOG_USE_NSLOG
-       #if defined(__APPLE__) && defined(__MACH__)
-               #define BT_LOG_USE_NSLOG 1
-       #else
-               #define BT_LOG_USE_NSLOG 0
-       #endif
-#else
-       #define BT_LOG_USE_NSLOG 0
-#endif
-/* When defined, OutputDebugString() will be used instead of stderr (ignored on
- * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with
- * UTF-8 data.
- */
-#ifdef BT_LOG_USE_DEBUGSTRING
-       #undef BT_LOG_USE_DEBUGSTRING
-       #if defined(_WIN32) || defined(_WIN64)
-               #define BT_LOG_USE_DEBUGSTRING 1
-       #else
-               #define BT_LOG_USE_DEBUGSTRING 0
-       #endif
-#else
-       #define BT_LOG_USE_DEBUGSTRING 0
-#endif
-/* When defined, bt_log library will not contain definition of tag prefix
- * variable. In that case it must be defined elsewhere using
- * BT_LOG_DEFINE_TAG_PREFIX macro, for example:
- *
- *   BT_LOG_DEFINE_TAG_PREFIX = "ProcessName";
- *
- * This allows to specify custom value for static initialization and avoid
- * overhead of setting this value in runtime.
- */
-#ifdef BT_LOG_EXTERN_TAG_PREFIX
-       #undef BT_LOG_EXTERN_TAG_PREFIX
-       #define BT_LOG_EXTERN_TAG_PREFIX 1
-#else
-       #define BT_LOG_EXTERN_TAG_PREFIX 0
-#endif
-/* When defined, bt_log library will not contain definition of global format
- * variable. In that case it must be defined elsewhere using
- * BT_LOG_DEFINE_GLOBAL_FORMAT macro, for example:
- *
- *   BT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH};
- *
- * This allows to specify custom value for static initialization and avoid
- * overhead of setting this value in runtime.
- */
-#ifdef BT_LOG_EXTERN_GLOBAL_FORMAT
-       #undef BT_LOG_EXTERN_GLOBAL_FORMAT
-       #define BT_LOG_EXTERN_GLOBAL_FORMAT 1
-#else
-       #define BT_LOG_EXTERN_GLOBAL_FORMAT 0
-#endif
-/* When defined, bt_log library will not contain definition of global output
- * variable. In that case it must be defined elsewhere using
- * BT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example:
- *
- *   BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_PUT_STD, custom_output_callback};
- *
- * This allows to specify custom value for static initialization and avoid
- * overhead of setting this value in runtime.
- */
-#ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT
-       #undef BT_LOG_EXTERN_GLOBAL_OUTPUT
-       #define BT_LOG_EXTERN_GLOBAL_OUTPUT 1
-#else
-       #define BT_LOG_EXTERN_GLOBAL_OUTPUT 0
-#endif
-/* When defined, bt_log library will not contain definition of global output
- * level variable. In that case it must be defined elsewhere using
- * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example:
- *
- *   BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = BT_LOG_WARNING;
- *
- * This allows to specify custom value for static initialization and avoid
- * overhead of setting this value in runtime.
- */
-#ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
-       #undef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
-       #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1
-#else
-       #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0
-#endif
-/* When defined, implementation will prefer smaller code size over speed.
- * Very rough estimate is that code will be up to 2x smaller and up to 2x
- * slower. Disabled by default.
- */
-#ifdef BT_LOG_OPTIMIZE_SIZE
-       #undef BT_LOG_OPTIMIZE_SIZE
-       #define BT_LOG_OPTIMIZE_SIZE 1
-#else
-       #define BT_LOG_OPTIMIZE_SIZE 0
-#endif
-/* Size of the log line buffer. The buffer is a globally allocated per thread.
- */
-#ifndef BT_LOG_BUF_SZ
-       #define BT_LOG_BUF_SZ (4 * 4096)
-#endif
-/* Default number of bytes in one line of memory output. For large values
- * BT_LOG_BUF_SZ also must be increased.
- */
-#ifndef BT_LOG_MEM_WIDTH
-       #define BT_LOG_MEM_WIDTH 32
-#endif
-/* String to put in the end of each log line (can be empty). Its value used by
- * stderr output callback. Its size used as a default value for BT_LOG_EOL_SZ.
- */
-#ifndef BT_LOG_EOL
-       #define BT_LOG_EOL "\n"
-#endif
-/* Default delimiter that separates parts of log message. Can NOT contain '%'
- * or '\0'.
- *
- * Log message format specifications can override (or ignore) this value. For
- * more details see BT_LOG_MESSAGE_CTX_FORMAT, BT_LOG_MESSAGE_SRC_FORMAT and
- * BT_LOG_MESSAGE_TAG_FORMAT.
- */
-#ifndef BT_LOG_DEF_DELIMITER
-       #define BT_LOG_DEF_DELIMITER " "
-#endif
-/* Specifies log message context format. Log message context includes date,
- * time, process id, thread id and message's log level. Custom information can
- * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND,
- * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements),
- * F_UINT(width, value).
- *
- * Must be defined as a tuple, for example:
- *
- *   #define BT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY, S(" > "))
- *
- * In that case, resulting log message will be:
- *
- *   2016.12.22 > TAG function@filename.c:line Message text
- *
- * Note, that tag, source location and message text are not impacted by
- * this setting. See BT_LOG_MESSAGE_TAG_FORMAT and BT_LOG_MESSAGE_SRC_FORMAT.
- *
- * If message context must be visually separated from the rest of the message,
- * it must be reflected in context format (notice trailing S(" > ") in the
- * example above).
- *
- * S(str) adds constant string str. String can NOT contain '%' or '\0'.
- *
- * F_INIT(statements) adds initialization statement(s) that will be evaluated
- * once for each log message. All statements are evaluated in specified order.
- * Several F_INIT() fields can be used in every log message format
- * specification. Fields, like F_UINT(width, value), are allowed to use results
- * of initialization statements. If statement introduces variables (or other
- * names, like structures) they must be prefixed with "f_". Statements  must be
- * enclosed into additional "()". Example:
- *
- *   #define BT_LOG_MESSAGE_CTX_FORMAT \
- *       (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \
- *        YEAR, S("."), MONTH, S("."), DAY, S(" "), \
- *        F_UINT(5, f_ru.ru_nsignals), \
- *        S(" "))
- *
- * F_UINT(width, value) adds unsigned integer value extended with up to width
- * spaces (for alignment purposes). Value can be any expression that evaluates
- * to unsigned integer. If expression contains non-standard functions, they
- * must be declared with F_INIT(). Example:
- *
- *   #define BT_LOG_MESSAGE_CTX_FORMAT \
- *        (YEAR, S("."), MONTH, S("."), DAY, S(" "), \
- *        F_INIT(( unsigned tickcount(); )), \
- *        F_UINT(5, tickcount()), \
- *        S(" "))
- *
- * Other log message format specifications follow same rules, but have a
- * different set of supported fields.
- */
-#ifndef BT_LOG_MESSAGE_CTX_FORMAT
-       #define BT_LOG_MESSAGE_CTX_FORMAT \
-               (MONTH, S("-"), DAY, S(BT_LOG_DEF_DELIMITER), \
-                HOUR, S(":"), MINUTE, S(":"), SECOND, S("."), MILLISECOND, S(BT_LOG_DEF_DELIMITER), \
-                PID, S(BT_LOG_DEF_DELIMITER), TID, S(BT_LOG_DEF_DELIMITER), \
-                LEVEL, S(BT_LOG_DEF_DELIMITER))
-#endif
-/* Example:
- */
-/* Specifies log message tag format. It includes tag prefix and tag. Custom
- * information can be added as well. Supported fields:
- * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements),
- * F_UINT(width, value).
- *
- * TAG(prefix_delimiter, tag_delimiter) adds following string to log message:
- *
- *   PREFIX<prefix_delimiter>TAG<tag_delimiter>
- *
- * Prefix delimiter will be used only when prefix is not empty. Tag delimiter
- * will be used only when prefixed tag is not empty. Example:
- *
- *   #define BT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] "))
- *
- * See BT_LOG_MESSAGE_CTX_FORMAT for details.
- */
-#ifndef BT_LOG_MESSAGE_TAG_FORMAT
-       #define BT_LOG_MESSAGE_TAG_FORMAT \
-               (TAG(".", BT_LOG_DEF_DELIMITER))
-#endif
-/* Specifies log message source location format. It includes function name,
- * file name and file line. Custom information can be added as well. Supported
- * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements),
- * F_UINT(width, value).
- *
- * See BT_LOG_MESSAGE_CTX_FORMAT for details.
- */
-#ifndef BT_LOG_MESSAGE_SRC_FORMAT
-       #define BT_LOG_MESSAGE_SRC_FORMAT \
-               (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(BT_LOG_DEF_DELIMITER))
-#endif
-/* Fields that can be used in log message format specifications (see above).
- * Mentioning them here explicitly, so we know that nobody else defined them
- * before us. See BT_LOG_MESSAGE_CTX_FORMAT for details.
- */
-#define YEAR YEAR
-#define MONTH MONTH
-#define DAY DAY
-#define MINUTE MINUTE
-#define SECOND SECOND
-#define MILLISECOND MILLISECOND
-#define PID PID
-#define TID TID
-#define LEVEL LEVEL
-#define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim)
-#define FUNCTION FUNCTION
-#define FILENAME FILENAME
-#define FILELINE FILELINE
-#define S(str) S(str)
-#define F_INIT(statements) F_INIT(statements)
-#define F_UINT(width, value) F_UINT(width, value)
-/* Number of bytes to reserve for EOL in the log line buffer (must be >0).
- * Must be larger than or equal to length of BT_LOG_EOL with terminating null.
- */
-#ifndef BT_LOG_EOL_SZ
-       #define BT_LOG_EOL_SZ sizeof(BT_LOG_EOL)
-#endif
-/* Compile instrumented version of the library to facilitate unit testing.
- */
-#ifndef BT_LOG_INSTRUMENTED
-       #define BT_LOG_INSTRUMENTED 0
-#endif
-
-#if defined(__linux__)
-       #if !defined(__ANDROID__) && !defined(_GNU_SOURCE)
-               #define _GNU_SOURCE
-       #endif
-#endif
-#if defined(__MINGW32__)
-       #ifdef __STRICT_ANSI__
-               #undef __STRICT_ANSI__
-       #endif
-#endif
-#include "common/assert.h"
-#include <ctype.h>
-#include <string.h>
-#include <time.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#define BT_LOG_OUTPUT_LEVEL dummy
-
-#include "log.h"
-#include <babeltrace2/logging.h>
-
-#if defined(_WIN32) || defined(_WIN64)
-       #include <windows.h>
-#else
-       #include <unistd.h>
-       #include <sys/time.h>
-       #if defined(__linux__)
-               #include <linux/limits.h>
-       #elif (defined(__sun__) || defined(__CYGWIN__) || defined(__GNU__))
-               /* Solaris, Cygwin and Hurd have no sys/syslimits.h */
-       #else
-               #include <sys/syslimits.h>
-       #endif
-#endif
-
-#if defined(__linux__)
-       #include <sys/prctl.h>
-       #include <sys/types.h>
-       #if !defined(__ANDROID__)
-               #include <sys/syscall.h>
-       #endif
-#endif
-#if defined(__MACH__)
-       #include <pthread.h>
-#endif
-#if defined(__GNU__)
-       #include <mach.h>
-#endif
-
-#define INLINE _BT_LOG_INLINE
-#define VAR_UNUSED(var) (void)var
-#define RETVAL_UNUSED(expr) do { while(expr) break; } while(0)
-#define STATIC_ASSERT(name, cond) \
-       typedef char assert_##name[(cond)? 1: -1]
-#define ASSERT_UNREACHABLE(why) assert(!sizeof(why))
-#ifndef _countof
-       #define _countof(xs) (sizeof(xs) / sizeof((xs)[0]))
-#endif
-
-#if BT_LOG_INSTRUMENTED
-       #define INSTRUMENTED_CONST
-#else
-       #define INSTRUMENTED_CONST const
-#endif
-
-#define _PP_PASTE_2(a, b) a ## b
-#define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b)
-
-#define _PP_PASTE_3(a, b, c) a ## b ## c
-#define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c)
-
-/* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__
- * as a single token and requires additional expansion to realize that it's
- * actually a list. If not for it, there would be no need in this extra
- * expansion.
- */
-#define _PP_ID(x) x
-#define _PP_NARGS_N(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,...) _24
-#define _PP_NARGS(...) _PP_ID(_PP_NARGS_N(__VA_ARGS__,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))
-
-/* There is a more efficient way to implement this, but it requires
- * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't
- * have one.
- */
-#define _PP_HEAD__(x, ...) x
-#define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~))
-#define _PP_HEAD(xs) _PP_HEAD_ xs
-#define _PP_TAIL_(x, ...) (__VA_ARGS__)
-#define _PP_TAIL(xs) _PP_TAIL_ xs
-#define _PP_UNTUPLE_(...) __VA_ARGS__
-#define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs
-
-/* Apply function macro to each element in tuple. Output is not
- * enforced to be a tuple.
- */
-#define _PP_MAP_1(f, xs) f(_PP_HEAD(xs))
-#define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs))
-#define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs))
-#define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs))
-#define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs))
-#define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs))
-#define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs))
-#define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs))
-#define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs))
-#define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs))
-#define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs))
-#define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs))
-#define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs))
-#define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs))
-#define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs))
-#define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs))
-#define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs))
-#define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs))
-#define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs))
-#define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs))
-#define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs))
-#define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs))
-#define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs))
-#define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs))
-#define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs) (f, xs)
-
-/* Apply function macro to each element in tuple in reverse order.
- * Output is not enforced to be a tuple.
- */
-#define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs))
-#define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
-#define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs) (f, xs)
-
-/* Used to implement _BT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All possible
- * fields must be mentioned here. Not counting F_INIT() here because it's
- * somewhat special and is handled spearatly (at least for now).
- */
-#define _BT_LOG_MESSAGE_FORMAT_MASK__             (0<<0)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__YEAR         (1<<1)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__MONTH        (1<<2)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__DAY          (1<<3)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__HOUR         (1<<4)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__MINUTE       (1<<5)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__SECOND       (1<<6)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND  (1<<7)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__PID          (1<<8)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__TID          (1<<9)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__LEVEL        (1<<10)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts)  (1<<11)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__FUNCTION     (1<<12)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__FILENAME     (1<<13)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__FILELINE     (1<<14)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__S(s)         (1<<15)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0<<16)
-#define _BT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1<<17)
-#define _BT_LOG_MESSAGE_FORMAT_MASK(field) \
-       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_MASK_, _, field)
-
-/* Logical "or" of masks of fields used in specified format specification.
- */
-#define _BT_LOG_MESSAGE_FORMAT_FIELDS(format) \
-       (0 _PP_MAP(| _BT_LOG_MESSAGE_FORMAT_MASK, format))
-
-/* Expands to expressions that evaluates to true if field is used in
- * specified format specification. Example:
- *
- *   #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT, BT_LOG_MESSAGE_CTX_FORMAT)
- *       ...
- *   #endif
- */
-#define _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \
-       (_BT_LOG_MESSAGE_FORMAT_MASK(field) & _BT_LOG_MESSAGE_FORMAT_FIELDS(format))
-
-/* Same, but checks all supported format specifications.
- */
-#define _BT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \
-       (_BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_CTX_FORMAT) || \
-        _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_TAG_FORMAT) || \
-        _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_SRC_FORMAT))
-
-#define _BT_LOG_MESSAGE_FORMAT_DATETIME_USED \
-       (_BT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, BT_LOG_MESSAGE_CTX_FORMAT) || \
-        _BT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, BT_LOG_MESSAGE_CTX_FORMAT) || \
-        _BT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, BT_LOG_MESSAGE_CTX_FORMAT) || \
-        _BT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, BT_LOG_MESSAGE_CTX_FORMAT) || \
-        _BT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, BT_LOG_MESSAGE_CTX_FORMAT) || \
-        _BT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, BT_LOG_MESSAGE_CTX_FORMAT) || \
-        _BT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, BT_LOG_MESSAGE_CTX_FORMAT))
-
-#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
-       #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */
-       #define memccpy _memccpy
-#endif
-
-#if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || \
-               (defined(__MINGW64__) && !defined(__USE_MINGW_ANSI_STDIO))
-       #define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va)
-       static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap)
-       {
-               const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap);
-               return 0 < n? n: (int)sz + 1; /* no need in _vscprintf() for now */
-       }
-       #if BT_LOG_OPTIMIZE_SIZE
-       #define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__)
-       static int fake_snprintf(char *s, size_t sz, const char *fmt, ...)
-       {
-               va_list va;
-               va_start(va, fmt);
-               const int n = fake_vsnprintf(s, sz, fmt, va);
-               va_end(va);
-               return n;
-       }
-       #endif
-#endif
-
-typedef void (*time_cb)(struct tm *const tm, unsigned *const usec);
-typedef void (*pid_cb)(int *const pid, int *const tid);
-typedef void (*buffer_cb)(bt_log_message *msg, char *buf);
-
-typedef struct src_location
-{
-       const char *const func;
-       const char *const file;
-       const unsigned line;
-}
-src_location;
-
-typedef struct mem_block
-{
-       const void *const d;
-       const unsigned d_sz;
-}
-mem_block;
-
-static void time_callback(struct tm *const tm, unsigned *const usec);
-static void pid_callback(int *const pid, int *const tid);
-static void buffer_callback(bt_log_message *msg, char *buf);
-
-STATIC_ASSERT(eol_fits_eol_sz, sizeof(BT_LOG_EOL) <= BT_LOG_EOL_SZ);
-STATIC_ASSERT(eol_sz_greater_than_zero, 0 < BT_LOG_EOL_SZ);
-STATIC_ASSERT(eol_sz_less_than_buf_sz, BT_LOG_EOL_SZ < BT_LOG_BUF_SZ);
-static const char c_hex[] = "0123456789abcdef";
-
-static __thread char logging_buf[4 * 4096];
-
-static INSTRUMENTED_CONST unsigned g_buf_sz = BT_LOG_BUF_SZ - BT_LOG_EOL_SZ;
-static INSTRUMENTED_CONST time_cb g_time_cb = time_callback;
-static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback;
-static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback;
-
-#if BT_LOG_USE_ANDROID_LOG
-       #include <android/log.h>
-
-       static INLINE int android_lvl(const int lvl)
-       {
-               switch (lvl)
-               {
-               case BT_LOG_TRACE:
-                       return ANDROID_LOG_VERBOSE;
-               case BT_LOG_DEBUG:
-                       return ANDROID_LOG_DEBUG;
-               case BT_LOG_INFO:
-                       return ANDROID_LOG_INFO;
-               case BT_LOG_WARNING:
-                       return ANDROID_LOG_WARN;
-               case BT_LOG_ERROR:
-                       return ANDROID_LOG_ERROR;
-               case BT_LOG_FATAL:
-                       return ANDROID_LOG_FATAL;
-               default:
-                       ASSERT_UNREACHABLE("Bad log level");
-                       return ANDROID_LOG_UNKNOWN;
-               }
-       }
-
-       static void out_android_callback(const bt_log_message *const msg, void *arg)
-       {
-               VAR_UNUSED(arg);
-               *msg->p = 0;
-               const char *tag = msg->p;
-               if (msg->tag_e != msg->tag_b)
-               {
-                       tag = msg->tag_b;
-                       *msg->tag_e = 0;
-               }
-               __android_log_print(android_lvl(msg->lvl), tag, "%s", msg->msg_b);
-       }
-
-       enum { OUT_ANDROID_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX };
-       #define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback
-#endif
-
-#if BT_LOG_USE_NSLOG
-       #include <CoreFoundation/CoreFoundation.h>
-       CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...);
-
-       static INLINE int apple_lvl(const int lvl)
-       {
-               switch (lvl)
-               {
-               case BT_LOG_TRACE:
-                       return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */;
-               case BT_LOG_DEBUG:
-                       return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */;
-               case BT_LOG_INFO:
-                       return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */;
-               case BT_LOG_WARNING:
-                       return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */;
-               case BT_LOG_ERROR:
-                       return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */;
-               case BT_LOG_FATAL:
-                       return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */;
-               default:
-                       ASSERT_UNREACHABLE("Bad log level");
-                       return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */;
-               }
-       }
-
-       static void out_nslog_callback(const bt_log_message *const msg, void *arg)
-       {
-               VAR_UNUSED(arg);
-               *msg->p = 0;
-               CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b);
-       }
-
-       enum { OUT_NSLOG_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX };
-       #define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback
-#endif
-
-#if BT_LOG_USE_DEBUGSTRING
-       #include <windows.h>
-
-       static void out_debugstring_callback(const bt_log_message *const msg, void *arg)
-       {
-               VAR_UNUSED(arg);
-               msg->p[0] = '\n';
-               msg->p[1] = '\0';
-               OutputDebugStringA(msg->buf);
-       }
-
-       enum { OUT_DEBUGSTRING_MASK = BT_LOG_PUT_STD };
-       #define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback
-#endif
-
-BT_HIDDEN
-void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg)
-{
-       VAR_UNUSED(arg);
-       const size_t eol_len = sizeof(BT_LOG_EOL) - 1;
-       memcpy(msg->p, BT_LOG_EOL, eol_len);
-#if defined(_WIN32) || defined(_WIN64)
-       /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and
-          without FILE_WRITE_DATA */
-       WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf,
-                         (DWORD)(msg->p - msg->buf + eol_len), 0, 0);
-#else
-       /* write() is atomic for buffers less than or equal to PIPE_BUF. */
-       RETVAL_UNUSED(write(STDERR_FILENO, msg->buf,
-                                               (size_t)(msg->p - msg->buf) + eol_len));
-#endif
-}
-
-static const bt_log_output out_stderr = {BT_LOG_OUT_STDERR};
-
-#if !BT_LOG_EXTERN_TAG_PREFIX
-       BT_LOG_DEFINE_TAG_PREFIX = 0;
-#endif
-
-#if !BT_LOG_EXTERN_GLOBAL_FORMAT
-       BT_LOG_DEFINE_GLOBAL_FORMAT = {BT_LOG_MEM_WIDTH};
-#endif
-
-#if !BT_LOG_EXTERN_GLOBAL_OUTPUT
-       #if BT_LOG_USE_ANDROID_LOG
-               BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID};
-       #elif BT_LOG_USE_NSLOG
-               BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG};
-       #elif BT_LOG_USE_DEBUGSTRING
-               BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING};
-       #else
-               BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR};
-       #endif
-#endif
-
-#if !BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
-       BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0;
-#endif
-
-BT_HIDDEN
-const bt_log_spec _bt_log_stderr_spec =
-{
-       BT_LOG_GLOBAL_FORMAT,
-       &out_stderr,
-};
-
-static const bt_log_spec global_spec =
-{
-       BT_LOG_GLOBAL_FORMAT,
-       BT_LOG_GLOBAL_OUTPUT,
-};
-
-#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, BT_LOG_MESSAGE_CTX_FORMAT)
-static char lvl_char(const int lvl)
-{
-       switch (lvl)
-       {
-       case BT_LOG_TRACE:
-               return 'T';
-       case BT_LOG_DEBUG:
-               return 'D';
-       case BT_LOG_INFO:
-               return 'I';
-       case BT_LOG_WARNING:
-               return 'W';
-       case BT_LOG_ERROR:
-               return 'E';
-       case BT_LOG_FATAL:
-               return 'F';
-       default:
-               ASSERT_UNREACHABLE("Bad log level");
-               return '?';
-       }
-}
-#endif
-
-#define GCCVER_LESS(MAJOR, MINOR, PATCH) \
-       (__GNUC__ < MAJOR || \
-               (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \
-                       (__GNUC_MINOR__ == MINOR && __GNUC_PATCHLEVEL__ < PATCH))))
-
-#if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4,7,0)
-       #define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0)
-       #define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n)
-       #define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n)
-       #define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n)
-       #define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n)
-       /* Note: will not store old value of *vp in *ep (non-standard behaviour) */
-       #define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \
-               __sync_bool_compare_and_swap(vp, *(ep), d)
-#endif
-
-#if !BT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64)
-#define TCACHE
-#define TCACHE_STALE (0x40000000)
-#define TCACHE_FLUID (0x40000000 | 0x80000000)
-static unsigned g_tcache_mode = TCACHE_STALE;
-static struct timeval g_tcache_tv = {0, 0};
-static struct tm g_tcache_tm = {0};
-
-static INLINE int tcache_get(const struct timeval *const tv, struct tm *const tm)
-{
-       unsigned mode;
-       mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED);
-       if (0 == (mode & TCACHE_FLUID))
-       {
-               mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE);
-               if (0 == (mode & TCACHE_FLUID))
-               {
-                       if (g_tcache_tv.tv_sec == tv->tv_sec)
-                       {
-                               *tm = g_tcache_tm;
-                               __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
-                               return !0;
-                       }
-                       __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED);
-               }
-               __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
-       }
-       return 0;
-}
-
-static INLINE void tcache_set(const struct timeval *const tv, struct tm *const tm)
-{
-       unsigned stale = TCACHE_STALE;
-       if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID,
-                                                                       0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
-       {
-               g_tcache_tv = *tv;
-               g_tcache_tm = *tm;
-               __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE);
-       }
-}
-#endif
-
-static void time_callback(struct tm *const tm, unsigned *const msec)
-{
-#if !_BT_LOG_MESSAGE_FORMAT_DATETIME_USED
-       VAR_UNUSED(tm);
-       VAR_UNUSED(msec);
-#else
-       #if defined(_WIN32) || defined(_WIN64)
-       SYSTEMTIME st;
-       GetLocalTime(&st);
-       tm->tm_year = st.wYear;
-       tm->tm_mon = st.wMonth;
-       tm->tm_mday = st.wDay;
-       tm->tm_wday = st.wDayOfWeek;
-       tm->tm_hour = st.wHour;
-       tm->tm_min = st.wMinute;
-       tm->tm_sec = st.wSecond;
-       *msec = st.wMilliseconds;
-       #else
-       struct timeval tv;
-       gettimeofday(&tv, 0);
-               #ifndef TCACHE
-               localtime_r(&tv.tv_sec, tm);
-               #else
-               if (!tcache_get(&tv, tm))
-               {
-                       localtime_r(&tv.tv_sec, tm);
-                       tcache_set(&tv, tm);
-               }
-               #endif
-       *msec = (unsigned)tv.tv_usec / 1000;
-       #endif
-#endif
-}
-
-static void pid_callback(int *const pid, int *const tid)
-{
-#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT)
-       VAR_UNUSED(pid);
-#else
-       #if defined(_WIN32) || defined(_WIN64)
-       *pid = GetCurrentProcessId();
-       #else
-       *pid = getpid();
-       #endif
-#endif
-
-#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT)
-       VAR_UNUSED(tid);
-#else
-       #if defined(_WIN32) || defined(_WIN64)
-       *tid = GetCurrentThreadId();
-       #elif defined(__CYGWIN__)
-       pthread_t thr = pthread_self();
-       *tid = (int)pthread_getsequence_np(&thr);
-       #elif defined(__sun__)
-       *tid = (int)pthread_self();
-       #elif defined(__ANDROID__)
-       *tid = gettid();
-       #elif defined(__linux__)
-       *tid = syscall(SYS_gettid);
-       #elif defined(__APPLE__) && defined(__MACH__)
-       *tid = (int)pthread_mach_thread_np(pthread_self());
-       #elif defined(__GNU__)
-       mach_port_t mach_port = mach_thread_self();
-       mach_port_deallocate(mach_task_self(), mach_port);
-       *tid = (int)mach_port;
-       #else
-               #define Platform not supported
-       #endif
-#endif
-}
-
-static void buffer_callback(bt_log_message *msg, char *buf)
-{
-       msg->e = (msg->p = msg->buf = buf) + g_buf_sz;
-}
-
-#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT)
-static const char *funcname(const char *func)
-{
-       return func? func: "";
-}
-#endif
-
-#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT)
-static const char *filename(const char *file)
-{
-       const char *f = file;
-       for (const char *p = file; 0 != *p; ++p)
-       {
-               if ('/' == *p || '\\' == *p)
-               {
-                       f = p + 1;
-               }
-       }
-       return f;
-}
-#endif
-
-static INLINE size_t nprintf_size(bt_log_message *const msg)
-{
-       // *nprintf() always puts 0 in the end when input buffer is not empty. This
-       // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which
-       // leaves space for one more character. Some put_xxx() functions don't use
-       // *nprintf() and could use that last character. In that case log line will
-       // have multiple (two) half-written parts which is confusing. To workaround
-       // that we allow *nprintf() to write its 0 in the eol area (which is always
-       // not empty).
-       return (size_t)(msg->e - msg->p + 1);
-}
-
-static INLINE void put_nprintf(bt_log_message *const msg, const int n)
-{
-       if (0 < n)
-       {
-               msg->p = n < msg->e - msg->p? msg->p + n: msg->e;
-       }
-}
-
-static INLINE char *put_padding_r(const unsigned w, const char wc,
-                                                                 char *p, char *e)
-{
-       for (char *const b = e - w; b < p; *--p = wc) {}
-       return p;
-}
-
-static char *put_integer_r(unsigned v, const int sign,
-                                                  const unsigned w, const char wc, char *const e)
-{
-       static const char _signs[] = {'-', '0', '+'};
-       const char *const signs = _signs + 1;
-       char *p = e;
-       do { *--p = '0' + v % 10; } while (0 != (v /= 10));
-       if (0 == sign) return put_padding_r(w, wc, p, e);
-       if ('0' != wc)
-       {
-               *--p = signs[sign];
-               return put_padding_r(w, wc, p, e);
-       }
-       p = put_padding_r(w, wc, p, e + 1);
-       *--p = signs[sign];
-       return p;
-}
-
-static INLINE char *put_uint_r(const unsigned v, const unsigned w, const char wc,
-                                                          char *const e)
-{
-       return put_integer_r(v, 0, w, wc, e);
-}
-
-static INLINE char *put_int_r(const int v, const unsigned w, const char wc,
-                                                         char *const e)
-{
-       return 0 <= v? put_integer_r((unsigned)v, 0, w, wc, e)
-                                : put_integer_r((unsigned)-v, -1, w, wc, e);
-}
-
-static INLINE char *put_stringn(const char *const s_p, const char *const s_e,
-                                                               char *const p, char *const e)
-{
-       const ptrdiff_t m = e - p;
-       ptrdiff_t n = s_e - s_p;
-       if (n > m)
-       {
-               n = m;
-       }
-       memcpy(p, s_p, n);
-       return p + n;
-}
-
-static INLINE char *put_string(const char *s, char *p, char *const e)
-{
-       const ptrdiff_t n = e - p;
-       char *const c = (char *)memccpy(p, s, '\0', n);
-       return 0 != c? c - 1: e;
-}
-
-static INLINE char *put_uint(unsigned v, const unsigned w, const char wc,
-                                                        char *const p, char *const e)
-{
-       char buf[16];
-       char *const se = buf + _countof(buf);
-       char *sp = put_uint_r(v, w, wc, se);
-       return put_stringn(sp, se, p, e);
-}
-
-#define PUT_CSTR_R(p, STR) \
-       do { \
-               for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \
-                       *--(p) = (STR)[i]; \
-               } \
-       } _BT_LOG_ONCE
-
-#define PUT_CSTR_CHECKED(p, e, STR) \
-       do { \
-               for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \
-                       *(p)++ = (STR)[i]; \
-               } \
-       } _BT_LOG_ONCE
-
-/* F_INIT field support.
- */
-#define _BT_LOG_MESSAGE_FORMAT_INIT__
-#define _BT_LOG_MESSAGE_FORMAT_INIT__YEAR
-#define _BT_LOG_MESSAGE_FORMAT_INIT__MONTH
-#define _BT_LOG_MESSAGE_FORMAT_INIT__DAY
-#define _BT_LOG_MESSAGE_FORMAT_INIT__HOUR
-#define _BT_LOG_MESSAGE_FORMAT_INIT__MINUTE
-#define _BT_LOG_MESSAGE_FORMAT_INIT__SECOND
-#define _BT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND
-#define _BT_LOG_MESSAGE_FORMAT_INIT__PID
-#define _BT_LOG_MESSAGE_FORMAT_INIT__TID
-#define _BT_LOG_MESSAGE_FORMAT_INIT__LEVEL
-#define _BT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts)
-#define _BT_LOG_MESSAGE_FORMAT_INIT__FUNCTION
-#define _BT_LOG_MESSAGE_FORMAT_INIT__FILENAME
-#define _BT_LOG_MESSAGE_FORMAT_INIT__FILELINE
-#define _BT_LOG_MESSAGE_FORMAT_INIT__S(s)
-#define _BT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr);
-#define _BT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v)
-#define _BT_LOG_MESSAGE_FORMAT_INIT(field) \
-       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_INIT_, _, field)
-
-/* Implements generation of printf-like format string for log message
- * format specification.
- */
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__             ""
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR         "%04u"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH        "%02u"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY          "%02u"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR         "%02u"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE       "%02u"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND       "%02u"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND  "%03u"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID          "%5i"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID          "%5i"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL        "%c"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG          UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION     "%s"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME     "%s"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE     "%u"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s)         s
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) ""
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u"
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \
-       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field)
-
-/* Implements generation of printf-like format parameters for log message
- * format specification.
- */
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR         ,(unsigned)(tm.tm_year + 1900)
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH        ,(unsigned)(tm.tm_mon + 1)
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY          ,(unsigned)tm.tm_mday
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR         ,(unsigned)tm.tm_hour
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE       ,(unsigned)tm.tm_min
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND       ,(unsigned)tm.tm_sec
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND  ,(unsigned)msec
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID          ,pid
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID          ,tid
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL        ,(char)lvl_char(msg->lvl)
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG          UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION     ,funcname(src->func)
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME     ,filename(src->file)
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE     ,src->line
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s)
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr)
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) ,v
-#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \
-       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field)
-
-/* Implements generation of put_xxx_t statements for log message specification.
- */
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__YEAR         p = put_uint_r(tm.tm_year + 1900, 4, '0', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MONTH        p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__DAY          p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__HOUR         p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE       p = put_uint_r((unsigned)tm.tm_min, 2, '0', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__SECOND       p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND  p = put_uint_r(msec, 3, '0', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__PID          p = put_int_r(pid, 5, ' ', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__TID          p = put_int_r(tid, 5, ' ', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL        *--p = lvl_char(msg->lvl);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__TAG          UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION     UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME     UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE     UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__S(s)         PUT_CSTR_R(p, s);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr)
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) p = put_uint_r(v, w, ' ', p);
-#define _BT_LOG_MESSAGE_FORMAT_PUT_R(field) \
-       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_R_, _, field)
-
-static void put_ctx(bt_log_message *const msg)
-{
-       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_CTX_FORMAT)
-#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_CTX_FORMAT)
-       VAR_UNUSED(msg);
-#else
-       #if _BT_LOG_MESSAGE_FORMAT_DATETIME_USED
-       struct tm tm;
-       unsigned msec;
-       g_time_cb(&tm, &msec);
-       #endif
-       #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT) || \
-               _BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT)
-       int pid, tid;
-       g_pid_cb(&pid, &tid);
-       #endif
-
-       #if BT_LOG_OPTIMIZE_SIZE
-       int n;
-       n = snprintf(msg->p, nprintf_size(msg),
-                                _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_CTX_FORMAT)
-                 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_CTX_FORMAT));
-       put_nprintf(msg, n);
-       #else
-       char buf[64];
-       char *const e = buf + sizeof(buf);
-       char *p = e;
-       _PP_RMAP(_BT_LOG_MESSAGE_FORMAT_PUT_R, BT_LOG_MESSAGE_CTX_FORMAT)
-       msg->p = put_stringn(p, e, msg->p, msg->e);
-       #endif
-#endif
-}
-
-#define PUT_TAG(msg, tag, prefix_delim, tag_delim) \
-       do { \
-               const char *ch; \
-               msg->tag_b = msg->p; \
-               if (0 != (ch = _bt_log_tag_prefix)) { \
-                       for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \
-               } \
-               if (0 != (ch = tag) && 0 != tag[0]) { \
-                       if (msg->tag_b != msg->p) { \
-                               PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \
-                       } \
-                       for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \
-               } \
-               msg->tag_e = msg->p; \
-               if (msg->tag_b != msg->p) { \
-                       PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \
-               } \
-       } _BT_LOG_ONCE
-
-/* Implements simple put statements for log message specification.
- */
-#define _BT_LOG_MESSAGE_FORMAT_PUT__
-#define _BT_LOG_MESSAGE_FORMAT_PUT__YEAR         UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__MONTH        UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__DAY          UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__HOUR         UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__MINUTE       UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__SECOND       UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND  UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__PID          UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__TID          UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__LEVEL        UNDEFINED
-#define _BT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td)  PUT_TAG(msg, tag, pd, td);
-#define _BT_LOG_MESSAGE_FORMAT_PUT__FUNCTION     msg->p = put_string(funcname(src->func), msg->p, msg->e);
-#define _BT_LOG_MESSAGE_FORMAT_PUT__FILENAME     msg->p = put_string(filename(src->file), msg->p, msg->e);
-#define _BT_LOG_MESSAGE_FORMAT_PUT__FILELINE     msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e);
-#define _BT_LOG_MESSAGE_FORMAT_PUT__S(s)         PUT_CSTR_CHECKED(msg->p, msg->e, s);
-#define _BT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr)
-#define _BT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) msg->p = put_uint(v, w, ' ', msg->p, msg->e);
-#define _BT_LOG_MESSAGE_FORMAT_PUT(field) \
-       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_, _, field)
-
-static void put_tag(bt_log_message *const msg, const char *const tag)
-{
-       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_TAG_FORMAT)
-
-/*
- * This generates a -Wundef warning.  The issue was reported upstream:
- *
- *   https://github.com/wonder-mice/zf_log/issues/40
- *
- * but there's not much we can do here, so just silence it.
- */
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wundef"
-#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, BT_LOG_MESSAGE_TAG_FORMAT)
-       VAR_UNUSED(tag);
-#endif
-#pragma GCC diagnostic pop
-
-#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_TAG_FORMAT)
-       VAR_UNUSED(msg);
-#else
-       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_TAG_FORMAT)
-#endif
-}
-
-static void put_src(bt_log_message *const msg, const src_location *const src)
-{
-       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_SRC_FORMAT)
-#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT) && \
-       !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT) && \
-       !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, BT_LOG_MESSAGE_SRC_FORMAT)
-       VAR_UNUSED(src);
-#endif
-#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_SRC_FORMAT)
-       VAR_UNUSED(msg);
-#else
-       #if BT_LOG_OPTIMIZE_SIZE
-       int n;
-       n = snprintf(msg->p, nprintf_size(msg),
-                                _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_SRC_FORMAT)
-                 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_SRC_FORMAT));
-       put_nprintf(msg, n);
-       #else
-       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_SRC_FORMAT)
-       #endif
-#endif
-}
-
-static _BT_LOG_PRINTFLIKE(2, 0)
-void put_msg(bt_log_message *const msg,
-                                       const char *const fmt, va_list va)
-{
-       int n;
-       msg->msg_b = msg->p;
-       n = vsnprintf(msg->p, nprintf_size(msg), fmt, va);
-       put_nprintf(msg, n);
-}
-
-static void output_mem(const bt_log_spec *log, bt_log_message *const msg,
-                                          const mem_block *const mem)
-{
-       if (0 == mem->d || 0 == mem->d_sz)
-       {
-               return;
-       }
-       const unsigned char *mem_p = (const unsigned char *)mem->d;
-       const unsigned char *const mem_e = mem_p + mem->d_sz;
-       const unsigned char *mem_cut;
-       const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width;
-       char *const hex_b = msg->msg_b;
-       char *const ascii_b = hex_b + 2 * mem_width + 2;
-       char *const ascii_e = ascii_b + mem_width;
-       if (msg->e < ascii_e)
-       {
-               return;
-       }
-       while (mem_p != mem_e)
-       {
-               char *hex = hex_b;
-               char *ascii = ascii_b;
-               for (mem_cut = mem_width < mem_e - mem_p? mem_p + mem_width: mem_e;
-                        mem_cut != mem_p; ++mem_p)
-               {
-                       const unsigned char ch = *mem_p;
-                       *hex++ = c_hex[(0xf0 & ch) >> 4];
-                       *hex++ = c_hex[(0x0f & ch)];
-                       *ascii++ = isprint(ch)? (char)ch: '?';
-               }
-               while (hex != ascii_b)
-               {
-                       *hex++ = ' ';
-               }
-               msg->p = ascii;
-               log->output->callback(msg, log->output->arg);
-       }
-}
-
-BT_HIDDEN
-void bt_log_set_tag_prefix(const char *const prefix)
-{
-       _bt_log_tag_prefix = prefix;
-}
-
-BT_HIDDEN
-void bt_log_set_mem_width(const unsigned w)
-{
-       _bt_log_global_format.mem_width = w;
-}
-
-BT_HIDDEN
-void bt_log_set_output_level(const int lvl)
-{
-       _bt_log_global_output_lvl = lvl;
-}
-
-BT_HIDDEN
-void bt_log_set_output_v(const unsigned mask, void *const arg,
-                                                const bt_log_output_cb callback)
-{
-       _bt_log_global_output.mask = mask;
-       _bt_log_global_output.arg = arg;
-       _bt_log_global_output.callback = callback;
-}
-
-static _BT_LOG_PRINTFLIKE(6, 0)
-void _bt_log_write_imp(
-               const bt_log_spec *log,
-               const src_location *const src, const mem_block *const mem,
-               const int lvl, const char *const tag, const char *const fmt, va_list va)
-{
-       bt_log_message msg;
-       char *buf = logging_buf;
-       const unsigned mask = log->output->mask;
-       msg.lvl = lvl;
-       msg.tag = tag;
-       g_buffer_cb(&msg, buf);
-       const char *rst_color_p = bt_common_color_reset();
-       const char *rst_color_e = rst_color_p + strlen(rst_color_p);
-       const char *color_p = "";
-       const char *color_e = color_p;
-
-       switch (lvl) {
-       case BT_LOG_INFO:
-               color_p = bt_common_color_fg_blue();
-               color_e = color_p + strlen(color_p);
-               break;
-       case BT_LOG_WARNING:
-               color_p = bt_common_color_fg_yellow();
-               color_e = color_p + strlen(color_p);
-               break;
-       case BT_LOG_ERROR:
-       case BT_LOG_FATAL:
-               color_p = bt_common_color_fg_red();
-               color_e = color_p + strlen(color_p);
-               break;
-       default:
-               break;
-       }
-
-       msg.p = put_stringn(color_p, color_e, msg.p, msg.e);
-
-       if (BT_LOG_PUT_CTX & mask)
-       {
-               put_ctx(&msg);
-       }
-       if (BT_LOG_PUT_TAG & mask)
-       {
-               put_tag(&msg, tag);
-       }
-       if (0 != src && BT_LOG_PUT_SRC & mask)
-       {
-               put_src(&msg, src);
-       }
-       if (BT_LOG_PUT_MSG & mask)
-       {
-               put_msg(&msg, fmt, va);
-       }
-       msg.p = put_stringn(rst_color_p, rst_color_e, msg.p, msg.e);
-       log->output->callback(&msg, log->output->arg);
-       if (0 != mem && BT_LOG_PUT_MSG & mask)
-       {
-               output_mem(log, &msg, mem);
-       }
-}
-
-BT_HIDDEN
-void _bt_log_write_d(
-               const char *const func, const char *const file, const unsigned line,
-               const int lvl, const char *const tag,
-               const char *const fmt, ...)
-{
-       const src_location src = {func, file, line};
-       va_list va;
-       va_start(va, fmt);
-       _bt_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va);
-       va_end(va);
-}
-
-BT_HIDDEN
-void _bt_log_write_aux_d(
-               const char *const func, const char *const file, const unsigned line,
-               const bt_log_spec *const log, const int lvl, const char *const tag,
-               const char *const fmt, ...)
-{
-       const src_location src = {func, file, line};
-       va_list va;
-       va_start(va, fmt);
-       _bt_log_write_imp(log, &src, 0, lvl, tag, fmt, va);
-       va_end(va);
-}
-
-BT_HIDDEN
-void _bt_log_write(const int lvl, const char *const tag,
-                                  const char *const fmt, ...)
-{
-       va_list va;
-       va_start(va, fmt);
-       _bt_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va);
-       va_end(va);
-}
-
-BT_HIDDEN
-void _bt_log_write_aux(
-               const bt_log_spec *const log, const int lvl, const char *const tag,
-               const char *const fmt, ...)
-{
-       va_list va;
-       va_start(va, fmt);
-       _bt_log_write_imp(log, 0, 0, lvl, tag, fmt, va);
-       va_end(va);
-}
-
-BT_HIDDEN
-void _bt_log_write_mem_d(
-               const char *const func, const char *const file, const unsigned line,
-               const int lvl, const char *const tag,
-               const void *const d, const unsigned d_sz,
-               const char *const fmt, ...)
-{
-       const src_location src = {func, file, line};
-       const mem_block mem = {d, d_sz};
-       va_list va;
-       va_start(va, fmt);
-       _bt_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va);
-       va_end(va);
-}
-
-BT_HIDDEN
-void _bt_log_write_mem_aux_d(
-               const char *const func, const char *const file, const unsigned line,
-               const bt_log_spec *const log, const int lvl, const char *const tag,
-               const void *const d, const unsigned d_sz,
-               const char *const fmt, ...)
-{
-       const src_location src = {func, file, line};
-       const mem_block mem = {d, d_sz};
-       va_list va;
-       va_start(va, fmt);
-       _bt_log_write_imp(log, &src, &mem, lvl, tag, fmt, va);
-       va_end(va);
-}
-
-BT_HIDDEN
-void _bt_log_write_mem(const int lvl, const char *const tag,
-                                          const void *const d, const unsigned d_sz,
-                                          const char *const fmt, ...)
-{
-       const mem_block mem = {d, d_sz};
-       va_list va;
-       va_start(va, fmt);
-       _bt_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va);
-       va_end(va);
-}
-
-BT_HIDDEN
-void _bt_log_write_mem_aux(
-               const bt_log_spec *const log, const int lvl, const char *const tag,
-               const void *const d, const unsigned d_sz,
-               const char *const fmt, ...)
-{
-       const mem_block mem = {d, d_sz};
-       va_list va;
-       va_start(va, fmt);
-       _bt_log_write_imp(log, 0, &mem, lvl, tag, fmt, va);
-       va_end(va);
-}
index 41370963d68c9638c382702298588b6c367b26a8..93c9d6ed1376e6726099a4c627b60d89f8703197 100644 (file)
  * SPDX-License-Identifier: MIT
  *
  * Copyright (c) 2016 wonder-mice
+ * Copyright (c) 2016-2023 Philippe Proulx <pproulx@efficios.com>
  *
- * This is zf_log.h, modified with Babeltrace prefixes.
- * See <https://github.com/wonder-mice/zf_log/>.
+ * This is very inspired by zf_log.h (see
+ * <https://github.com/wonder-mice/zf_log/>), but modified (mostly
+ * stripped down) for the use cases of Babeltrace.
  */
 
-#pragma once
-
-#ifndef BABELTRACE_LOGGING_INTERNAL_H
-#define BABELTRACE_LOGGING_INTERNAL_H
+#ifndef BABELTRACE_LOGGING_LOG_H
+#define BABELTRACE_LOGGING_LOG_H
 
 #include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <glib.h>
 #include <babeltrace2/babeltrace.h>
 
-/* Access private __BT_LOGGING_LEVEL_* macros. */
-#define __BT_IN_BABELTRACE_H
-#include <babeltrace2/logging-defs.h>
-#undef __BT_IN_BABELTRACE_H
-
-#include "common/macros.h"
-#include "common/assert.h"
-
-/* To detect incompatible changes you can define BT_LOG_VERSION_REQUIRED to be
- * the current value of BT_LOG_VERSION before including this file (or via
- * compiler command line):
- *
- *   #define BT_LOG_VERSION_REQUIRED 4
- *   #include "logging.h"
- *
- * Compilation will fail when included file has different version.
- */
-#define BT_LOG_VERSION 4
-#if defined(BT_LOG_VERSION_REQUIRED)
-       #if BT_LOG_VERSION_REQUIRED != BT_LOG_VERSION
-               #error different bt_log version required
-       #endif
-#endif
-
-/* Log level guideline:
- * - BT_LOG_FATAL - happened something impossible and absolutely unexpected.
- *   Process can't continue and must be terminated.
- *   Example: division by zero, unexpected modifications from other thread.
- * - BT_LOG_ERROR - happened something possible, but highly unexpected. The
- *   process is able to recover and continue execution.
- *   Example: out of memory (could also be FATAL if not handled properly).
- * - BT_LOG_WARNING - happened something that *usually* should not happen and
- *   significantly changes application behavior for some period of time.
- *   Example: configuration file not found, auth error.
- * - BT_LOG_INFO - happened significant life cycle event or major state
- *   transition.
- *   Example: app started, user logged in.
- * - BT_LOG_DEBUG - minimal set of events that could help to reconstruct the
- *   execution path. Usually disabled in release builds.
- * - BT_LOG_TRACE - all other events. Usually disabled in release builds.
- *
- * *Ideally*, log file of debugged, well tested, production ready application
- * should be empty or very small. Choosing a right log level is as important as
- * providing short and self descriptive log message.
- */
-#define BT_LOG_TRACE   __BT_LOGGING_LEVEL_TRACE
-#define BT_LOG_DEBUG   __BT_LOGGING_LEVEL_DEBUG
-#define BT_LOG_INFO    __BT_LOGGING_LEVEL_INFO
-#define BT_LOG_WARNING __BT_LOGGING_LEVEL_WARNING
-#define BT_LOG_ERROR   __BT_LOGGING_LEVEL_ERROR
-#define BT_LOG_FATAL   __BT_LOGGING_LEVEL_FATAL
-#define BT_LOG_NONE    __BT_LOGGING_LEVEL_NONE
-
-/* "Current" log level is a compile time check and has no runtime overhead. Log
- * level that is below current log level it said to be "disabled".
- * Otherwise, it's "enabled". Log messages that are disabled has no
- * runtime overhead - they are converted to no-op by preprocessor and
- * then eliminated by compiler. Current log level is configured per
- * compilation module (.c/.cpp/.m file) by defining BT_LOG_DEF_LEVEL or
- * BT_MINIMAL_LOG_LEVEL. BT_MINIMAL_LOG_LEVEL has higer priority and
- * when defined overrides value provided by BT_LOG_DEF_LEVEL.
- *
- * Common practice is to define default current log level with BT_LOG_DEF_LEVEL
- * in build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire
- * project or target:
- *
- *   CC_ARGS := -DBT_LOG_DEF_LEVEL=BT_LOG_INFO
- *
- * And when necessary to override it with BT_MINIMAL_LOG_LEVEL in .c/.cpp/.m files
- * before including bt_log.h:
- *
- *   #define BT_MINIMAL_LOG_LEVEL BT_LOG_TRACE
- *   #include "logging.h"
- *
- * If both BT_LOG_DEF_LEVEL and BT_MINIMAL_LOG_LEVEL are undefined, then
- * BT_LOG_INFO will be used for release builds (BT_DEBUG_MODE is NOT
- * defined) and BT_LOG_DEBUG otherwise (BT_DEBUG_MODE is defined).
- */
-#if defined(BT_MINIMAL_LOG_LEVEL)
-       #define _BT_MINIMAL_LOG_LEVEL BT_MINIMAL_LOG_LEVEL
-#elif defined(BT_LOG_DEF_LEVEL)
-       #define _BT_MINIMAL_LOG_LEVEL BT_LOG_DEF_LEVEL
-#else
-       #ifdef BT_DEBUG_MODE
-               #define _BT_MINIMAL_LOG_LEVEL BT_LOG_DEBUG
-       #else
-               #define _BT_MINIMAL_LOG_LEVEL BT_LOG_INFO
-       #endif
-#endif
+#include "log-api.h"
 
-/* "Output" log level is a runtime check. When log level is below output log
- * level it said to be "turned off" (or just "off" for short). Otherwise
- * it's "turned on" (or just "on"). Log levels that were "disabled" (see
- * BT_MINIMAL_LOG_LEVEL and BT_LOG_DEF_LEVEL) can't be "turned on", but
- * "enabled" log levels could be "turned off". Only messages with log
- * level which is "turned on" will reach output facility. All other
- * messages will be ignored (and their arguments will not be evaluated).
- * Output log level is a global property and configured per process
- * using bt_log_set_output_level() function which can be called at any
- * time.
+/*
+ * `BT_LOG_OUTPUT_LEVEL` (expression having the type `int`, optional):
+ * valid expression evaluating to the current (run-time) log level.
  *
- * Though in some cases it could be useful to configure output log level per
- * compilation module or per library. There are two ways to achieve that:
- * - Define BT_LOG_OUTPUT_LEVEL to expresion that evaluates to desired output
- *   log level.
- * - Copy bt_log.h and bt_log.c files into your library and build it with
- *   BT_LOG_LIBRARY_PREFIX defined to library specific prefix. See
- *   BT_LOG_LIBRARY_PREFIX for more details.
+ * This is configured per translation unit.
  *
- * When defined, BT_LOG_OUTPUT_LEVEL must evaluate to integral value
- * that corresponds to desired output log level. Use it only when
- * compilation module is required to have output log level which is
- * different from global output log level set by
- * bt_log_set_output_level() function. For other cases, consider
- * defining BT_MINIMAL_LOG_LEVEL or using bt_log_set_output_level()
- * function.
+ * Note that BT_LOG*() statements will evaluate this expression each
+ * time if their level is equally or less verbose than
+ * `BT_LOG_MINIMAL_LEVEL`. Keep this expression as simple as possible,
+ * otherwise it will not only add run-time overhead, but also will
+ * increase the size of call sites (which will result in larger
+ * executable).
  *
  * Example:
  *
- *   #define BT_LOG_OUTPUT_LEVEL g_module_log_level
- *   #include "logging.h"
- *   static int g_module_log_level = BT_LOG_INFO;
- *   static void foo() {
- *       BT_LOGI("Will check g_module_log_level for output log level");
- *   }
- *   void debug_log(bool on) {
- *       g_module_log_level = on? BT_LOG_DEBUG: BT_LOG_INFO;
- *   }
- *
- * Note on performance. This expression will be evaluated each time
- * message is logged (except when message log level is "disabled" - see
- * BT_MINIMAL_LOG_LEVEL for details). Keep this expression as simple as
- * possible, otherwise it will not only add runtime overhead, but also
- * will increase size of call site (which will result in larger
- * executable). The prefered way is to use integer variable (as in
- * example above). If structure must be used, log_level field must be
- * the first field in this structure:
+ *     #define BT_LOG_OUTPUT_LEVEL log_level
+ *     #include "logging/log.h"
  *
- *   #define BT_LOG_OUTPUT_LEVEL (g_config.log_level)
- *   #include "logging.h"
- *   struct config {
- *       int log_level;
- *       unsigned other_field;
- *       [...]
- *   };
- *   static config g_config = {BT_LOG_INFO, 0, ...};
- *
- * This allows compiler to generate more compact load instruction (no need to
- * specify offset since it's zero). Calling a function to get output log level
- * is generaly a bad idea, since it will increase call site size and runtime
- * overhead even further.
+ * If missing, you may only use logging macros having a `_cur_lvl`
+ * parameter.
  */
-#if defined(BT_LOG_OUTPUT_LEVEL)
-       #define _BT_LOG_OUTPUT_LEVEL BT_LOG_OUTPUT_LEVEL
+#ifdef BT_LOG_OUTPUT_LEVEL
+# define _BT_LOG_OUTPUT_LEVEL  BT_LOG_OUTPUT_LEVEL
 #else
-       /*
-        * We disallow this to make sure Babeltrace modules always
-        * have their own local log level.
-        */
-       #error No log level symbol specified: please define BT_LOG_OUTPUT_LEVEL before including this header.
+# define _BT_LOG_OUTPUT_LEVEL  "Please define `BT_LOG_OUTPUT_LEVEL`"
 #endif
 
-/* "Tag" is a compound string that could be associated with a log message. It
- * consists of tag prefix and tag (both are optional).
- *
- * Tag prefix is a global property and configured per process using
- * bt_log_set_tag_prefix() function. Tag prefix identifies context in which
- * component or module is running (e.g. process name). For example, the same
- * library could be used in both client and server processes that work on the
- * same machine. Tag prefix could be used to easily distinguish between them.
- * For more details about tag prefix see bt_log_set_tag_prefix() function. Tag
- * prefix
- *
- * Tag identifies component or module. It is configured per compilation module
- * (.c/.cpp/.m file) by defining BT_LOG_TAG or BT_LOG_DEF_TAG. BT_LOG_TAG has
- * higer priority and when defined overrides value provided by BT_LOG_DEF_TAG.
- * When defined, value must evaluate to (const char *), so for strings double
- * quotes must be used.
- *
- * Default tag could be defined with BT_LOG_DEF_TAG in build script (e.g.
- * Makefile, CMakeLists.txt, gyp, etc.) for the entire project or target:
- *
- *   CC_ARGS := -DBT_LOG_DEF_TAG=\"MISC\"
- *
- * And when necessary could be overriden with BT_LOG_TAG in .c/.cpp/.m files
- * before including bt_log.h:
- *
- *   #define BT_LOG_TAG "MAIN"
- *   #include "logging.h"
- *
- * If both BT_LOG_DEF_TAG and BT_LOG_TAG are undefined no tag will be added to
- * the log message (tag prefix still could be added though).
- *
- * Output example:
- *
- *   04-29 22:43:20.244 40059  1299 I hello.MAIN Number of arguments: 1
- *                                    |     |
- *                                    |     +- tag (e.g. module)
- *                                    +- tag prefix (e.g. process name)
- */
-#if defined(BT_LOG_TAG)
-       #define _BT_LOG_TAG BT_LOG_TAG
-#elif defined(BT_LOG_DEF_TAG)
-       #define _BT_LOG_TAG BT_LOG_DEF_TAG
-#else
-       #define _BT_LOG_TAG 0
-#endif
-
-/* Source location is part of a log line that describes location (function or
- * method name, file name and line number, e.g. "runloop@main.cpp:68") of a
- * log statement that produced it.
- * Source location formats are:
- * - BT_LOG_SRCLOC_NONE - don't add source location to log line.
- * - BT_LOG_SRCLOC_SHORT - add source location in short form (file and line
- *   number, e.g. "@main.cpp:68").
- * - BT_LOG_SRCLOC_LONG - add source location in long form (function or method
- *   name, file and line number, e.g. "runloop@main.cpp:68").
- */
-#define BT_LOG_SRCLOC_NONE  0
-#define BT_LOG_SRCLOC_SHORT 1
-#define BT_LOG_SRCLOC_LONG  2
-
-#define _BT_LOG_SRCLOC BT_LOG_SRCLOC_LONG
-
-#if BT_LOG_SRCLOC_LONG == _BT_LOG_SRCLOC
-       #define _BT_LOG_SRCLOC_FUNCTION _BT_LOG_FUNCTION
-#else
-       #define _BT_LOG_SRCLOC_FUNCTION 0
-#endif
-
-/* Censoring provides conditional logging of secret information, also known as
- * Personally Identifiable Information (PII) or Sensitive Personal Information
- * (SPI). Censoring can be either enabled (BT_LOG_CENSORED) or disabled
- * (BT_LOG_UNCENSORED). When censoring is enabled, log statements marked as
- * "secrets" will be ignored and will have zero overhead (arguments also will
- * not be evaluated).
- */
-#define BT_LOG_CENSORED   1
-#define BT_LOG_UNCENSORED 0
-
-/* Censoring is configured per compilation module (.c/.cpp/.m file) by defining
- * BT_LOG_DEF_CENSORING or BT_LOG_CENSORING. BT_LOG_CENSORING has higer priority
- * and when defined overrides value provided by BT_LOG_DEF_CENSORING.
- *
- * Common practice is to define default censoring with BT_LOG_DEF_CENSORING in
- * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire
- * project or target:
- *
- *   CC_ARGS := -DBT_LOG_DEF_CENSORING=BT_LOG_CENSORED
- *
- * And when necessary to override it with BT_LOG_CENSORING in .c/.cpp/.m files
- * before including bt_log.h (consider doing it only for debug purposes and be
- * very careful not to push such temporary changes to source control):
- *
- *   #define BT_LOG_CENSORING BT_LOG_UNCENSORED
- *   #include "logging.h"
- *
- * If both BT_LOG_DEF_CENSORING and BT_LOG_CENSORING are undefined, then
- * BT_LOG_CENSORED will be used for release builds (BT_DEBUG_MODE is NOT
- * defined) and BT_LOG_UNCENSORED otherwise (BT_DEBUG_MODE is defined).
- */
-#if defined(BT_LOG_CENSORING)
-       #define _BT_LOG_CENSORING BT_LOG_CENSORING
-#elif defined(BT_LOG_DEF_CENSORING)
-       #define _BT_LOG_CENSORING BT_LOG_DEF_CENSORING
-#else
-       #ifdef BT_DEBUG_MODE
-               #define _BT_LOG_CENSORING BT_LOG_UNCENSORED
-       #else
-               #define _BT_LOG_CENSORING BT_LOG_CENSORED
-       #endif
-#endif
-
-/* Check censoring at compile time. Evaluates to true when censoring is disabled
- * (i.e. when secrets will be logged). For example:
- *
- *   #if BT_LOG_SECRETS
- *       char ssn[16];
- *       getSocialSecurityNumber(ssn);
- *       BT_LOGI("Customer ssn: %s", ssn);
- *   #endif
- *
- * See BT_LOG_SECRET() macro for a more convenient way of guarding single log
- * statement.
- */
-#define BT_LOG_SECRETS (BT_LOG_UNCENSORED == _BT_LOG_CENSORING)
-
-/* Static (compile-time) initialization support allows to configure logging
- * before entering main() function. This mostly useful in C++ where functions
- * and methods could be called during initialization of global objects. Those
- * functions and methods could record log messages too and for that reason
- * static initialization of logging configuration is customizable.
- *
- * Macros below allow to specify values to use for initial configuration:
- * - BT_LOG_EXTERN_TAG_PREFIX - tag prefix (default: none)
- * - BT_LOG_EXTERN_GLOBAL_FORMAT - global format options (default: see
- *   BT_LOG_MEM_WIDTH in bt_log.c)
- * - BT_LOG_EXTERN_GLOBAL_OUTPUT - global output facility (default: stderr or
- *   platform specific, see BT_LOG_USE_XXX macros in bt_log.c)
- * - BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - global output log level (default: 0 -
- *   all levals are "turned on")
- *
- * For example, in log_config.c:
- *
- *   #include "logging.h"
- *   BT_LOG_DEFINE_TAG_PREFIX = "MyApp";
- *   BT_LOG_DEFINE_GLOBAL_FORMAT = {CUSTOM_MEM_WIDTH};
- *   BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_PUT_STD, custom_output_callback, 0};
- *   BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = BT_LOG_INFO;
- *
- * However, to use any of those macros bt_log library must be compiled with
- * following macros defined:
- * - to use BT_LOG_DEFINE_TAG_PREFIX define BT_LOG_EXTERN_TAG_PREFIX
- * - to use BT_LOG_DEFINE_GLOBAL_FORMAT define BT_LOG_EXTERN_GLOBAL_FORMAT
- * - to use BT_LOG_DEFINE_GLOBAL_OUTPUT define BT_LOG_EXTERN_GLOBAL_OUTPUT
- * - to use BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL define
- *   BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
- *
- * When bt_log library compiled with one of BT_LOG_EXTERN_XXX macros defined,
- * corresponding BT_LOG_DEFINE_XXX macro MUST be used exactly once somewhere.
- * Otherwise build will fail with link error (undefined symbol).
- */
-#define BT_LOG_DEFINE_TAG_PREFIX BT_HIDDEN const char *_bt_log_tag_prefix
-#define BT_LOG_DEFINE_GLOBAL_FORMAT BT_HIDDEN bt_log_format _bt_log_global_format
-#define BT_LOG_DEFINE_GLOBAL_OUTPUT BT_HIDDEN bt_log_output _bt_log_global_output
-#define BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL BT_HIDDEN int _bt_log_global_output_lvl
-
-/* Pointer to global format options. Direct modification is not allowed. Use
- * bt_log_set_mem_width() instead. Could be used to initialize bt_log_spec
- * structure:
- *
- *   const bt_log_output g_output = {BT_LOG_PUT_STD, output_callback, 0};
- *   const bt_log_spec g_spec = {BT_LOG_GLOBAL_FORMAT, &g_output};
- *   BT_LOGI_AUX(&g_spec, "Hello");
- */
-#define BT_LOG_GLOBAL_FORMAT ((const bt_log_format *)&_bt_log_global_format)
-
-/* Pointer to global output variable. Direct modification is not allowed. Use
- * bt_log_set_output_v() or bt_log_set_output_p() instead. Could be used to
- * initialize bt_log_spec structure:
- *
- *   const bt_log_format g_format = {40};
- *   const bt_log_spec g_spec = {g_format, BT_LOG_GLOBAL_OUTPUT};
- *   BT_LOGI_AUX(&g_spec, "Hello");
- */
-#define BT_LOG_GLOBAL_OUTPUT ((const bt_log_output *)&_bt_log_global_output)
-
-/* When defined, all library symbols produced by linker will be prefixed with
- * provided value. That allows to use bt_log library privately in another
- * libraries without exposing bt_log symbols in their original form (to avoid
- * possible conflicts with other libraries / components that also could use
- * bt_log for logging). Value must be without quotes, for example:
- *
- *   CC_ARGS := -DBT_LOG_LIBRARY_PREFIX=my_lib_
- *
- * Note, that in this mode BT_LOG_LIBRARY_PREFIX must be defined when building
- * bt_log library AND it also must be defined to the same value when building
- * a library that uses it. For example, consider fictional KittyHttp library
- * that wants to use bt_log for logging. First approach that could be taken is
- * to add bt_log.h and bt_log.c to the KittyHttp's source code tree directly.
- * In that case it will be enough just to define BT_LOG_LIBRARY_PREFIX in
- * KittyHttp's build script:
- *
- *   // KittyHttp/CMakeLists.txt
- *   target_compile_definitions(KittyHttp PRIVATE
- *                              "BT_LOG_LIBRARY_PREFIX=KittyHttp_")
+/*
+ * `BT_LOG_TAG` (expression having the type `const char *`, optional):
+ * component or module identifier.
  *
- * If KittyHttp doesn't want to include bt_log source code in its source tree
- * and wants to build bt_log as a separate library than bt_log library must be
- * built with BT_LOG_LIBRARY_PREFIX defined to KittyHttp_ AND KittyHttp library
- * itself also needs to define BT_LOG_LIBRARY_PREFIX to KittyHttp_. It can do
- * so either in its build script, as in example above, or by providing a
- * wrapper header that KittyHttp library will need to use instead of bt_log.h:
+ * This is configured per translation unit.
  *
- *   // KittyHttpLogging.h
- *   #define BT_LOG_LIBRARY_PREFIX KittyHttp_
- *   #include "logging.h"
+ * Example:
  *
- * Regardless of the method chosen, the end result is that bt_log symbols will
- * be prefixed with "KittyHttp_", so if a user of KittyHttp (say DogeBrowser)
- * also uses bt_log for logging, they will not interferer with each other. Both
- * will have their own log level, output facility, format options etc.
+ *     // ...
+ *     #define BT_LOG_TAG "MY.MODULE"
+ *     #include "logging/log.h"
  */
-#ifdef BT_LOG_LIBRARY_PREFIX
-       #define _BT_LOG_DECOR__(prefix, name) prefix ## name
-       #define _BT_LOG_DECOR_(prefix, name) _BT_LOG_DECOR__(prefix, name)
-       #define _BT_LOG_DECOR(name) _BT_LOG_DECOR_(BT_LOG_LIBRARY_PREFIX, name)
-
-       #define bt_log_set_tag_prefix _BT_LOG_DECOR(bt_log_set_tag_prefix)
-       #define bt_log_set_mem_width _BT_LOG_DECOR(bt_log_set_mem_width)
-       #define bt_log_set_output_level _BT_LOG_DECOR(bt_log_set_output_level)
-       #define bt_log_set_output_v _BT_LOG_DECOR(bt_log_set_output_v)
-       #define bt_log_set_output_p _BT_LOG_DECOR(bt_log_set_output_p)
-       #define bt_log_out_stderr_callback _BT_LOG_DECOR(bt_log_out_stderr_callback)
-       #define _bt_log_tag_prefix _BT_LOG_DECOR(_bt_log_tag_prefix)
-       #define _bt_log_global_format _BT_LOG_DECOR(_bt_log_global_format)
-       #define _bt_log_global_output _BT_LOG_DECOR(_bt_log_global_output)
-       #define _bt_log_global_output_lvl _BT_LOG_DECOR(_bt_log_global_output_lvl)
-       #define _bt_log_write_d _BT_LOG_DECOR(_bt_log_write_d)
-       #define _bt_log_write_aux_d _BT_LOG_DECOR(_bt_log_write_aux_d)
-       #define _bt_log_write _BT_LOG_DECOR(_bt_log_write)
-       #define _bt_log_write_aux _BT_LOG_DECOR(_bt_log_write_aux)
-       #define _bt_log_write_mem_d _BT_LOG_DECOR(_bt_log_write_mem_d)
-       #define _bt_log_write_mem_aux_d _BT_LOG_DECOR(_bt_log_write_mem_aux_d)
-       #define _bt_log_write_mem _BT_LOG_DECOR(_bt_log_write_mem)
-       #define _bt_log_write_mem_aux _BT_LOG_DECOR(_bt_log_write_mem_aux)
-       #define _bt_log_stderr_spec _BT_LOG_DECOR(_bt_log_stderr_spec)
-#endif
-
-#if defined(__printflike)
-       #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \
-               __printflike(str_index, first_to_check)
-#elif defined(__MINGW_PRINTF_FORMAT)
-       #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \
-               __attribute__((format(__MINGW_PRINTF_FORMAT, str_index, first_to_check)))
-#elif defined(__GNUC__)
-       #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \
-               __attribute__((format(__printf__, str_index, first_to_check)))
-#else
-       #define _BT_LOG_PRINTFLIKE(str_index, first_to_check)
-#endif
-
-#if (defined(_WIN32) || defined(_WIN64)) && !defined(__GNUC__)
-       #define _BT_LOG_FUNCTION __FUNCTION__
-#else
-       #define _BT_LOG_FUNCTION __func__
-#endif
-
-#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
-       #define _BT_LOG_INLINE __inline
-       #define _BT_LOG_IF(cond) \
-               __pragma(warning(push)) \
-               __pragma(warning(disable:4127)) \
-               if(cond) \
-               __pragma(warning(pop))
-       #define _BT_LOG_WHILE(cond) \
-               __pragma(warning(push)) \
-               __pragma(warning(disable:4127)) \
-               while(cond) \
-               __pragma(warning(pop))
+#ifdef BT_LOG_TAG
+# define _BT_LOG_TAG    BT_LOG_TAG
 #else
-       #define _BT_LOG_INLINE inline
-       #define _BT_LOG_IF(cond) if(cond)
-       #define _BT_LOG_WHILE(cond) while(cond)
+# define _BT_LOG_TAG    NULL
 #endif
-#define _BT_LOG_NEVER _BT_LOG_IF(0)
-#define _BT_LOG_ONCE _BT_LOG_WHILE(0)
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Set tag prefix. Prefix will be separated from the tag with dot ('.').
- * Use 0 or empty string to disable (default). Common use is to set it to
- * the process (or build target) name (e.g. to separate client and server
- * processes). Function will NOT copy provided prefix string, but will store the
- * pointer. Hence specified prefix string must remain valid. See
- * BT_LOG_DEFINE_TAG_PREFIX for a way to set it before entering main() function.
- * See BT_LOG_TAG for more information about tag and tag prefix.
- */
-void bt_log_set_tag_prefix(const char *const prefix);
-
-/* Set number of bytes per log line in memory (ASCII-HEX) output. Example:
- *
- *   I hello.MAIN 4c6f72656d20697073756d20646f6c6f  Lorem ipsum dolo
- *                |<-          w bytes         ->|  |<-  w chars ->|
- *
- * See BT_LOGF_MEM and BT_LOGF_MEM_AUX for more details.
+/*
+ * BT_LOG_ON_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the current
+ * (run-time) level.
  */
-void bt_log_set_mem_width(const unsigned w);
+#define BT_LOG_ON(_lvl)                BT_LOG_ON_CUR_LVL(_lvl, _BT_LOG_OUTPUT_LEVEL)
+#define BT_LOG_ON_TRACE                BT_LOG_ON(BT_LOG_TRACE)
+#define BT_LOG_ON_DEBUG                BT_LOG_ON(BT_LOG_DEBUG)
+#define BT_LOG_ON_INFO         BT_LOG_ON(BT_LOG_INFO)
+#define BT_LOG_ON_WARNING      BT_LOG_ON(BT_LOG_WARNING)
+#define BT_LOG_ON_ERROR                BT_LOG_ON(BT_LOG_ERROR)
+#define BT_LOG_ON_FATAL                BT_LOG_ON(BT_LOG_FATAL)
 
-/* Set "output" log level. See BT_MINIMAL_LOG_LEVEL and BT_LOG_OUTPUT_LEVEL for more
- * info about log levels.
+/*
+ * BT_LOG_WRITE_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the current
+ * (run-time) level.
  */
-void bt_log_set_output_level(const int lvl);
+#define BT_LOG_WRITE(_lvl, _tag, _msg)                                 \
+       BT_LOG_WRITE_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL, (_tag), (_msg))
 
-/* Put mask is a set of flags that define what fields will be added to each
- * log message. Default value is BT_LOG_PUT_STD and other flags could be used to
- * alter its behavior. See bt_log_set_output_v() for more details.
- *
- * Note about BT_LOG_PUT_SRC: it will be added only in debug builds
- * (BT_DEBUG_MODE is defined).
+/*
+ * BT_LOG_WRITE_PRINTF_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the
+ * current (run-time) level.
  */
-enum
-{
-       BT_LOG_PUT_CTX = 1 << 0, /* context (time, pid, tid, log level) */
-       BT_LOG_PUT_TAG = 1 << 1, /* tag (including tag prefix) */
-       BT_LOG_PUT_SRC = 1 << 2, /* source location (file, line, function) */
-       BT_LOG_PUT_MSG = 1 << 3, /* message text (formatted string) */
-       BT_LOG_PUT_STD = 0xffff, /* everything (default) */
-};
-
-typedef struct bt_log_message
-{
-       int lvl; /* Log level of the message */
-       const char *tag; /* Associated tag (without tag prefix) */
-       char *buf; /* Buffer start */
-       char *e; /* Buffer end (last position where EOL with 0 could be written) */
-       char *p; /* Buffer content end (append position) */
-       char *tag_b; /* Prefixed tag start */
-       char *tag_e; /* Prefixed tag end (if != tag_b, points to msg separator) */
-       char *msg_b; /* Message start (expanded format string) */
-}
-bt_log_message;
+#define BT_LOG_WRITE_PRINTF(_lvl, _tag, _fmt, ...)                     \
+       BT_LOG_WRITE_PRINTF_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL,       \
+               (_tag), (_fmt), ##__VA_ARGS__)
 
-/* Type of output callback function. It will be called for each log line allowed
- * by both "current" and "output" log levels ("enabled" and "turned on").
- * Callback function is allowed to modify content of the buffers pointed by the
- * msg, but it's not allowed to modify any of msg fields. Buffer pointed by msg
- * is UTF-8 encoded (no BOM mark).
+/*
+ * BT_LOG_WRITE_MEM_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the current
+ * (run-time) level.
  */
-typedef void (*bt_log_output_cb)(const bt_log_message *msg, void *arg);
+#define BT_LOG_WRITE_MEM(_lvl, _tag, _mem_data, _mem_len, _msg)                \
+       BT_LOG_WRITE_MEM_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL, (_tag),  \
+               (_mem_data), (_mem_len), (_msg))
 
-/* Format options. For more details see bt_log_set_mem_width().
+/*
+ * BT_LOG_WRITE_MEM_PRINTF_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the
+ * current (run-time) level.
  */
-typedef struct bt_log_format
-{
-       unsigned mem_width; /* Bytes per line in memory (ASCII-HEX) dump */
-}
-bt_log_format;
+#define BT_LOG_WRITE_MEM_PRINTF(_lvl, _tag, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL,   \
+               (_tag), (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
 
-/* Output facility.
+/*
+ * BT_LOG_WRITE_ERRNO_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the
+ * current (run-time) level.
  */
-typedef struct bt_log_output
-{
-       unsigned mask; /* What to put into log line buffer (see BT_LOG_PUT_XXX) */
-       void *arg; /* User provided output callback argument */
-       bt_log_output_cb callback; /* Output callback function */
-}
-bt_log_output;
+#define BT_LOG_WRITE_ERRNO(_lvl, _tag, _init_msg, _msg)                        \
+       BT_LOG_WRITE_ERRNO_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL,        \
+               (_tag), (_init_msg), (_msg))
 
-/* Set output callback function.
- *
- * Mask allows to control what information will be added to the log line buffer
- * before callback function is invoked. Default mask value is BT_LOG_PUT_STD.
+/*
+ * BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as
+ * the current (run-time) level.
  */
-void bt_log_set_output_v(const unsigned mask, void *const arg,
-                                                const bt_log_output_cb callback);
-static _BT_LOG_INLINE void bt_log_set_output_p(const bt_log_output *const output)
-{
-       bt_log_set_output_v(output->mask, output->arg, output->callback);
-}
+#define BT_LOG_WRITE_ERRNO_PRINTF(_lvl, _tag, _init_msg, _fmt, ...)    \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL, \
+               (_tag), (_init_msg), (_fmt), ##__VA_ARGS__)
 
-/* Used with _AUX macros and allows to override global format and output
- * facility. Use BT_LOG_GLOBAL_FORMAT and BT_LOG_GLOBAL_OUTPUT for values from
- * global configuration. Example:
- *
- *   static const bt_log_output module_output = {
- *       BT_LOG_PUT_STD, 0, custom_output_callback
- *   };
- *   static const bt_log_spec module_spec = {
- *       BT_LOG_GLOBAL_FORMAT, &module_output
- *   };
- *   BT_LOGI_AUX(&module_spec, "Position: %ix%i", x, y);
- *
- * See BT_LOGF_AUX and BT_LOGF_MEM_AUX for details.
- */
-typedef struct bt_log_spec
+/* Internal: no-op function when a log level is disabled */
+static inline
+void _bt_log_unused(int dummy __attribute__((unused)), ...)
 {
-       const bt_log_format *format;
-       const bt_log_output *output;
-}
-bt_log_spec;
-
-#ifdef __cplusplus
 }
-#endif
-
-/* Execute log statement if condition is true. Example:
- *
- *   BT_LOG_IF(1 < 2, BT_LOGI("Log this"));
- *   BT_LOG_IF(1 > 2, BT_LOGI("Don't log this"));
- *
- * Keep in mind though, that if condition can't be evaluated at compile time,
- * then it will be evaluated at run time. This will increase exectuable size
- * and can have noticeable performance overhead. Try to limit conditions to
- * expressions that can be evaluated at compile time.
- */
-#define BT_LOG_IF(cond, f) do { _BT_LOG_IF((cond)) { f; } } _BT_LOG_ONCE
-
-/* Mark log statement as "secret". Log statements that are marked as secrets
- * will NOT be executed when censoring is enabled (see BT_LOG_CENSORED).
- * Example:
- *
- *   BT_LOG_SECRET(BT_LOGI("Credit card: %s", credit_card));
- *   BT_LOG_SECRET(BT_LOGD_MEM(cipher, cipher_sz, "Cipher bytes:"));
- */
-#define BT_LOG_SECRET(f) BT_LOG_IF(BT_LOG_SECRETS, f)
-
-/* Check "current" log level at compile time (ignoring "output" log level).
- * Evaluates to true when specified log level is enabled. For example:
- *
- *   #if BT_LOG_ENABLED_DEBUG
- *       const char *const g_enum_strings[] = {
- *           "enum_value_0", "enum_value_1", "enum_value_2"
- *       };
- *   #endif
- *   // ...
- *   #if BT_LOG_ENABLED_DEBUG
- *       BT_LOGD("enum value: %s", g_enum_strings[v]);
- *   #endif
- *
- * See BT_MINIMAL_LOG_LEVEL for details.
- */
-#define BT_LOG_ENABLED(lvl)     ((lvl) >= _BT_MINIMAL_LOG_LEVEL)
-#define BT_LOG_ENABLED_TRACE    BT_LOG_ENABLED(BT_LOG_TRACE)
-#define BT_LOG_ENABLED_DEBUG    BT_LOG_ENABLED(BT_LOG_DEBUG)
-#define BT_LOG_ENABLED_INFO     BT_LOG_ENABLED(BT_LOG_INFO)
-#define BT_LOG_ENABLED_WARNING  BT_LOG_ENABLED(BT_LOG_WARNING)
-#define BT_LOG_ENABLED_ERROR    BT_LOG_ENABLED(BT_LOG_ERROR)
-#define BT_LOG_ENABLED_FATAL    BT_LOG_ENABLED(BT_LOG_FATAL)
-
-/* Check "output" log level at run time (taking into account "current" log
- * level as well). Evaluates to true when specified log level is turned on AND
- * enabled. For example:
- *
- *   if (BT_LOG_ON_DEBUG)
- *   {
- *       char hash[65];
- *       sha256(data_ptr, data_sz, hash);
- *       BT_LOGD("data: len=%u, sha256=%s", data_sz, hash);
- *   }
- *
- * See BT_LOG_OUTPUT_LEVEL for details.
- */
-#define BT_LOG_ON_CUR_LVL(lvl, cur_lvl) \
-               G_UNLIKELY(BT_LOG_ENABLED((lvl)) && (lvl) >= (cur_lvl))
-#define BT_LOG_ON(lvl) \
-               G_UNLIKELY(BT_LOG_ENABLED((lvl)) && (lvl) >= _BT_LOG_OUTPUT_LEVEL)
-#define BT_LOG_ON_TRACE     BT_LOG_ON(BT_LOG_TRACE)
-#define BT_LOG_ON_DEBUG     BT_LOG_ON(BT_LOG_DEBUG)
-#define BT_LOG_ON_INFO      BT_LOG_ON(BT_LOG_INFO)
-#define BT_LOG_ON_WARNING   BT_LOG_ON(BT_LOG_WARNING)
-#define BT_LOG_ON_ERROR     BT_LOG_ON(BT_LOG_ERROR)
-#define BT_LOG_ON_FATAL     BT_LOG_ON(BT_LOG_FATAL)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern const char *_bt_log_tag_prefix;
-extern bt_log_format _bt_log_global_format;
-extern bt_log_output _bt_log_global_output;
-extern int _bt_log_global_output_lvl;
-extern const bt_log_spec _bt_log_stderr_spec;
-
-BT_HIDDEN
-void _bt_log_write_d(
-               const char *const func, const char *const file, const unsigned line,
-               const int lvl, const char *const tag,
-               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(6, 7);
-
-BT_HIDDEN
-void _bt_log_write_aux_d(
-               const char *const func, const char *const file, const unsigned line,
-               const bt_log_spec *const log, const int lvl, const char *const tag,
-               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(7, 8);
-
-BT_HIDDEN
-void _bt_log_write(
-               const int lvl, const char *const tag,
-               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(3, 4);
-
-BT_HIDDEN
-void _bt_log_write_aux(
-               const bt_log_spec *const log, const int lvl, const char *const tag,
-               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(4, 5);
-
-BT_HIDDEN
-void _bt_log_write_mem_d(
-               const char *const func, const char *const file, const unsigned line,
-               const int lvl, const char *const tag,
-               const void *const d, const unsigned d_sz,
-               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(8, 9);
-
-BT_HIDDEN
-void _bt_log_write_mem_aux_d(
-               const char *const func, const char *const file, const unsigned line,
-               const bt_log_spec *const log, const int lvl, const char *const tag,
-               const void *const d, const unsigned d_sz,
-               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(9, 10);
-
-BT_HIDDEN
-void _bt_log_write_mem(
-               const int lvl, const char *const tag,
-               const void *const d, const unsigned d_sz,
-               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(5, 6);
-
-BT_HIDDEN
-void _bt_log_write_mem_aux(
-               const bt_log_spec *const log, const int lvl, const char *const tag,
-               const void *const d, const unsigned d_sz,
-               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(6, 7);
-
-#ifdef __cplusplus
-}
-#endif
-
-/* Message logging macros:
- * - BT_LOGT("format string", args, ...)
- * - BT_LOGD("format string", args, ...)
- * - BT_LOGI("format string", args, ...)
- * - BT_LOGW("format string", args, ...)
- * - BT_LOGE("format string", args, ...)
- * - BT_LOGF("format string", args, ...)
- *
- * Message and error string (errno) logging macros:
- * - BT_LOGT_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGD_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGI_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGW_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGE_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGF_ERRNO("initial message", "format string", args, ...)
- *
- * Memory logging macros:
- * - BT_LOGT_MEM(data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGD_MEM(data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGI_MEM(data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGW_MEM(data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGE_MEM(data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGF_MEM(data_ptr, data_sz, "format string", args, ...)
- *
- * Auxiliary logging macros:
- * - BT_LOGT_AUX(&log_instance, "format string", args, ...)
- * - BT_LOGD_AUX(&log_instance, "format string", args, ...)
- * - BT_LOGI_AUX(&log_instance, "format string", args, ...)
- * - BT_LOGW_AUX(&log_instance, "format string", args, ...)
- * - BT_LOGE_AUX(&log_instance, "format string", args, ...)
- * - BT_LOGF_AUX(&log_instance, "format string", args, ...)
- *
- * Auxiliary memory logging macros:
- * - BT_LOGT_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGD_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGI_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGW_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGE_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
- * - BT_LOGF_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
- *
- * Preformatted string logging macros:
- * - BT_LOGT_STR("preformatted string");
- * - BT_LOGD_STR("preformatted string");
- * - BT_LOGI_STR("preformatted string");
- * - BT_LOGW_STR("preformatted string");
- * - BT_LOGE_STR("preformatted string");
- * - BT_LOGF_STR("preformatted string");
- *
- * Explicit log level and tag macros:
- * - BT_LOG_WRITE(level, tag, "format string", args, ...)
- * - BT_LOG_WRITE_MEM(level, tag, data_ptr, data_sz, "format string", args, ...)
- * - BT_LOG_WRITE_AUX(&log_instance, level, tag, "format string", args, ...)
- * - BT_LOG_WRITE_MEM_AUX(&log_instance, level, tag, data_ptr, data_sz,
- *                        "format string", args, ...)
- *
- * Explicit log level, current log level, and tag:
- * - BT_LOG_WRITE_CUR_LVL(level, cur_level, tag, "format string", args, ...)
- *
- * Format string follows printf() conventions. Both data_ptr and data_sz could
- * be 0. Tag can be 0 as well. Most compilers will verify that type of arguments
- * match format specifiers in format string.
- *
- * Library assuming UTF-8 encoding for all strings (char *), including format
- * string itself.
- */
-#if BT_LOG_SRCLOC_NONE == _BT_LOG_SRCLOC
-       #define BT_LOG_WRITE(lvl, tag, ...) \
-                       do { \
-                               if (BT_LOG_ON(lvl)) \
-                                       _bt_log_write(lvl, tag, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-       #define BT_LOG_WRITE_CUR_LVL(lvl, cur_lvl, tag, ...) \
-                       do { \
-                               if (BT_LOG_ON_CUR_LVL((lvl), (cur_lvl))) \
-                                       _bt_log_write(lvl, tag, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-       #define BT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \
-                       do { \
-                               if (BT_LOG_ON(lvl)) \
-                                       _bt_log_write_mem(lvl, tag, d, d_sz, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-       #define BT_LOG_WRITE_AUX(log, lvl, tag, ...) \
-                       do { \
-                               if (BT_LOG_ON(lvl)) \
-                                       _bt_log_write_aux(log, lvl, tag, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-       #define BT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \
-                       do { \
-                               if (BT_LOG_ON(lvl)) \
-                                       _bt_log_write_mem_aux(log, lvl, tag, d, d_sz, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-#else
-       #define BT_LOG_WRITE(lvl, tag, ...) \
-                       do { \
-                               if (BT_LOG_ON(lvl)) \
-                                       _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \
-                                                       lvl, tag, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-       #define BT_LOG_WRITE_CUR_LVL(lvl, cur_lvl, tag, ...) \
-                       do { \
-                               if (BT_LOG_ON_CUR_LVL((lvl), (cur_lvl))) \
-                                       _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \
-                                                       lvl, tag, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-       #define BT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \
-                       do { \
-                               if (BT_LOG_ON(lvl)) \
-                                       _bt_log_write_mem_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \
-                                                       lvl, tag, d, d_sz, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-       #define BT_LOG_WRITE_AUX(log, lvl, tag, ...) \
-                       do { \
-                               if (BT_LOG_ON(lvl)) \
-                                       _bt_log_write_aux_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \
-                                                       log, lvl, tag, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-       #define BT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \
-                       do { \
-                               if (BT_LOG_ON(lvl)) \
-                                       _bt_log_write_mem_aux_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \
-                                                       log, lvl, tag, d, d_sz, __VA_ARGS__); \
-                       } _BT_LOG_ONCE
-#endif
-
-#define BT_LOG_WRITE_ERRNO_CUR_LVL(lvl, cur_lvl, tag, _msg, _fmt, args...) \
-               do { \
-                       const char *error_str; \
-                       error_str = g_strerror(errno); \
-                       BT_LOG_WRITE_CUR_LVL(lvl, cur_lvl, tag, _msg ": %s" _fmt, error_str, ## args); \
-               } _BT_LOG_ONCE
-
-#define BT_LOG_WRITE_ERRNO(lvl, tag, _msg, _fmt, args...) \
-               do { \
-                       BT_LOG_WRITE_ERRNO_CUR_LVL(lvl, _BT_LOG_OUTPUT_LEVEL, tag, _msg, _fmt, ## args); \
-               } _BT_LOG_ONCE
-
-static _BT_LOG_INLINE void _bt_log_unused(const int dummy, ...) {(void)dummy;}
 
 #define _BT_LOG_UNUSED(...) \
-               do { _BT_LOG_NEVER _bt_log_unused(0, __VA_ARGS__); } _BT_LOG_ONCE
+       do { if (0) _bt_log_unused(0, ##__VA_ARGS__); } while (0)
 
+/* Trace level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_TRACE
-       #define BT_LOGT(...) \
-                       BT_LOG_WRITE(BT_LOG_TRACE, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGT_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(BT_LOG_TRACE, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGT_AUX(log, ...) \
-                       BT_LOG_WRITE_AUX(log, BT_LOG_TRACE, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGT_MEM(d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(BT_LOG_TRACE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
-       #define BT_LOGT_MEM_AUX(log, d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(log, BT_LOG_TRACE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+# define BT_LOGT_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGT_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_TRACE, _BT_LOG_TAG, (_msg))
+# define BT_LOGT_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGT(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_TRACE, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGT_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGT_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_TRACE, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGT_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGT_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_TRACE, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGT_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGT_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_TRACE, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGT_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGT_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_TRACE, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGT(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGT_ERRNO(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGT_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGT_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGT_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-#endif
-
+# define BT_LOGT_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_TRACE */
+
+/* Debug level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_DEBUG
-       #define BT_LOGD(...) \
-                       BT_LOG_WRITE(BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGD_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGD_AUX(log, ...) \
-                       BT_LOG_WRITE_AUX(log, BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGD_MEM(d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(BT_LOG_DEBUG, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
-       #define BT_LOGD_MEM_AUX(log, d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_DEBUG, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+# define BT_LOGD_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGD_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_DEBUG, _BT_LOG_TAG, (_msg))
+# define BT_LOGD_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGD(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_DEBUG, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGD_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGD_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_DEBUG, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGD_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGD_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_DEBUG, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGD_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGD_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_DEBUG, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGD_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGD_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_DEBUG, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGD(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGD_ERRNO(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGD_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGD_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGD_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-#endif
-
+# define BT_LOGD_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_DEBUG */
+
+/* Info level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_INFO
-       #define BT_LOGI(...) \
-                       BT_LOG_WRITE(BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGI_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGI_AUX(log, ...) \
-                       BT_LOG_WRITE_AUX(log, BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGI_MEM(d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(BT_LOG_INFO, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
-       #define BT_LOGI_MEM_AUX(log, d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_INFO, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+# define BT_LOGI_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGI_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_INFO, _BT_LOG_TAG, (_msg))
+# define BT_LOGI_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGI(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_INFO, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGI_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGI_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_INFO, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGI_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGI_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_INFO, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGI_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGI_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_INFO, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGI_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGI_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_INFO, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGI(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGI_ERRNO(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGI_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGI_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGI_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-#endif
-
+# define BT_LOGI_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_INFO */
+
+/* Warning level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_WARNING
-       #define BT_LOGW(...) \
-                       BT_LOG_WRITE(BT_LOG_WARNING, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGW_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(BT_LOG_WARNING, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGW_AUX(log, ...) \
-                       BT_LOG_WRITE_AUX(log, BT_LOG_WARNING, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGW_MEM(d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(BT_LOG_WARNING, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
-       #define BT_LOGW_MEM_AUX(log, d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_WARNING, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+# define BT_LOGW_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGW_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_WARNING, _BT_LOG_TAG, (_msg))
+# define BT_LOGW_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGW(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_WARNING, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGW_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGW_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_WARNING, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGW_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGW_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_WARNING, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGW_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGW_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_WARNING, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGW_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGW_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_WARNING, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGW(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGW_ERRNO(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGW_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGW_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGW_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-#endif
-
+# define BT_LOGW_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_WARNING */
+
+/* Error level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_ERROR
-       #define BT_LOGE(...) \
-                       BT_LOG_WRITE(BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGE_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGE_AUX(log, ...) \
-                       BT_LOG_WRITE_AUX(log, BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGE_MEM(d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(BT_LOG_ERROR, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
-       #define BT_LOGE_MEM_AUX(log, d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_ERROR, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+# define BT_LOGE_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGE_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_ERROR, _BT_LOG_TAG, (_msg))
+# define BT_LOGE_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGE(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_ERROR, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGE_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGE_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_ERROR, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGE_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGE_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_ERROR, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGE_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGE_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_ERROR, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGE_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGE_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_ERROR, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGE(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGE_ERRNO(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGE_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGE_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGE_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-#endif
-
+# define BT_LOGE_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_ERROR */
+
+/* Fatal level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_FATAL
-       #define BT_LOGF(...) \
-                       BT_LOG_WRITE(BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGF_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGF_AUX(log, ...) \
-                       BT_LOG_WRITE_AUX(log, BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGF_MEM(d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(BT_LOG_FATAL, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
-       #define BT_LOGF_MEM_AUX(log, d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_FATAL, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+# define BT_LOGF_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGF_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_FATAL, _BT_LOG_TAG, (_msg))
+# define BT_LOGF_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGF(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_FATAL, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGF_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGF_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_FATAL, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGF_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGF_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_FATAL, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGF_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGF_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_FATAL, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGF_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGF_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_FATAL, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGF(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGF_ERRNO(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGF_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGF_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGF_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-#endif
-
-#define BT_LOGT_STR(s) BT_LOGT("%s", (s))
-#define BT_LOGD_STR(s) BT_LOGD("%s", (s))
-#define BT_LOGI_STR(s) BT_LOGI("%s", (s))
-#define BT_LOGW_STR(s) BT_LOGW("%s", (s))
-#define BT_LOGE_STR(s) BT_LOGE("%s", (s))
-#define BT_LOGF_STR(s) BT_LOGF("%s", (s))
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Output to standard error stream. Library uses it by default, though in few
- * cases it could be necessary to specify it explicitly. For example, when
- * bt_log library is compiled with BT_LOG_EXTERN_GLOBAL_OUTPUT, application must
- * define and initialize global output variable:
- *
- *   BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR};
- *
- * Another example is when using custom output, stderr could be used as a
- * fallback when custom output facility failed to initialize:
- *
- *   bt_log_set_output_v(BT_LOG_OUT_STDERR);
- */
-enum { BT_LOG_OUT_STDERR_MASK = BT_LOG_PUT_STD };
-
-BT_HIDDEN
-void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg);
-#define BT_LOG_OUT_STDERR BT_LOG_OUT_STDERR_MASK, 0, bt_log_out_stderr_callback
-
-/* Predefined spec for stderr. Uses global format options (BT_LOG_GLOBAL_FORMAT)
- * and BT_LOG_OUT_STDERR. Could be used to force output to stderr for a
- * particular message. Example:
- *
- *   f = fopen("foo.log", "w");
- *   if (!f)
- *       BT_LOGE_AUX(BT_LOG_STDERR, "Failed to open log file");
- */
-#define BT_LOG_STDERR (&_bt_log_stderr_spec)
-
-/*
- * Returns the equivalent letter of the log level `level`.
- *
- * `level` must be a valid log level.
- */
-static inline
-char bt_log_get_letter_from_level(int level)
-{
-       char letter;
-
-       switch (level) {
-       case BT_LOG_TRACE:
-               letter = 'T';
-               break;
-       case BT_LOG_DEBUG:
-               letter = 'D';
-               break;
-       case BT_LOG_INFO:
-               letter = 'I';
-               break;
-       case BT_LOG_WARNING:
-               letter = 'W';
-               break;
-       case BT_LOG_ERROR:
-               letter = 'E';
-               break;
-       case BT_LOG_FATAL:
-               letter = 'F';
-               break;
-       case BT_LOG_NONE:
-               letter = 'N';
-               break;
-       default:
-               abort();
-       }
-
-       return letter;
-}
-
-/*
- * Returns the log level for the string `str`, or -1 if `str` is not a
- * valid log level string.
- */
-static inline
-int bt_log_get_level_from_string(const char *str)
-{
-       int level = -1;
-
-       BT_ASSERT(str);
-
-       if (strcmp(str, "TRACE") == 0 ||
-                       strcmp(str, "T") == 0) {
-               level = BT_LOG_TRACE;
-       } else if (strcmp(str, "DEBUG") == 0 ||
-                       strcmp(str, "D") == 0) {
-               level = BT_LOG_DEBUG;
-       } else if (strcmp(str, "INFO") == 0 ||
-                       strcmp(str, "I") == 0) {
-               level = BT_LOG_INFO;
-       } else if (strcmp(str, "WARN") == 0 ||
-                       strcmp(str, "WARNING") == 0 ||
-                       strcmp(str, "W") == 0) {
-               level = BT_LOG_WARNING;
-       } else if (strcmp(str, "ERROR") == 0 ||
-                       strcmp(str, "E") == 0) {
-               level = BT_LOG_ERROR;
-       } else if (strcmp(str, "FATAL") == 0 ||
-                       strcmp(str, "F") == 0) {
-               level = BT_LOG_FATAL;
-       } else if (strcmp(str, "NONE") == 0 ||
-                       strcmp(str, "N") == 0) {
-               level = BT_LOG_NONE;
-       } else {
-               /* FIXME: Should we warn here? How? */
-       }
-
-       return level;
-}
+# define BT_LOGF_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_FATAL */
 
 /*
- * Returns the log level for the letter `letter`, or -1 if `letter` is
- * not a valid log level string.
+ * Tell the world that `log.h` was included without having to rely on
+ * the specific include guard.
  */
-static inline
-int bt_log_get_level_from_letter(char letter)
-{
-       char str[] = {letter, '\0'};
-
-       return bt_log_get_level_from_string(str);
-}
-
-static inline
-int bt_log_get_level_from_env(const char *var)
-{
-       const char *varval = getenv(var);
-       int level = BT_LOG_NONE;
-
-       if (!varval) {
-               goto end;
-       }
-
-       level = bt_log_get_level_from_string(varval);
-       if (level < 0) {
-               /* FIXME: Should we warn here? How? */
-               level = BT_LOG_NONE;
-       }
-
-end:
-       return level;
-}
-
-#define BT_LOG_LEVEL_EXTERN_SYMBOL(_level_sym)                         \
-       extern int _level_sym
-
-#define BT_LOG_INIT_LOG_LEVEL(_level_sym, _env_var)                    \
-       BT_HIDDEN int _level_sym = BT_LOG_NONE;                         \
-       static                                                          \
-       void __attribute__((constructor)) _bt_log_level_ctor(void)      \
-       {                                                               \
-               _level_sym = bt_log_get_level_from_env(_env_var);       \
-       }
-
 #define BT_LOG_SUPPORTED
 
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* BABELTRACE_LOGGING_INTERNAL_H */
+#endif /* BABELTRACE_LOGGING_LOG_H */
diff --git a/src/param-parse/Makefile.am b/src/param-parse/Makefile.am
deleted file mode 100644 (file)
index 4c2479e..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-param-parse.la
-
-libbabeltrace2_param_parse_la_SOURCES = \
-       param-parse.c \
-       param-parse.h
index 6edb97f9ef071bcac53f52edf68cda421b8bfc08..1a44d69879d37f327048dda26fcd773db815a76d 100644 (file)
@@ -553,7 +553,6 @@ end:
  *
  * Return value is owned by the caller.
  */
-BT_HIDDEN
 bt_value *bt_param_parse(const char *arg, GString *ini_error)
 {
        /* Lexical scanner configuration */
index 046262fbf41ba98102555c0ba1456b02fe83313f..8ca0e3eb4b1da8da582bd4979396fd8fe63cc1b3 100644 (file)
@@ -13,7 +13,6 @@
 
 #include "common/macros.h"
 
-BT_HIDDEN
 bt_value *bt_param_parse(const char *arg, GString *ini_error);
 
 #endif /* SRC_VALUE_PARSE_VALUE_PARSE_H */
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
deleted file mode 100644 (file)
index c2a0d4c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = common utils text ctf
-
-if ENABLE_DEBUG_INFO
-SUBDIRS += lttng-utils
-endif
diff --git a/src/plugins/common/Makefile.am b/src/plugins/common/Makefile.am
deleted file mode 100644 (file)
index 58321eb..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = muxing param-validation
diff --git a/src/plugins/common/muxing/Makefile.am b/src/plugins/common/muxing/Makefile.am
deleted file mode 100644 (file)
index 1ab4343..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugins-common-muxing.la
-
-libbabeltrace2_plugins_common_muxing_la_SOURCES = muxing.c muxing.h
index 4675e6e0dbe294acfdf82b9d69d7de0965bb8281..5e52c4f497cdf77f17bf5e47b11c7c1aafcef5cb 100644 (file)
@@ -66,10 +66,10 @@ int message_type_weight(const bt_message_type msg_type)
 }
 
 /*
- * Compare 2 messages to order them in a determinitic way based on their
+ * Compare 2 messages to order them in a deterministic way based on their
  * types.
- * Returns -1 is left mesage must go first
- * Returns 1 is right mesage must go first
+ * Returns -1 is left message must go first
+ * Returns 1 is right message must go first
  */
 static
 int compare_messages_by_type(struct messages_to_compare *msgs)
@@ -161,6 +161,78 @@ end:
        return ret;
 }
 
+
+static
+int compare_clock_classes(const bt_clock_class *left_cc,
+               const bt_clock_class *right_cc)
+{
+       int ret;
+       const char *left_clock_class_name, *right_clock_class_name;
+       bt_uuid left_clock_class_uuid, right_clock_class_uuid;
+       uint64_t left_freq, right_freq, left_prec, right_prec;
+       bool left_origin_is_unix, right_origin_is_unix;
+
+       left_clock_class_uuid = bt_clock_class_get_uuid(left_cc);
+       right_clock_class_uuid = bt_clock_class_get_uuid(right_cc);
+
+       if (left_clock_class_uuid && !right_clock_class_uuid) {
+               ret = -1;
+               goto end;
+       } else if (!left_clock_class_uuid && right_clock_class_uuid) {
+               ret = 1;
+               goto end;
+       } else if (left_clock_class_uuid && right_clock_class_uuid) {
+               ret = bt_uuid_compare(left_clock_class_uuid,
+                       right_clock_class_uuid);
+               if (ret != 0) {
+                       goto end;
+               }
+       }
+
+       left_origin_is_unix = bt_clock_class_origin_is_unix_epoch(left_cc);
+       right_origin_is_unix = bt_clock_class_origin_is_unix_epoch(right_cc);
+
+       if (left_origin_is_unix != right_origin_is_unix) {
+               ret = left_origin_is_unix - right_origin_is_unix;
+               goto end;
+       }
+
+       left_clock_class_name = bt_clock_class_get_name(left_cc);
+       right_clock_class_name = bt_clock_class_get_name(right_cc);
+
+       if (left_clock_class_name && !right_clock_class_name) {
+               ret = -1;
+               goto end;
+       } else if (!left_clock_class_name && right_clock_class_name) {
+               ret = 1;
+               goto end;
+       } else if (left_clock_class_name && right_clock_class_name) {
+               ret = strcmp(left_clock_class_name, right_clock_class_name);
+               if (ret != 0) {
+                       goto end;
+               }
+       }
+
+       left_freq = bt_clock_class_get_frequency(left_cc);
+       right_freq = bt_clock_class_get_frequency(right_cc);
+
+       ret = right_freq - left_freq;
+       if (ret != 0) {
+               goto end;
+       }
+
+       left_prec = bt_clock_class_get_precision(left_cc);
+       right_prec = bt_clock_class_get_precision(right_cc);
+
+       ret = right_prec - left_prec;
+       if (ret != 0) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
 static
 int compare_streams(const bt_stream *left_stream, const bt_stream *right_stream)
 {
@@ -168,12 +240,13 @@ int compare_streams(const bt_stream *left_stream, const bt_stream *right_stream)
        const char *left_stream_name, *right_stream_name,
              *left_stream_class_name, *right_stream_class_name;
        const bt_stream_class *left_stream_class, *right_stream_class;
+       const bt_clock_class *left_cc, *right_cc;
 
        /*
         * No need to compare stream id as it was checked earlier and if we are
         * here it means they are identical or both absent.
         */
-       BT_ASSERT(bt_stream_get_id(left_stream) ==
+       BT_ASSERT_DBG(bt_stream_get_id(left_stream) ==
                bt_stream_get_id(right_stream));
 
        /* Compare stream name. */
@@ -200,7 +273,7 @@ int compare_streams(const bt_stream *left_stream, const bt_stream *right_stream)
         * No need to compare stream class id as it was checked earlier and if
         * we are here it means they are identical.
         */
-       BT_ASSERT(bt_stream_class_get_id(left_stream_class) ==
+       BT_ASSERT_DBG(bt_stream_class_get_id(left_stream_class) ==
                bt_stream_class_get_id(right_stream_class));
 
        /* Compare stream class name. */
@@ -275,133 +348,73 @@ int compare_streams(const bt_stream *left_stream, const bt_stream *right_stream)
                goto end;
        }
 
-       if (!bt_stream_class_supports_packets(left_stream_class)) {
-               /* Skip all packet related checks. */
-               goto end;
-       }
-
-       /*
-        * Compare stream class presence of discarded packets beginning default
-        * clock snapshot.
-        */
-       if (bt_stream_class_packets_have_beginning_default_clock_snapshot(left_stream_class) &&
-                       !bt_stream_class_packets_have_beginning_default_clock_snapshot(right_stream_class)) {
-               ret = 1;
-               goto end;
-       } else if (!bt_stream_class_packets_have_beginning_default_clock_snapshot(left_stream_class) &&
-                       bt_stream_class_packets_have_beginning_default_clock_snapshot(right_stream_class)) {
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * Compare stream class presence of discarded packets end default clock
-        * snapshot.
-        */
-       if (bt_stream_class_packets_have_end_default_clock_snapshot(left_stream_class) &&
-                       !bt_stream_class_packets_have_end_default_clock_snapshot(right_stream_class)) {
-               ret = 1;
-               goto end;
-       } else if (!bt_stream_class_packets_have_end_default_clock_snapshot(left_stream_class) &&
-                       bt_stream_class_packets_have_end_default_clock_snapshot(right_stream_class)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Compare stream class support of discarded packets. */
-       if (bt_stream_class_supports_discarded_packets(left_stream_class) &&
-                       !bt_stream_class_supports_discarded_packets(right_stream_class)) {
-               ret = 1;
-               goto end;
-       } else if (!bt_stream_class_supports_discarded_packets(left_stream_class) &&
-                       bt_stream_class_supports_discarded_packets(right_stream_class)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Compare stream class discarded packets default clock snapshot. */
-       if (bt_stream_class_discarded_packets_have_default_clock_snapshots(left_stream_class) &&
-                       !bt_stream_class_discarded_packets_have_default_clock_snapshots(right_stream_class)) {
-               ret = 1;
-               goto end;
-       } else if (!bt_stream_class_discarded_packets_have_default_clock_snapshots(left_stream_class) &&
-                       bt_stream_class_discarded_packets_have_default_clock_snapshots(right_stream_class)) {
-               ret = -1;
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static
-int compare_clock_snapshots_and_clock_classes(const bt_clock_snapshot *left_cs,
-               const bt_clock_snapshot *right_cs)
-{
-       int ret;
-       uint64_t left_freq, right_freq, left_prec, right_prec;
-       uint64_t left_cs_value, right_cs_value;
-       const bt_clock_class *left_clock_class, *right_clock_class;
-       const char *left_clock_class_name, *right_clock_class_name;
-       left_cs_value = bt_clock_snapshot_get_value(left_cs);
-       right_cs_value = bt_clock_snapshot_get_value(right_cs);
-       bt_uuid left_clock_class_uuid, right_clock_class_uuid;
-
-       ret = left_cs_value - right_cs_value;
-       if (ret != 0) {
-               goto end;
-       }
+       if (bt_stream_class_supports_packets(left_stream_class)) {
+               /*
+               * Compare stream class presence of discarded packets beginning default
+               * clock snapshot.
+               */
+               if (bt_stream_class_packets_have_beginning_default_clock_snapshot(left_stream_class) &&
+                               !bt_stream_class_packets_have_beginning_default_clock_snapshot(right_stream_class)) {
+                       ret = 1;
+                       goto end;
+               } else if (!bt_stream_class_packets_have_beginning_default_clock_snapshot(left_stream_class) &&
+                               bt_stream_class_packets_have_beginning_default_clock_snapshot(right_stream_class)) {
+                       ret = -1;
+                       goto end;
+               }
 
-       left_clock_class = bt_clock_snapshot_borrow_clock_class_const(left_cs);
-       right_clock_class = bt_clock_snapshot_borrow_clock_class_const(right_cs);
+               /*
+               * Compare stream class presence of discarded packets end default clock
+               * snapshot.
+               */
+               if (bt_stream_class_packets_have_end_default_clock_snapshot(left_stream_class) &&
+                               !bt_stream_class_packets_have_end_default_clock_snapshot(right_stream_class)) {
+                       ret = 1;
+                       goto end;
+               } else if (!bt_stream_class_packets_have_end_default_clock_snapshot(left_stream_class) &&
+                               bt_stream_class_packets_have_end_default_clock_snapshot(right_stream_class)) {
+                       ret = -1;
+                       goto end;
+               }
 
-       left_clock_class_uuid = bt_clock_class_get_uuid(left_clock_class);
-       right_clock_class_uuid = bt_clock_class_get_uuid(right_clock_class);
+               /* Compare stream class support of discarded packets. */
+               if (bt_stream_class_supports_discarded_packets(left_stream_class) &&
+                               !bt_stream_class_supports_discarded_packets(right_stream_class)) {
+                       ret = 1;
+                       goto end;
+               } else if (!bt_stream_class_supports_discarded_packets(left_stream_class) &&
+                               bt_stream_class_supports_discarded_packets(right_stream_class)) {
+                       ret = -1;
+                       goto end;
+               }
 
-       if (left_clock_class_uuid && !right_clock_class_uuid) {
-               ret = -1;
-               goto end;
-       } else if (!left_clock_class_uuid && right_clock_class_uuid) {
-               ret = 1;
-               goto end;
-       } else if (left_clock_class_uuid && right_clock_class_uuid) {
-               ret = bt_uuid_compare(left_clock_class_uuid,
-                       right_clock_class_uuid);
-               if (ret != 0) {
+               /* Compare stream class discarded packets default clock snapshot. */
+               if (bt_stream_class_discarded_packets_have_default_clock_snapshots(left_stream_class) &&
+                               !bt_stream_class_discarded_packets_have_default_clock_snapshots(right_stream_class)) {
+                       ret = 1;
+                       goto end;
+               } else if (!bt_stream_class_discarded_packets_have_default_clock_snapshots(left_stream_class) &&
+                               bt_stream_class_discarded_packets_have_default_clock_snapshots(right_stream_class)) {
+                       ret = -1;
                        goto end;
                }
        }
 
+       /* Compare the clock classes associated to the stream classes. */
+       left_cc = bt_stream_class_borrow_default_clock_class_const(left_stream_class);
+       right_cc = bt_stream_class_borrow_default_clock_class_const(right_stream_class);
 
-       left_clock_class_name = bt_clock_class_get_name(left_clock_class);
-       right_clock_class_name = bt_clock_class_get_name(right_clock_class);
+       if (!left_cc && !right_cc) {
+               ret = compare_clock_classes(left_cc, right_cc);
 
-       if (left_clock_class_name && !right_clock_class_name) {
-               ret = -1;
-               goto end;
-       } else if (!left_clock_class_name && right_clock_class_name) {
-               ret = 1;
-               goto end;
-       } else if (left_clock_class_name && right_clock_class_name) {
-               ret = strcmp(left_clock_class_name, right_clock_class_name);
                if (ret != 0) {
                        goto end;
                }
-       }
-
-       left_freq = bt_clock_class_get_frequency(left_clock_class);
-       right_freq = bt_clock_class_get_frequency(right_clock_class);
-
-       ret = right_freq - left_freq;
-       if (ret != 0) {
+       } else if (left_cc && !right_cc) {
+               ret = -1;
                goto end;
-       }
-
-       left_prec = bt_clock_class_get_precision(left_clock_class);
-       right_prec = bt_clock_class_get_precision(right_clock_class);
-
-       ret = right_prec - left_prec;
-       if (ret != 0) {
+       } else if (!left_cc && right_cc) {
+               ret = 1;
                goto end;
        }
 
@@ -409,6 +422,16 @@ end:
        return ret;
 }
 
+static
+int compare_clock_snapshots(const bt_clock_snapshot *left_cs,
+               const bt_clock_snapshot *right_cs)
+{
+       uint64_t left_cs_value = bt_clock_snapshot_get_value(left_cs);
+       uint64_t right_cs_value = bt_clock_snapshot_get_value(right_cs);
+
+       return left_cs_value - right_cs_value;
+}
+
 static
 const bt_stream *borrow_stream(const bt_message *msg)
 {
@@ -633,10 +656,10 @@ int compare_messages_same_type(struct messages_to_compare *msgs)
        int ret = 0;
 
        /*
-        * Both messages are of the same type, we must compare characterics of
+        * Both messages are of the same type, we must compare characteristics of
         * the messages such as the attributes of the event in a event message.
         */
-       BT_ASSERT(bt_message_get_type(msgs->left.msg) ==
+       BT_ASSERT_DBG(bt_message_get_type(msgs->left.msg) ==
                bt_message_get_type(msgs->right.msg));
 
        switch (bt_message_get_type(msgs->left.msg)) {
@@ -699,17 +722,22 @@ int compare_messages_same_type(struct messages_to_compare *msgs)
                        const bt_clock_snapshot *right_end_cs =
                                bt_message_discarded_events_borrow_end_default_clock_snapshot_const(msgs->right.msg);
 
-                       ret = compare_clock_snapshots_and_clock_classes(
-                               left_beg_cs, right_beg_cs);
+                       ret = compare_clock_snapshots(left_beg_cs, right_beg_cs);
                        if (ret) {
                                goto end;
                        }
 
-                       ret = compare_clock_snapshots_and_clock_classes(
-                               left_end_cs, right_end_cs);
+                       ret = compare_clock_snapshots(left_end_cs, right_end_cs);
                        if (ret) {
                                goto end;
                        }
+
+                       ret = compare_clock_classes(
+                               bt_clock_snapshot_borrow_clock_class_const(left_beg_cs),
+                               bt_clock_snapshot_borrow_clock_class_const(right_beg_cs));
+                       if (ret != 0) {
+                               goto end;
+                       }
                }
 
                left_event_count_avail =
@@ -766,17 +794,22 @@ int compare_messages_same_type(struct messages_to_compare *msgs)
                        const bt_clock_snapshot *right_end_cs =
                                bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(msgs->right.msg);
 
-                       ret = compare_clock_snapshots_and_clock_classes(
-                               left_beg_cs, right_beg_cs);
+                       ret = compare_clock_snapshots(left_beg_cs, right_beg_cs);
                        if (ret) {
                                goto end;
                        }
 
-                       ret = compare_clock_snapshots_and_clock_classes(
-                               left_end_cs, right_end_cs);
+                       ret = compare_clock_snapshots(left_end_cs, right_end_cs);
                        if (ret) {
                                goto end;
                        }
+
+                       ret = compare_clock_classes(
+                               bt_clock_snapshot_borrow_clock_class_const(left_beg_cs),
+                               bt_clock_snapshot_borrow_clock_class_const(right_beg_cs));
+                       if (ret != 0) {
+                               goto end;
+                       }
                }
 
                left_packet_count_avail = bt_message_discarded_packets_get_count(
@@ -808,8 +841,14 @@ int compare_messages_same_type(struct messages_to_compare *msgs)
                const bt_clock_snapshot *right_cs =
                        bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(msgs->right.msg);
 
-               ret = compare_clock_snapshots_and_clock_classes(
-                       left_cs, right_cs);
+               ret = compare_clock_snapshots(left_cs, right_cs);
+               if (ret != 0) {
+                       goto end;
+               }
+
+               ret = compare_clock_classes(
+                       bt_clock_snapshot_borrow_clock_class_const(left_cs),
+                       bt_clock_snapshot_borrow_clock_class_const(right_cs));
                if (ret != 0) {
                        goto end;
                }
@@ -824,14 +863,13 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int common_muxing_compare_messages(const bt_message *left_msg,
                const bt_message *right_msg)
 {
        int ret = 0;
        struct messages_to_compare msgs;
 
-       BT_ASSERT(left_msg != right_msg);
+       BT_ASSERT_DBG(left_msg != right_msg);
 
        msgs.left.msg = left_msg;
        msgs.left.trace = borrow_trace(left_msg);
index 07232ee2a445da28d38de209f972671eedecfd32..d724f8a423c1728cd0a6e03391c2f3965e7c8b98 100644 (file)
@@ -10,7 +10,7 @@
 #include <babeltrace2/babeltrace.h>
 #include "common/macros.h"
 
-BT_EXTERN_C BT_HIDDEN
+BT_EXTERN_C
 int common_muxing_compare_messages(const bt_message *left_msg,
                const bt_message *right_msg);
 
diff --git a/src/plugins/common/param-validation/Makefile.am b/src/plugins/common/param-validation/Makefile.am
deleted file mode 100644 (file)
index d431b44..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-param-validation.la
-
-libbabeltrace2_param_validation_la_SOURCES = \
-       param-validation.c \
-       param-validation.h
index 4919463dcecbd52268f8b41e69a8896d6305d29e..d187441628f6179a2e5afa31515aa93cc2ec4bf1 100644 (file)
@@ -101,12 +101,12 @@ enum bt_param_validation_status bt_param_validation_error(
 
                g_string_assign(str, "Error validating parameter `");
 
-               append_scope_to_string(str, &g_array_index(ctx->scope_stack,
+               append_scope_to_string(str, &bt_g_array_index(ctx->scope_stack,
                        struct validate_ctx_stack_element, 0), true);
 
                for (i = 1; i < ctx->scope_stack->len; i++) {
                        append_scope_to_string(str,
-                               &g_array_index(ctx->scope_stack,
+                               &bt_g_array_index(ctx->scope_stack,
                                        struct validate_ctx_stack_element, i), false);
                }
 
index e1110faa284931c582b291b77d0afca918520e2e..40d5760998d533d260be87e379a024b429b402b0 100644 (file)
@@ -145,13 +145,13 @@ struct bt_param_validation_map_value_entry_descr {
        const struct bt_param_validation_value_descr value_descr;
 };
 
-BT_EXTERN_C BT_HIDDEN
+BT_EXTERN_C
 enum bt_param_validation_status bt_param_validation_validate(
                const bt_value *params,
                const struct bt_param_validation_map_value_entry_descr *entries,
                gchar **error);
 
-BT_EXTERN_C BT_HIDDEN __BT_ATTR_FORMAT_PRINTF(2, 3)
+BT_EXTERN_C __BT_ATTR_FORMAT_PRINTF(2, 3)
 enum bt_param_validation_status bt_param_validation_error(
                struct bt_param_validation_context *ctx,
                const char *format, ...);
diff --git a/src/plugins/ctf/Makefile.am b/src/plugins/ctf/Makefile.am
deleted file mode 100644 (file)
index b5050b8..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = common \
-       fs-src \
-       fs-sink \
-       lttng-live
-
-plugindir = "$(BABELTRACE_PLUGINS_DIR)"
-plugin_LTLIBRARIES = babeltrace-plugin-ctf.la
-
-# ctf plugin
-babeltrace_plugin_ctf_la_SOURCES = plugin.cpp
-
-babeltrace_plugin_ctf_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LT_NO_UNDEFINED) \
-       -avoid-version -module $(LD_NOTEXT)
-
-babeltrace_plugin_ctf_la_LIBADD = \
-       common/libbabeltrace2-plugin-ctf-common.la \
-       fs-sink/libbabeltrace2-plugin-ctf-fs-sink.la \
-       fs-src/libbabeltrace2-plugin-ctf-fs-src.la \
-       lttng-live/libbabeltrace2-plugin-ctf-lttng-live.la \
-       $(top_builddir)/src/plugins/common/param-validation/libbabeltrace2-param-validation.la
-
-if !ENABLE_BUILT_IN_PLUGINS
-babeltrace_plugin_ctf_la_LIBADD += \
-       $(top_builddir)/src/lib/libbabeltrace2.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/ctfser/libbabeltrace2-ctfser.la
-endif
diff --git a/src/plugins/ctf/common/Makefile.am b/src/plugins/ctf/common/Makefile.am
deleted file mode 100644 (file)
index 78b76fe..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = metadata bfcr msg-iter
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-common.la
-libbabeltrace2_plugin_ctf_common_la_SOURCES = print.hpp
-libbabeltrace2_plugin_ctf_common_la_LIBADD =           \
-       $(builddir)/metadata/libctf-parser.la           \
-       $(builddir)/metadata/libctf-ast.la              \
-       $(builddir)/bfcr/libctf-bfcr.la                 \
-       $(builddir)/msg-iter/libctf-msg-iter.la
diff --git a/src/plugins/ctf/common/bfcr/Makefile.am b/src/plugins/ctf/common/bfcr/Makefile.am
deleted file mode 100644 (file)
index 17b7705..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libctf-bfcr.la
-libctf_bfcr_la_SOURCES = \
-       bfcr.cpp \
-       bfcr.hpp
diff --git a/src/plugins/ctf/common/bfcr/bfcr.cpp b/src/plugins/ctf/common/bfcr/bfcr.cpp
deleted file mode 100644 (file)
index d1495a6..0000000
+++ /dev/null
@@ -1,1272 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
- * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
- *
- * Babeltrace - CTF binary field class reader (BFCR)
- */
-
-#define BT_COMP_LOG_SELF_COMP (bfcr->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (bfcr->log_level)
-#define BT_LOG_TAG            "PLUGIN/CTF/BFCR"
-#include "logging/comp-logging.h"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include "common/assert.h"
-#include <string.h>
-#include "compat/bitfield.h"
-#include "common/common.h"
-#include <babeltrace2/babeltrace.h>
-#include "common/align.h"
-#include <glib.h>
-
-#include "bfcr.hpp"
-#include "../metadata/ctf-meta.hpp"
-
-#define DIV8(_x)                ((_x) >> 3)
-#define BYTES_TO_BITS(_x)       ((_x) *8)
-#define BITS_TO_BYTES_FLOOR(_x) DIV8(_x)
-#define BITS_TO_BYTES_CEIL(_x)  DIV8((_x) + 7)
-#define IN_BYTE_OFFSET(_at)     ((_at) &7)
-
-/* A visit stack entry */
-struct stack_entry
-{
-    /*
-     * Current class of base field, one of:
-     *
-     *   * Structure
-     *   * Array
-     *   * Sequence
-     *   * Variant
-     */
-    struct ctf_field_class *base_class;
-
-    /* Length of base field (always 1 for a variant class) */
-    int64_t base_len;
-
-    /* Index of next field to read */
-    int64_t index;
-};
-
-struct bt_bfcr;
-
-/* Visit stack */
-struct stack
-{
-    struct bt_bfcr *bfcr;
-
-    /* Entries (struct stack_entry) */
-    GArray *entries;
-
-    /* Number of active entries */
-    size_t size;
-};
-
-/* Reading states */
-enum bfcr_state
-{
-    BFCR_STATE_NEXT_FIELD,
-    BFCR_STATE_ALIGN_BASIC,
-    BFCR_STATE_ALIGN_COMPOUND,
-    BFCR_STATE_READ_BASIC_BEGIN,
-    BFCR_STATE_READ_BASIC_CONTINUE,
-    BFCR_STATE_DONE,
-};
-
-/* Binary class reader */
-struct bt_bfcr
-{
-    bt_logging_level log_level;
-
-    /* Weak */
-    bt_self_component *self_comp;
-
-    /* BFCR stack */
-    struct stack *stack;
-
-    /* Current basic field class */
-    struct ctf_field_class *cur_basic_field_class;
-
-    /* Current state */
-    enum bfcr_state state;
-
-    /*
-     * Last basic field class's byte order.
-     *
-     * This is used to detect errors since two contiguous basic
-     * classes for which the common boundary is not the boundary of
-     * a byte cannot have different byte orders.
-     *
-     * This is set to CTF_BYTE_ORDER_UNKNOWN on reset and when the last
-     * basic field class was a string class.
-     */
-    enum ctf_byte_order last_bo;
-
-    /* Current byte order (copied to last_bo after a successful read) */
-    enum ctf_byte_order cur_bo;
-
-    /* Stitch buffer infos */
-    struct
-    {
-        /* Stitch buffer */
-        uint8_t buf[16];
-
-        /* Offset, within stitch buffer, of first bit */
-        size_t offset;
-
-        /* Length (bits) of data in stitch buffer from offset */
-        size_t at;
-    } stitch;
-
-    /* User buffer infos */
-    struct
-    {
-        /* Address */
-        const uint8_t *addr;
-
-        /* Offset of data from address (bits) */
-        size_t offset;
-
-        /* Current position from offset (bits) */
-        size_t at;
-
-        /* Offset of offset within whole packet (bits) */
-        size_t packet_offset;
-
-        /* Data size in buffer (bits) */
-        size_t sz;
-
-        /* Buffer size (bytes) */
-        size_t buf_sz;
-    } buf;
-
-    /* User stuff */
-    struct
-    {
-        /* Callback functions */
-        struct bt_bfcr_cbs cbs;
-
-        /* Private data */
-        void *data;
-    } user;
-};
-
-static inline const char *bfcr_state_string(enum bfcr_state state)
-{
-    switch (state) {
-    case BFCR_STATE_NEXT_FIELD:
-        return "NEXT_FIELD";
-    case BFCR_STATE_ALIGN_BASIC:
-        return "ALIGN_BASIC";
-    case BFCR_STATE_ALIGN_COMPOUND:
-        return "ALIGN_COMPOUND";
-    case BFCR_STATE_READ_BASIC_BEGIN:
-        return "READ_BASIC_BEGIN";
-    case BFCR_STATE_READ_BASIC_CONTINUE:
-        return "READ_BASIC_CONTINUE";
-    case BFCR_STATE_DONE:
-        return "DONE";
-    }
-
-    bt_common_abort();
-}
-
-static struct stack *stack_new(struct bt_bfcr *bfcr)
-{
-    struct stack *stack = NULL;
-
-    stack = g_new0(struct stack, 1);
-    if (!stack) {
-        BT_COMP_LOGE_STR("Failed to allocate one stack.");
-        goto error;
-    }
-
-    stack->bfcr = bfcr;
-    stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry));
-    if (!stack->entries) {
-        BT_COMP_LOGE_STR("Failed to allocate a GArray.");
-        goto error;
-    }
-
-    BT_COMP_LOGD("Created stack: addr=%p", stack);
-    return stack;
-
-error:
-    g_free(stack);
-    return NULL;
-}
-
-static void stack_destroy(struct stack *stack)
-{
-    struct bt_bfcr *bfcr;
-
-    if (!stack) {
-        return;
-    }
-
-    bfcr = stack->bfcr;
-    BT_COMP_LOGD("Destroying stack: addr=%p", stack);
-
-    if (stack->entries) {
-        g_array_free(stack->entries, TRUE);
-    }
-
-    g_free(stack);
-}
-
-static int stack_push(struct stack *stack, struct ctf_field_class *base_class, size_t base_len)
-{
-    struct stack_entry *entry;
-    struct bt_bfcr *bfcr;
-
-    BT_ASSERT_DBG(stack);
-    BT_ASSERT_DBG(base_class);
-    bfcr = stack->bfcr;
-    BT_COMP_LOGT("Pushing field class on stack: stack-addr=%p, "
-                 "fc-addr=%p, fc-type=%d, base-length=%zu, "
-                 "stack-size-before=%zu, stack-size-after=%zu",
-                 stack, base_class, base_class->type, base_len, stack->size, stack->size + 1);
-
-    if (stack->entries->len == stack->size) {
-        g_array_set_size(stack->entries, stack->size + 1);
-    }
-
-    entry = &g_array_index(stack->entries, struct stack_entry, stack->size);
-    entry->base_class = base_class;
-    entry->base_len = base_len;
-    entry->index = 0;
-    stack->size++;
-    return 0;
-}
-
-static inline int64_t get_compound_field_class_length(struct bt_bfcr *bfcr,
-                                                      struct ctf_field_class *fc)
-{
-    int64_t length;
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
-
-        length = (int64_t) struct_fc->members->len;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        /* Variant field classes always "contain" a single class */
-        length = 1;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    {
-        struct ctf_field_class_array *array_fc = ctf_field_class_as_array(fc);
-
-        length = (int64_t) array_fc->length;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-        length = bfcr->user.cbs.query.get_sequence_length(fc, bfcr->user.data);
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    return length;
-}
-
-static int stack_push_with_len(struct bt_bfcr *bfcr, struct ctf_field_class *base_class)
-{
-    int ret;
-    int64_t length = get_compound_field_class_length(bfcr, base_class);
-
-    if (length < 0) {
-        BT_COMP_LOGW("Cannot get compound field class's field count: "
-                     "bfcr-addr=%p, fc-addr=%p, fc-type=%d",
-                     bfcr, base_class, base_class->type);
-        ret = BT_BFCR_STATUS_ERROR;
-        goto end;
-    }
-
-    ret = stack_push(bfcr->stack, base_class, (size_t) length);
-
-end:
-    return ret;
-}
-
-static inline unsigned int stack_size(struct stack *stack)
-{
-    BT_ASSERT_DBG(stack);
-    return stack->size;
-}
-
-static void stack_pop(struct stack *stack)
-{
-    struct bt_bfcr *bfcr;
-
-    BT_ASSERT_DBG(stack);
-    BT_ASSERT_DBG(stack_size(stack));
-    bfcr = stack->bfcr;
-    BT_COMP_LOGT("Popping from stack: "
-                 "stack-addr=%p, stack-size-before=%u, stack-size-after=%u",
-                 stack, stack->entries->len, stack->entries->len - 1);
-    stack->size--;
-}
-
-static inline bool stack_empty(struct stack *stack)
-{
-    return stack_size(stack) == 0;
-}
-
-static void stack_clear(struct stack *stack)
-{
-    BT_ASSERT_DBG(stack);
-    stack->size = 0;
-}
-
-static inline struct stack_entry *stack_top(struct stack *stack)
-{
-    BT_ASSERT_DBG(stack);
-    BT_ASSERT_DBG(stack_size(stack));
-    return &g_array_index(stack->entries, struct stack_entry, stack->size - 1);
-}
-
-static inline size_t available_bits(struct bt_bfcr *bfcr)
-{
-    return bfcr->buf.sz - bfcr->buf.at;
-}
-
-static inline void consume_bits(struct bt_bfcr *bfcr, size_t incr)
-{
-    BT_COMP_LOGT("Advancing cursor: bfcr-addr=%p, cur-before=%zu, cur-after=%zu", bfcr,
-                 bfcr->buf.at, bfcr->buf.at + incr);
-    bfcr->buf.at += incr;
-}
-
-static inline bool has_enough_bits(struct bt_bfcr *bfcr, size_t sz)
-{
-    return available_bits(bfcr) >= sz;
-}
-
-static inline bool at_least_one_bit_left(struct bt_bfcr *bfcr)
-{
-    return has_enough_bits(bfcr, 1);
-}
-
-static inline size_t packet_at(struct bt_bfcr *bfcr)
-{
-    return bfcr->buf.packet_offset + bfcr->buf.at;
-}
-
-static inline size_t buf_at_from_addr(struct bt_bfcr *bfcr)
-{
-    /*
-     * Considering this:
-     *
-     *     ====== offset ===== (17)
-     *
-     *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
-     *     ^
-     *     addr (0)           ==== at ==== (12)
-     *
-     * We want this:
-     *
-     *     =============================== (29)
-     */
-    return bfcr->buf.offset + bfcr->buf.at;
-}
-
-static void stitch_reset(struct bt_bfcr *bfcr)
-{
-    bfcr->stitch.offset = 0;
-    bfcr->stitch.at = 0;
-}
-
-static inline size_t stitch_at_from_addr(struct bt_bfcr *bfcr)
-{
-    return bfcr->stitch.offset + bfcr->stitch.at;
-}
-
-static void stitch_append_from_buf(struct bt_bfcr *bfcr, size_t sz)
-{
-    size_t stitch_byte_at;
-    size_t buf_byte_at;
-    size_t nb_bytes;
-
-    if (sz == 0) {
-        return;
-    }
-
-    stitch_byte_at = BITS_TO_BYTES_FLOOR(stitch_at_from_addr(bfcr));
-    buf_byte_at = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr));
-    nb_bytes = BITS_TO_BYTES_CEIL(sz);
-    BT_ASSERT(nb_bytes > 0);
-    BT_ASSERT(bfcr->buf.addr);
-    memcpy(&bfcr->stitch.buf[stitch_byte_at], &bfcr->buf.addr[buf_byte_at], nb_bytes);
-    bfcr->stitch.at += sz;
-    consume_bits(bfcr, sz);
-}
-
-static void stitch_append_from_remaining_buf(struct bt_bfcr *bfcr)
-{
-    stitch_append_from_buf(bfcr, available_bits(bfcr));
-}
-
-static void stitch_set_from_remaining_buf(struct bt_bfcr *bfcr)
-{
-    stitch_reset(bfcr);
-    bfcr->stitch.offset = IN_BYTE_OFFSET(buf_at_from_addr(bfcr));
-    stitch_append_from_remaining_buf(bfcr);
-}
-
-static inline void read_unsigned_bitfield(struct bt_bfcr *bfcr, const uint8_t *buf, size_t at,
-                                          unsigned int field_size, enum ctf_byte_order bo,
-                                          uint64_t *v)
-{
-    switch (bo) {
-    case CTF_BYTE_ORDER_BIG:
-        bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
-        break;
-    case CTF_BYTE_ORDER_LITTLE:
-        bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    BT_COMP_LOGT("Read unsigned bit array: cur=%zu, size=%u, "
-                 "bo=%d, val=%" PRIu64,
-                 at, field_size, bo, *v);
-}
-
-static inline void read_signed_bitfield(struct bt_bfcr *bfcr, const uint8_t *buf, size_t at,
-                                        unsigned int field_size, enum ctf_byte_order bo, int64_t *v)
-{
-    switch (bo) {
-    case CTF_BYTE_ORDER_BIG:
-        bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
-        break;
-    case CTF_BYTE_ORDER_LITTLE:
-        bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    BT_COMP_LOGT("Read signed bit array: cur=%zu, size=%u, "
-                 "bo=%d, val=%" PRId64,
-                 at, field_size, bo, *v);
-}
-
-typedef enum bt_bfcr_status (*read_basic_and_call_cb_t)(struct bt_bfcr *, const uint8_t *, size_t);
-
-static inline enum bt_bfcr_status validate_contiguous_bo(struct bt_bfcr *bfcr,
-                                                         enum ctf_byte_order next_bo)
-{
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-    /* Always valid when at a byte boundary */
-    if (packet_at(bfcr) % 8 == 0) {
-        goto end;
-    }
-
-    /* Always valid if last byte order is unknown */
-    if (bfcr->last_bo == CTF_BYTE_ORDER_UNKNOWN) {
-        goto end;
-    }
-
-    /* Always valid if next byte order is unknown */
-    if (next_bo == CTF_BYTE_ORDER_UNKNOWN) {
-        goto end;
-    }
-
-    /* Make sure last byte order is compatible with the next byte order */
-    switch (bfcr->last_bo) {
-    case CTF_BYTE_ORDER_BIG:
-        if (next_bo != CTF_BYTE_ORDER_BIG) {
-            status = BT_BFCR_STATUS_ERROR;
-        }
-        break;
-    case CTF_BYTE_ORDER_LITTLE:
-        if (next_bo != CTF_BYTE_ORDER_LITTLE) {
-            status = BT_BFCR_STATUS_ERROR;
-        }
-        break;
-    default:
-        status = BT_BFCR_STATUS_ERROR;
-    }
-
-end:
-    if (status < 0) {
-        BT_COMP_LOGW("Cannot read bit array: two different byte orders not at a byte boundary: "
-                     "bfcr-addr=%p, last-bo=%d, next-bo=%d",
-                     bfcr, bfcr->last_bo, next_bo);
-    }
-
-    return status;
-}
-
-static enum bt_bfcr_status read_basic_float_and_call_cb(struct bt_bfcr *bfcr, const uint8_t *buf,
-                                                        size_t at)
-{
-    double dblval;
-    unsigned int field_size;
-    enum ctf_byte_order bo;
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-    ctf_field_class_float *fc = ctf_field_class_as_float(bfcr->cur_basic_field_class);
-
-    BT_ASSERT_DBG(fc);
-    field_size = fc->base.size;
-    bo = fc->base.byte_order;
-    bfcr->cur_bo = bo;
-
-    switch (field_size) {
-    case 32:
-    {
-        uint64_t v;
-        union
-        {
-            uint32_t u;
-            float f;
-        } f32;
-
-        read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &v);
-        f32.u = (uint32_t) v;
-        dblval = (double) f32.f;
-        break;
-    }
-    case 64:
-    {
-        union
-        {
-            uint64_t u;
-            double d;
-        } f64;
-
-        read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &f64.u);
-        dblval = f64.d;
-        break;
-    }
-    default:
-        /* Only 32-bit and 64-bit fields are supported currently */
-        bt_common_abort();
-    }
-
-    BT_COMP_LOGT("Read floating point number value: bfcr=%p, cur=%zu, val=%f", bfcr, at, dblval);
-
-    if (bfcr->user.cbs.classes.floating_point) {
-        BT_COMP_LOGT("Calling user function (floating point number).");
-        status = bfcr->user.cbs.classes.floating_point(dblval, bfcr->cur_basic_field_class,
-                                                       bfcr->user.data);
-        BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(status));
-        if (status != BT_BFCR_STATUS_OK) {
-            BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s", bfcr,
-                         bt_bfcr_status_string(status));
-        }
-    }
-
-    return status;
-}
-
-static inline enum bt_bfcr_status read_basic_int_and_call_cb(struct bt_bfcr *bfcr,
-                                                             const uint8_t *buf, size_t at)
-{
-    unsigned int field_size;
-    enum ctf_byte_order bo;
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-    ctf_field_class_int *fc = ctf_field_class_as_int(bfcr->cur_basic_field_class);
-
-    field_size = fc->base.size;
-    bo = fc->base.byte_order;
-
-    /*
-     * Update current byte order now because we could be reading
-     * the integer value of an enumeration class, and thus we know
-     * here the actual supporting integer class's byte order.
-     */
-    bfcr->cur_bo = bo;
-
-    if (fc->is_signed) {
-        int64_t v;
-
-        read_signed_bitfield(bfcr, buf, at, field_size, bo, &v);
-
-        if (bfcr->user.cbs.classes.signed_int) {
-            BT_COMP_LOGT("Calling user function (signed integer).");
-            status =
-                bfcr->user.cbs.classes.signed_int(v, bfcr->cur_basic_field_class, bfcr->user.data);
-            BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(status));
-            if (status != BT_BFCR_STATUS_OK) {
-                BT_COMP_LOGW("User function failed: "
-                             "bfcr-addr=%p, status=%s",
-                             bfcr, bt_bfcr_status_string(status));
-            }
-        }
-    } else {
-        uint64_t v;
-
-        read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &v);
-
-        if (bfcr->user.cbs.classes.unsigned_int) {
-            BT_COMP_LOGT("Calling user function (unsigned integer).");
-            status = bfcr->user.cbs.classes.unsigned_int(v, bfcr->cur_basic_field_class,
-                                                         bfcr->user.data);
-            BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(status));
-            if (status != BT_BFCR_STATUS_OK) {
-                BT_COMP_LOGW("User function failed: "
-                             "bfcr-addr=%p, status=%s",
-                             bfcr, bt_bfcr_status_string(status));
-            }
-        }
-    }
-
-    return status;
-}
-
-static inline enum bt_bfcr_status
-read_bit_array_class_and_call_continue(struct bt_bfcr *bfcr,
-                                       read_basic_and_call_cb_t read_basic_and_call_cb)
-{
-    size_t available;
-    size_t needed_bits;
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-    ctf_field_class_bit_array *fc = ctf_field_class_as_bit_array(bfcr->cur_basic_field_class);
-
-    if (!at_least_one_bit_left(bfcr)) {
-        BT_COMP_LOGT("Reached end of data: bfcr-addr=%p", bfcr);
-        status = BT_BFCR_STATUS_EOF;
-        goto end;
-    }
-
-    available = available_bits(bfcr);
-    needed_bits = fc->size - bfcr->stitch.at;
-    BT_COMP_LOGT("Continuing basic field decoding: "
-                 "bfcr-addr=%p, field-size=%u, needed-size=%zu, "
-                 "available-size=%zu",
-                 bfcr, fc->size, needed_bits, available);
-    if (needed_bits <= available) {
-        /* We have all the bits; append to stitch, then decode */
-        stitch_append_from_buf(bfcr, needed_bits);
-        status = read_basic_and_call_cb(bfcr, bfcr->stitch.buf, bfcr->stitch.offset);
-        if (status != BT_BFCR_STATUS_OK) {
-            BT_COMP_LOGW("Cannot read basic field: "
-                         "bfcr-addr=%p, fc-addr=%p, status=%s",
-                         bfcr, bfcr->cur_basic_field_class, bt_bfcr_status_string(status));
-            goto end;
-        }
-
-        if (stack_empty(bfcr->stack)) {
-            /* Root is a basic class */
-            bfcr->state = BFCR_STATE_DONE;
-        } else {
-            /* Go to next field */
-            stack_top(bfcr->stack)->index++;
-            bfcr->state = BFCR_STATE_NEXT_FIELD;
-            bfcr->last_bo = bfcr->cur_bo;
-        }
-        goto end;
-    }
-
-    /* We are here; it means we don't have enough data to decode this */
-    BT_COMP_LOGT_STR("Not enough data to read the next basic field: appending to stitch buffer.");
-    stitch_append_from_remaining_buf(bfcr);
-    status = BT_BFCR_STATUS_EOF;
-
-end:
-    return status;
-}
-
-static inline enum bt_bfcr_status
-read_bit_array_class_and_call_begin(struct bt_bfcr *bfcr,
-                                    read_basic_and_call_cb_t read_basic_and_call_cb)
-{
-    size_t available;
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-    ctf_field_class_bit_array *fc = ctf_field_class_as_bit_array(bfcr->cur_basic_field_class);
-
-    if (!at_least_one_bit_left(bfcr)) {
-        BT_COMP_LOGT("Reached end of data: bfcr-addr=%p", bfcr);
-        status = BT_BFCR_STATUS_EOF;
-        goto end;
-    }
-
-    status = validate_contiguous_bo(bfcr, fc->byte_order);
-    if (status != BT_BFCR_STATUS_OK) {
-        /* validate_contiguous_bo() logs errors */
-        goto end;
-    }
-
-    available = available_bits(bfcr);
-
-    if (fc->size <= available) {
-        /* We have all the bits; decode and set now */
-        BT_ASSERT_DBG(bfcr->buf.addr);
-        status = read_basic_and_call_cb(bfcr, bfcr->buf.addr, buf_at_from_addr(bfcr));
-        if (status != BT_BFCR_STATUS_OK) {
-            BT_COMP_LOGW("Cannot read basic field: "
-                         "bfcr-addr=%p, fc-addr=%p, status=%s",
-                         bfcr, bfcr->cur_basic_field_class, bt_bfcr_status_string(status));
-            goto end;
-        }
-
-        consume_bits(bfcr, fc->size);
-
-        if (stack_empty(bfcr->stack)) {
-            /* Root is a basic class */
-            bfcr->state = BFCR_STATE_DONE;
-        } else {
-            /* Go to next field */
-            stack_top(bfcr->stack)->index++;
-            bfcr->state = BFCR_STATE_NEXT_FIELD;
-            bfcr->last_bo = bfcr->cur_bo;
-        }
-
-        goto end;
-    }
-
-    /* We are here; it means we don't have enough data to decode this */
-    BT_COMP_LOGT_STR("Not enough data to read the next basic field: setting stitch buffer.");
-    stitch_set_from_remaining_buf(bfcr);
-    bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE;
-    status = BT_BFCR_STATUS_EOF;
-
-end:
-    return status;
-}
-
-static inline enum bt_bfcr_status read_basic_int_class_and_call_begin(struct bt_bfcr *bfcr)
-{
-    return read_bit_array_class_and_call_begin(bfcr, read_basic_int_and_call_cb);
-}
-
-static inline enum bt_bfcr_status read_basic_int_class_and_call_continue(struct bt_bfcr *bfcr)
-{
-    return read_bit_array_class_and_call_continue(bfcr, read_basic_int_and_call_cb);
-}
-
-static inline enum bt_bfcr_status read_basic_float_class_and_call_begin(struct bt_bfcr *bfcr)
-{
-    return read_bit_array_class_and_call_begin(bfcr, read_basic_float_and_call_cb);
-}
-
-static inline enum bt_bfcr_status read_basic_float_class_and_call_continue(struct bt_bfcr *bfcr)
-{
-    return read_bit_array_class_and_call_continue(bfcr, read_basic_float_and_call_cb);
-}
-
-static inline enum bt_bfcr_status read_basic_string_class_and_call(struct bt_bfcr *bfcr, bool begin)
-{
-    size_t buf_at_bytes;
-    const uint8_t *result;
-    size_t available_bytes;
-    const uint8_t *first_chr;
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-    if (!at_least_one_bit_left(bfcr)) {
-        BT_COMP_LOGT("Reached end of data: bfcr-addr=%p", bfcr);
-        status = BT_BFCR_STATUS_EOF;
-        goto end;
-    }
-
-    BT_ASSERT_DBG(buf_at_from_addr(bfcr) % 8 == 0);
-    available_bytes = BITS_TO_BYTES_FLOOR(available_bits(bfcr));
-    buf_at_bytes = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr));
-    BT_ASSERT_DBG(bfcr->buf.addr);
-    first_chr = &bfcr->buf.addr[buf_at_bytes];
-    result = (const uint8_t *) memchr(first_chr, '\0', available_bytes);
-
-    if (begin && bfcr->user.cbs.classes.string_begin) {
-        BT_COMP_LOGT("Calling user function (string, beginning).");
-        status = bfcr->user.cbs.classes.string_begin(bfcr->cur_basic_field_class, bfcr->user.data);
-        BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(status));
-        if (status != BT_BFCR_STATUS_OK) {
-            BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s", bfcr,
-                         bt_bfcr_status_string(status));
-            goto end;
-        }
-    }
-
-    if (!result) {
-        /* No null character yet */
-        if (bfcr->user.cbs.classes.string) {
-            BT_COMP_LOGT("Calling user function (substring).");
-            status = bfcr->user.cbs.classes.string((const char *) first_chr, available_bytes,
-                                                   bfcr->cur_basic_field_class, bfcr->user.data);
-            BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(status));
-            if (status != BT_BFCR_STATUS_OK) {
-                BT_COMP_LOGW("User function failed: "
-                             "bfcr-addr=%p, status=%s",
-                             bfcr, bt_bfcr_status_string(status));
-                goto end;
-            }
-        }
-
-        consume_bits(bfcr, BYTES_TO_BITS(available_bytes));
-        bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE;
-        status = BT_BFCR_STATUS_EOF;
-    } else {
-        /* Found the null character */
-        size_t result_len = (size_t) (result - first_chr);
-
-        if (bfcr->user.cbs.classes.string && result_len) {
-            BT_COMP_LOGT("Calling user function (substring).");
-            status = bfcr->user.cbs.classes.string((const char *) first_chr, result_len,
-                                                   bfcr->cur_basic_field_class, bfcr->user.data);
-            BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(status));
-            if (status != BT_BFCR_STATUS_OK) {
-                BT_COMP_LOGW("User function failed: "
-                             "bfcr-addr=%p, status=%s",
-                             bfcr, bt_bfcr_status_string(status));
-                goto end;
-            }
-        }
-
-        if (bfcr->user.cbs.classes.string_end) {
-            BT_COMP_LOGT("Calling user function (string, end).");
-            status =
-                bfcr->user.cbs.classes.string_end(bfcr->cur_basic_field_class, bfcr->user.data);
-            BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(status));
-            if (status != BT_BFCR_STATUS_OK) {
-                BT_COMP_LOGW("User function failed: "
-                             "bfcr-addr=%p, status=%s",
-                             bfcr, bt_bfcr_status_string(status));
-                goto end;
-            }
-        }
-
-        consume_bits(bfcr, BYTES_TO_BITS(result_len + 1));
-
-        if (stack_empty(bfcr->stack)) {
-            /* Root is a basic class */
-            bfcr->state = BFCR_STATE_DONE;
-        } else {
-            /* Go to next field */
-            stack_top(bfcr->stack)->index++;
-            bfcr->state = BFCR_STATE_NEXT_FIELD;
-            bfcr->last_bo = bfcr->cur_bo;
-        }
-    }
-
-end:
-    return status;
-}
-
-static inline enum bt_bfcr_status read_basic_begin_state(struct bt_bfcr *bfcr)
-{
-    enum bt_bfcr_status status;
-
-    BT_ASSERT_DBG(bfcr->cur_basic_field_class);
-
-    switch (bfcr->cur_basic_field_class->type) {
-    case CTF_FIELD_CLASS_TYPE_INT:
-    case CTF_FIELD_CLASS_TYPE_ENUM:
-        status = read_basic_int_class_and_call_begin(bfcr);
-        break;
-    case CTF_FIELD_CLASS_TYPE_FLOAT:
-        status = read_basic_float_class_and_call_begin(bfcr);
-        break;
-    case CTF_FIELD_CLASS_TYPE_STRING:
-        status = read_basic_string_class_and_call(bfcr, true);
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    return status;
-}
-
-static inline enum bt_bfcr_status read_basic_continue_state(struct bt_bfcr *bfcr)
-{
-    enum bt_bfcr_status status;
-
-    BT_ASSERT_DBG(bfcr->cur_basic_field_class);
-
-    switch (bfcr->cur_basic_field_class->type) {
-    case CTF_FIELD_CLASS_TYPE_INT:
-    case CTF_FIELD_CLASS_TYPE_ENUM:
-        status = read_basic_int_class_and_call_continue(bfcr);
-        break;
-    case CTF_FIELD_CLASS_TYPE_FLOAT:
-        status = read_basic_float_class_and_call_continue(bfcr);
-        break;
-    case CTF_FIELD_CLASS_TYPE_STRING:
-        status = read_basic_string_class_and_call(bfcr, false);
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    return status;
-}
-
-static inline size_t bits_to_skip_to_align_to(struct bt_bfcr *bfcr, size_t align)
-{
-    size_t aligned_packet_at;
-
-    aligned_packet_at = BT_ALIGN(packet_at(bfcr), align);
-    return aligned_packet_at - packet_at(bfcr);
-}
-
-static inline enum bt_bfcr_status align_class_state(struct bt_bfcr *bfcr,
-                                                    struct ctf_field_class *field_class,
-                                                    enum bfcr_state next_state)
-{
-    unsigned int field_alignment;
-    size_t skip_bits;
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-    /* Get field's alignment */
-    field_alignment = field_class->alignment;
-
-    /*
-     * 0 means "undefined" for variants; what we really want is 1
-     * (always aligned)
-     */
-    BT_ASSERT_DBG(field_alignment >= 1);
-
-    /* Compute how many bits we need to skip */
-    skip_bits = bits_to_skip_to_align_to(bfcr, (size_t) field_alignment);
-
-    /* Nothing to skip? aligned */
-    if (skip_bits == 0) {
-        bfcr->state = next_state;
-        goto end;
-    }
-
-    /* Make sure there's at least one bit left */
-    if (!at_least_one_bit_left(bfcr)) {
-        status = BT_BFCR_STATUS_EOF;
-        goto end;
-    }
-
-    /* Consume as many bits as possible in what's left */
-    consume_bits(bfcr, MIN(available_bits(bfcr), skip_bits));
-
-    /* Are we done now? */
-    skip_bits = bits_to_skip_to_align_to(bfcr, field_alignment);
-    if (skip_bits == 0) {
-        /* Yes: go to next state */
-        bfcr->state = next_state;
-        goto end;
-    } else {
-        /* No: need more data */
-        BT_COMP_LOGT("Reached end of data when aligning: bfcr-addr=%p", bfcr);
-        status = BT_BFCR_STATUS_EOF;
-    }
-
-end:
-    return status;
-}
-
-static inline enum bt_bfcr_status next_field_state(struct bt_bfcr *bfcr)
-{
-    int ret;
-    struct stack_entry *top;
-    struct ctf_field_class *next_field_class = NULL;
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-    if (stack_empty(bfcr->stack)) {
-        goto end;
-    }
-
-    top = stack_top(bfcr->stack);
-
-    /* Are we done with this base class? */
-    while (top->index == top->base_len) {
-        if (bfcr->user.cbs.classes.compound_end) {
-            BT_COMP_LOGT("Calling user function (compound, end).");
-            status = bfcr->user.cbs.classes.compound_end(top->base_class, bfcr->user.data);
-            BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(status));
-            if (status != BT_BFCR_STATUS_OK) {
-                BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s", bfcr,
-                             bt_bfcr_status_string(status));
-                goto end;
-            }
-        }
-
-        stack_pop(bfcr->stack);
-
-        /* Are we done with the root class? */
-        if (stack_empty(bfcr->stack)) {
-            bfcr->state = BFCR_STATE_DONE;
-            goto end;
-        }
-
-        top = stack_top(bfcr->stack);
-        top->index++;
-    }
-
-    /* Get next field's class */
-    switch (top->base_class->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-        next_field_class = ctf_field_class_struct_borrow_member_by_index(
-                               ctf_field_class_as_struct(top->base_class), (uint64_t) top->index)
-                               ->fc;
-        break;
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(top->base_class);
-
-        next_field_class = array_fc->elem_fc;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-        /* Variant classes are dynamic: the user should know! */
-        next_field_class = bfcr->user.cbs.query.borrow_variant_selected_field_class(
-            top->base_class, bfcr->user.data);
-        break;
-    default:
-        break;
-    }
-
-    if (!next_field_class) {
-        BT_COMP_LOGW("Cannot get the field class of the next field: "
-                     "bfcr-addr=%p, base-fc-addr=%p, base-fc-type=%d, "
-                     "index=%" PRId64,
-                     bfcr, top->base_class, top->base_class->type, top->index);
-        status = BT_BFCR_STATUS_ERROR;
-        goto end;
-    }
-
-    if (next_field_class->is_compound) {
-        if (bfcr->user.cbs.classes.compound_begin) {
-            BT_COMP_LOGT("Calling user function (compound, begin).");
-            status = bfcr->user.cbs.classes.compound_begin(next_field_class, bfcr->user.data);
-            BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(status));
-            if (status != BT_BFCR_STATUS_OK) {
-                BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s", bfcr,
-                             bt_bfcr_status_string(status));
-                goto end;
-            }
-        }
-
-        ret = stack_push_with_len(bfcr, next_field_class);
-        if (ret) {
-            /* stack_push_with_len() logs errors */
-            status = BT_BFCR_STATUS_ERROR;
-            goto end;
-        }
-
-        /* Next state: align a compound class */
-        bfcr->state = BFCR_STATE_ALIGN_COMPOUND;
-    } else {
-        /* Replace current basic field class */
-        BT_COMP_LOGT("Replacing current basic field class: "
-                     "bfcr-addr=%p, cur-basic-fc-addr=%p, "
-                     "next-basic-fc-addr=%p",
-                     bfcr, bfcr->cur_basic_field_class, next_field_class);
-        bfcr->cur_basic_field_class = next_field_class;
-
-        /* Next state: align a basic class */
-        bfcr->state = BFCR_STATE_ALIGN_BASIC;
-    }
-
-end:
-    return status;
-}
-
-static inline enum bt_bfcr_status handle_state(struct bt_bfcr *bfcr)
-{
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-    BT_COMP_LOGT("Handling state: bfcr-addr=%p, state=%s", bfcr, bfcr_state_string(bfcr->state));
-
-    switch (bfcr->state) {
-    case BFCR_STATE_NEXT_FIELD:
-        status = next_field_state(bfcr);
-        break;
-    case BFCR_STATE_ALIGN_BASIC:
-        status = align_class_state(bfcr, bfcr->cur_basic_field_class, BFCR_STATE_READ_BASIC_BEGIN);
-        break;
-    case BFCR_STATE_ALIGN_COMPOUND:
-        status = align_class_state(bfcr, stack_top(bfcr->stack)->base_class, BFCR_STATE_NEXT_FIELD);
-        break;
-    case BFCR_STATE_READ_BASIC_BEGIN:
-        status = read_basic_begin_state(bfcr);
-        break;
-    case BFCR_STATE_READ_BASIC_CONTINUE:
-        status = read_basic_continue_state(bfcr);
-        break;
-    case BFCR_STATE_DONE:
-        break;
-    }
-
-    BT_COMP_LOGT("Handled state: bfcr-addr=%p, status=%s", bfcr, bt_bfcr_status_string(status));
-    return status;
-}
-
-BT_HIDDEN
-struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data, bt_logging_level log_level,
-                               bt_self_component *self_comp)
-{
-    struct bt_bfcr *bfcr;
-
-    BT_COMP_LOG_CUR_LVL(BT_LOG_DEBUG, log_level, self_comp,
-                        "Creating binary field class reader (BFCR).");
-    bfcr = g_new0(struct bt_bfcr, 1);
-    if (!bfcr) {
-        BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                            "Failed to allocate one binary class reader.");
-        goto end;
-    }
-
-    bfcr->log_level = log_level;
-    bfcr->self_comp = self_comp;
-    bfcr->stack = stack_new(bfcr);
-    if (!bfcr->stack) {
-        BT_COMP_LOGE_STR("Cannot create BFCR's stack.");
-        bt_bfcr_destroy(bfcr);
-        bfcr = NULL;
-        goto end;
-    }
-
-    bfcr->state = BFCR_STATE_NEXT_FIELD;
-    bfcr->user.cbs = cbs;
-    bfcr->user.data = data;
-    BT_COMP_LOGD("Created BFCR: addr=%p", bfcr);
-
-end:
-    return bfcr;
-}
-
-BT_HIDDEN
-void bt_bfcr_destroy(struct bt_bfcr *bfcr)
-{
-    if (bfcr->stack) {
-        stack_destroy(bfcr->stack);
-    }
-
-    BT_COMP_LOGD("Destroying BFCR: addr=%p", bfcr);
-    g_free(bfcr);
-}
-
-static void reset(struct bt_bfcr *bfcr)
-{
-    BT_COMP_LOGD("Resetting BFCR: addr=%p", bfcr);
-    stack_clear(bfcr->stack);
-    stitch_reset(bfcr);
-    bfcr->buf.addr = NULL;
-    bfcr->last_bo = CTF_BYTE_ORDER_UNKNOWN;
-}
-
-static void update_packet_offset(struct bt_bfcr *bfcr)
-{
-    BT_COMP_LOGT("Updating packet offset for next call: "
-                 "bfcr-addr=%p, cur-packet-offset=%zu, next-packet-offset=%zu",
-                 bfcr, bfcr->buf.packet_offset, bfcr->buf.packet_offset + bfcr->buf.at);
-    bfcr->buf.packet_offset += bfcr->buf.at;
-}
-
-BT_HIDDEN
-size_t bt_bfcr_start(struct bt_bfcr *bfcr, struct ctf_field_class *cls, const uint8_t *buf,
-                     size_t offset, size_t packet_offset, size_t sz, enum bt_bfcr_status *status)
-{
-    BT_ASSERT_DBG(bfcr);
-    BT_ASSERT_DBG(BYTES_TO_BITS(sz) >= offset);
-    reset(bfcr);
-    bfcr->buf.addr = buf;
-    bfcr->buf.offset = offset;
-    bfcr->buf.at = 0;
-    bfcr->buf.packet_offset = packet_offset;
-    bfcr->buf.buf_sz = sz;
-    bfcr->buf.sz = BYTES_TO_BITS(sz) - offset;
-    *status = BT_BFCR_STATUS_OK;
-
-    BT_COMP_LOGT("Starting decoding: bfcr-addr=%p, fc-addr=%p, "
-                 "buf-addr=%p, buf-size=%zu, offset=%zu, "
-                 "packet-offset=%zu",
-                 bfcr, cls, buf, sz, offset, packet_offset);
-
-    /* Set root class */
-    if (cls->is_compound) {
-        /* Compound class: push on visit stack */
-        int stack_ret;
-
-        if (bfcr->user.cbs.classes.compound_begin) {
-            BT_COMP_LOGT("Calling user function (compound, begin).");
-            *status = bfcr->user.cbs.classes.compound_begin(cls, bfcr->user.data);
-            BT_COMP_LOGT("User function returned: status=%s", bt_bfcr_status_string(*status));
-            if (*status != BT_BFCR_STATUS_OK) {
-                BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s", bfcr,
-                             bt_bfcr_status_string(*status));
-                goto end;
-            }
-        }
-
-        stack_ret = stack_push_with_len(bfcr, cls);
-        if (stack_ret) {
-            /* stack_push_with_len() logs errors */
-            *status = BT_BFCR_STATUS_ERROR;
-            goto end;
-        }
-
-        bfcr->state = BFCR_STATE_ALIGN_COMPOUND;
-    } else {
-        /* Basic class: set as current basic class */
-        bfcr->cur_basic_field_class = cls;
-        bfcr->state = BFCR_STATE_ALIGN_BASIC;
-    }
-
-    /* Run the machine! */
-    BT_COMP_LOGT_STR("Running the state machine.");
-
-    while (true) {
-        *status = handle_state(bfcr);
-        if (*status != BT_BFCR_STATUS_OK || bfcr->state == BFCR_STATE_DONE) {
-            break;
-        }
-    }
-
-    /* Update packet offset for next time */
-    update_packet_offset(bfcr);
-
-end:
-    return bfcr->buf.at;
-}
-
-BT_HIDDEN
-size_t bt_bfcr_continue(struct bt_bfcr *bfcr, const uint8_t *buf, size_t sz,
-                        enum bt_bfcr_status *status)
-{
-    BT_ASSERT_DBG(bfcr);
-    BT_ASSERT_DBG(buf);
-    BT_ASSERT_DBG(sz > 0);
-    bfcr->buf.addr = buf;
-    bfcr->buf.offset = 0;
-    bfcr->buf.at = 0;
-    bfcr->buf.buf_sz = sz;
-    bfcr->buf.sz = BYTES_TO_BITS(sz);
-    *status = BT_BFCR_STATUS_OK;
-
-    BT_COMP_LOGT("Continuing decoding: bfcr-addr=%p, buf-addr=%p, buf-size=%zu", bfcr, buf, sz);
-
-    /* Continue running the machine */
-    BT_COMP_LOGT_STR("Running the state machine.");
-
-    while (true) {
-        *status = handle_state(bfcr);
-        if (*status != BT_BFCR_STATUS_OK || bfcr->state == BFCR_STATE_DONE) {
-            break;
-        }
-    }
-
-    /* Update packet offset for next time */
-    update_packet_offset(bfcr);
-    return bfcr->buf.at;
-}
-
-BT_HIDDEN
-void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr, bt_bfcr_unsigned_int_cb_func cb)
-{
-    BT_ASSERT_DBG(bfcr);
-    BT_ASSERT_DBG(cb);
-    bfcr->user.cbs.classes.unsigned_int = cb;
-}
diff --git a/src/plugins/ctf/common/bfcr/bfcr.hpp b/src/plugins/ctf/common/bfcr/bfcr.hpp
deleted file mode 100644 (file)
index b67d6bd..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
- * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
- *
- * Babeltrace - CTF binary field class reader (BFCR)
- */
-
-#ifndef CTF_BFCR_H
-#define CTF_BFCR_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-
-#include "../metadata/ctf-meta.hpp"
-
-/**
- * @file bfcr.h
- *
- * Event-driven CTF binary field class reader (BFCR).
- *
- * This is a common, internal API used by CTF source plugins. It allows
- * a binary CTF IR field class to be decoded from user-provided buffers.
- * As the class is decoded (and, possibly, its nested classes),
- * registered user callback functions are called.
- *
- * This API is only concerned with reading one CTF class at a time from
- * one or more buffer of bytes. It does not know CTF dynamic scopes,
- * events, or streams. Sequence lengths and selected variant classes are
- * requested to the user when needed.
- */
-
-/**
- * Binary class reader API status codes.
- */
-enum bt_bfcr_status
-{
-    /** Out of memory. */
-    BT_BFCR_STATUS_ENOMEM = -5,
-    /**
-     * The binary stream reader reached the end of the user-provided
-     * buffer, but data is still needed to finish decoding the
-     * requested class.
-     *
-     * The user needs to call bt_bfcr_continue() as long as
-     * #BT_BFCR_STATUS_EOF is returned to complete the decoding
-     * process of a given class.
-     */
-    BT_BFCR_STATUS_EOF = 1,
-
-    /** Invalid argument. */
-    BT_BFCR_STATUS_INVAL = -3,
-
-    /** General error. */
-    BT_BFCR_STATUS_ERROR = -1,
-
-    /** Everything okay. */
-    BT_BFCR_STATUS_OK = 0,
-};
-
-/** Field class reader. */
-struct bt_bfcr;
-
-typedef enum bt_bfcr_status (*bt_bfcr_unsigned_int_cb_func)(uint64_t, struct ctf_field_class *,
-                                                            void *);
-
-/*
- * Field class reader user callback functions.
- */
-struct bt_bfcr_cbs
-{
-    /**
-     * Field class callback functions.
-     *
-     * This CTF binary class reader is event-driven. The following
-     * functions are called during the decoding process, either when
-     * a compound class begins/ends, or when a basic class is
-     * completely decoded (along with its value).
-     *
-     * Each function also receives the CTF field class associated
-     * with the call, and user data (registered to the class reader
-     * calling them).
-     *
-     * Actual trace IR fields are \em not created here; this would
-     * be the responsibility of a class reader's user (the provider
-     * of those callback functions).
-     *
-     * All the class callback functions return one of the following
-     * values:
-     *
-     *   - <b>#BT_BFCR_STATUS_OK</b>: Everything is okay;
-     *     continue the decoding process.
-     *   - <b>#BT_BFCR_STATUS_ERROR</b>: General error (reported
-     *     to class reader's user).
-     *
-     * Any member of this structure may be set to \c NULL, should
-     * a specific message be not needed.
-     */
-    struct
-    {
-        /**
-         * Called when a signed integer class is completely
-         * decoded. This could also be the supporting signed
-         * integer class of an enumeration class (\p class will
-         * indicate this).
-         *
-         * @param value                Signed integer value
-         * @param class                Integer or enumeration class
-         * @param data         User data
-         * @returns            #BT_BFCR_STATUS_OK or
-         *                     #BT_BFCR_STATUS_ERROR
-         */
-        enum bt_bfcr_status (*signed_int)(int64_t value, struct ctf_field_class *cls, void *data);
-
-        /**
-         * Called when an unsigned integer class is completely
-         * decoded. This could also be the supporting signed
-         * integer class of an enumeration class (\p class will
-         * indicate this).
-         *
-         * @param value                Unsigned integer value
-         * @param class                Integer or enumeration class
-         * @param data         User data
-         * @returns            #BT_BFCR_STATUS_OK or
-         *                     #BT_BFCR_STATUS_ERROR
-         */
-        bt_bfcr_unsigned_int_cb_func unsigned_int;
-
-        /**
-         * Called when a floating point number class is
-         * completely decoded.
-         *
-         * @param value                Floating point number value
-         * @param class                Floating point number class
-         * @param data         User data
-         * @returns            #BT_BFCR_STATUS_OK or
-         *                     #BT_BFCR_STATUS_ERROR
-         */
-        enum bt_bfcr_status (*floating_point)(double value, struct ctf_field_class *cls,
-                                              void *data);
-
-        /**
-         * Called when a string class begins.
-         *
-         * All the following user callback function calls will
-         * be made to bt_bfcr_cbs::classes::string(), each of
-         * them providing one substring of the complete string
-         * class's value.
-         *
-         * @param class                Beginning string class
-         * @param data         User data
-         * @returns            #BT_BFCR_STATUS_OK or
-         *                     #BT_BFCR_STATUS_ERROR
-         */
-        enum bt_bfcr_status (*string_begin)(struct ctf_field_class *cls, void *data);
-
-        /**
-         * Called when a string class's substring is decoded
-         * (between a call to bt_bfcr_cbs::classes::string_begin()
-         * and a call to bt_bfcr_cbs::classes::string_end()).
-         *
-         * @param value                String value (\em not null-terminated)
-         * @param len          String value length
-         * @param class                String class
-         * @param data         User data
-         * @returns            #BT_BFCR_STATUS_OK or
-         *                     #BT_BFCR_STATUS_ERROR
-         */
-        enum bt_bfcr_status (*string)(const char *value, size_t len, struct ctf_field_class *cls,
-                                      void *data);
-
-        /**
-         * Called when a string class ends.
-         *
-         * @param class                Ending string class
-         * @param data         User data
-         * @returns            #BT_BFCR_STATUS_OK or
-         *                     #BT_BFCR_STATUS_ERROR
-         */
-        enum bt_bfcr_status (*string_end)(struct ctf_field_class *cls, void *data);
-
-        /**
-         * Called when a compound class begins.
-         *
-         * All the following class callback function calls will
-         * signal sequential elements of this compound class,
-         * until the next corresponding
-         * bt_bfcr_cbs::classes::compound_end() is called.
-         *
-         * If \p class is a variant class, then only one class
-         * callback function call will follow before the call to
-         * bt_bfcr_cbs::classes::compound_end(). This single
-         * call indicates the selected class of this variant
-         * class.
-         *
-         * @param class                Beginning compound class
-         * @param data         User data
-         * @returns            #BT_BFCR_STATUS_OK or
-         *                     #BT_BFCR_STATUS_ERROR
-         */
-        enum bt_bfcr_status (*compound_begin)(struct ctf_field_class *cls, void *data);
-
-        /**
-         * Called when a compound class ends.
-         *
-         * @param class                Ending compound class
-         * @param data         User data
-         * @returns            #BT_BFCR_STATUS_OK or
-         *                     #BT_BFCR_STATUS_ERROR
-         */
-        enum bt_bfcr_status (*compound_end)(struct ctf_field_class *cls, void *data);
-    } classes;
-
-    /**
-     * Query callback functions are used when the class reader needs
-     * dynamic information, i.e. a sequence class's current length
-     * or a variant class's current selected class.
-     *
-     * Both functions need to be set unless it is known that no
-     * sequences or variants will have to be decoded.
-     */
-    struct
-    {
-        /**
-         * Called to query the current length of a given sequence
-         * class.
-         *
-         * @param class                Sequence class
-         * @param data         User data
-         * @returns            Sequence length or
-         *                     #BT_BFCR_STATUS_ERROR on error
-         */
-        int64_t (*get_sequence_length)(struct ctf_field_class *cls, void *data);
-
-        /**
-         * Called to query the current selected class of a given
-         * variant class.
-         *
-         * @param class                Variant class
-         * @param data         User data
-         * @returns            Current selected class (owned by
-         *                     this) or \c NULL on error
-         */
-        struct ctf_field_class *(*borrow_variant_selected_field_class)(struct ctf_field_class *cls,
-                                                                       void *data);
-    } query;
-};
-
-/**
- * Creates a CTF binary class reader.
- *
- * @param cbs          User callback functions
- * @param data         User data (passed to user callback functions)
- * @returns            New binary class reader on success, or \c NULL on error
- */
-BT_HIDDEN
-struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data, bt_logging_level log_level,
-                               bt_self_component *self_comp);
-
-/**
- * Destroys a CTF binary class reader, freeing all internal resources.
- *
- * @param bfcr Binary class reader
- */
-BT_HIDDEN
-void bt_bfcr_destroy(struct bt_bfcr *bfcr);
-
-/**
- * Decodes a given CTF class from a buffer of bytes.
- *
- * The number of \em bits consumed by this function is returned.
- *
- * The \p status output parameter is where a status is written, amongst
- * the following:
- *
- *   - <b>#BT_BFCR_STATUS_OK</b>: Decoding is done.
- *   - <b>#BT_BFCR_STATUS_EOF</b>: The end of the buffer was reached,
- *     but more data is needed to finish the decoding process of the
- *     requested class. The user needs to call bt_bfcr_continue()
- *     as long as #BT_BFCR_STATUS_EOF is returned to complete the
- *     decoding process of the original class.
- *   - <b>#BT_BFCR_STATUS_INVAL</b>: Invalid argument.
- *   - <b>#BT_BFCR_STATUS_ERROR</b>: General error.
- *
- * Calling this function resets the class reader's internal state. If
- * #BT_BFCR_STATUS_EOF is returned, bt_bfcr_continue() needs to
- * be called next, \em not bt_bfcr_decode().
- *
- * @param bfcr                 Binary class reader
- * @param class                        Field class to decode
- * @param buf                  Buffer
- * @param offset               Offset of first bit from \p buf (bits)
- * @param packet_offset                Offset of \p offset within the CTF
- *                             binary packet containing \p class (bits)
- * @param sz                   Size of buffer in bytes (from \p buf)
- * @param status               Returned status (see description above)
- * @returns                    Number of consumed bits
- */
-BT_HIDDEN
-size_t bt_bfcr_start(struct bt_bfcr *bfcr, struct ctf_field_class *cls, const uint8_t *buf,
-                     size_t offset, size_t packet_offset, size_t sz, enum bt_bfcr_status *status);
-
-/**
- * Continues the decoding process a given CTF class.
- *
- * The number of bits consumed by this function is returned.
- *
- * The \p status output parameter is where a status is placed, amongst
- * the following:
- *
- *   - <b>#BT_BFCR_STATUS_OK</b>: decoding is done.
- *   - <b>#BT_BFCR_STATUS_EOF</b>: the end of the buffer was reached,
- *     but more data is needed to finish the decoding process of the
- *     requested class. The user needs to call bt_bfcr_continue()
- *     as long as #BT_BFCR_STATUS_EOF is returned to complete the
- *     decoding process of the original class.
- *   - <b>#BT_BFCR_STATUS_INVAL</b>: invalid argument.
- *   - <b>#BT_BFCR_STATUS_ERROR</b>: general error.
- *
- * @param bfcr         Binary class reader
- * @param buf          Buffer
- * @param sz           Size of buffer in bytes (from \p offset)
- * @param status       Returned status (see description above)
- * @returns            Number of consumed bits
- */
-BT_HIDDEN
-size_t bt_bfcr_continue(struct bt_bfcr *bfcr, const uint8_t *buf, size_t sz,
-                        enum bt_bfcr_status *status);
-
-BT_HIDDEN
-void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr, bt_bfcr_unsigned_int_cb_func cb);
-
-static inline const char *bt_bfcr_status_string(enum bt_bfcr_status status)
-{
-    switch (status) {
-    case BT_BFCR_STATUS_ENOMEM:
-        return "ENOMEM";
-    case BT_BFCR_STATUS_EOF:
-        return "EOF";
-    case BT_BFCR_STATUS_INVAL:
-        return "INVAL";
-    case BT_BFCR_STATUS_ERROR:
-        return "ERROR";
-    case BT_BFCR_STATUS_OK:
-        return "OK";
-    }
-
-    bt_common_abort();
-}
-
-#endif /* CTF_BFCR_H */
diff --git a/src/plugins/ctf/common/bfcr/btr.gdb b/src/plugins/ctf/common/bfcr/btr.gdb
deleted file mode 100644 (file)
index 71cc0f6..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-define ctf-btr-show-stack
-    if (stack_empty($arg0))
-        printf "stack is empty!\n"
-    else
-        set $stack_size = stack_size($arg0)
-        set $stack_at = (int) ($stack_size - 1)
-        printf "%3s    %10s   %4s    %3s\n", "pos", "base addr", "blen", "idx"
-
-        while ($stack_at >= 0)
-            set $stack_entry = (struct stack_entry *) g_ptr_array_index($arg0->entries, $stack_at)
-
-            if ($stack_at == $stack_size - 1)
-                printf "%3d    %10p    %3d    %3d  <-- top\n", $stack_at, \
-                    $stack_entry->base_class, $stack_entry->base_len, \
-                    $stack_entry->index
-            else
-                printf "%3d    %10p    %3d    %3d\n", $stack_at, \
-                    $stack_entry->base_class, $stack_entry->base_len, \
-                    $stack_entry->index
-            end
-            set $stack_at = $stack_at - 1
-        end
-    end
-end
diff --git a/src/plugins/ctf/common/metadata/Makefile.am b/src/plugins/ctf/common/metadata/Makefile.am
deleted file mode 100644 (file)
index 7d843d6..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-AM_CPPFLAGS += -I$(builddir) -I$(srcdir)
-AM_YFLAGS = -t -d -v -Wno-yacc
-
-noinst_LTLIBRARIES = libctf-parser.la libctf-ast.la
-
-BUILT_SOURCES = parser.hpp
-
-libctf_parser_la_SOURCES = lexer.lpp parser.ypp objstack.cpp
-# scanner-symbols.h is included to prefix generated yy_* symbols
-# with bt_.
-libctf_parser_la_CPPFLAGS = $(AM_CPPFLAGS) \
-               -include $(srcdir)/scanner-symbols.hpp
-
-# This library contains (mostly) generated code, silence some warnings that it
-# produces.
-libctf_parser_la_CXXFLAGS = $(AM_CXXFLAGS) \
-       -Wno-unused-function \
-       -Wno-null-dereference
-
-libctf_ast_la_SOURCES = \
-       visitor-generate-ir.cpp \
-       visitor-semantic-validator.cpp \
-       visitor-parent-links.cpp \
-       ast.hpp \
-       objstack.hpp \
-       parser.hpp \
-       parser-wrap.hpp \
-       scanner.hpp \
-       scanner-symbols.hpp \
-       decoder.cpp \
-       decoder.hpp \
-       decoder-packetized-file-stream-to-buf.cpp \
-       decoder-packetized-file-stream-to-buf.hpp \
-       logging.cpp \
-       logging.hpp \
-       ctf-meta.hpp \
-       ctf-meta-visitors.hpp \
-       ctf-meta-validate.cpp \
-       ctf-meta-update-meanings.cpp \
-       ctf-meta-update-in-ir.cpp \
-       ctf-meta-update-default-clock-classes.cpp \
-       ctf-meta-update-text-array-sequence.cpp \
-       ctf-meta-update-alignments.cpp \
-       ctf-meta-update-value-storing-indexes.cpp \
-       ctf-meta-update-stream-class-config.cpp \
-       ctf-meta-warn-meaningless-header-fields.cpp \
-       ctf-meta-translate.cpp \
-       ctf-meta-resolve.cpp \
-       ctf-meta-configure-ir-trace.cpp \
-       ctf-meta-configure-ir-trace.hpp
-
-if BABELTRACE_BUILD_WITH_MINGW
-libctf_ast_la_LIBADD = -lintl -liconv -lole32
-endif
-
-# start with empty files to clean
-CLEANFILES =
-
-if HAVE_BISON
-# we have bison: we can clean the generated parser files
-CLEANFILES += parser.cpp parser.hpp parser.output
-else # HAVE_BISON
-# create target used to stop the build if we want to build the parser,
-# but we don't have the necessary tool to do so
-ERR_MSG = "Error: Cannot build target because bison is missing."
-ERR_MSG += "Make sure bison is installed and run the configure script again."
-
-parser.cpp parser.hpp: parser.ypp
-       @echo $(ERR_MSG)
-       @false
-
-all-local: parser.cpp parser.hpp
-endif # HAVE_BISON
-
-if HAVE_FLEX
-# we have flex: we can clean the generated lexer files
-CLEANFILES += lexer.cpp
-else # HAVE_FLEX
-# create target used to stop the build if we want to build the lexer,
-# but we don't have the necessary tool to do so
-ERR_MSG = "Error: Cannot build target because flex is missing."
-ERR_MSG += "Make sure flex is installed and run the configure script again."
-
-filter-lexer.cpp: lexer.lpp
-       @echo $(ERR_MSG)
-       @false
-
-all-local: lexer.cpp
-endif # HAVE_FLEX
diff --git a/src/plugins/ctf/common/metadata/ast.hpp b/src/plugins/ctf/common/metadata/ast.hpp
deleted file mode 100644 (file)
index 1a70b56..0000000
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#ifndef _CTF_AST_H
-#define _CTF_AST_H
-
-#include <stdint.h>
-#include <stdio.h>
-#include <glib.h>
-#include "common/list.h"
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-
-#include "decoder.hpp"
-#include "ctf-meta.hpp"
-
-// the parameter name (of the reentrant 'yyparse' function)
-// data is a pointer to a 'SParserParam' structure
-//#define YYPARSE_PARAM        scanner
-
-struct ctf_node;
-struct ctf_parser;
-struct ctf_visitor_generate_ir;
-
-#define EINCOMPLETE 1000
-
-#define FOREACH_CTF_NODES(F)                                                                       \
-    F(NODE_UNKNOWN)                                                                                \
-    F(NODE_ROOT)                                                                                   \
-    F(NODE_ERROR)                                                                                  \
-    F(NODE_EVENT)                                                                                  \
-    F(NODE_STREAM)                                                                                 \
-    F(NODE_ENV)                                                                                    \
-    F(NODE_TRACE)                                                                                  \
-    F(NODE_CLOCK)                                                                                  \
-    F(NODE_CALLSITE)                                                                               \
-    F(NODE_CTF_EXPRESSION)                                                                         \
-    F(NODE_UNARY_EXPRESSION)                                                                       \
-    F(NODE_TYPEDEF)                                                                                \
-    F(NODE_TYPEALIAS_TARGET)                                                                       \
-    F(NODE_TYPEALIAS_ALIAS)                                                                        \
-    F(NODE_TYPEALIAS)                                                                              \
-    F(NODE_TYPE_SPECIFIER)                                                                         \
-    F(NODE_TYPE_SPECIFIER_LIST)                                                                    \
-    F(NODE_POINTER)                                                                                \
-    F(NODE_TYPE_DECLARATOR)                                                                        \
-    F(NODE_FLOATING_POINT)                                                                         \
-    F(NODE_INTEGER)                                                                                \
-    F(NODE_STRING)                                                                                 \
-    F(NODE_ENUMERATOR)                                                                             \
-    F(NODE_ENUM)                                                                                   \
-    F(NODE_STRUCT_OR_VARIANT_DECLARATION)                                                          \
-    F(NODE_VARIANT)                                                                                \
-    F(NODE_STRUCT)
-
-enum node_type
-{
-#define ENTRY(S) S,
-    FOREACH_CTF_NODES(ENTRY)
-#undef ENTRY
-};
-
-enum ctf_unary
-{
-    UNARY_UNKNOWN = 0,
-    UNARY_STRING,
-    UNARY_SIGNED_CONSTANT,
-    UNARY_UNSIGNED_CONSTANT,
-    UNARY_SBRAC,
-};
-
-enum ctf_unary_link
-{
-    UNARY_LINK_UNKNOWN = 0,
-    UNARY_DOTLINK,
-    UNARY_ARROWLINK,
-    UNARY_DOTDOTDOT,
-};
-
-enum ctf_typedec
-{
-    TYPEDEC_UNKNOWN = 0,
-    TYPEDEC_ID,     /* identifier */
-    TYPEDEC_NESTED, /* (), array or sequence */
-};
-
-enum ctf_typespec
-{
-    TYPESPEC_UNKNOWN = 0,
-    TYPESPEC_VOID,
-    TYPESPEC_CHAR,
-    TYPESPEC_SHORT,
-    TYPESPEC_INT,
-    TYPESPEC_LONG,
-    TYPESPEC_FLOAT,
-    TYPESPEC_DOUBLE,
-    TYPESPEC_SIGNED,
-    TYPESPEC_UNSIGNED,
-    TYPESPEC_BOOL,
-    TYPESPEC_COMPLEX,
-    TYPESPEC_IMAGINARY,
-    TYPESPEC_CONST,
-    TYPESPEC_ID_TYPE,
-    TYPESPEC_FLOATING_POINT,
-    TYPESPEC_INTEGER,
-    TYPESPEC_STRING,
-    TYPESPEC_STRUCT,
-    TYPESPEC_VARIANT,
-    TYPESPEC_ENUM,
-};
-
-struct ctf_node
-{
-    /*
-     * Parent node is only set on demand by specific visitor.
-     */
-    struct ctf_node *parent;
-    struct bt_list_head siblings;
-    struct bt_list_head tmp_head;
-    unsigned int lineno;
-    /*
-     * We mark nodes visited in the generate-ir phase (last
-     * phase). We only mark the 1-depth level nodes as visited
-     * (never the root node, and not their sub-nodes). This allows
-     * skipping already visited nodes when doing incremental
-     * metadata append.
-     */
-    int visited;
-
-    enum node_type type;
-    union
-    {
-        struct
-        {
-        } unknown;
-        struct
-        {
-            /*
-             * Children nodes are ctf_expression, field_class_def,
-             * field_class_alias and field_class_specifier_list.
-             */
-            struct bt_list_head declaration_list;
-            struct bt_list_head trace;
-            struct bt_list_head env;
-            struct bt_list_head stream;
-            struct bt_list_head event;
-            struct bt_list_head clock;
-            struct bt_list_head callsite;
-        } root;
-        struct
-        {
-            /*
-             * Children nodes are ctf_expression, field_class_def,
-             * field_class_alias and field_class_specifier_list.
-             */
-            struct bt_list_head declaration_list;
-        } event;
-        struct
-        {
-            /*
-             * Children nodes are ctf_expression, field_class_def,
-             * field_class_alias and field_class_specifier_list.
-             */
-            struct bt_list_head declaration_list;
-        } stream;
-        struct
-        {
-            /*
-             * Children nodes are ctf_expression, field_class_def,
-             * field_class_alias and field_class_specifier_list.
-             */
-            struct bt_list_head declaration_list;
-        } env;
-        struct
-        {
-            /*
-             * Children nodes are ctf_expression, field_class_def,
-             * field_class_alias and field_class_specifier_list.
-             */
-            struct bt_list_head declaration_list;
-        } trace;
-        struct
-        {
-            /*
-             * Children nodes are ctf_expression, field_class_def,
-             * field_class_alias and field_class_specifier_list.
-             */
-            struct bt_list_head declaration_list;
-        } clock;
-        struct
-        {
-            /*
-             * Children nodes are ctf_expression, field_class_def,
-             * field_class_alias and field_class_specifier_list.
-             */
-            struct bt_list_head declaration_list;
-        } callsite;
-        struct
-        {
-            struct bt_list_head left;  /* Should be string */
-            struct bt_list_head right; /* Unary exp. or type */
-        } ctf_expression;
-        struct
-        {
-            ctf_unary type;
-            union
-            {
-                /*
-                 * string for identifier, id_type, keywords,
-                 * string literals and character constants.
-                 */
-                char *string;
-                int64_t signed_constant;
-                uint64_t unsigned_constant;
-                struct ctf_node *sbrac_exp;
-            } u;
-            ctf_unary_link link;
-        } unary_expression;
-        struct
-        {
-            struct ctf_node *field_class_specifier_list;
-            struct bt_list_head field_class_declarators;
-        } field_class_def;
-        /* new type is "alias", existing type "target" */
-        struct
-        {
-            struct ctf_node *field_class_specifier_list;
-            struct bt_list_head field_class_declarators;
-        } field_class_alias_target;
-        struct
-        {
-            struct ctf_node *field_class_specifier_list;
-            struct bt_list_head field_class_declarators;
-        } field_class_alias_name;
-        struct
-        {
-            struct ctf_node *target;
-            struct ctf_node *alias;
-        } field_class_alias;
-        struct
-        {
-            ctf_typespec type;
-            /* For struct, variant and enum */
-            struct ctf_node *node;
-            const char *id_type;
-        } field_class_specifier;
-        struct
-        {
-            /* list of field_class_specifier */
-            struct bt_list_head head;
-        } field_class_specifier_list;
-        struct
-        {
-            unsigned int const_qualifier;
-        } pointer;
-        struct
-        {
-            struct bt_list_head pointers;
-            ctf_typedec type;
-            union
-            {
-                char *id;
-                struct
-                {
-                    /* typedec has no pointer list */
-                    struct ctf_node *field_class_declarator;
-                    /*
-                     * unary expression (value) or
-                     * field_class_specifier_list.
-                     */
-                    struct bt_list_head length;
-                    /* for abstract type declarator */
-                    unsigned int abstract_array;
-                } nested;
-            } u;
-            struct ctf_node *bitfield_len;
-        } field_class_declarator;
-        struct
-        {
-            /* Children nodes are ctf_expression. */
-            struct bt_list_head expressions;
-        } floating_point;
-        struct
-        {
-            /* Children nodes are ctf_expression. */
-            struct bt_list_head expressions;
-        } integer;
-        struct
-        {
-            /* Children nodes are ctf_expression. */
-            struct bt_list_head expressions;
-        } string;
-        struct
-        {
-            char *id;
-            /*
-             * Range list or single value node. Contains unary
-             * expressions.
-             */
-            struct bt_list_head values;
-        } enumerator;
-        struct
-        {
-            char *enum_id;
-            /*
-             * Either NULL, or points to unary expression or
-             * field_class_specifier_list.
-             */
-            struct ctf_node *container_field_class;
-            struct bt_list_head enumerator_list;
-            int has_body;
-        } _enum;
-        struct
-        {
-            struct ctf_node *field_class_specifier_list;
-            struct bt_list_head field_class_declarators;
-        } struct_or_variant_declaration;
-        struct
-        {
-            char *name;
-            char *choice;
-            /*
-             * list of field_class_def, field_class_alias and
-             * declarations
-             */
-            struct bt_list_head declaration_list;
-            int has_body;
-        } variant;
-        struct
-        {
-            char *name;
-            /*
-             * list of field_class_def, field_class_alias and
-             * declarations
-             */
-            struct bt_list_head declaration_list;
-            int has_body;
-            struct bt_list_head min_align; /* align() attribute */
-        } _struct;
-    } u;
-};
-
-struct ctf_ast
-{
-    struct ctf_node root;
-};
-
-const char *node_type(struct ctf_node *node);
-
-struct meta_log_config;
-
-BT_HIDDEN
-struct ctf_visitor_generate_ir *
-ctf_visitor_generate_ir_create(const struct ctf_metadata_decoder_config *config);
-
-void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor);
-
-BT_HIDDEN
-bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class(struct ctf_visitor_generate_ir *visitor);
-
-BT_HIDDEN
-struct ctf_trace_class *
-ctf_visitor_generate_ir_borrow_ctf_trace_class(struct ctf_visitor_generate_ir *visitor);
-
-BT_HIDDEN
-int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *visitor,
-                                       struct ctf_node *node);
-
-BT_HIDDEN
-int ctf_visitor_semantic_check(int depth, struct ctf_node *node, struct meta_log_config *log_cfg);
-
-BT_HIDDEN
-int ctf_visitor_parent_links(int depth, struct ctf_node *node, struct meta_log_config *log_cfg);
-
-static inline char *ctf_ast_concatenate_unary_strings(struct bt_list_head *head)
-{
-    int i = 0;
-    GString *str;
-    struct ctf_node *node;
-
-    str = g_string_new(NULL);
-    BT_ASSERT(str);
-
-    bt_list_for_each_entry (node, head, siblings) {
-        char *src_string;
-
-        if (node->type != NODE_UNARY_EXPRESSION || node->u.unary_expression.type != UNARY_STRING ||
-            !((node->u.unary_expression.link != UNARY_LINK_UNKNOWN) ^ (i == 0))) {
-            goto error;
-        }
-
-        switch (node->u.unary_expression.link) {
-        case UNARY_DOTLINK:
-            g_string_append(str, ".");
-            break;
-        case UNARY_ARROWLINK:
-            g_string_append(str, "->");
-            break;
-        case UNARY_DOTDOTDOT:
-            g_string_append(str, "...");
-            break;
-        default:
-            break;
-        }
-
-        src_string = node->u.unary_expression.u.string;
-        g_string_append(str, src_string);
-        i++;
-    }
-
-    /* Destroys the container, returns the underlying string */
-    return g_string_free(str, FALSE);
-
-error:
-    /* This always returns NULL */
-    return g_string_free(str, TRUE);
-}
-
-static inline int ctf_ast_get_unary_uuid(struct bt_list_head *head, bt_uuid_t uuid, int log_level,
-                                         bt_self_component *self_comp)
-{
-    int i = 0;
-    int ret = 0;
-    struct ctf_node *node;
-
-    bt_list_for_each_entry (node, head, siblings) {
-        int uexpr_type = node->u.unary_expression.type;
-        int uexpr_link = node->u.unary_expression.link;
-        const char *src_string;
-
-        if (node->type != NODE_UNARY_EXPRESSION || uexpr_type != UNARY_STRING ||
-            uexpr_link != UNARY_LINK_UNKNOWN || i != 0) {
-            ret = -EINVAL;
-            goto end;
-        }
-
-        src_string = node->u.unary_expression.u.string;
-        ret = bt_uuid_from_str(src_string, uuid);
-        if (ret) {
-#ifdef BT_COMP_LOG_CUR_LVL
-            BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                                "Cannot parse UUID: uuid=\"%s\"", src_string);
-#endif
-            goto end;
-        }
-    }
-
-end:
-    return ret;
-}
-
-#endif /* _CTF_AST_H */
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.cpp b/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.cpp
deleted file mode 100644 (file)
index fbda3d7..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-
-#include "ctf-meta-configure-ir-trace.hpp"
-
-BT_HIDDEN
-int ctf_trace_class_configure_ir_trace(struct ctf_trace_class *tc, bt_trace *ir_trace)
-{
-    int ret = 0;
-    uint64_t i;
-
-    BT_ASSERT(tc);
-    BT_ASSERT(ir_trace);
-
-    if (tc->is_uuid_set) {
-        bt_trace_set_uuid(ir_trace, tc->uuid);
-    }
-
-    for (i = 0; i < tc->env_entries->len; i++) {
-        struct ctf_trace_class_env_entry *env_entry =
-            ctf_trace_class_borrow_env_entry_by_index(tc, i);
-
-        switch (env_entry->type) {
-        case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT:
-            ret = bt_trace_set_environment_entry_integer(ir_trace, env_entry->name->str,
-                                                         env_entry->value.i);
-            break;
-        case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR:
-            ret = bt_trace_set_environment_entry_string(ir_trace, env_entry->name->str,
-                                                        env_entry->value.str->str);
-            break;
-        default:
-            bt_common_abort();
-        }
-
-        if (ret) {
-            goto end;
-        }
-    }
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.hpp b/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.hpp
deleted file mode 100644 (file)
index af84fc8..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef _CTF_META_CONFIGURE_IR_TRACE_H
-#define _CTF_META_CONFIGURE_IR_TRACE_H
-
-#include "common/macros.h"
-
-#include "ctf-meta.hpp"
-
-BT_HIDDEN
-int ctf_trace_class_configure_ir_trace(struct ctf_trace_class *tc, bt_trace *ir_trace);
-
-#endif /* _CTF_META_CONFIGURE_IR_TRACE_H */
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-resolve.cpp b/src/plugins/ctf/common/metadata/ctf-meta-resolve.cpp
deleted file mode 100644 (file)
index 06fa55f..0000000
+++ /dev/null
@@ -1,1263 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016-2018 Philippe Proulx <pproulx@efficios.com>
- * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP       (ctx->self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (ctx->self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL         (ctx->log_level)
-#define BT_LOG_TAG                  "PLUGIN/CTF/META/RESOLVE"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include "common/common.h"
-#include <glib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <glib.h>
-
-#include "ctf-meta-visitors.hpp"
-#include "logging.hpp"
-
-using field_class_stack_t = GPtrArray;
-
-/*
- * A stack frame.
- *
- * `fc` contains a compound field class (structure, variant, array,
- * or sequence) and `index` indicates the index of the field class in
- * the upper frame (-1 for array and sequence field classes). `name`
- * indicates the name of the field class in the upper frame (empty
- * string for array and sequence field classes).
- */
-struct field_class_stack_frame
-{
-    struct ctf_field_class *fc;
-    int64_t index;
-};
-
-/*
- * The current context of the resolving engine.
- */
-struct resolve_context
-{
-    bt_logging_level log_level;
-
-    /* Weak, exactly one of these must be set */
-    bt_self_component *self_comp;
-    bt_self_component_class *self_comp_class;
-
-    struct ctf_trace_class *tc;
-    struct ctf_stream_class *sc;
-    struct ctf_event_class *ec;
-
-    struct
-    {
-        struct ctf_field_class *packet_header;
-        struct ctf_field_class *packet_context;
-        struct ctf_field_class *event_header;
-        struct ctf_field_class *event_common_context;
-        struct ctf_field_class *event_spec_context;
-        struct ctf_field_class *event_payload;
-    } scopes;
-
-    /* Root scope being visited */
-    enum ctf_scope root_scope;
-    field_class_stack_t *field_class_stack;
-    struct ctf_field_class *cur_fc;
-};
-
-/* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */
-static const char * const absolute_path_prefixes[] = {
-    /* CTF_SCOPE_PACKET_HEADER */ "trace.packet.header.",
-    /* CTF_SCOPE_PACKET_CONTEXT */ "stream.packet.context.",
-    /* CTF_SCOPE_EVENT_HEADER */ "stream.event.header.",
-    /* CTF_SCOPE_EVENT_COMMON_CONTEXT */ "stream.event.context.",
-    /* CTF_SCOPE_EVENT_SPECIFIC_CONTEXT */ "event.context.",
-    /* CTF_SCOPE_EVENT_PAYLOAD */ "event.fields.",
-};
-
-/* Number of path tokens used for the absolute prefixes */
-static const uint64_t absolute_path_prefix_ptoken_counts[] = {
-    /* CTF_SCOPE_PACKET_HEADER */ 3,
-    /* CTF_SCOPE_PACKET_CONTEXT */ 3,
-    /* CTF_SCOPE_EVENT_HEADER */ 3,
-    /* CTF_SCOPE_EVENT_COMMON_CONTEXT */ 3,
-    /* CTF_SCOPE_EVENT_SPECIFIC_CONTEXT */ 2,
-    /* CTF_SCOPE_EVENT_PAYLOAD */ 2,
-};
-
-static void destroy_field_class_stack_frame(struct field_class_stack_frame *frame)
-{
-    if (!frame) {
-        return;
-    }
-
-    g_free(frame);
-}
-
-/*
- * Creates a class stack.
- */
-static field_class_stack_t *field_class_stack_create(void)
-{
-    return g_ptr_array_new_with_free_func((GDestroyNotify) destroy_field_class_stack_frame);
-}
-
-/*
- * Destroys a class stack.
- */
-static void field_class_stack_destroy(field_class_stack_t *stack)
-{
-    if (stack) {
-        g_ptr_array_free(stack, TRUE);
-    }
-}
-
-/*
- * Pushes a field class onto a class stack.
- */
-static int field_class_stack_push(field_class_stack_t *stack, struct ctf_field_class *fc,
-                                  struct resolve_context *ctx)
-{
-    int ret = 0;
-    struct field_class_stack_frame *frame = NULL;
-
-    if (!stack || !fc) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Invalid parameter: stack or field class is `NULL`.");
-        ret = -1;
-        goto end;
-    }
-
-    frame = g_new0(struct field_class_stack_frame, 1);
-    if (!frame) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one field class stack frame.");
-        ret = -1;
-        goto end;
-    }
-
-    BT_COMP_LOGD("Pushing field class on context's stack: "
-                 "fc-addr=%p, stack-size-before=%u",
-                 fc, stack->len);
-    frame->fc = fc;
-    g_ptr_array_add(stack, frame);
-
-end:
-    return ret;
-}
-
-/*
- * Checks whether or not `stack` is empty.
- */
-static bool field_class_stack_empty(field_class_stack_t *stack)
-{
-    return stack->len == 0;
-}
-
-/*
- * Returns the number of frames in `stack`.
- */
-static size_t field_class_stack_size(field_class_stack_t *stack)
-{
-    return stack->len;
-}
-
-/*
- * Returns the top frame of `stack`.
- */
-static struct field_class_stack_frame *field_class_stack_peek(field_class_stack_t *stack)
-{
-    BT_ASSERT(stack);
-    BT_ASSERT(!field_class_stack_empty(stack));
-
-    return (field_class_stack_frame *) g_ptr_array_index(stack, stack->len - 1);
-}
-
-/*
- * Returns the frame at index `index` in `stack`.
- */
-static struct field_class_stack_frame *field_class_stack_at(field_class_stack_t *stack,
-                                                            size_t index)
-{
-    BT_ASSERT(stack);
-    BT_ASSERT(index < stack->len);
-
-    return (field_class_stack_frame *) g_ptr_array_index(stack, index);
-}
-
-/*
- * Removes the top frame of `stack`.
- */
-static void field_class_stack_pop(field_class_stack_t *stack, struct resolve_context *ctx)
-{
-    if (!field_class_stack_empty(stack)) {
-        /*
-         * This will call the frame's destructor and free it, as
-         * well as put its contained field class.
-         */
-        BT_COMP_LOGD("Popping context's stack: stack-size-before=%u", stack->len);
-        g_ptr_array_set_size(stack, stack->len - 1);
-    }
-}
-
-/*
- * Returns the scope field class of `scope` in the context `ctx`.
- */
-static struct ctf_field_class *borrow_class_from_ctx(struct resolve_context *ctx,
-                                                     enum ctf_scope scope)
-{
-    switch (scope) {
-    case CTF_SCOPE_PACKET_HEADER:
-        return ctx->scopes.packet_header;
-    case CTF_SCOPE_PACKET_CONTEXT:
-        return ctx->scopes.packet_context;
-    case CTF_SCOPE_EVENT_HEADER:
-        return ctx->scopes.event_header;
-    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-        return ctx->scopes.event_common_context;
-    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-        return ctx->scopes.event_spec_context;
-    case CTF_SCOPE_EVENT_PAYLOAD:
-        return ctx->scopes.event_payload;
-    default:
-        bt_common_abort();
-    }
-
-    return NULL;
-}
-
-/*
- * Returns the CTF scope from a path string. May return -1 if the path
- * is found to be relative.
- */
-static enum ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr,
-                                                           struct resolve_context *ctx)
-{
-    enum ctf_scope scope;
-    enum ctf_scope ret = CTF_SCOPE_PACKET_UNKNOWN;
-    const size_t prefixes_count = sizeof(absolute_path_prefixes) / sizeof(*absolute_path_prefixes);
-
-    for (scope = CTF_SCOPE_PACKET_HEADER; scope < CTF_SCOPE_PACKET_HEADER + prefixes_count;
-         scope = (ctf_scope) (scope + 1)) {
-        /*
-         * Check if path string starts with a known absolute
-         * path prefix.
-         *
-         * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES.
-         */
-        if (strncmp(pathstr, absolute_path_prefixes[scope],
-                    strlen(absolute_path_prefixes[scope]))) {
-            /* Prefix does not match: try the next one */
-            BT_COMP_LOGD("Prefix does not match: trying the next one: "
-                         "path=\"%s\", path-prefix=\"%s\", scope=%s",
-                         pathstr, absolute_path_prefixes[scope], ctf_scope_string(scope));
-            continue;
-        }
-
-        /* Found it! */
-        ret = scope;
-        BT_COMP_LOGD("Found root scope from absolute path: "
-                     "path=\"%s\", scope=%s",
-                     pathstr, ctf_scope_string(scope));
-        goto end;
-    }
-
-end:
-    return ret;
-}
-
-/*
- * Destroys a path token.
- */
-static void ptokens_destroy_func(gpointer ptoken, gpointer data)
-{
-    g_string_free((GString *) ptoken, TRUE);
-}
-
-/*
- * Destroys a path token list.
- */
-static void ptokens_destroy(GList *ptokens)
-{
-    if (!ptokens) {
-        return;
-    }
-
-    g_list_foreach(ptokens, ptokens_destroy_func, NULL);
-    g_list_free(ptokens);
-}
-
-/*
- * Returns the string contained in a path token.
- */
-static const char *ptoken_get_string(GList *ptoken)
-{
-    GString *tokenstr = (GString *) ptoken->data;
-
-    return tokenstr->str;
-}
-
-/*
- * Converts a path string to a path token list, that is, splits the
- * individual words of a path string into a list of individual
- * strings.
- */
-static GList *pathstr_to_ptokens(const char *pathstr, struct resolve_context *ctx)
-{
-    const char *at = pathstr;
-    const char *last = at;
-    GList *ptokens = NULL;
-
-    for (;;) {
-        if (*at == '.' || *at == '\0') {
-            GString *tokenstr;
-
-            if (at == last) {
-                /* Error: empty token */
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Empty path token: path=\"%s\", pos=%u",
-                                                         pathstr, (unsigned int) (at - pathstr));
-                goto error;
-            }
-
-            tokenstr = g_string_new(NULL);
-            g_string_append_len(tokenstr, last, at - last);
-            ptokens = g_list_append(ptokens, tokenstr);
-            last = at + 1;
-        }
-
-        if (*at == '\0') {
-            break;
-        }
-
-        at++;
-    }
-
-    return ptokens;
-
-error:
-    ptokens_destroy(ptokens);
-    return NULL;
-}
-
-/*
- * Converts a path token list to a field path object. The path token
- * list is relative from `fc`. The index of the source looking for its
- * target within `fc` is indicated by `src_index`. This can be
- * `INT64_MAX` if the source is contained in `fc`.
- *
- * `field_path` is an output parameter owned by the caller that must be
- * filled here.
- */
-static int ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
-                                 struct ctf_field_class *fc, int64_t src_index,
-                                 struct resolve_context *ctx)
-{
-    int ret = 0;
-    GList *cur_ptoken = ptokens;
-    bool first_level_done = false;
-
-    /* Locate target */
-    while (cur_ptoken) {
-        int64_t child_index;
-        struct ctf_field_class *child_fc;
-        const char *ft_name = ptoken_get_string(cur_ptoken);
-
-        BT_COMP_LOGD("Current path token: token=\"%s\"", ft_name);
-
-        /* Find to which index corresponds the current path token */
-        if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-            child_index = -1;
-        } else {
-            child_index =
-                ctf_field_class_compound_get_field_class_index_from_orig_name(fc, ft_name);
-            if (child_index < 0) {
-                /*
-                 * Error: field name does not exist or
-                 * wrong current class.
-                 */
-                BT_COMP_LOGD("Cannot get index of field class: "
-                             "field-name=\"%s\", "
-                             "src-index=%" PRId64 ", "
-                             "child-index=%" PRId64 ", "
-                             "first-level-done=%d",
-                             ft_name, src_index, child_index, first_level_done);
-                ret = -1;
-                goto end;
-            } else if (child_index > src_index && !first_level_done) {
-                BT_COMP_LOGD("Child field class is located after source field class: "
-                             "field-name=\"%s\", "
-                             "src-index=%" PRId64 ", "
-                             "child-index=%" PRId64 ", "
-                             "first-level-done=%d",
-                             ft_name, src_index, child_index, first_level_done);
-                ret = -1;
-                goto end;
-            }
-
-            /* Next path token */
-            cur_ptoken = g_list_next(cur_ptoken);
-            first_level_done = true;
-        }
-
-        /* Create new field path entry */
-        ctf_field_path_append_index(field_path, child_index);
-
-        /* Get child field class */
-        child_fc = ctf_field_class_compound_borrow_field_class_by_index(fc, child_index);
-        BT_ASSERT(child_fc);
-
-        /* Move child class to current class */
-        fc = child_fc;
-    }
-
-end:
-    return ret;
-}
-
-/*
- * Converts a known absolute path token list to a field path object
- * within the resolving context `ctx`.
- *
- * `field_path` is an output parameter owned by the caller that must be
- * filled here.
- */
-static int absolute_ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
-                                          struct resolve_context *ctx)
-{
-    int ret = 0;
-    GList *cur_ptoken;
-    struct ctf_field_class *fc;
-
-    /*
-     * Make sure we're not referring to a scope within a translated
-     * object.
-     */
-    switch (field_path->root) {
-    case CTF_SCOPE_PACKET_HEADER:
-        if (ctx->tc->is_translated) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Trace class is already translated: "
-                                                     "root-scope=%s",
-                                                     ctf_scope_string(field_path->root));
-            ret = -1;
-            goto end;
-        }
-
-        break;
-    case CTF_SCOPE_PACKET_CONTEXT:
-    case CTF_SCOPE_EVENT_HEADER:
-    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-        if (!ctx->sc) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("No current stream class: "
-                                                     "root-scope=%s",
-                                                     ctf_scope_string(field_path->root));
-            ret = -1;
-            goto end;
-        }
-
-        if (ctx->sc->is_translated) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Stream class is already translated: "
-                                                     "root-scope=%s",
-                                                     ctf_scope_string(field_path->root));
-            ret = -1;
-            goto end;
-        }
-
-        break;
-    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-    case CTF_SCOPE_EVENT_PAYLOAD:
-        if (!ctx->ec) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("No current event class: "
-                                                     "root-scope=%s",
-                                                     ctf_scope_string(field_path->root));
-            ret = -1;
-            goto end;
-        }
-
-        if (ctx->ec->is_translated) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Event class is already translated: "
-                                                     "root-scope=%s",
-                                                     ctf_scope_string(field_path->root));
-            ret = -1;
-            goto end;
-        }
-
-        break;
-
-    default:
-        bt_common_abort();
-    }
-
-    /* Skip absolute path tokens */
-    cur_ptoken = g_list_nth(ptokens, absolute_path_prefix_ptoken_counts[field_path->root]);
-
-    /* Start with root class */
-    fc = borrow_class_from_ctx(ctx, field_path->root);
-    if (!fc) {
-        /* Error: root class is not available */
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Root field class is not available: "
-                                                 "root-scope=%s",
-                                                 ctf_scope_string(field_path->root));
-        ret = -1;
-        goto end;
-    }
-
-    /* Locate target */
-    ret = ptokens_to_field_path(cur_ptoken, field_path, fc, INT64_MAX, ctx);
-
-end:
-    return ret;
-}
-
-/*
- * Converts a known relative path token list to a field path object
- * within the resolving context `ctx`.
- *
- * `field_path` is an output parameter owned by the caller that must be
- * filled here.
- */
-static int relative_ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
-                                          struct resolve_context *ctx)
-{
-    int ret = 0;
-    int64_t parent_pos_in_stack;
-    struct ctf_field_path tail_field_path;
-
-    ctf_field_path_init(&tail_field_path);
-    parent_pos_in_stack = field_class_stack_size(ctx->field_class_stack) - 1;
-
-    while (parent_pos_in_stack >= 0) {
-        struct ctf_field_class *parent_class =
-            field_class_stack_at(ctx->field_class_stack, parent_pos_in_stack)->fc;
-        int64_t cur_index =
-            field_class_stack_at(ctx->field_class_stack, parent_pos_in_stack)->index;
-
-        BT_COMP_LOGD("Locating target field class from current parent field class: "
-                     "parent-pos=%" PRId64 ", parent-fc-addr=%p, "
-                     "cur-index=%" PRId64,
-                     parent_pos_in_stack, parent_class, cur_index);
-
-        /* Locate target from current parent class */
-        ret = ptokens_to_field_path(ptokens, &tail_field_path, parent_class, cur_index, ctx);
-        if (ret) {
-            /* Not found... yet */
-            BT_COMP_LOGD_STR("Not found at this point.");
-            ctf_field_path_clear(&tail_field_path);
-        } else {
-            /* Found: stitch tail field path to head field path */
-            uint64_t i = 0;
-            size_t tail_field_path_len = tail_field_path.path->len;
-
-            while (BT_TRUE) {
-                struct ctf_field_class *cur_class =
-                    field_class_stack_at(ctx->field_class_stack, i)->fc;
-                int64_t index = field_class_stack_at(ctx->field_class_stack, i)->index;
-
-                if (cur_class == parent_class) {
-                    break;
-                }
-
-                ctf_field_path_append_index(field_path, index);
-                i++;
-            }
-
-            for (i = 0; i < tail_field_path_len; i++) {
-                int64_t index = ctf_field_path_borrow_index_by_index(&tail_field_path, i);
-
-                ctf_field_path_append_index(field_path, (int64_t) index);
-            }
-            break;
-        }
-
-        parent_pos_in_stack--;
-    }
-
-    if (parent_pos_in_stack < 0) {
-        /* Not found */
-        ret = -1;
-    }
-
-    ctf_field_path_fini(&tail_field_path);
-    return ret;
-}
-
-/*
- * Converts a path string to a field path object within the resolving
- * context `ctx`.
- */
-static int pathstr_to_field_path(const char *pathstr, struct ctf_field_path *field_path,
-                                 struct resolve_context *ctx)
-{
-    int ret = 0;
-    enum ctf_scope root_scope;
-    GList *ptokens = NULL;
-
-    /* Convert path string to path tokens */
-    ptokens = pathstr_to_ptokens(pathstr, ctx);
-    if (!ptokens) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot convert path string to path tokens: "
-                                                 "path=\"%s\"",
-                                                 pathstr);
-        ret = -1;
-        goto end;
-    }
-
-    /* Absolute or relative path? */
-    root_scope = get_root_scope_from_absolute_pathstr(pathstr, ctx);
-
-    if (root_scope == CTF_SCOPE_PACKET_UNKNOWN) {
-        /* Relative path: start with current root scope */
-        field_path->root = ctx->root_scope;
-        BT_COMP_LOGD("Detected relative path: starting with current root scope: "
-                     "scope=%s",
-                     ctf_scope_string(field_path->root));
-        ret = relative_ptokens_to_field_path(ptokens, field_path, ctx);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot get relative field path of path string: "
-                "path=\"%s\", start-scope=%s, end-scope=%s",
-                pathstr, ctf_scope_string(ctx->root_scope), ctf_scope_string(field_path->root));
-            goto end;
-        }
-    } else {
-        /* Absolute path: use found root scope */
-        field_path->root = root_scope;
-        BT_COMP_LOGD("Detected absolute path: using root scope: "
-                     "scope=%s",
-                     ctf_scope_string(field_path->root));
-        ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot get absolute field path of path string: "
-                "path=\"%s\", root-scope=%s",
-                pathstr, ctf_scope_string(root_scope));
-            goto end;
-        }
-    }
-
-    if (BT_LOG_ON_TRACE && ret == 0) {
-        GString *field_path_pretty = ctf_field_path_string(field_path);
-        const char *field_path_pretty_str = field_path_pretty ? field_path_pretty->str : NULL;
-
-        BT_COMP_LOGD("Found field path: path=\"%s\", field-path=\"%s\"", pathstr,
-                     field_path_pretty_str);
-
-        if (field_path_pretty) {
-            g_string_free(field_path_pretty, TRUE);
-        }
-    }
-
-end:
-    ptokens_destroy(ptokens);
-    return ret;
-}
-
-/*
- * Retrieves a field class by following the field path `field_path` in
- * the resolving context `ctx`.
- */
-static struct ctf_field_class *field_path_to_field_class(struct ctf_field_path *field_path,
-                                                         struct resolve_context *ctx)
-{
-    uint64_t i;
-    struct ctf_field_class *fc;
-
-    /* Start with root class */
-    fc = borrow_class_from_ctx(ctx, field_path->root);
-    if (!fc) {
-        /* Error: root class is not available */
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Root field class is not available: root-scope=%s",
-                                                 ctf_scope_string(field_path->root));
-        goto end;
-    }
-
-    /* Locate target */
-    for (i = 0; i < field_path->path->len; i++) {
-        struct ctf_field_class *child_fc;
-        int64_t child_index = ctf_field_path_borrow_index_by_index(field_path, i);
-
-        /* Get child field class */
-        child_fc = ctf_field_class_compound_borrow_field_class_by_index(fc, child_index);
-        BT_ASSERT(child_fc);
-
-        /* Move child class to current class */
-        fc = child_fc;
-    }
-
-end:
-    return fc;
-}
-
-/*
- * Fills the equivalent field path object of the context class stack.
- */
-static void get_ctx_stack_field_path(struct resolve_context *ctx, struct ctf_field_path *field_path)
-{
-    uint64_t i;
-
-    BT_ASSERT(field_path);
-    field_path->root = ctx->root_scope;
-    ctf_field_path_clear(field_path);
-
-    for (i = 0; i < field_class_stack_size(ctx->field_class_stack); i++) {
-        struct field_class_stack_frame *frame = field_class_stack_at(ctx->field_class_stack, i);
-
-        ctf_field_path_append_index(field_path, frame->index);
-    }
-}
-
-/*
- * Returns the index of the lowest common ancestor of two field path
- * objects having the same root scope.
- */
-static int64_t get_field_paths_lca_index(struct ctf_field_path *field_path1,
-                                         struct ctf_field_path *field_path2,
-                                         struct resolve_context *ctx)
-{
-    int64_t lca_index = 0;
-    uint64_t field_path1_len, field_path2_len;
-
-    if (BT_LOG_ON_TRACE) {
-        GString *field_path1_pretty = ctf_field_path_string(field_path1);
-        GString *field_path2_pretty = ctf_field_path_string(field_path2);
-        const char *field_path1_pretty_str = field_path1_pretty ? field_path1_pretty->str : NULL;
-        const char *field_path2_pretty_str = field_path2_pretty ? field_path2_pretty->str : NULL;
-
-        BT_COMP_LOGD("Finding lowest common ancestor (LCA) between two field paths: "
-                     "field-path-1=\"%s\", field-path-2=\"%s\"",
-                     field_path1_pretty_str, field_path2_pretty_str);
-
-        if (field_path1_pretty) {
-            g_string_free(field_path1_pretty, TRUE);
-        }
-
-        if (field_path2_pretty) {
-            g_string_free(field_path2_pretty, TRUE);
-        }
-    }
-
-    /*
-     * Start from both roots and find the first mismatch.
-     */
-    BT_ASSERT(field_path1->root == field_path2->root);
-    field_path1_len = field_path1->path->len;
-    field_path2_len = field_path2->path->len;
-
-    while (true) {
-        int64_t target_index, ctx_index;
-
-        if (lca_index == (int64_t) field_path2_len || lca_index == (int64_t) field_path1_len) {
-            /*
-             * This means that both field paths never split.
-             * This is invalid because the target cannot be
-             * an ancestor of the source.
-             */
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Source field class is an ancestor of target field class or vice versa: "
-                "lca-index=%" PRId64 ", "
-                "field-path-1-len=%" PRIu64 ", "
-                "field-path-2-len=%" PRIu64,
-                lca_index, field_path1_len, field_path2_len);
-            lca_index = -1;
-            break;
-        }
-
-        target_index = ctf_field_path_borrow_index_by_index(field_path1, lca_index);
-        ctx_index = ctf_field_path_borrow_index_by_index(field_path2, lca_index);
-
-        if (target_index != ctx_index) {
-            /* LCA index is the previous */
-            break;
-        }
-
-        lca_index++;
-    }
-
-    BT_COMP_LOGD("Found LCA: lca-index=%" PRId64, lca_index);
-    return lca_index;
-}
-
-/*
- * Validates a target field path.
- */
-static int validate_target_field_path(struct ctf_field_path *target_field_path,
-                                      struct ctf_field_class *target_fc,
-                                      struct resolve_context *ctx)
-{
-    int ret = 0;
-    struct ctf_field_path ctx_field_path;
-    uint64_t target_field_path_len = target_field_path->path->len;
-    int64_t lca_index;
-
-    /* Get context field path */
-    ctf_field_path_init(&ctx_field_path);
-    get_ctx_stack_field_path(ctx, &ctx_field_path);
-
-    /*
-     * Make sure the target is not a root.
-     */
-    if (target_field_path_len == 0) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Target field path's length is 0 (targeting the root).");
-        ret = -1;
-        goto end;
-    }
-
-    /*
-     * Make sure the root of the target field path is not located
-     * after the context field path's root.
-     */
-    if (target_field_path->root > ctx_field_path.root) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Target field class is located after source field class: "
-            "target-root=%s, source-root=%s",
-            ctf_scope_string(target_field_path->root), ctf_scope_string(ctx_field_path.root));
-        ret = -1;
-        goto end;
-    }
-
-    if (target_field_path->root == ctx_field_path.root) {
-        int64_t target_index, ctx_index;
-
-        /*
-         * Find the index of the lowest common ancestor of both field
-         * paths.
-         */
-        lca_index = get_field_paths_lca_index(target_field_path, &ctx_field_path, ctx);
-        if (lca_index < 0) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get least common ancestor.");
-            ret = -1;
-            goto end;
-        }
-
-        /*
-         * Make sure the target field path is located before the
-         * context field path.
-         */
-        target_index =
-            ctf_field_path_borrow_index_by_index(target_field_path, (uint64_t) lca_index);
-        ctx_index = ctf_field_path_borrow_index_by_index(&ctx_field_path, (uint64_t) lca_index);
-
-        if (target_index >= ctx_index) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Target field class's index is greater than or equal to source field class's index in LCA: "
-                "lca-index=%" PRId64 ", "
-                "target-index=%" PRId64 ", "
-                "source-index=%" PRId64,
-                lca_index, target_index, ctx_index);
-            ret = -1;
-            goto end;
-        }
-    }
-
-    /*
-     * Make sure the target class has the right class and properties.
-     */
-    switch (ctx->cur_fc->type) {
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-        if (target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Variant field class's tag field class is not an enumeration field class: "
-                "tag-fc-addr=%p, tag-fc-id=%d",
-                target_fc, target_fc->type);
-            ret = -1;
-            goto end;
-        }
-        break;
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_int *int_fc = ctf_field_class_as_int(target_fc);
-
-        if (target_fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-            target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Sequence field class's length field class is not an unsigned integer field class: "
-                "length-fc-addr=%p, length-fc-id=%d",
-                target_fc, target_fc->type);
-            ret = -1;
-            goto end;
-        }
-
-        if (int_fc->is_signed) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Sequence field class's length field class is not an unsigned integer field class: "
-                "length-fc-addr=%p, length-fc-id=%d",
-                target_fc, target_fc->type);
-            ret = -1;
-            goto end;
-        }
-        break;
-    }
-    default:
-        bt_common_abort();
-    }
-
-end:
-    ctf_field_path_fini(&ctx_field_path);
-    return ret;
-}
-
-/*
- * Resolves a variant or sequence field class `fc`.
- */
-static int resolve_sequence_or_variant_field_class(struct ctf_field_class *fc,
-                                                   struct resolve_context *ctx)
-{
-    int ret = 0;
-    const char *pathstr;
-    struct ctf_field_path target_field_path;
-    struct ctf_field_class *target_fc = NULL;
-    GString *target_field_path_pretty = NULL;
-    const char *target_field_path_pretty_str;
-
-    ctf_field_path_init(&target_field_path);
-
-    /* Get path string */
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
-        pathstr = seq_fc->length_ref->str;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-        pathstr = var_fc->tag_ref->str;
-        break;
-    }
-    default:
-        bt_common_abort();
-    }
-
-    if (!pathstr) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get path string.");
-        ret = -1;
-        goto end;
-    }
-
-    /* Get target field path out of path string */
-    ret = pathstr_to_field_path(pathstr, &target_field_path, ctx);
-    if (ret) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get target field path for path string: "
-                                                 "path=\"%s\"",
-                                                 pathstr);
-        goto end;
-    }
-
-    target_field_path_pretty = ctf_field_path_string(&target_field_path);
-    target_field_path_pretty_str = target_field_path_pretty ? target_field_path_pretty->str : NULL;
-
-    /* Get target field class */
-    target_fc = field_path_to_field_class(&target_field_path, ctx);
-    if (!target_fc) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get target field class for path string: "
-                                                 "path=\"%s\", target-field-path=\"%s\"",
-                                                 pathstr, target_field_path_pretty_str);
-        ret = -1;
-        goto end;
-    }
-
-    ret = validate_target_field_path(&target_field_path, target_fc, ctx);
-    if (ret) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid target field path for path string: "
-                                                 "path=\"%s\", target-field-path=\"%s\"",
-                                                 pathstr, target_field_path_pretty_str);
-        goto end;
-    }
-
-    /* Set target field path and target field class */
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
-
-        ctf_field_path_copy_content(&seq_fc->length_path, &target_field_path);
-        seq_fc->length_fc = ctf_field_class_as_int(target_fc);
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        ctf_field_path_copy_content(&var_fc->tag_path, &target_field_path);
-        ctf_field_class_variant_set_tag_field_class(var_fc, ctf_field_class_as_enum(target_fc));
-        break;
-    }
-    default:
-        bt_common_abort();
-    }
-
-end:
-    if (target_field_path_pretty) {
-        g_string_free(target_field_path_pretty, TRUE);
-    }
-
-    ctf_field_path_fini(&target_field_path);
-    return ret;
-}
-
-/*
- * Resolves a field class `fc`.
- */
-static int resolve_field_class(struct ctf_field_class *fc, struct resolve_context *ctx)
-{
-    int ret = 0;
-
-    if (!fc) {
-        /* Field class is not available; still valid */
-        goto end;
-    }
-
-    ctx->cur_fc = fc;
-
-    /* Resolve sequence/variant field class */
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-        ret = resolve_sequence_or_variant_field_class(fc, ctx);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot resolve sequence field class's length or variant field class's tag: "
-                "ret=%d, fc-addr=%p",
-                ret, fc);
-            goto end;
-        }
-
-        break;
-    default:
-        break;
-    }
-
-    /* Recurse into compound classes */
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    {
-        uint64_t i;
-        uint64_t field_count = ctf_field_class_compound_get_field_class_count(fc);
-
-        ret = field_class_stack_push(ctx->field_class_stack, fc, ctx);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot push field class on context's stack: "
-                                                     "fc-addr=%p",
-                                                     fc);
-            goto end;
-        }
-
-        for (i = 0; i < field_count; i++) {
-            struct ctf_field_class *child_fc =
-                ctf_field_class_compound_borrow_field_class_by_index(fc, i);
-
-            BT_ASSERT(child_fc);
-
-            if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
-                fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-                field_class_stack_peek(ctx->field_class_stack)->index = -1;
-            } else {
-                field_class_stack_peek(ctx->field_class_stack)->index = (int64_t) i;
-            }
-
-            BT_COMP_LOGD("Resolving field class's child field class: "
-                         "parent-fc-addr=%p, child-fc-addr=%p, "
-                         "index=%" PRIu64 ", count=%" PRIu64,
-                         fc, child_fc, i, field_count);
-            ret = resolve_field_class(child_fc, ctx);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        field_class_stack_pop(ctx->field_class_stack, ctx);
-        break;
-    }
-    default:
-        break;
-    }
-
-end:
-    return ret;
-}
-
-/*
- * Resolves the root field class corresponding to the scope `root_scope`.
- */
-static int resolve_root_class(enum ctf_scope root_scope, struct resolve_context *ctx)
-{
-    int ret;
-
-    BT_ASSERT(field_class_stack_size(ctx->field_class_stack) == 0);
-    ctx->root_scope = root_scope;
-    ret = resolve_field_class(borrow_class_from_ctx(ctx, root_scope), ctx);
-    ctx->root_scope = CTF_SCOPE_PACKET_UNKNOWN;
-    return ret;
-}
-
-static int resolve_event_class_field_classes(struct resolve_context *ctx,
-                                             struct ctf_event_class *ec)
-{
-    int ret = 0;
-
-    BT_ASSERT(!ctx->scopes.event_spec_context);
-    BT_ASSERT(!ctx->scopes.event_payload);
-
-    if (ec->is_translated) {
-        goto end;
-    }
-
-    ctx->ec = ec;
-    ctx->scopes.event_spec_context = ec->spec_context_fc;
-    ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
-    if (ret) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Cannot resolve event specific context field class: "
-            "ret=%d",
-            ret);
-        goto end;
-    }
-
-    ctx->scopes.event_payload = ec->payload_fc;
-    ret = resolve_root_class(CTF_SCOPE_EVENT_PAYLOAD, ctx);
-    if (ret) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event payload field class: "
-                                                 "ret=%d",
-                                                 ret);
-        goto end;
-    }
-
-end:
-    ctx->scopes.event_spec_context = NULL;
-    ctx->scopes.event_payload = NULL;
-    ctx->ec = NULL;
-    return ret;
-}
-
-static int resolve_stream_class_field_classes(struct resolve_context *ctx,
-                                              struct ctf_stream_class *sc)
-{
-    int ret = 0;
-    uint64_t i;
-
-    BT_ASSERT(!ctx->scopes.packet_context);
-    BT_ASSERT(!ctx->scopes.event_header);
-    BT_ASSERT(!ctx->scopes.event_common_context);
-    ctx->sc = sc;
-
-    if (!sc->is_translated) {
-        ctx->scopes.packet_context = sc->packet_context_fc;
-        ret = resolve_root_class(CTF_SCOPE_PACKET_CONTEXT, ctx);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve packet context field class: "
-                                                     "ret=%d",
-                                                     ret);
-            goto end;
-        }
-
-        ctx->scopes.event_header = sc->event_header_fc;
-        ret = resolve_root_class(CTF_SCOPE_EVENT_HEADER, ctx);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event header field class: "
-                                                     "ret=%d",
-                                                     ret);
-            goto end;
-        }
-
-        ctx->scopes.event_common_context = sc->event_common_context_fc;
-        ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot resolve event common context field class: "
-                "ret=%d",
-                ret);
-            goto end;
-        }
-    }
-
-    ctx->scopes.packet_context = sc->packet_context_fc;
-    ctx->scopes.event_header = sc->event_header_fc;
-    ctx->scopes.event_common_context = sc->event_common_context_fc;
-
-    for (i = 0; i < sc->event_classes->len; i++) {
-        ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[i];
-
-        ret = resolve_event_class_field_classes(ctx, ec);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event class's field classes: "
-                                                     "ec-id=%" PRIu64 ", ec-name=\"%s\"",
-                                                     ec->id, ec->name->str);
-            goto end;
-        }
-    }
-
-end:
-    ctx->scopes.packet_context = NULL;
-    ctx->scopes.event_header = NULL;
-    ctx->scopes.event_common_context = NULL;
-    ctx->sc = NULL;
-    return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc,
-                                          struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-    uint64_t i;
-
-    resolve_context local_ctx {};
-    local_ctx.log_level = log_cfg->log_level;
-    local_ctx.self_comp = log_cfg->self_comp;
-    local_ctx.self_comp_class = log_cfg->self_comp_class;
-    local_ctx.tc = tc;
-    local_ctx.scopes.packet_header = tc->packet_header_fc;
-    local_ctx.root_scope = CTF_SCOPE_PACKET_HEADER;
-
-    struct resolve_context *ctx = &local_ctx;
-
-    /* Initialize class stack */
-    ctx->field_class_stack = field_class_stack_create();
-    if (!ctx->field_class_stack) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create field class stack.");
-        ret = -1;
-        goto end;
-    }
-
-    if (!tc->is_translated) {
-        ctx->scopes.packet_header = tc->packet_header_fc;
-        ret = resolve_root_class(CTF_SCOPE_PACKET_HEADER, ctx);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve packet header field class: "
-                                                     "ret=%d",
-                                                     ret);
-            goto end;
-        }
-    }
-
-    ctx->scopes.packet_header = tc->packet_header_fc;
-
-    for (i = 0; i < tc->stream_classes->len; i++) {
-        ctf_stream_class *sc = (ctf_stream_class *) tc->stream_classes->pdata[i];
-
-        ret = resolve_stream_class_field_classes(ctx, sc);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve stream class's field classes: "
-                                                     "sc-id=%" PRIu64,
-                                                     sc->id);
-            goto end;
-        }
-    }
-
-end:
-    field_class_stack_destroy(ctx->field_class_stack);
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-translate.cpp b/src/plugins/ctf/common/metadata/ctf-meta-translate.cpp
deleted file mode 100644 (file)
index c39948e..0000000
+++ /dev/null
@@ -1,655 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.hpp"
-
-struct ctx
-{
-    bt_self_component *self_comp;
-    bt_trace_class *ir_tc;
-    bt_stream_class *ir_sc;
-    struct ctf_trace_class *tc;
-    struct ctf_stream_class *sc;
-    struct ctf_event_class *ec;
-    enum ctf_scope scope;
-};
-
-static inline bt_field_class *ctf_field_class_to_ir(struct ctx *ctx, struct ctf_field_class *fc);
-
-static inline void ctf_field_class_int_set_props(struct ctf_field_class_int *fc,
-                                                 bt_field_class *ir_fc)
-{
-    bt_field_class_integer_set_field_value_range(ir_fc, fc->base.size);
-    bt_field_class_integer_set_preferred_display_base(ir_fc, fc->disp_base);
-}
-
-static inline bt_field_class *ctf_field_class_int_to_ir(struct ctx *ctx,
-                                                        struct ctf_field_class_int *fc)
-{
-    bt_field_class *ir_fc;
-
-    if (fc->is_signed) {
-        ir_fc = bt_field_class_integer_signed_create(ctx->ir_tc);
-    } else {
-        ir_fc = bt_field_class_integer_unsigned_create(ctx->ir_tc);
-    }
-
-    BT_ASSERT(ir_fc);
-    ctf_field_class_int_set_props(fc, ir_fc);
-    return ir_fc;
-}
-
-static inline bt_field_class *ctf_field_class_enum_to_ir(struct ctx *ctx,
-                                                         struct ctf_field_class_enum *fc)
-{
-    int ret;
-    bt_field_class *ir_fc;
-    uint64_t i;
-
-    if (fc->base.is_signed) {
-        ir_fc = bt_field_class_enumeration_signed_create(ctx->ir_tc);
-    } else {
-        ir_fc = bt_field_class_enumeration_unsigned_create(ctx->ir_tc);
-    }
-
-    BT_ASSERT(ir_fc);
-    ctf_field_class_int_set_props(&fc->base, ir_fc);
-
-    for (i = 0; i < fc->mappings->len; i++) {
-        struct ctf_field_class_enum_mapping *mapping =
-            ctf_field_class_enum_borrow_mapping_by_index(fc, i);
-        bt_integer_range_set_signed *range_set_signed = NULL;
-        bt_integer_range_set_unsigned *range_set_unsigned = NULL;
-        uint64_t range_i;
-
-        if (fc->base.is_signed) {
-            range_set_signed = bt_integer_range_set_signed_create();
-            BT_ASSERT(range_set_signed);
-        } else {
-            range_set_unsigned = bt_integer_range_set_unsigned_create();
-            BT_ASSERT(range_set_unsigned);
-        }
-
-        for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
-            struct ctf_range *range =
-                ctf_field_class_enum_mapping_borrow_range_by_index(mapping, range_i);
-
-            if (fc->base.is_signed) {
-                ret = bt_integer_range_set_signed_add_range(range_set_signed, range->lower.i,
-                                                            range->upper.i);
-            } else {
-                ret = bt_integer_range_set_unsigned_add_range(range_set_unsigned, range->lower.u,
-                                                              range->upper.u);
-            }
-
-            BT_ASSERT(ret == 0);
-        }
-
-        if (fc->base.is_signed) {
-            ret = bt_field_class_enumeration_signed_add_mapping(ir_fc, mapping->label->str,
-                                                                range_set_signed);
-            BT_INTEGER_RANGE_SET_SIGNED_PUT_REF_AND_RESET(range_set_signed);
-        } else {
-            ret = bt_field_class_enumeration_unsigned_add_mapping(ir_fc, mapping->label->str,
-                                                                  range_set_unsigned);
-            BT_INTEGER_RANGE_SET_UNSIGNED_PUT_REF_AND_RESET(range_set_unsigned);
-        }
-
-        BT_ASSERT(ret == 0);
-    }
-
-    return ir_fc;
-}
-
-static inline bt_field_class *ctf_field_class_float_to_ir(struct ctx *ctx,
-                                                          struct ctf_field_class_float *fc)
-{
-    bt_field_class *ir_fc;
-
-    if (fc->base.size == 32) {
-        ir_fc = bt_field_class_real_single_precision_create(ctx->ir_tc);
-    } else {
-        ir_fc = bt_field_class_real_double_precision_create(ctx->ir_tc);
-    }
-    BT_ASSERT(ir_fc);
-
-    return ir_fc;
-}
-
-static inline bt_field_class *ctf_field_class_string_to_ir(struct ctx *ctx,
-                                                           struct ctf_field_class_string *fc)
-{
-    bt_field_class *ir_fc = bt_field_class_string_create(ctx->ir_tc);
-
-    BT_ASSERT(ir_fc);
-    return ir_fc;
-}
-
-static inline void translate_struct_field_class_members(struct ctx *ctx,
-                                                        struct ctf_field_class_struct *fc,
-                                                        bt_field_class *ir_fc,
-                                                        bool with_header_prefix,
-                                                        struct ctf_field_class_struct *context_fc)
-{
-    uint64_t i;
-    int ret;
-
-    for (i = 0; i < fc->members->len; i++) {
-        struct ctf_named_field_class *named_fc =
-            ctf_field_class_struct_borrow_member_by_index(fc, i);
-        bt_field_class *member_ir_fc;
-        const char *name = named_fc->name->str;
-
-        if (!named_fc->fc->in_ir) {
-            continue;
-        }
-
-        member_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc);
-        BT_ASSERT(member_ir_fc);
-        ret = bt_field_class_structure_append_member(ir_fc, name, member_ir_fc);
-        BT_ASSERT(ret == 0);
-        bt_field_class_put_ref(member_ir_fc);
-    }
-}
-
-static inline bt_field_class *ctf_field_class_struct_to_ir(struct ctx *ctx,
-                                                           struct ctf_field_class_struct *fc)
-{
-    bt_field_class *ir_fc = bt_field_class_structure_create(ctx->ir_tc);
-
-    BT_ASSERT(ir_fc);
-    translate_struct_field_class_members(ctx, fc, ir_fc, false, NULL);
-    return ir_fc;
-}
-
-static inline bt_field_class *borrow_ir_fc_from_field_path(struct ctx *ctx,
-                                                           struct ctf_field_path *field_path)
-{
-    bt_field_class *ir_fc = NULL;
-    struct ctf_field_class *fc =
-        ctf_field_path_borrow_field_class(field_path, ctx->tc, ctx->sc, ctx->ec);
-
-    BT_ASSERT(fc);
-
-    if (fc->in_ir) {
-        ir_fc = fc->ir_fc;
-    }
-
-    return ir_fc;
-}
-
-static inline const bt_field_class_enumeration_mapping *
-find_ir_enum_field_class_mapping_by_label(const bt_field_class *fc, const char *label,
-                                          bool is_signed)
-{
-    const bt_field_class_enumeration_mapping *mapping = NULL;
-    uint64_t i;
-
-    for (i = 0; i < bt_field_class_enumeration_get_mapping_count(fc); i++) {
-        const bt_field_class_enumeration_mapping *this_mapping;
-        const bt_field_class_enumeration_signed_mapping *signed_this_mapping = NULL;
-        const bt_field_class_enumeration_unsigned_mapping *unsigned_this_mapping = NULL;
-
-        if (is_signed) {
-            signed_this_mapping =
-                bt_field_class_enumeration_signed_borrow_mapping_by_index_const(fc, i);
-            BT_ASSERT(signed_this_mapping);
-            this_mapping =
-                bt_field_class_enumeration_signed_mapping_as_mapping_const(signed_this_mapping);
-        } else {
-            unsigned_this_mapping =
-                bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(fc, i);
-            BT_ASSERT(unsigned_this_mapping);
-            this_mapping =
-                bt_field_class_enumeration_unsigned_mapping_as_mapping_const(unsigned_this_mapping);
-        }
-
-        BT_ASSERT(this_mapping);
-
-        if (strcmp(bt_field_class_enumeration_mapping_get_label(this_mapping), label) == 0) {
-            mapping = this_mapping;
-            goto end;
-        }
-    }
-
-end:
-    return mapping;
-}
-
-static inline bt_field_class *ctf_field_class_variant_to_ir(struct ctx *ctx,
-                                                            struct ctf_field_class_variant *fc)
-{
-    int ret;
-    bt_field_class *ir_fc;
-    uint64_t i;
-    bt_field_class *ir_tag_fc = NULL;
-
-    if (fc->tag_path.root != CTF_SCOPE_PACKET_HEADER &&
-        fc->tag_path.root != CTF_SCOPE_EVENT_HEADER) {
-        ir_tag_fc = borrow_ir_fc_from_field_path(ctx, &fc->tag_path);
-        BT_ASSERT(ir_tag_fc);
-    }
-
-    ir_fc = bt_field_class_variant_create(ctx->ir_tc, ir_tag_fc);
-    BT_ASSERT(ir_fc);
-
-    for (i = 0; i < fc->options->len; i++) {
-        struct ctf_named_field_class *named_fc =
-            ctf_field_class_variant_borrow_option_by_index(fc, i);
-        bt_field_class *option_ir_fc;
-
-        BT_ASSERT(named_fc->fc->in_ir);
-        option_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc);
-        BT_ASSERT(option_ir_fc);
-
-        if (ir_tag_fc) {
-            /*
-             * At this point the trace IR selector
-             * (enumeration) field class already exists if
-             * the variant is tagged (`ir_tag_fc`). This one
-             * already contains range sets for its mappings,
-             * so we just reuse the same, finding them by
-             * matching a variant field class's option's
-             * _original_ name (with a leading underscore,
-             * possibly) with a selector field class's
-             * mapping name.
-             */
-            if (fc->tag_fc->base.is_signed) {
-                const bt_field_class_enumeration_signed_mapping *mapping =
-                    (bt_field_class_enumeration_signed_mapping *)
-                        find_ir_enum_field_class_mapping_by_label(ir_tag_fc,
-                                                                  named_fc->orig_name->str, true);
-                const bt_integer_range_set_signed *range_set;
-
-                BT_ASSERT(mapping);
-                range_set = bt_field_class_enumeration_signed_mapping_borrow_ranges_const(mapping);
-                BT_ASSERT(range_set);
-                ret = bt_field_class_variant_with_selector_field_integer_signed_append_option(
-                    ir_fc, named_fc->name->str, option_ir_fc, range_set);
-            } else {
-                const bt_field_class_enumeration_unsigned_mapping *mapping =
-                    (bt_field_class_enumeration_unsigned_mapping *)
-                        find_ir_enum_field_class_mapping_by_label(ir_tag_fc,
-                                                                  named_fc->orig_name->str, false);
-                const bt_integer_range_set_unsigned *range_set;
-
-                BT_ASSERT(mapping);
-                range_set =
-                    bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(mapping);
-                BT_ASSERT(range_set);
-                ret = bt_field_class_variant_with_selector_field_integer_unsigned_append_option(
-                    ir_fc, named_fc->name->str, option_ir_fc, range_set);
-            }
-        } else {
-            ret = bt_field_class_variant_without_selector_append_option(ir_fc, named_fc->name->str,
-                                                                        option_ir_fc);
-        }
-
-        BT_ASSERT(ret == 0);
-        bt_field_class_put_ref(option_ir_fc);
-    }
-
-    return ir_fc;
-}
-
-static inline bt_field_class *ctf_field_class_array_to_ir(struct ctx *ctx,
-                                                          struct ctf_field_class_array *fc)
-{
-    bt_field_class *ir_fc;
-    bt_field_class *elem_ir_fc;
-
-    if (fc->base.is_text) {
-        ir_fc = bt_field_class_string_create(ctx->ir_tc);
-        BT_ASSERT(ir_fc);
-        goto end;
-    }
-
-    elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc);
-    BT_ASSERT(elem_ir_fc);
-    ir_fc = bt_field_class_array_static_create(ctx->ir_tc, elem_ir_fc, fc->length);
-    BT_ASSERT(ir_fc);
-    bt_field_class_put_ref(elem_ir_fc);
-
-end:
-    return ir_fc;
-}
-
-static inline bt_field_class *ctf_field_class_sequence_to_ir(struct ctx *ctx,
-                                                             struct ctf_field_class_sequence *fc)
-{
-    bt_field_class *ir_fc;
-    bt_field_class *elem_ir_fc;
-    bt_field_class *length_fc = NULL;
-
-    if (fc->base.is_text) {
-        ir_fc = bt_field_class_string_create(ctx->ir_tc);
-        BT_ASSERT(ir_fc);
-        goto end;
-    }
-
-    elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc);
-    BT_ASSERT(elem_ir_fc);
-
-    if (fc->length_path.root != CTF_SCOPE_PACKET_HEADER &&
-        fc->length_path.root != CTF_SCOPE_EVENT_HEADER) {
-        length_fc = borrow_ir_fc_from_field_path(ctx, &fc->length_path);
-        BT_ASSERT(length_fc);
-    }
-
-    ir_fc = bt_field_class_array_dynamic_create(ctx->ir_tc, elem_ir_fc, length_fc);
-    BT_ASSERT(ir_fc);
-    bt_field_class_put_ref(elem_ir_fc);
-    BT_ASSERT(ir_fc);
-
-end:
-    return ir_fc;
-}
-
-static inline bt_field_class *ctf_field_class_to_ir(struct ctx *ctx, struct ctf_field_class *fc)
-{
-    bt_field_class *ir_fc = NULL;
-
-    BT_ASSERT(fc);
-    BT_ASSERT(fc->in_ir);
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_INT:
-        ir_fc = ctf_field_class_int_to_ir(ctx, ctf_field_class_as_int(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_ENUM:
-        ir_fc = ctf_field_class_enum_to_ir(ctx, ctf_field_class_as_enum(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_FLOAT:
-        ir_fc = ctf_field_class_float_to_ir(ctx, ctf_field_class_as_float(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_STRING:
-        ir_fc = ctf_field_class_string_to_ir(ctx, ctf_field_class_as_string(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-        ir_fc = ctf_field_class_struct_to_ir(ctx, ctf_field_class_as_struct(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-        ir_fc = ctf_field_class_array_to_ir(ctx, ctf_field_class_as_array(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-        ir_fc = ctf_field_class_sequence_to_ir(ctx, ctf_field_class_as_sequence(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-        ir_fc = ctf_field_class_variant_to_ir(ctx, ctf_field_class_as_variant(fc));
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    fc->ir_fc = ir_fc;
-    return ir_fc;
-}
-
-static inline bool
-ctf_field_class_struct_has_immediate_member_in_ir(struct ctf_field_class_struct *fc)
-{
-    uint64_t i;
-    bool has_immediate_member_in_ir = false;
-
-    /*
-     * If the structure field class has no members at all, then it
-     * was an empty structure in the beginning, so leave it existing
-     * and empty.
-     */
-    if (fc->members->len == 0) {
-        has_immediate_member_in_ir = true;
-        goto end;
-    }
-
-    for (i = 0; i < fc->members->len; i++) {
-        struct ctf_named_field_class *named_fc =
-            ctf_field_class_struct_borrow_member_by_index(fc, i);
-
-        if (named_fc->fc->in_ir) {
-            has_immediate_member_in_ir = true;
-            goto end;
-        }
-    }
-
-end:
-    return has_immediate_member_in_ir;
-}
-
-static inline bt_field_class *scope_ctf_field_class_to_ir(struct ctx *ctx)
-{
-    bt_field_class *ir_fc = NULL;
-    struct ctf_field_class *fc = NULL;
-
-    switch (ctx->scope) {
-    case CTF_SCOPE_PACKET_CONTEXT:
-        fc = ctx->sc->packet_context_fc;
-        break;
-    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-        fc = ctx->sc->event_common_context_fc;
-        break;
-    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-        fc = ctx->ec->spec_context_fc;
-        break;
-    case CTF_SCOPE_EVENT_PAYLOAD:
-        fc = ctx->ec->payload_fc;
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    if (fc && ctf_field_class_struct_has_immediate_member_in_ir(ctf_field_class_as_struct(fc))) {
-        ir_fc = ctf_field_class_to_ir(ctx, fc);
-    }
-
-    return ir_fc;
-}
-
-static inline void ctf_event_class_to_ir(struct ctx *ctx)
-{
-    int ret;
-    bt_event_class *ir_ec = NULL;
-    bt_field_class *ir_fc;
-
-    BT_ASSERT(ctx->ec);
-
-    if (ctx->ec->is_translated) {
-        ir_ec = bt_stream_class_borrow_event_class_by_id(ctx->ir_sc, ctx->ec->id);
-        BT_ASSERT(ir_ec);
-        goto end;
-    }
-
-    ir_ec = bt_event_class_create_with_id(ctx->ir_sc, ctx->ec->id);
-    BT_ASSERT(ir_ec);
-    bt_event_class_put_ref(ir_ec);
-    ctx->scope = CTF_SCOPE_EVENT_SPECIFIC_CONTEXT;
-    ir_fc = scope_ctf_field_class_to_ir(ctx);
-    if (ir_fc) {
-        ret = bt_event_class_set_specific_context_field_class(ir_ec, ir_fc);
-        BT_ASSERT(ret == 0);
-        bt_field_class_put_ref(ir_fc);
-    }
-
-    ctx->scope = CTF_SCOPE_EVENT_PAYLOAD;
-    ir_fc = scope_ctf_field_class_to_ir(ctx);
-    if (ir_fc) {
-        ret = bt_event_class_set_payload_field_class(ir_ec, ir_fc);
-        BT_ASSERT(ret == 0);
-        bt_field_class_put_ref(ir_fc);
-    }
-
-    if (ctx->ec->name->len > 0) {
-        ret = bt_event_class_set_name(ir_ec, ctx->ec->name->str);
-        BT_ASSERT(ret == 0);
-    }
-
-    if (ctx->ec->emf_uri->len > 0) {
-        ret = bt_event_class_set_emf_uri(ir_ec, ctx->ec->emf_uri->str);
-        BT_ASSERT(ret == 0);
-    }
-
-    if (ctx->ec->is_log_level_set) {
-        bt_event_class_set_log_level(ir_ec, ctx->ec->log_level);
-    }
-
-    ctx->ec->is_translated = true;
-    ctx->ec->ir_ec = ir_ec;
-
-end:
-    return;
-}
-
-static inline void ctf_stream_class_to_ir(struct ctx *ctx)
-{
-    int ret;
-    bt_field_class *ir_fc;
-
-    BT_ASSERT(ctx->sc);
-
-    if (ctx->sc->is_translated) {
-        ctx->ir_sc = bt_trace_class_borrow_stream_class_by_id(ctx->ir_tc, ctx->sc->id);
-        BT_ASSERT(ctx->ir_sc);
-        goto end;
-    }
-
-    ctx->ir_sc = bt_stream_class_create_with_id(ctx->ir_tc, ctx->sc->id);
-    BT_ASSERT(ctx->ir_sc);
-    bt_stream_class_put_ref(ctx->ir_sc);
-
-    if (ctx->sc->default_clock_class) {
-        BT_ASSERT(ctx->sc->default_clock_class->ir_cc);
-        ret = bt_stream_class_set_default_clock_class(ctx->ir_sc,
-                                                      ctx->sc->default_clock_class->ir_cc);
-        BT_ASSERT(ret == 0);
-    }
-
-    bt_stream_class_set_supports_packets(ctx->ir_sc, BT_TRUE, ctx->sc->packets_have_ts_begin,
-                                         ctx->sc->packets_have_ts_end);
-    bt_stream_class_set_supports_discarded_events(ctx->ir_sc, ctx->sc->has_discarded_events,
-                                                  ctx->sc->discarded_events_have_default_cs);
-    bt_stream_class_set_supports_discarded_packets(ctx->ir_sc, ctx->sc->has_discarded_packets,
-                                                   ctx->sc->discarded_packets_have_default_cs);
-    ctx->scope = CTF_SCOPE_PACKET_CONTEXT;
-    ir_fc = scope_ctf_field_class_to_ir(ctx);
-    if (ir_fc) {
-        ret = bt_stream_class_set_packet_context_field_class(ctx->ir_sc, ir_fc);
-        BT_ASSERT(ret == 0);
-        bt_field_class_put_ref(ir_fc);
-    }
-
-    ctx->scope = CTF_SCOPE_EVENT_COMMON_CONTEXT;
-    ir_fc = scope_ctf_field_class_to_ir(ctx);
-    if (ir_fc) {
-        ret = bt_stream_class_set_event_common_context_field_class(ctx->ir_sc, ir_fc);
-        BT_ASSERT(ret == 0);
-        bt_field_class_put_ref(ir_fc);
-    }
-
-    bt_stream_class_set_assigns_automatic_event_class_id(ctx->ir_sc, BT_FALSE);
-    bt_stream_class_set_assigns_automatic_stream_id(ctx->ir_sc, BT_FALSE);
-
-    ctx->sc->is_translated = true;
-    ctx->sc->ir_sc = ctx->ir_sc;
-
-end:
-    return;
-}
-
-static inline void ctf_clock_class_to_ir(bt_clock_class *ir_cc, struct ctf_clock_class *cc)
-{
-    int ret;
-
-    if (strlen(cc->name->str) > 0) {
-        ret = bt_clock_class_set_name(ir_cc, cc->name->str);
-        BT_ASSERT(ret == 0);
-    }
-
-    if (strlen(cc->description->str) > 0) {
-        ret = bt_clock_class_set_description(ir_cc, cc->description->str);
-        BT_ASSERT(ret == 0);
-    }
-
-    bt_clock_class_set_frequency(ir_cc, cc->frequency);
-    bt_clock_class_set_precision(ir_cc, cc->precision);
-    bt_clock_class_set_offset(ir_cc, cc->offset_seconds, cc->offset_cycles);
-
-    if (cc->has_uuid) {
-        bt_clock_class_set_uuid(ir_cc, cc->uuid);
-    }
-
-    bt_clock_class_set_origin_is_unix_epoch(ir_cc, cc->is_absolute);
-}
-
-static inline int ctf_trace_class_to_ir(struct ctx *ctx)
-{
-    int ret = 0;
-    uint64_t i;
-
-    BT_ASSERT(ctx->tc);
-    BT_ASSERT(ctx->ir_tc);
-
-    if (ctx->tc->is_translated) {
-        goto end;
-    }
-
-    for (i = 0; i < ctx->tc->clock_classes->len; i++) {
-        ctf_clock_class *cc = (ctf_clock_class *) ctx->tc->clock_classes->pdata[i];
-
-        cc->ir_cc = bt_clock_class_create(ctx->self_comp);
-        ctf_clock_class_to_ir(cc->ir_cc, cc);
-    }
-
-    bt_trace_class_set_assigns_automatic_stream_class_id(ctx->ir_tc, BT_FALSE);
-    ctx->tc->is_translated = true;
-    ctx->tc->ir_tc = ctx->ir_tc;
-
-end:
-    return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_translate(bt_self_component *self_comp, bt_trace_class *ir_tc,
-                              struct ctf_trace_class *tc)
-{
-    int ret = 0;
-    uint64_t i;
-    struct ctx ctx = {0};
-
-    ctx.self_comp = self_comp;
-    ctx.tc = tc;
-    ctx.ir_tc = ir_tc;
-    ret = ctf_trace_class_to_ir(&ctx);
-    if (ret) {
-        goto end;
-    }
-
-    for (i = 0; i < tc->stream_classes->len; i++) {
-        uint64_t j;
-        ctx.sc = (ctf_stream_class *) tc->stream_classes->pdata[i];
-
-        ctf_stream_class_to_ir(&ctx);
-
-        for (j = 0; j < ctx.sc->event_classes->len; j++) {
-            ctx.ec = (ctf_event_class *) ctx.sc->event_classes->pdata[j];
-
-            ctf_event_class_to_ir(&ctx);
-            ctx.ec = NULL;
-        }
-
-        ctx.sc = NULL;
-    }
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-alignments.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-alignments.cpp
deleted file mode 100644 (file)
index b4c9a26..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2020 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.hpp"
-
-static inline int set_alignments(struct ctf_field_class *fc)
-{
-    int ret = 0;
-    uint64_t i;
-
-    if (!fc) {
-        goto end;
-    }
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
-
-        for (i = 0; i < struct_fc->members->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-
-            ret = set_alignments(named_fc->fc);
-            if (ret) {
-                goto end;
-            }
-
-            if (named_fc->fc->alignment > fc->alignment) {
-                fc->alignment = named_fc->fc->alignment;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        for (i = 0; i < var_fc->options->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-
-            ret = set_alignments(named_fc->fc);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        ret = set_alignments(array_fc->elem_fc);
-        if (ret) {
-            goto end;
-        }
-
-        /*
-         * Use the alignment of the array/sequence field class's
-         * element FC as its own alignment.
-         *
-         * This is especially important when the array/sequence
-         * field's effective length is zero: as per CTF 1.8, the
-         * stream data decoding process still needs to align the
-         * cursor using the element's alignment [1]:
-         *
-         * > Arrays are always aligned on their element
-         * > alignment requirement.
-         *
-         * For example:
-         *
-         *     struct {
-         *         integer { size = 8; } a;
-         *         integer { size = 8; align = 16; } b[0];
-         *         integer { size = 8; } c;
-         *     };
-         *
-         * When using this to decode the bytes 1, 2, and 3, then
-         * the decoded values are:
-         *
-         * `a`: 1
-         * `b`: []
-         * `c`: 3
-         *
-         * [1]: https://diamon.org/ctf/#spec4.2.3
-         */
-        array_fc->base.alignment = array_fc->elem_fc->alignment;
-        break;
-    }
-    default:
-        break;
-    }
-
-end:
-    return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_alignments(struct ctf_trace_class *ctf_tc)
-{
-    int ret = 0;
-    uint64_t i;
-
-    if (!ctf_tc->is_translated) {
-        ret = set_alignments(ctf_tc->packet_header_fc);
-        if (ret) {
-            goto end;
-        }
-    }
-
-    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
-        uint64_t j;
-
-        if (!sc->is_translated) {
-            ret = set_alignments(sc->packet_context_fc);
-            if (ret) {
-                goto end;
-            }
-
-            ret = set_alignments(sc->event_header_fc);
-            if (ret) {
-                goto end;
-            }
-
-            ret = set_alignments(sc->event_common_context_fc);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        for (j = 0; j < sc->event_classes->len; j++) {
-            struct ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[j];
-
-            if (ec->is_translated) {
-                continue;
-            }
-
-            ret = set_alignments(ec->spec_context_fc);
-            if (ret) {
-                goto end;
-            }
-
-            ret = set_alignments(ec->payload_fc);
-            if (ret) {
-                goto end;
-            }
-        }
-    }
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.cpp
deleted file mode 100644 (file)
index a1e1ecf..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP       (log_cfg->self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (log_cfg->self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL         (log_cfg->log_level)
-#define BT_LOG_TAG                  "PLUGIN/CTF/META/UPDATE-DEF-CC"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.hpp"
-#include "logging.hpp"
-
-static inline int find_mapped_clock_class(struct ctf_field_class *fc,
-                                          struct ctf_clock_class **clock_class,
-                                          struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-    uint64_t i;
-
-    if (!fc) {
-        goto end;
-    }
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_INT:
-    case CTF_FIELD_CLASS_TYPE_ENUM:
-    {
-        struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
-
-        if (int_fc->mapped_clock_class) {
-            if (*clock_class && *clock_class != int_fc->mapped_clock_class) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Stream class contains more than one "
-                                                         "clock class: expected-cc-name=\"%s\", "
-                                                         "other-cc-name=\"%s\"",
-                                                         (*clock_class)->name->str,
-                                                         int_fc->mapped_clock_class->name->str);
-                ret = -1;
-                goto end;
-            }
-
-            *clock_class = int_fc->mapped_clock_class;
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
-
-        for (i = 0; i < struct_fc->members->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-
-            ret = find_mapped_clock_class(named_fc->fc, clock_class, log_cfg);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        for (i = 0; i < var_fc->options->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-
-            ret = find_mapped_clock_class(named_fc->fc, clock_class, log_cfg);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        ret = find_mapped_clock_class(array_fc->elem_fc, clock_class, log_cfg);
-        if (ret) {
-            goto end;
-        }
-
-        break;
-    }
-    default:
-        break;
-    }
-
-end:
-    return ret;
-}
-
-static inline int update_stream_class_default_clock_class(struct ctf_stream_class *stream_class,
-                                                          struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-    struct ctf_clock_class *clock_class = stream_class->default_clock_class;
-    uint64_t i;
-
-    ret = find_mapped_clock_class(stream_class->packet_context_fc, &clock_class, log_cfg);
-    if (ret) {
-        goto end;
-    }
-
-    ret = find_mapped_clock_class(stream_class->event_header_fc, &clock_class, log_cfg);
-    if (ret) {
-        goto end;
-    }
-
-    ret = find_mapped_clock_class(stream_class->event_common_context_fc, &clock_class, log_cfg);
-    if (ret) {
-        goto end;
-    }
-
-    for (i = 0; i < stream_class->event_classes->len; i++) {
-        struct ctf_event_class *event_class =
-            (ctf_event_class *) stream_class->event_classes->pdata[i];
-
-        ret = find_mapped_clock_class(event_class->spec_context_fc, &clock_class, log_cfg);
-        if (ret) {
-            goto end;
-        }
-
-        ret = find_mapped_clock_class(event_class->payload_fc, &clock_class, log_cfg);
-        if (ret) {
-            goto end;
-        }
-    }
-
-    if (!stream_class->default_clock_class) {
-        stream_class->default_clock_class = clock_class;
-    }
-
-end:
-    return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_default_clock_classes(struct ctf_trace_class *ctf_tc,
-                                                 struct meta_log_config *log_cfg)
-{
-    uint64_t i;
-    int ret = 0;
-    struct ctf_clock_class *clock_class = NULL;
-
-    ret = find_mapped_clock_class(ctf_tc->packet_header_fc, &clock_class, log_cfg);
-    if (ret) {
-        goto end;
-    }
-
-    if (clock_class) {
-        ret = -1;
-        goto end;
-    }
-
-    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-        struct ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
-
-        ret = update_stream_class_default_clock_class(
-            (ctf_stream_class *) ctf_tc->stream_classes->pdata[i], log_cfg);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Stream class contains more than one "
-                                                     "clock class: stream-class-id=%" PRIu64,
-                                                     sc->id);
-            goto end;
-        }
-    }
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.cpp
deleted file mode 100644 (file)
index 6967c44..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include "compat/glib.h"
-#include <glib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-#include "common/assert.h"
-
-#include "ctf-meta-visitors.hpp"
-
-static void force_update_field_class_in_ir(struct ctf_field_class *fc, bool in_ir)
-{
-    uint64_t i;
-
-    if (!fc) {
-        goto end;
-    }
-
-    fc->in_ir = in_ir;
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
-
-        for (i = 0; i < struct_fc->members->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-
-            force_update_field_class_in_ir(named_fc->fc, in_ir);
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_named_field_class *named_fc;
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        for (i = 0; i < var_fc->options->len; i++) {
-            named_fc = ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-
-            force_update_field_class_in_ir(named_fc->fc, in_ir);
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        force_update_field_class_in_ir(array_fc->elem_fc, in_ir);
-        break;
-    }
-    default:
-        break;
-    }
-
-end:
-    return;
-}
-
-static void update_field_class_in_ir(struct ctf_field_class *fc, GHashTable *ft_dependents)
-{
-    int64_t i;
-
-    if (!fc) {
-        goto end;
-    }
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_INT:
-    case CTF_FIELD_CLASS_TYPE_ENUM:
-    {
-        struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
-
-        /*
-         * Conditions to be in trace IR; one of:
-         *
-         * 1. Does NOT have a mapped clock class AND does not
-         *    have a special meaning.
-         * 2. Another field class depends on it.
-         */
-        if ((!int_fc->mapped_clock_class && int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE) ||
-            bt_g_hash_table_contains(ft_dependents, fc)) {
-            fc->in_ir = true;
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
-
-        /*
-         * Make it part of IR if it's empty because it was
-         * originally empty.
-         */
-        if (struct_fc->members->len == 0) {
-            fc->in_ir = true;
-        }
-
-        /* Reverse order */
-        for (i = (int64_t) struct_fc->members->len - 1; i >= 0; i--) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-
-            update_field_class_in_ir(named_fc->fc, ft_dependents);
-
-            if (named_fc->fc->in_ir) {
-                /* At least one member is part of IR */
-                fc->in_ir = true;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_named_field_class *named_fc;
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        /*
-         * Reverse order, although it is not important for this
-         * loop because a field class within a variant field
-         * type's option cannot depend on a field class in
-         * another option of the same variant field class.
-         */
-        for (i = (int64_t) var_fc->options->len - 1; i >= 0; i--) {
-            named_fc = ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-
-            update_field_class_in_ir(named_fc->fc, ft_dependents);
-
-            if (named_fc->fc->in_ir) {
-                /* At least one option is part of IR */
-                fc->in_ir = true;
-            }
-        }
-
-        if (fc->in_ir) {
-            /*
-             * At least one option will make it to IR. In
-             * this case, make all options part of IR
-             * because the variant's tag could still select
-             * (dynamically) a removed option. This can mean
-             * having an empty structure as an option, for
-             * example, but at least all the options are
-             * selectable.
-             */
-            for (i = 0; i < var_fc->options->len; i++) {
-                ctf_field_class_variant_borrow_option_by_index(var_fc, i)->fc->in_ir = true;
-            }
-
-            /*
-             * This variant field class is part of IR and
-             * depends on a tag field class (which must also
-             * be part of IR).
-             */
-            g_hash_table_insert(ft_dependents, var_fc->tag_fc, var_fc->tag_fc);
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        update_field_class_in_ir(array_fc->elem_fc, ft_dependents);
-        fc->in_ir = array_fc->elem_fc->in_ir;
-
-        if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) {
-            struct ctf_field_class_array *arr_fc = ctf_field_class_as_array(fc);
-
-            assert(arr_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE ||
-                   arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID);
-
-            /*
-             * UUID field class: nothing depends on this, so
-             * it's not part of IR.
-             */
-            if (arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID) {
-                fc->in_ir = false;
-                array_fc->elem_fc->in_ir = false;
-            }
-        } else if (fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-            if (fc->in_ir) {
-                struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
-
-                /*
-                 * This sequence field class is part of
-                 * IR and depends on a length field class
-                 * (which must also be part of IR).
-                 */
-                g_hash_table_insert(ft_dependents, seq_fc->length_fc, seq_fc->length_fc);
-            }
-        }
-
-        break;
-    }
-    default:
-        fc->in_ir = true;
-        break;
-    }
-
-end:
-    return;
-}
-
-/*
- * Scopes and field classes are processed in reverse order because we need
- * to know if a given integer field class has dependents (sequence or
- * variant field classes) when we reach it. Dependents can only be located
- * after the length/tag field class in the metadata tree.
- */
-BT_HIDDEN
-int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc)
-{
-    int ret = 0;
-    uint64_t i;
-
-    GHashTable *ft_dependents = g_hash_table_new(g_direct_hash, g_direct_equal);
-
-    BT_ASSERT(ft_dependents);
-
-    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
-        uint64_t j;
-
-        for (j = 0; j < sc->event_classes->len; j++) {
-            ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[j];
-
-            if (ec->is_translated) {
-                continue;
-            }
-
-            update_field_class_in_ir(ec->payload_fc, ft_dependents);
-            update_field_class_in_ir(ec->spec_context_fc, ft_dependents);
-        }
-
-        if (!sc->is_translated) {
-            update_field_class_in_ir(sc->event_common_context_fc, ft_dependents);
-            force_update_field_class_in_ir(sc->event_header_fc, false);
-            update_field_class_in_ir(sc->packet_context_fc, ft_dependents);
-        }
-    }
-
-    if (!ctf_tc->is_translated) {
-        force_update_field_class_in_ir(ctf_tc->packet_header_fc, false);
-    }
-
-    g_hash_table_destroy(ft_dependents);
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-meanings.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-meanings.cpp
deleted file mode 100644 (file)
index 2a4c1fd..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.hpp"
-
-static int set_int_field_class_meaning_by_name(struct ctf_field_class *fc, const char *field_name,
-                                               const char *id_name,
-                                               enum ctf_field_class_meaning meaning)
-{
-    int ret = 0;
-    uint64_t i;
-
-    if (!fc) {
-        goto end;
-    }
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_INT:
-    case CTF_FIELD_CLASS_TYPE_ENUM:
-    {
-        struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
-
-        if (field_name && strcmp(field_name, id_name) == 0) {
-            int_fc->meaning = meaning;
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
-
-        for (i = 0; i < struct_fc->members->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-
-            ret = set_int_field_class_meaning_by_name(named_fc->fc, named_fc->name->str, id_name,
-                                                      meaning);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        for (i = 0; i < var_fc->options->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-
-            ret = set_int_field_class_meaning_by_name(named_fc->fc, NULL, id_name, meaning);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        ret = set_int_field_class_meaning_by_name(array_fc->elem_fc, NULL, id_name, meaning);
-        if (ret) {
-            goto end;
-        }
-
-        break;
-    }
-    default:
-        break;
-    }
-
-end:
-    return ret;
-}
-
-static int update_stream_class_meanings(struct ctf_stream_class *sc)
-{
-    int ret = 0;
-    struct ctf_field_class_int *int_fc;
-    uint64_t i;
-
-    if (!sc->is_translated) {
-        if (sc->packet_context_fc) {
-            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_begin");
-            if (int_fc) {
-                int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME;
-            }
-
-            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_end");
-            if (int_fc) {
-                int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_END_TIME;
-
-                /*
-                 * Remove mapped clock class to avoid updating
-                 * the clock immediately when decoding.
-                 */
-                int_fc->mapped_clock_class = NULL;
-            }
-
-            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                ctf_field_class_as_struct(sc->packet_context_fc), "events_discarded");
-            if (int_fc) {
-                int_fc->meaning = CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT;
-            }
-
-            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                ctf_field_class_as_struct(sc->packet_context_fc), "packet_seq_num");
-            if (int_fc) {
-                int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT;
-            }
-
-            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                ctf_field_class_as_struct(sc->packet_context_fc), "packet_size");
-            if (int_fc) {
-                int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE;
-            }
-
-            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                ctf_field_class_as_struct(sc->packet_context_fc), "content_size");
-            if (int_fc) {
-                int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE;
-            }
-        }
-
-        if (sc->event_header_fc) {
-            ret = set_int_field_class_meaning_by_name(sc->event_header_fc, NULL, "id",
-                                                      CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID);
-            if (ret) {
-                goto end;
-            }
-        }
-    }
-
-    for (i = 0; i < sc->event_classes->len; i++) {
-        struct ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[i];
-
-        if (ec->is_translated) {
-            continue;
-        }
-    }
-
-end:
-    return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc)
-{
-    int ret = 0;
-    struct ctf_field_class_int *int_fc;
-    struct ctf_named_field_class *named_fc;
-    uint64_t i;
-
-    if (!ctf_tc->is_translated && ctf_tc->packet_header_fc) {
-        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "magic");
-        if (int_fc) {
-            int_fc->meaning = CTF_FIELD_CLASS_MEANING_MAGIC;
-        }
-
-        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_id");
-        if (int_fc) {
-            int_fc->meaning = CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID;
-        }
-
-        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_instance_id");
-        if (int_fc) {
-            int_fc->meaning = CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID;
-        }
-
-        named_fc = ctf_field_class_struct_borrow_member_by_name(
-            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "uuid");
-        if (named_fc && named_fc->fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) {
-            struct ctf_field_class_array *array_fc = ctf_field_class_as_array(named_fc->fc);
-
-            array_fc->meaning = CTF_FIELD_CLASS_MEANING_UUID;
-        }
-    }
-
-    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-        ret = update_stream_class_meanings((ctf_stream_class *) ctf_tc->stream_classes->pdata[i]);
-        if (ret) {
-            goto end;
-        }
-    }
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.cpp
deleted file mode 100644 (file)
index f783a7e..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.hpp"
-
-BT_HIDDEN
-int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc)
-{
-    struct ctf_field_class_int *int_fc;
-    uint64_t i;
-
-    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-        struct ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
-
-        if (sc->is_translated) {
-            continue;
-        }
-
-        if (!sc->packet_context_fc) {
-            continue;
-        }
-
-        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-            ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_begin");
-        if (int_fc && int_fc->meaning == CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME) {
-            sc->packets_have_ts_begin = true;
-        }
-
-        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-            ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_end");
-        if (int_fc && int_fc->meaning == CTF_FIELD_CLASS_MEANING_PACKET_END_TIME) {
-            sc->packets_have_ts_end = true;
-        }
-
-        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-            ctf_field_class_as_struct(sc->packet_context_fc), "events_discarded");
-        if (int_fc && int_fc->meaning == CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT) {
-            sc->has_discarded_events = true;
-        }
-
-        sc->discarded_events_have_default_cs =
-            sc->has_discarded_events && sc->packets_have_ts_begin && sc->packets_have_ts_end;
-        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-            ctf_field_class_as_struct(sc->packet_context_fc), "packet_seq_num");
-        if (int_fc && int_fc->meaning == CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT) {
-            sc->has_discarded_packets = true;
-        }
-
-        sc->discarded_packets_have_default_cs =
-            sc->has_discarded_packets && sc->packets_have_ts_begin && sc->packets_have_ts_end;
-    }
-
-    return 0;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.cpp
deleted file mode 100644 (file)
index 55dd27a..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.hpp"
-
-static inline int set_text_array_sequence_field_class(struct ctf_field_class *fc)
-{
-    int ret = 0;
-    uint64_t i;
-
-    if (!fc) {
-        goto end;
-    }
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
-
-        for (i = 0; i < struct_fc->members->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-
-            ret = set_text_array_sequence_field_class(named_fc->fc);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        for (i = 0; i < var_fc->options->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-
-            ret = set_text_array_sequence_field_class(named_fc->fc);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        if (array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_INT ||
-            array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_ENUM) {
-            struct ctf_field_class_int *int_fc = ctf_field_class_as_int(array_fc->elem_fc);
-
-            if (int_fc->base.base.alignment == 8 && int_fc->base.size == 8 &&
-                int_fc->encoding == CTF_ENCODING_UTF8) {
-                array_fc->is_text = true;
-
-                /*
-                 * Force integer element to be unsigned;
-                 * this makes the decoder enter a single
-                 * path when reading a text
-                 * array/sequence and we can safely
-                 * decode bytes as characters anyway.
-                 */
-                int_fc->is_signed = false;
-            }
-        }
-
-        ret = set_text_array_sequence_field_class(array_fc->elem_fc);
-        if (ret) {
-            goto end;
-        }
-
-        break;
-    }
-    default:
-        break;
-    }
-
-end:
-    return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc)
-{
-    int ret = 0;
-    uint64_t i;
-
-    if (!ctf_tc->is_translated) {
-        ret = set_text_array_sequence_field_class(ctf_tc->packet_header_fc);
-        if (ret) {
-            goto end;
-        }
-    }
-
-    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
-        uint64_t j;
-
-        if (!sc->is_translated) {
-            ret = set_text_array_sequence_field_class(sc->packet_context_fc);
-            if (ret) {
-                goto end;
-            }
-
-            ret = set_text_array_sequence_field_class(sc->event_header_fc);
-            if (ret) {
-                goto end;
-            }
-
-            ret = set_text_array_sequence_field_class(sc->event_common_context_fc);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        for (j = 0; j < sc->event_classes->len; j++) {
-            struct ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[j];
-
-            if (ec->is_translated) {
-                continue;
-            }
-
-            ret = set_text_array_sequence_field_class(ec->spec_context_fc);
-            if (ret) {
-                goto end;
-            }
-
-            ret = set_text_array_sequence_field_class(ec->payload_fc);
-            if (ret) {
-                goto end;
-            }
-        }
-    }
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.cpp
deleted file mode 100644 (file)
index 5962ae2..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.hpp"
-
-static int update_field_class_stored_value_index(struct ctf_field_class *fc,
-                                                 struct ctf_trace_class *tc,
-                                                 struct ctf_stream_class *sc,
-                                                 struct ctf_event_class *ec)
-{
-    int ret = 0;
-    uint64_t i;
-    struct ctf_field_path *field_path = NULL;
-    struct ctf_field_class_int *tgt_fc = NULL;
-    uint64_t *stored_value_index = NULL;
-
-    if (!fc) {
-        goto end;
-    }
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        field_path = &var_fc->tag_path;
-        stored_value_index = &var_fc->stored_tag_index;
-        tgt_fc = &var_fc->tag_fc->base;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
-
-        field_path = &seq_fc->length_path;
-        stored_value_index = &seq_fc->stored_length_index;
-        tgt_fc = seq_fc->length_fc;
-        break;
-    }
-    default:
-        break;
-    }
-
-    if (field_path) {
-        BT_ASSERT(tgt_fc);
-        BT_ASSERT(tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_INT ||
-                  tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_ENUM);
-        if (tgt_fc->storing_index >= 0) {
-            /* Already storing its value */
-            *stored_value_index = (uint64_t) tgt_fc->storing_index;
-        } else {
-            /* Not storing its value: allocate new index */
-            tgt_fc->storing_index = tc->stored_value_count;
-            *stored_value_index = (uint64_t) tgt_fc->storing_index;
-            tc->stored_value_count++;
-        }
-    }
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
-
-        for (i = 0; i < struct_fc->members->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-
-            ret = update_field_class_stored_value_index(named_fc->fc, tc, sc, ec);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        for (i = 0; i < var_fc->options->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-
-            ret = update_field_class_stored_value_index(named_fc->fc, tc, sc, ec);
-            if (ret) {
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        ret = update_field_class_stored_value_index(array_fc->elem_fc, tc, sc, ec);
-        if (ret) {
-            goto end;
-        }
-
-        break;
-    }
-    default:
-        break;
-    }
-
-end:
-    return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc)
-{
-    uint64_t i;
-
-    if (!ctf_tc->is_translated) {
-        update_field_class_stored_value_index(ctf_tc->packet_header_fc, ctf_tc, NULL, NULL);
-    }
-
-    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-        uint64_t j;
-        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
-
-        if (!sc->is_translated) {
-            update_field_class_stored_value_index(sc->packet_context_fc, ctf_tc, sc, NULL);
-            update_field_class_stored_value_index(sc->event_header_fc, ctf_tc, sc, NULL);
-            update_field_class_stored_value_index(sc->event_common_context_fc, ctf_tc, sc, NULL);
-        }
-
-        for (j = 0; j < sc->event_classes->len; j++) {
-            struct ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[j];
-
-            if (!ec->is_translated) {
-                update_field_class_stored_value_index(ec->spec_context_fc, ctf_tc, sc, ec);
-                update_field_class_stored_value_index(ec->payload_fc, ctf_tc, sc, ec);
-            }
-        }
-    }
-
-    return 0;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-validate.cpp b/src/plugins/ctf/common/metadata/ctf-meta-validate.cpp
deleted file mode 100644 (file)
index 4d531eb..0000000
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP       (log_cfg->self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (log_cfg->self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL         (log_cfg->log_level)
-#define BT_LOG_TAG                  "PLUGIN/CTF/META/VALIDATE"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.hpp"
-#include "logging.hpp"
-
-static int validate_stream_class(struct ctf_stream_class *sc, struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-    struct ctf_field_class_int *int_fc;
-    struct ctf_field_class *fc;
-
-    if (sc->is_translated) {
-        goto end;
-    }
-
-    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-        ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_begin");
-    if (fc) {
-        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Invalid packet context field class: "
-                "`timestamp_begin` member is not an integer field class.");
-            goto invalid;
-        }
-
-        int_fc = ctf_field_class_as_int(fc);
-
-        if (int_fc->is_signed) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                                                     "`timestamp_begin` member is signed.");
-            goto invalid;
-        }
-    }
-
-    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-        ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_end");
-    if (fc) {
-        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Invalid packet context field class: "
-                "`timestamp_end` member is not an integer field class.");
-            goto invalid;
-        }
-
-        int_fc = ctf_field_class_as_int(fc);
-
-        if (int_fc->is_signed) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                                                     "`timestamp_end` member is signed.");
-            goto invalid;
-        }
-    }
-
-    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-        ctf_field_class_as_struct(sc->packet_context_fc), "events_discarded");
-    if (fc) {
-        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Invalid packet context field class: "
-                "`events_discarded` member is not an integer field class.");
-            goto invalid;
-        }
-
-        int_fc = ctf_field_class_as_int(fc);
-
-        if (int_fc->is_signed) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                                                     "`events_discarded` member is signed.");
-            goto invalid;
-        }
-    }
-
-    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-        ctf_field_class_as_struct(sc->packet_context_fc), "packet_seq_num");
-    if (fc) {
-        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Invalid packet context field class: "
-                "`packet_seq_num` member is not an integer field class.");
-            goto invalid;
-        }
-
-        int_fc = ctf_field_class_as_int(fc);
-
-        if (int_fc->is_signed) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                                                     "`packet_seq_num` member is signed.");
-            goto invalid;
-        }
-    }
-
-    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-        ctf_field_class_as_struct(sc->packet_context_fc), "packet_size");
-    if (fc) {
-        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Invalid packet context field class: "
-                "`packet_size` member is not an integer field class.");
-            goto invalid;
-        }
-
-        int_fc = ctf_field_class_as_int(fc);
-
-        if (int_fc->is_signed) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                                                     "`packet_size` member is signed.");
-            goto invalid;
-        }
-    }
-
-    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-        ctf_field_class_as_struct(sc->packet_context_fc), "content_size");
-    if (fc) {
-        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Invalid packet context field class: "
-                "`content_size` member is not an integer field class.");
-            goto invalid;
-        }
-
-        int_fc = ctf_field_class_as_int(fc);
-
-        if (int_fc->is_signed) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                                                     "`content_size` member is signed.");
-            goto invalid;
-        }
-    }
-
-    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-        ctf_field_class_as_struct(sc->event_header_fc), "id");
-    if (fc) {
-        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid event header field class: "
-                                                     "`id` member is not an integer field class.");
-            goto invalid;
-        }
-
-        int_fc = ctf_field_class_as_int(fc);
-
-        if (int_fc->is_signed) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid event header field class: "
-                                                     "`id` member is signed.");
-            goto invalid;
-        }
-    } else {
-        if (sc->event_classes->len > 1) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid event header field class: "
-                                                     "missing `id` member as there's "
-                                                     "more than one event class.");
-            goto invalid;
-        }
-    }
-
-    goto end;
-
-invalid:
-    ret = -1;
-
-end:
-    return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc, struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-    struct ctf_field_class_int *int_fc;
-    uint64_t i;
-
-    if (!ctf_tc->is_translated) {
-        struct ctf_field_class *fc;
-
-        fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "magic");
-        if (fc) {
-            struct ctf_named_field_class *named_fc = ctf_field_class_struct_borrow_member_by_index(
-                ctf_field_class_as_struct(ctf_tc->packet_header_fc), 0);
-
-            if (named_fc->fc != fc) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                                         "`magic` member is not the first member.");
-                goto invalid;
-            }
-
-            if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Invalid packet header field class: "
-                    "`magic` member is not an integer field class.");
-                goto invalid;
-            }
-
-            int_fc = ctf_field_class_as_int(fc);
-
-            if (int_fc->is_signed) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                                         "`magic` member is signed.");
-                goto invalid;
-            }
-
-            if (int_fc->base.size != 32) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                                         "`magic` member is not 32-bit.");
-                goto invalid;
-            }
-        }
-
-        fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_id");
-        if (fc) {
-            if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Invalid packet header field class: "
-                    "`stream_id` member is not an integer field class.");
-                goto invalid;
-            }
-
-            int_fc = ctf_field_class_as_int(fc);
-
-            if (int_fc->is_signed) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                                         "`stream_id` member is signed.");
-                goto invalid;
-            }
-        } else {
-            if (ctf_tc->stream_classes->len > 1) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                                         "missing `stream_id` member as there's "
-                                                         "more than one stream class.");
-                goto invalid;
-            }
-        }
-
-        fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_instance_id");
-        if (fc) {
-            if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Invalid packet header field class: "
-                    "`stream_instance_id` member is not an integer field class.");
-                goto invalid;
-            }
-
-            int_fc = ctf_field_class_as_int(fc);
-
-            if (int_fc->is_signed) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                                         "`stream_instance_id` member is signed.");
-                goto invalid;
-            }
-        }
-
-        fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "uuid");
-        if (fc) {
-            if (fc->type != CTF_FIELD_CLASS_TYPE_ARRAY) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Invalid packet header field class: "
-                    "`uuid` member is not an array field class.");
-                goto invalid;
-            }
-
-            ctf_field_class_array *array_fc = ctf_field_class_as_array(fc);
-
-            if (array_fc->length != 16) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Invalid packet header field class: "
-                    "`uuid` member is not a 16-element array field class.");
-                goto invalid;
-            }
-
-            if (array_fc->base.elem_fc->type != CTF_FIELD_CLASS_TYPE_INT) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Invalid packet header field class: "
-                    "`uuid` member's element field class is not "
-                    "an integer field class.");
-                goto invalid;
-            }
-
-            int_fc = ctf_field_class_as_int(array_fc->base.elem_fc);
-
-            if (int_fc->is_signed) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                                         "`uuid` member's element field class "
-                                                         "is a signed integer field class.");
-                goto invalid;
-            }
-
-            if (int_fc->base.size != 8) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                                         "`uuid` member's element field class "
-                                                         "is not an 8-bit integer field class.");
-                goto invalid;
-            }
-
-            if (int_fc->base.base.alignment != 8) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                                         "`uuid` member's element field class's "
-                                                         "alignment is not 8.");
-                goto invalid;
-            }
-        }
-    }
-
-    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-        struct ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
-
-        ret = validate_stream_class(sc, log_cfg);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid stream class: sc-id=%" PRIu64,
-                                                     sc->id);
-            goto invalid;
-        }
-    }
-
-    goto end;
-
-invalid:
-    ret = -1;
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-visitors.hpp b/src/plugins/ctf/common/metadata/ctf-meta-visitors.hpp
deleted file mode 100644 (file)
index a5d27db..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef _CTF_META_VISITORS_H
-#define _CTF_META_VISITORS_H
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-
-#include "ctf-meta.hpp"
-
-struct meta_log_config;
-
-BT_HIDDEN
-int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc,
-                                          struct meta_log_config *log_cfg);
-
-BT_HIDDEN
-int ctf_trace_class_translate(bt_self_component *self_comp, bt_trace_class *ir_tc,
-                              struct ctf_trace_class *tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_default_clock_classes(struct ctf_trace_class *ctf_tc,
-                                                 struct meta_log_config *log_cfg);
-
-BT_HIDDEN
-int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_alignments(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc, struct meta_log_config *log_cfg);
-
-BT_HIDDEN
-void ctf_trace_class_warn_meaningless_header_fields(struct ctf_trace_class *ctf_tc,
-                                                    struct meta_log_config *log_cfg);
-
-#endif /* _CTF_META_VISITORS_H */
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.cpp b/src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.cpp
deleted file mode 100644 (file)
index ccad81c..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (log_cfg->log_level)
-#define BT_LOG_TAG            "PLUGIN/CTF/META/WARN-MEANINGLESS-HEADER-FIELDS"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.hpp"
-#include "logging.hpp"
-
-static inline void warn_meaningless_field(const char *name, const char *scope_name,
-                                          struct meta_log_config *log_cfg)
-{
-    BT_ASSERT(name);
-    BT_COMP_LOGW("User field found in %s: ignoring: name=\"%s\"", scope_name, name);
-}
-
-static inline void warn_meaningless_fields(struct ctf_field_class *fc, const char *name,
-                                           const char *scope_name, struct meta_log_config *log_cfg)
-{
-    uint64_t i;
-
-    if (!fc) {
-        goto end;
-    }
-
-    /*
-     * 'name' is guaranteed to be non-NULL whenever the field class is not a
-     * structure. In the case of a structure field class, its members' names
-     * are used.
-     */
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_FLOAT:
-    case CTF_FIELD_CLASS_TYPE_STRING:
-        warn_meaningless_field(name, scope_name, log_cfg);
-        break;
-    case CTF_FIELD_CLASS_TYPE_INT:
-    case CTF_FIELD_CLASS_TYPE_ENUM:
-    {
-        struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
-
-        if (int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE && !int_fc->mapped_clock_class) {
-            warn_meaningless_field(name, scope_name, log_cfg);
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
-
-        for (i = 0; i < struct_fc->members->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-
-            warn_meaningless_fields(named_fc->fc, named_fc->name->str, scope_name, log_cfg);
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-
-        for (i = 0; i < var_fc->options->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-
-            warn_meaningless_fields(named_fc->fc, named_fc->name->str, scope_name, log_cfg);
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    {
-        struct ctf_field_class_array *array_fc = ctf_field_class_as_array(fc);
-
-        if (array_fc->meaning != CTF_FIELD_CLASS_MEANING_NONE) {
-            goto end;
-        }
-    }
-    /* fall-through */
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        warn_meaningless_fields(array_fc->elem_fc, name, scope_name, log_cfg);
-        break;
-    }
-    default:
-        bt_common_abort();
-    }
-
-end:
-    return;
-}
-
-BT_HIDDEN
-void ctf_trace_class_warn_meaningless_header_fields(struct ctf_trace_class *ctf_tc,
-                                                    struct meta_log_config *log_cfg)
-{
-    uint64_t i;
-
-    if (!ctf_tc->is_translated) {
-        warn_meaningless_fields(ctf_tc->packet_header_fc, NULL, "packet header", log_cfg);
-    }
-
-    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
-
-        if (!sc->is_translated) {
-            warn_meaningless_fields(sc->event_header_fc, NULL, "event header", log_cfg);
-        }
-    }
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta.hpp b/src/plugins/ctf/common/metadata/ctf-meta.hpp
deleted file mode 100644 (file)
index ae20f88..0000000
+++ /dev/null
@@ -1,1750 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef _CTF_META_H
-#define _CTF_META_H
-
-#include <babeltrace2/babeltrace.h>
-#include "common/common.h"
-#include "common/uuid.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-
-enum ctf_field_class_type
-{
-    CTF_FIELD_CLASS_TYPE_INT,
-    CTF_FIELD_CLASS_TYPE_ENUM,
-    CTF_FIELD_CLASS_TYPE_FLOAT,
-    CTF_FIELD_CLASS_TYPE_STRING,
-    CTF_FIELD_CLASS_TYPE_STRUCT,
-    CTF_FIELD_CLASS_TYPE_ARRAY,
-    CTF_FIELD_CLASS_TYPE_SEQUENCE,
-    CTF_FIELD_CLASS_TYPE_VARIANT,
-};
-
-enum ctf_field_class_meaning
-{
-    CTF_FIELD_CLASS_MEANING_NONE,
-    CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME,
-    CTF_FIELD_CLASS_MEANING_PACKET_END_TIME,
-    CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID,
-    CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID,
-    CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID,
-    CTF_FIELD_CLASS_MEANING_MAGIC,
-    CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT,
-    CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT,
-    CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE,
-    CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE,
-    CTF_FIELD_CLASS_MEANING_UUID,
-};
-
-enum ctf_byte_order
-{
-    CTF_BYTE_ORDER_UNKNOWN,
-    CTF_BYTE_ORDER_DEFAULT,
-    CTF_BYTE_ORDER_LITTLE,
-    CTF_BYTE_ORDER_BIG,
-};
-
-enum ctf_encoding
-{
-    CTF_ENCODING_NONE,
-    CTF_ENCODING_UTF8,
-};
-
-enum ctf_scope
-{
-    CTF_SCOPE_PACKET_UNKNOWN = -1,
-    CTF_SCOPE_PACKET_HEADER = 0,
-    CTF_SCOPE_PACKET_CONTEXT,
-    CTF_SCOPE_EVENT_HEADER,
-    CTF_SCOPE_EVENT_COMMON_CONTEXT,
-    CTF_SCOPE_EVENT_SPECIFIC_CONTEXT,
-    CTF_SCOPE_EVENT_PAYLOAD,
-};
-
-struct ctf_clock_class
-{
-    GString *name;
-    GString *description;
-    uint64_t frequency;
-    uint64_t precision;
-    int64_t offset_seconds;
-    uint64_t offset_cycles;
-    bt_uuid_t uuid;
-    bool has_uuid;
-    bool is_absolute;
-
-    /* Weak, set during translation */
-    bt_clock_class *ir_cc;
-};
-
-struct ctf_field_class
-{
-    enum ctf_field_class_type type;
-    unsigned int alignment;
-    bool is_compound;
-    bool in_ir;
-
-    /* Weak, set during translation. NULL if `in_ir` is false below. */
-    bt_field_class *ir_fc;
-};
-
-struct ctf_field_class_bit_array
-{
-    struct ctf_field_class base;
-    enum ctf_byte_order byte_order;
-    unsigned int size;
-};
-
-struct ctf_field_class_int
-{
-    struct ctf_field_class_bit_array base;
-    enum ctf_field_class_meaning meaning;
-    bool is_signed;
-    bt_field_class_integer_preferred_display_base disp_base;
-    enum ctf_encoding encoding;
-    int64_t storing_index;
-
-    /* Weak */
-    struct ctf_clock_class *mapped_clock_class;
-};
-
-struct ctf_range
-{
-    union
-    {
-        uint64_t u;
-        int64_t i;
-    } lower;
-
-    union
-    {
-        uint64_t u;
-        int64_t i;
-    } upper;
-};
-
-struct ctf_field_class_enum_mapping
-{
-    GString *label;
-
-    /* Array of `struct ctf_range` */
-    GArray *ranges;
-};
-
-struct ctf_field_class_enum
-{
-    struct ctf_field_class_int base;
-
-    /* Array of `struct ctf_field_class_enum_mapping` */
-    GArray *mappings;
-};
-
-struct ctf_field_class_float
-{
-    struct ctf_field_class_bit_array base;
-};
-
-struct ctf_field_class_string
-{
-    struct ctf_field_class base;
-    enum ctf_encoding encoding;
-};
-
-struct ctf_named_field_class
-{
-    /* Original name which can include a leading `_` */
-    GString *orig_name;
-
-    /* Name as translated to trace IR (leading `_` removed) */
-    GString *name;
-
-    /* Owned by this */
-    struct ctf_field_class *fc;
-};
-
-struct ctf_field_class_struct
-{
-    struct ctf_field_class base;
-
-    /* Array of `struct ctf_named_field_class` */
-    GArray *members;
-};
-
-struct ctf_field_path
-{
-    enum ctf_scope root;
-
-    /* Array of `int64_t` */
-    GArray *path;
-};
-
-struct ctf_field_class_variant_range
-{
-    struct ctf_range range;
-    uint64_t option_index;
-};
-
-struct ctf_field_class_variant
-{
-    struct ctf_field_class base;
-    GString *tag_ref;
-    struct ctf_field_path tag_path;
-    uint64_t stored_tag_index;
-
-    /* Array of `struct ctf_named_field_class` */
-    GArray *options;
-
-    /* Array of `struct ctf_field_class_variant_range` */
-    GArray *ranges;
-
-    /* Weak */
-    struct ctf_field_class_enum *tag_fc;
-};
-
-struct ctf_field_class_array_base
-{
-    struct ctf_field_class base;
-    struct ctf_field_class *elem_fc;
-    bool is_text;
-};
-
-struct ctf_field_class_array
-{
-    struct ctf_field_class_array_base base;
-    enum ctf_field_class_meaning meaning;
-    uint64_t length;
-};
-
-struct ctf_field_class_sequence
-{
-    struct ctf_field_class_array_base base;
-    GString *length_ref;
-    struct ctf_field_path length_path;
-    uint64_t stored_length_index;
-
-    /* Weak */
-    struct ctf_field_class_int *length_fc;
-};
-
-struct ctf_event_class
-{
-    GString *name;
-    uint64_t id;
-    GString *emf_uri;
-    bt_event_class_log_level log_level;
-    bool is_translated;
-    bool is_log_level_set;
-
-    /* Owned by this */
-    struct ctf_field_class *spec_context_fc;
-
-    /* Owned by this */
-    struct ctf_field_class *payload_fc;
-
-    /* Weak, set during translation */
-    bt_event_class *ir_ec;
-};
-
-struct ctf_stream_class
-{
-    uint64_t id;
-    bool is_translated;
-    bool packets_have_ts_begin;
-    bool packets_have_ts_end;
-    bool has_discarded_events;
-    bool has_discarded_packets;
-    bool discarded_events_have_default_cs;
-    bool discarded_packets_have_default_cs;
-
-    /* Owned by this */
-    struct ctf_field_class *packet_context_fc;
-
-    /* Owned by this */
-    struct ctf_field_class *event_header_fc;
-
-    /* Owned by this */
-    struct ctf_field_class *event_common_context_fc;
-
-    /* Array of `struct ctf_event_class *`, owned by this */
-    GPtrArray *event_classes;
-
-    /*
-     * Hash table mapping event class IDs to `struct ctf_event_class *`,
-     * weak.
-     */
-    GHashTable *event_classes_by_id;
-
-    /* Weak */
-    struct ctf_clock_class *default_clock_class;
-
-    /* Weak, set during translation */
-    bt_stream_class *ir_sc;
-};
-
-enum ctf_trace_class_env_entry_type
-{
-    CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT,
-    CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR,
-};
-
-struct ctf_trace_class_env_entry
-{
-    enum ctf_trace_class_env_entry_type type;
-    GString *name;
-
-    struct
-    {
-        int64_t i;
-        GString *str;
-    } value;
-};
-
-struct ctf_trace_class
-{
-    unsigned int major;
-    unsigned int minor;
-    bt_uuid_t uuid;
-    bool is_uuid_set;
-    enum ctf_byte_order default_byte_order;
-
-    /* Owned by this */
-    struct ctf_field_class *packet_header_fc;
-
-    uint64_t stored_value_count;
-
-    /* Array of `struct ctf_clock_class *` (owned by this) */
-    GPtrArray *clock_classes;
-
-    /* Array of `struct ctf_stream_class *` */
-    GPtrArray *stream_classes;
-
-    /* Array of `struct ctf_trace_class_env_entry` */
-    GArray *env_entries;
-
-    bool is_translated;
-
-    /* Weak, set during translation */
-    bt_trace_class *ir_tc;
-
-    struct
-    {
-        bool lttng_crash;
-        bool lttng_event_after_packet;
-        bool barectf_event_before_packet;
-    } quirks;
-};
-
-static inline ctf_field_class_bit_array *ctf_field_class_as_bit_array(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc ||
-                  (fc->type == CTF_FIELD_CLASS_TYPE_INT || fc->type == CTF_FIELD_CLASS_TYPE_ENUM ||
-                   fc->type == CTF_FIELD_CLASS_TYPE_FLOAT));
-    return (ctf_field_class_bit_array *) fc;
-}
-
-static inline ctf_field_class_int *ctf_field_class_as_int(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc ||
-                  (fc->type == CTF_FIELD_CLASS_TYPE_INT || fc->type == CTF_FIELD_CLASS_TYPE_ENUM));
-    return (ctf_field_class_int *) fc;
-}
-
-static inline ctf_field_class_enum *ctf_field_class_as_enum(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_ENUM);
-    return (ctf_field_class_enum *) fc;
-}
-
-static inline ctf_field_class_float *ctf_field_class_as_float(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_FLOAT);
-    return (ctf_field_class_float *) fc;
-}
-
-static inline ctf_field_class_string *ctf_field_class_as_string(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_STRING);
-    return (ctf_field_class_string *) fc;
-}
-
-static inline ctf_field_class_struct *ctf_field_class_as_struct(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_STRUCT);
-    return (ctf_field_class_struct *) fc;
-}
-
-static inline ctf_field_class_array_base *ctf_field_class_as_array_base(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc || (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
-                          fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE));
-    return (ctf_field_class_array_base *) fc;
-}
-
-static inline ctf_field_class_array *ctf_field_class_as_array(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_ARRAY);
-    return (ctf_field_class_array *) fc;
-}
-
-static inline ctf_field_class_sequence *ctf_field_class_as_sequence(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE);
-    return (ctf_field_class_sequence *) fc;
-}
-
-static inline ctf_field_class_variant *ctf_field_class_as_variant(ctf_field_class *fc)
-{
-    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_VARIANT);
-    return (ctf_field_class_variant *) fc;
-}
-
-static inline void ctf_field_class_destroy(struct ctf_field_class *fc);
-
-static inline void _ctf_field_class_init(struct ctf_field_class *fc, enum ctf_field_class_type type,
-                                         unsigned int alignment)
-{
-    BT_ASSERT(fc);
-    fc->type = type;
-    fc->alignment = alignment;
-    fc->in_ir = false;
-}
-
-static inline void _ctf_field_class_bit_array_init(struct ctf_field_class_bit_array *fc,
-                                                   enum ctf_field_class_type type)
-{
-    _ctf_field_class_init(&fc->base, type, 1);
-}
-
-static inline void _ctf_field_class_int_init(struct ctf_field_class_int *fc,
-                                             enum ctf_field_class_type type)
-{
-    _ctf_field_class_bit_array_init(&fc->base, type);
-    fc->meaning = CTF_FIELD_CLASS_MEANING_NONE;
-    fc->storing_index = -1;
-}
-
-static inline void ctf_field_path_init(struct ctf_field_path *field_path)
-{
-    BT_ASSERT(field_path);
-    field_path->path = g_array_new(FALSE, TRUE, sizeof(int64_t));
-    BT_ASSERT(field_path->path);
-}
-
-static inline void ctf_field_path_fini(struct ctf_field_path *field_path)
-{
-    BT_ASSERT(field_path);
-
-    if (field_path->path) {
-        g_array_free(field_path->path, TRUE);
-    }
-}
-
-static inline void _ctf_named_field_class_init(struct ctf_named_field_class *named_fc)
-{
-    BT_ASSERT(named_fc);
-    named_fc->name = g_string_new(NULL);
-    BT_ASSERT(named_fc->name);
-    named_fc->orig_name = g_string_new(NULL);
-    BT_ASSERT(named_fc->orig_name);
-}
-
-static inline void _ctf_named_field_class_fini(struct ctf_named_field_class *named_fc)
-{
-    BT_ASSERT(named_fc);
-
-    if (named_fc->name) {
-        g_string_free(named_fc->name, TRUE);
-    }
-
-    if (named_fc->orig_name) {
-        g_string_free(named_fc->orig_name, TRUE);
-    }
-
-    ctf_field_class_destroy(named_fc->fc);
-}
-
-static inline void _ctf_field_class_enum_mapping_init(struct ctf_field_class_enum_mapping *mapping)
-{
-    BT_ASSERT(mapping);
-    mapping->label = g_string_new(NULL);
-    BT_ASSERT(mapping->label);
-    mapping->ranges = g_array_new(FALSE, TRUE, sizeof(struct ctf_range));
-    BT_ASSERT(mapping->ranges);
-}
-
-static inline void _ctf_field_class_enum_mapping_fini(struct ctf_field_class_enum_mapping *mapping)
-{
-    BT_ASSERT(mapping);
-
-    if (mapping->label) {
-        g_string_free(mapping->label, TRUE);
-    }
-
-    if (mapping->ranges) {
-        g_array_free(mapping->ranges, TRUE);
-    }
-}
-
-static inline struct ctf_field_class_int *ctf_field_class_int_create(void)
-{
-    struct ctf_field_class_int *fc = g_new0(struct ctf_field_class_int, 1);
-
-    BT_ASSERT(fc);
-    _ctf_field_class_int_init(fc, CTF_FIELD_CLASS_TYPE_INT);
-    return fc;
-}
-
-static inline struct ctf_field_class_float *ctf_field_class_float_create(void)
-{
-    struct ctf_field_class_float *fc = g_new0(struct ctf_field_class_float, 1);
-
-    BT_ASSERT(fc);
-    _ctf_field_class_bit_array_init(&fc->base, CTF_FIELD_CLASS_TYPE_FLOAT);
-    return fc;
-}
-
-static inline struct ctf_field_class_string *ctf_field_class_string_create(void)
-{
-    struct ctf_field_class_string *fc = g_new0(struct ctf_field_class_string, 1);
-
-    BT_ASSERT(fc);
-    _ctf_field_class_init(&fc->base, CTF_FIELD_CLASS_TYPE_STRING, 8);
-    return fc;
-}
-
-static inline struct ctf_field_class_enum *ctf_field_class_enum_create(void)
-{
-    struct ctf_field_class_enum *fc = g_new0(struct ctf_field_class_enum, 1);
-
-    BT_ASSERT(fc);
-    _ctf_field_class_int_init(&fc->base, CTF_FIELD_CLASS_TYPE_ENUM);
-    fc->mappings = g_array_new(FALSE, TRUE, sizeof(struct ctf_field_class_enum_mapping));
-    BT_ASSERT(fc->mappings);
-    return fc;
-}
-
-static inline struct ctf_field_class_struct *ctf_field_class_struct_create(void)
-{
-    struct ctf_field_class_struct *fc = g_new0(struct ctf_field_class_struct, 1);
-
-    BT_ASSERT(fc);
-    _ctf_field_class_init(&fc->base, CTF_FIELD_CLASS_TYPE_STRUCT, 1);
-    fc->members = g_array_new(FALSE, TRUE, sizeof(struct ctf_named_field_class));
-    BT_ASSERT(fc->members);
-    fc->base.is_compound = true;
-    return fc;
-}
-
-static inline struct ctf_field_class_variant *ctf_field_class_variant_create(void)
-{
-    struct ctf_field_class_variant *fc = g_new0(struct ctf_field_class_variant, 1);
-
-    BT_ASSERT(fc);
-    _ctf_field_class_init(&fc->base, CTF_FIELD_CLASS_TYPE_VARIANT, 1);
-    fc->options = g_array_new(FALSE, TRUE, sizeof(struct ctf_named_field_class));
-    BT_ASSERT(fc->options);
-    fc->ranges = g_array_new(FALSE, TRUE, sizeof(struct ctf_field_class_variant_range));
-    BT_ASSERT(fc->ranges);
-    fc->tag_ref = g_string_new(NULL);
-    BT_ASSERT(fc->tag_ref);
-    ctf_field_path_init(&fc->tag_path);
-    fc->base.is_compound = true;
-    return fc;
-}
-
-static inline struct ctf_field_class_array *ctf_field_class_array_create(void)
-{
-    struct ctf_field_class_array *fc = g_new0(struct ctf_field_class_array, 1);
-
-    BT_ASSERT(fc);
-    _ctf_field_class_init(&fc->base.base, CTF_FIELD_CLASS_TYPE_ARRAY, 1);
-    fc->base.base.is_compound = true;
-    return fc;
-}
-
-static inline struct ctf_field_class_sequence *ctf_field_class_sequence_create(void)
-{
-    struct ctf_field_class_sequence *fc = g_new0(struct ctf_field_class_sequence, 1);
-
-    BT_ASSERT(fc);
-    _ctf_field_class_init(&fc->base.base, CTF_FIELD_CLASS_TYPE_SEQUENCE, 1);
-    fc->length_ref = g_string_new(NULL);
-    BT_ASSERT(fc->length_ref);
-    ctf_field_path_init(&fc->length_path);
-    fc->base.base.is_compound = true;
-    return fc;
-}
-
-static inline void _ctf_field_class_int_destroy(struct ctf_field_class_int *fc)
-{
-    BT_ASSERT(fc);
-    g_free(fc);
-}
-
-static inline void _ctf_field_class_enum_destroy(struct ctf_field_class_enum *fc)
-{
-    BT_ASSERT(fc);
-
-    if (fc->mappings) {
-        uint64_t i;
-
-        for (i = 0; i < fc->mappings->len; i++) {
-            struct ctf_field_class_enum_mapping *mapping =
-                &g_array_index(fc->mappings, struct ctf_field_class_enum_mapping, i);
-
-            _ctf_field_class_enum_mapping_fini(mapping);
-        }
-
-        g_array_free(fc->mappings, TRUE);
-    }
-
-    g_free(fc);
-}
-
-static inline void _ctf_field_class_float_destroy(struct ctf_field_class_float *fc)
-{
-    BT_ASSERT(fc);
-    g_free(fc);
-}
-
-static inline void _ctf_field_class_string_destroy(struct ctf_field_class_string *fc)
-{
-    BT_ASSERT(fc);
-    g_free(fc);
-}
-
-static inline void _ctf_field_class_struct_destroy(struct ctf_field_class_struct *fc)
-{
-    BT_ASSERT(fc);
-
-    if (fc->members) {
-        uint64_t i;
-
-        for (i = 0; i < fc->members->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                &g_array_index(fc->members, struct ctf_named_field_class, i);
-
-            _ctf_named_field_class_fini(named_fc);
-        }
-
-        g_array_free(fc->members, TRUE);
-    }
-
-    g_free(fc);
-}
-
-static inline void _ctf_field_class_array_base_fini(struct ctf_field_class_array_base *fc)
-{
-    BT_ASSERT(fc);
-    ctf_field_class_destroy(fc->elem_fc);
-}
-
-static inline void _ctf_field_class_array_destroy(struct ctf_field_class_array *fc)
-{
-    BT_ASSERT(fc);
-    _ctf_field_class_array_base_fini(&fc->base);
-    g_free(fc);
-}
-
-static inline void _ctf_field_class_sequence_destroy(struct ctf_field_class_sequence *fc)
-{
-    BT_ASSERT(fc);
-    _ctf_field_class_array_base_fini(&fc->base);
-
-    if (fc->length_ref) {
-        g_string_free(fc->length_ref, TRUE);
-    }
-
-    ctf_field_path_fini(&fc->length_path);
-    g_free(fc);
-}
-
-static inline void _ctf_field_class_variant_destroy(struct ctf_field_class_variant *fc)
-{
-    BT_ASSERT(fc);
-
-    if (fc->options) {
-        uint64_t i;
-
-        for (i = 0; i < fc->options->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                &g_array_index(fc->options, struct ctf_named_field_class, i);
-
-            _ctf_named_field_class_fini(named_fc);
-        }
-
-        g_array_free(fc->options, TRUE);
-    }
-
-    if (fc->ranges) {
-        g_array_free(fc->ranges, TRUE);
-    }
-
-    if (fc->tag_ref) {
-        g_string_free(fc->tag_ref, TRUE);
-    }
-
-    ctf_field_path_fini(&fc->tag_path);
-    g_free(fc);
-}
-
-static inline void ctf_field_class_destroy(struct ctf_field_class *fc)
-{
-    if (!fc) {
-        return;
-    }
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_INT:
-        _ctf_field_class_int_destroy(ctf_field_class_as_int(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_ENUM:
-        _ctf_field_class_enum_destroy(ctf_field_class_as_enum(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_FLOAT:
-        _ctf_field_class_float_destroy(ctf_field_class_as_float(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_STRING:
-        _ctf_field_class_string_destroy(ctf_field_class_as_string(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-        _ctf_field_class_struct_destroy(ctf_field_class_as_struct(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-        _ctf_field_class_array_destroy(ctf_field_class_as_array(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-        _ctf_field_class_sequence_destroy(ctf_field_class_as_sequence(fc));
-        break;
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-        _ctf_field_class_variant_destroy(ctf_field_class_as_variant(fc));
-        break;
-    default:
-        bt_common_abort();
-    }
-}
-
-static inline struct ctf_range *
-ctf_field_class_enum_mapping_borrow_range_by_index(struct ctf_field_class_enum_mapping *mapping,
-                                                   uint64_t index)
-{
-    BT_ASSERT_DBG(mapping);
-    BT_ASSERT_DBG(index < mapping->ranges->len);
-    return &g_array_index(mapping->ranges, struct ctf_range, index);
-}
-
-static inline struct ctf_field_class_enum_mapping *
-ctf_field_class_enum_borrow_mapping_by_index(struct ctf_field_class_enum *fc, uint64_t index)
-{
-    BT_ASSERT_DBG(fc);
-    BT_ASSERT_DBG(index < fc->mappings->len);
-    return &g_array_index(fc->mappings, struct ctf_field_class_enum_mapping, index);
-}
-
-static inline struct ctf_field_class_enum_mapping *
-ctf_field_class_enum_borrow_mapping_by_label(struct ctf_field_class_enum *fc, const char *label)
-{
-    struct ctf_field_class_enum_mapping *ret_mapping = NULL;
-    uint64_t i;
-
-    BT_ASSERT_DBG(fc);
-    BT_ASSERT_DBG(label);
-
-    for (i = 0; i < fc->mappings->len; i++) {
-        struct ctf_field_class_enum_mapping *mapping =
-            ctf_field_class_enum_borrow_mapping_by_index(fc, i);
-
-        if (strcmp(mapping->label->str, label) == 0) {
-            ret_mapping = mapping;
-            goto end;
-        }
-    }
-
-end:
-    return ret_mapping;
-}
-
-static inline void ctf_field_class_enum_map_range(struct ctf_field_class_enum *fc,
-                                                  const char *label, uint64_t u_lower,
-                                                  uint64_t u_upper)
-{
-    struct ctf_field_class_enum_mapping *mapping = NULL;
-    struct ctf_range range = {
-        .lower =
-            {
-                .u = u_lower,
-            },
-        .upper =
-            {
-                .u = u_upper,
-            },
-    };
-    uint64_t i;
-
-    BT_ASSERT(fc);
-    BT_ASSERT(label);
-
-    for (i = 0; i < fc->mappings->len; i++) {
-        mapping = ctf_field_class_enum_borrow_mapping_by_index(fc, i);
-
-        if (strcmp(mapping->label->str, label) == 0) {
-            break;
-        }
-    }
-
-    if (i == fc->mappings->len) {
-        mapping = NULL;
-    }
-
-    if (!mapping) {
-        g_array_set_size(fc->mappings, fc->mappings->len + 1);
-        mapping = ctf_field_class_enum_borrow_mapping_by_index(fc, fc->mappings->len - 1);
-        _ctf_field_class_enum_mapping_init(mapping);
-        g_string_assign(mapping->label, label);
-    }
-
-    g_array_append_val(mapping->ranges, range);
-}
-
-static inline struct ctf_named_field_class *
-ctf_field_class_struct_borrow_member_by_index(struct ctf_field_class_struct *fc, uint64_t index)
-{
-    BT_ASSERT_DBG(fc);
-    BT_ASSERT_DBG(index < fc->members->len);
-    return &g_array_index(fc->members, struct ctf_named_field_class, index);
-}
-
-static inline struct ctf_named_field_class *
-ctf_field_class_struct_borrow_member_by_name(struct ctf_field_class_struct *fc, const char *name)
-{
-    uint64_t i;
-    struct ctf_named_field_class *ret_named_fc = NULL;
-
-    BT_ASSERT_DBG(fc);
-    BT_ASSERT_DBG(name);
-
-    for (i = 0; i < fc->members->len; i++) {
-        struct ctf_named_field_class *named_fc =
-            ctf_field_class_struct_borrow_member_by_index(fc, i);
-
-        if (strcmp(name, named_fc->name->str) == 0) {
-            ret_named_fc = named_fc;
-            goto end;
-        }
-    }
-
-end:
-    return ret_named_fc;
-}
-
-static inline struct ctf_field_class *
-ctf_field_class_struct_borrow_member_field_class_by_name(struct ctf_field_class_struct *struct_fc,
-                                                         const char *name)
-{
-    struct ctf_named_field_class *named_fc = NULL;
-    struct ctf_field_class *fc = NULL;
-
-    if (!struct_fc) {
-        goto end;
-    }
-
-    named_fc = ctf_field_class_struct_borrow_member_by_name(struct_fc, name);
-    if (!named_fc) {
-        goto end;
-    }
-
-    fc = named_fc->fc;
-
-end:
-    return fc;
-}
-
-static inline struct ctf_field_class_int *
-ctf_field_class_struct_borrow_member_int_field_class_by_name(
-    struct ctf_field_class_struct *struct_fc, const char *name)
-{
-    struct ctf_field_class_int *int_fc = NULL;
-
-    int_fc = ctf_field_class_as_int(
-        ctf_field_class_struct_borrow_member_field_class_by_name(struct_fc, name));
-    if (!int_fc) {
-        goto end;
-    }
-
-    if (int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_INT &&
-        int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_ENUM) {
-        int_fc = NULL;
-        goto end;
-    }
-
-end:
-    return int_fc;
-}
-
-static inline void _ctf_named_field_class_unescape_orig_name(struct ctf_named_field_class *named_fc)
-{
-    const char *name = named_fc->orig_name->str;
-
-    if (name[0] == '_') {
-        name++;
-    }
-
-    g_string_assign(named_fc->name, name);
-}
-
-static inline void ctf_field_class_struct_append_member(struct ctf_field_class_struct *fc,
-                                                        const char *orig_name,
-                                                        struct ctf_field_class *member_fc)
-{
-    struct ctf_named_field_class *named_fc;
-
-    BT_ASSERT(fc);
-    BT_ASSERT(orig_name);
-    g_array_set_size(fc->members, fc->members->len + 1);
-
-    named_fc = &g_array_index(fc->members, struct ctf_named_field_class, fc->members->len - 1);
-    _ctf_named_field_class_init(named_fc);
-    g_string_assign(named_fc->orig_name, orig_name);
-    _ctf_named_field_class_unescape_orig_name(named_fc);
-    named_fc->fc = member_fc;
-
-    if (member_fc->alignment > fc->base.alignment) {
-        fc->base.alignment = member_fc->alignment;
-    }
-}
-
-static inline struct ctf_named_field_class *
-ctf_field_class_variant_borrow_option_by_index(struct ctf_field_class_variant *fc, uint64_t index)
-{
-    BT_ASSERT_DBG(fc);
-    BT_ASSERT_DBG(index < fc->options->len);
-    return &g_array_index(fc->options, struct ctf_named_field_class, index);
-}
-
-static inline struct ctf_named_field_class *
-ctf_field_class_variant_borrow_option_by_name(struct ctf_field_class_variant *fc, const char *name)
-{
-    uint64_t i;
-    struct ctf_named_field_class *ret_named_fc = NULL;
-
-    BT_ASSERT_DBG(fc);
-    BT_ASSERT_DBG(name);
-
-    for (i = 0; i < fc->options->len; i++) {
-        struct ctf_named_field_class *named_fc =
-            ctf_field_class_variant_borrow_option_by_index(fc, i);
-
-        if (strcmp(name, named_fc->name->str) == 0) {
-            ret_named_fc = named_fc;
-            goto end;
-        }
-    }
-
-end:
-    return ret_named_fc;
-}
-
-static inline struct ctf_field_class_variant_range *
-ctf_field_class_variant_borrow_range_by_index(struct ctf_field_class_variant *fc, uint64_t index)
-{
-    BT_ASSERT_DBG(fc);
-    BT_ASSERT_DBG(index < fc->ranges->len);
-    return &g_array_index(fc->ranges, struct ctf_field_class_variant_range, index);
-}
-
-static inline void ctf_field_class_variant_append_option(struct ctf_field_class_variant *fc,
-                                                         const char *orig_name,
-                                                         struct ctf_field_class *option_fc)
-{
-    struct ctf_named_field_class *named_fc;
-
-    BT_ASSERT(fc);
-    BT_ASSERT(orig_name);
-    g_array_set_size(fc->options, fc->options->len + 1);
-
-    named_fc = &g_array_index(fc->options, struct ctf_named_field_class, fc->options->len - 1);
-    _ctf_named_field_class_init(named_fc);
-    g_string_assign(named_fc->orig_name, orig_name);
-    _ctf_named_field_class_unescape_orig_name(named_fc);
-    named_fc->fc = option_fc;
-}
-
-static inline void ctf_field_class_variant_set_tag_field_class(struct ctf_field_class_variant *fc,
-                                                               struct ctf_field_class_enum *tag_fc)
-{
-    uint64_t option_i;
-
-    BT_ASSERT(fc);
-    BT_ASSERT(tag_fc);
-    fc->tag_fc = tag_fc;
-
-    for (option_i = 0; option_i < fc->options->len; option_i++) {
-        uint64_t range_i;
-        struct ctf_named_field_class *named_fc =
-            ctf_field_class_variant_borrow_option_by_index(fc, option_i);
-        struct ctf_field_class_enum_mapping *mapping;
-
-        mapping = ctf_field_class_enum_borrow_mapping_by_label(tag_fc, named_fc->orig_name->str);
-        if (!mapping) {
-            continue;
-        }
-
-        for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
-            struct ctf_range *range =
-                ctf_field_class_enum_mapping_borrow_range_by_index(mapping, range_i);
-            struct ctf_field_class_variant_range var_range;
-
-            var_range.range = *range;
-            var_range.option_index = option_i;
-            g_array_append_val(fc->ranges, var_range);
-        }
-    }
-}
-
-static inline struct ctf_field_class *
-ctf_field_class_compound_borrow_field_class_by_index(struct ctf_field_class *comp_fc,
-                                                     uint64_t index)
-{
-    struct ctf_field_class *fc = NULL;
-
-    switch (comp_fc->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_named_field_class *named_fc = ctf_field_class_struct_borrow_member_by_index(
-            (struct ctf_field_class_struct *) comp_fc, index);
-
-        BT_ASSERT_DBG(named_fc);
-        fc = named_fc->fc;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_named_field_class *named_fc = ctf_field_class_variant_borrow_option_by_index(
-            (struct ctf_field_class_variant *) comp_fc, index);
-
-        BT_ASSERT_DBG(named_fc);
-        fc = named_fc->fc;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-    {
-        struct ctf_field_class_array_base *array_fc = (struct ctf_field_class_array_base *) comp_fc;
-
-        fc = array_fc->elem_fc;
-        break;
-    }
-    default:
-        break;
-    }
-
-    return fc;
-}
-
-static inline uint64_t ctf_field_class_compound_get_field_class_count(struct ctf_field_class *fc)
-{
-    uint64_t field_count;
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = (struct ctf_field_class_struct *) fc;
-
-        field_count = struct_fc->members->len;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_field_class_variant *var_fc = (struct ctf_field_class_variant *) fc;
-
-        field_count = var_fc->options->len;
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-        /*
-         * Array and sequence types always contain a single
-         * member (the element type).
-         */
-        field_count = 1;
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    return field_count;
-}
-
-static inline int64_t
-ctf_field_class_compound_get_field_class_index_from_orig_name(struct ctf_field_class *fc,
-                                                              const char *orig_name)
-{
-    int64_t ret_index = -1;
-    uint64_t i;
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-    {
-        struct ctf_field_class_struct *struct_fc = (struct ctf_field_class_struct *) fc;
-
-        for (i = 0; i < struct_fc->members->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-
-            if (strcmp(orig_name, named_fc->orig_name->str) == 0) {
-                ret_index = (int64_t) i;
-                goto end;
-            }
-        }
-
-        break;
-    }
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-    {
-        struct ctf_field_class_variant *var_fc = (struct ctf_field_class_variant *) fc;
-
-        for (i = 0; i < var_fc->options->len; i++) {
-            struct ctf_named_field_class *named_fc =
-                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-
-            if (strcmp(orig_name, named_fc->orig_name->str) == 0) {
-                ret_index = (int64_t) i;
-                goto end;
-            }
-        }
-
-        break;
-    }
-    default:
-        break;
-    }
-
-end:
-    return ret_index;
-}
-
-static inline void ctf_field_path_append_index(struct ctf_field_path *fp, int64_t index)
-{
-    BT_ASSERT(fp);
-    g_array_append_val(fp->path, index);
-}
-
-static inline int64_t ctf_field_path_borrow_index_by_index(struct ctf_field_path *fp,
-                                                           uint64_t index)
-{
-    BT_ASSERT_DBG(fp);
-    BT_ASSERT_DBG(index < fp->path->len);
-    return g_array_index(fp->path, int64_t, index);
-}
-
-static inline void ctf_field_path_clear(struct ctf_field_path *fp)
-{
-    BT_ASSERT(fp);
-    g_array_set_size(fp->path, 0);
-}
-
-static inline const char *ctf_scope_string(enum ctf_scope scope)
-{
-    switch (scope) {
-    case CTF_SCOPE_PACKET_HEADER:
-        return "PACKET_HEADER";
-    case CTF_SCOPE_PACKET_CONTEXT:
-        return "PACKET_CONTEXT";
-    case CTF_SCOPE_EVENT_HEADER:
-        return "EVENT_HEADER";
-    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-        return "EVENT_COMMON_CONTEXT";
-    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-        return "EVENT_SPECIFIC_CONTEXT";
-    case CTF_SCOPE_EVENT_PAYLOAD:
-        return "EVENT_PAYLOAD";
-    default:
-        bt_common_abort();
-    }
-}
-
-static inline GString *ctf_field_path_string(struct ctf_field_path *path)
-{
-    GString *str = g_string_new(NULL);
-    uint64_t i;
-
-    BT_ASSERT(path);
-
-    if (!str) {
-        goto end;
-    }
-
-    g_string_append_printf(str, "[%s", ctf_scope_string(path->root));
-
-    for (i = 0; i < path->path->len; i++) {
-        g_string_append_printf(str, ", %" PRId64, ctf_field_path_borrow_index_by_index(path, i));
-    }
-
-    g_string_append(str, "]");
-
-end:
-    return str;
-}
-
-static inline struct ctf_field_class *
-ctf_field_path_borrow_field_class(struct ctf_field_path *field_path, struct ctf_trace_class *tc,
-                                  struct ctf_stream_class *sc, struct ctf_event_class *ec)
-{
-    uint64_t i;
-    struct ctf_field_class *fc;
-
-    switch (field_path->root) {
-    case CTF_SCOPE_PACKET_HEADER:
-        fc = tc->packet_header_fc;
-        break;
-    case CTF_SCOPE_PACKET_CONTEXT:
-        fc = sc->packet_context_fc;
-        break;
-    case CTF_SCOPE_EVENT_HEADER:
-        fc = sc->event_header_fc;
-        break;
-    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-        fc = sc->event_common_context_fc;
-        break;
-    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-        fc = ec->spec_context_fc;
-        break;
-    case CTF_SCOPE_EVENT_PAYLOAD:
-        fc = ec->payload_fc;
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    BT_ASSERT_DBG(fc);
-
-    for (i = 0; i < field_path->path->len; i++) {
-        int64_t child_index = ctf_field_path_borrow_index_by_index(field_path, i);
-        struct ctf_field_class *child_fc =
-            ctf_field_class_compound_borrow_field_class_by_index(fc, child_index);
-        BT_ASSERT_DBG(child_fc);
-        fc = child_fc;
-    }
-
-    BT_ASSERT_DBG(fc);
-    return fc;
-}
-
-static inline struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc);
-
-static inline void ctf_field_class_bit_array_copy_content(struct ctf_field_class_bit_array *dst_fc,
-                                                          struct ctf_field_class_bit_array *src_fc)
-{
-    BT_ASSERT(dst_fc);
-    BT_ASSERT(src_fc);
-    dst_fc->byte_order = src_fc->byte_order;
-    dst_fc->size = src_fc->size;
-}
-
-static inline void ctf_field_class_int_copy_content(struct ctf_field_class_int *dst_fc,
-                                                    struct ctf_field_class_int *src_fc)
-{
-    ctf_field_class_bit_array_copy_content(&dst_fc->base, &src_fc->base);
-    dst_fc->meaning = src_fc->meaning;
-    dst_fc->is_signed = src_fc->is_signed;
-    dst_fc->disp_base = src_fc->disp_base;
-    dst_fc->encoding = src_fc->encoding;
-    dst_fc->mapped_clock_class = src_fc->mapped_clock_class;
-    dst_fc->storing_index = src_fc->storing_index;
-}
-
-static inline struct ctf_field_class_int *_ctf_field_class_int_copy(struct ctf_field_class_int *fc)
-{
-    struct ctf_field_class_int *copy_fc = ctf_field_class_int_create();
-
-    BT_ASSERT(copy_fc);
-    ctf_field_class_int_copy_content(copy_fc, fc);
-    return copy_fc;
-}
-
-static inline struct ctf_field_class_enum *
-_ctf_field_class_enum_copy(struct ctf_field_class_enum *fc)
-{
-    struct ctf_field_class_enum *copy_fc = ctf_field_class_enum_create();
-    uint64_t i;
-
-    BT_ASSERT(copy_fc);
-    ctf_field_class_int_copy_content(&copy_fc->base, &fc->base);
-
-    for (i = 0; i < fc->mappings->len; i++) {
-        uint64_t range_i;
-
-        struct ctf_field_class_enum_mapping *mapping =
-            &g_array_index(fc->mappings, struct ctf_field_class_enum_mapping, i);
-
-        for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
-            struct ctf_range *range = &g_array_index(mapping->ranges, struct ctf_range, range_i);
-
-            ctf_field_class_enum_map_range(copy_fc, mapping->label->str, range->lower.u,
-                                           range->upper.u);
-        }
-    }
-
-    return copy_fc;
-}
-
-static inline struct ctf_field_class_float *
-_ctf_field_class_float_copy(struct ctf_field_class_float *fc)
-{
-    struct ctf_field_class_float *copy_fc = ctf_field_class_float_create();
-
-    BT_ASSERT(copy_fc);
-    ctf_field_class_bit_array_copy_content(&copy_fc->base, &fc->base);
-    return copy_fc;
-}
-
-static inline struct ctf_field_class_string *
-_ctf_field_class_string_copy(struct ctf_field_class_string *fc)
-{
-    struct ctf_field_class_string *copy_fc = ctf_field_class_string_create();
-
-    BT_ASSERT(copy_fc);
-    return copy_fc;
-}
-
-static inline struct ctf_field_class_struct *
-_ctf_field_class_struct_copy(struct ctf_field_class_struct *fc)
-{
-    struct ctf_field_class_struct *copy_fc = ctf_field_class_struct_create();
-    uint64_t i;
-
-    BT_ASSERT(copy_fc);
-
-    for (i = 0; i < fc->members->len; i++) {
-        struct ctf_named_field_class *named_fc =
-            &g_array_index(fc->members, struct ctf_named_field_class, i);
-
-        ctf_field_class_struct_append_member(copy_fc, named_fc->name->str,
-                                             ctf_field_class_copy(named_fc->fc));
-    }
-
-    return copy_fc;
-}
-
-static inline void ctf_field_path_copy_content(struct ctf_field_path *dst_fp,
-                                               struct ctf_field_path *src_fp)
-{
-    uint64_t i;
-
-    BT_ASSERT(dst_fp);
-    BT_ASSERT(src_fp);
-    dst_fp->root = src_fp->root;
-    ctf_field_path_clear(dst_fp);
-
-    for (i = 0; i < src_fp->path->len; i++) {
-        int64_t index = ctf_field_path_borrow_index_by_index(src_fp, i);
-
-        ctf_field_path_append_index(dst_fp, index);
-    }
-}
-
-static inline struct ctf_field_class_variant *
-_ctf_field_class_variant_copy(struct ctf_field_class_variant *fc)
-{
-    struct ctf_field_class_variant *copy_fc = ctf_field_class_variant_create();
-    uint64_t i;
-
-    BT_ASSERT(copy_fc);
-
-    for (i = 0; i < fc->options->len; i++) {
-        struct ctf_named_field_class *named_fc =
-            &g_array_index(fc->options, struct ctf_named_field_class, i);
-
-        ctf_field_class_variant_append_option(copy_fc, named_fc->name->str,
-                                              ctf_field_class_copy(named_fc->fc));
-    }
-
-    for (i = 0; i < fc->ranges->len; i++) {
-        struct ctf_field_class_variant_range *range =
-            &g_array_index(fc->ranges, struct ctf_field_class_variant_range, i);
-
-        g_array_append_val(copy_fc->ranges, *range);
-    }
-
-    ctf_field_path_copy_content(&copy_fc->tag_path, &fc->tag_path);
-    g_string_assign(copy_fc->tag_ref, fc->tag_ref->str);
-    copy_fc->stored_tag_index = fc->stored_tag_index;
-    return copy_fc;
-}
-
-static inline void
-ctf_field_class_array_base_copy_content(struct ctf_field_class_array_base *dst_fc,
-                                        struct ctf_field_class_array_base *src_fc)
-{
-    BT_ASSERT(dst_fc);
-    BT_ASSERT(src_fc);
-    dst_fc->elem_fc = ctf_field_class_copy(src_fc->elem_fc);
-    dst_fc->is_text = src_fc->is_text;
-}
-
-static inline struct ctf_field_class_array *
-_ctf_field_class_array_copy(struct ctf_field_class_array *fc)
-{
-    struct ctf_field_class_array *copy_fc = ctf_field_class_array_create();
-
-    BT_ASSERT(copy_fc);
-    ctf_field_class_array_base_copy_content(&copy_fc->base, &fc->base);
-    copy_fc->length = fc->length;
-    return copy_fc;
-}
-
-static inline struct ctf_field_class_sequence *
-_ctf_field_class_sequence_copy(struct ctf_field_class_sequence *fc)
-{
-    struct ctf_field_class_sequence *copy_fc = ctf_field_class_sequence_create();
-
-    BT_ASSERT(copy_fc);
-    ctf_field_class_array_base_copy_content(&copy_fc->base, &fc->base);
-    ctf_field_path_copy_content(&copy_fc->length_path, &fc->length_path);
-    g_string_assign(copy_fc->length_ref, fc->length_ref->str);
-    copy_fc->stored_length_index = fc->stored_length_index;
-    return copy_fc;
-}
-
-static inline struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc)
-{
-    struct ctf_field_class *copy_fc = NULL;
-
-    if (!fc) {
-        goto end;
-    }
-
-    /*
-     * Translation should not have happened yet.
-     */
-    BT_ASSERT(!fc->ir_fc);
-
-    switch (fc->type) {
-    case CTF_FIELD_CLASS_TYPE_INT:
-        copy_fc = &_ctf_field_class_int_copy(ctf_field_class_as_int(fc))->base.base;
-        break;
-    case CTF_FIELD_CLASS_TYPE_ENUM:
-        copy_fc = &_ctf_field_class_enum_copy(ctf_field_class_as_enum(fc))->base.base.base;
-        break;
-    case CTF_FIELD_CLASS_TYPE_FLOAT:
-        copy_fc = &_ctf_field_class_float_copy(ctf_field_class_as_float(fc))->base.base;
-        break;
-    case CTF_FIELD_CLASS_TYPE_STRING:
-        copy_fc = &_ctf_field_class_string_copy(ctf_field_class_as_string(fc))->base;
-        break;
-    case CTF_FIELD_CLASS_TYPE_STRUCT:
-        copy_fc = &_ctf_field_class_struct_copy(ctf_field_class_as_struct(fc))->base;
-        break;
-    case CTF_FIELD_CLASS_TYPE_ARRAY:
-        copy_fc = &_ctf_field_class_array_copy(ctf_field_class_as_array(fc))->base.base;
-        break;
-    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-        copy_fc = &_ctf_field_class_sequence_copy(ctf_field_class_as_sequence(fc))->base.base;
-        break;
-    case CTF_FIELD_CLASS_TYPE_VARIANT:
-        copy_fc = &_ctf_field_class_variant_copy(ctf_field_class_as_variant(fc))->base;
-        break;
-    default:
-        bt_common_abort();
-    }
-
-    copy_fc->type = fc->type;
-    copy_fc->alignment = fc->alignment;
-    copy_fc->in_ir = fc->in_ir;
-
-end:
-    return copy_fc;
-}
-
-static inline struct ctf_event_class *ctf_event_class_create(void)
-{
-    struct ctf_event_class *ec = g_new0(struct ctf_event_class, 1);
-
-    BT_ASSERT(ec);
-    ec->name = g_string_new(NULL);
-    BT_ASSERT(ec->name);
-    ec->emf_uri = g_string_new(NULL);
-    BT_ASSERT(ec->emf_uri);
-    ec->is_log_level_set = false;
-    return ec;
-}
-
-static inline void ctf_event_class_set_log_level(struct ctf_event_class *ec,
-                                                 enum bt_event_class_log_level log_level)
-{
-    BT_ASSERT(ec);
-    ec->log_level = log_level;
-    ec->is_log_level_set = true;
-}
-
-static inline void ctf_event_class_destroy(struct ctf_event_class *ec)
-{
-    if (!ec) {
-        return;
-    }
-
-    if (ec->name) {
-        g_string_free(ec->name, TRUE);
-    }
-
-    if (ec->emf_uri) {
-        g_string_free(ec->emf_uri, TRUE);
-    }
-
-    ctf_field_class_destroy(ec->spec_context_fc);
-    ctf_field_class_destroy(ec->payload_fc);
-    g_free(ec);
-}
-
-static inline struct ctf_stream_class *ctf_stream_class_create(void)
-{
-    struct ctf_stream_class *sc = g_new0(struct ctf_stream_class, 1);
-
-    BT_ASSERT(sc);
-    sc->event_classes = g_ptr_array_new_with_free_func((GDestroyNotify) ctf_event_class_destroy);
-    BT_ASSERT(sc->event_classes);
-    sc->event_classes_by_id = g_hash_table_new(g_direct_hash, g_direct_equal);
-    BT_ASSERT(sc->event_classes_by_id);
-    return sc;
-}
-
-static inline void ctf_stream_class_destroy(struct ctf_stream_class *sc)
-{
-    if (!sc) {
-        return;
-    }
-
-    if (sc->event_classes) {
-        g_ptr_array_free(sc->event_classes, TRUE);
-    }
-
-    if (sc->event_classes_by_id) {
-        g_hash_table_destroy(sc->event_classes_by_id);
-    }
-
-    ctf_field_class_destroy(sc->packet_context_fc);
-    ctf_field_class_destroy(sc->event_header_fc);
-    ctf_field_class_destroy(sc->event_common_context_fc);
-    g_free(sc);
-}
-
-static inline void ctf_stream_class_append_event_class(struct ctf_stream_class *sc,
-                                                       struct ctf_event_class *ec)
-{
-    g_ptr_array_add(sc->event_classes, ec);
-    g_hash_table_insert(sc->event_classes_by_id, GUINT_TO_POINTER((guint) ec->id), ec);
-}
-
-static inline struct ctf_event_class *
-ctf_stream_class_borrow_event_class_by_id(struct ctf_stream_class *sc, uint64_t type)
-{
-    BT_ASSERT_DBG(sc);
-    return (struct ctf_event_class *) g_hash_table_lookup(sc->event_classes_by_id,
-                                                          GUINT_TO_POINTER((guint) type));
-}
-
-static inline void _ctf_trace_class_env_entry_init(struct ctf_trace_class_env_entry *entry)
-{
-    BT_ASSERT(entry);
-    entry->name = g_string_new(NULL);
-    BT_ASSERT(entry->name);
-    entry->value.str = g_string_new(NULL);
-    BT_ASSERT(entry->value.str);
-}
-
-static inline void _ctf_trace_class_env_entry_fini(struct ctf_trace_class_env_entry *entry)
-{
-    BT_ASSERT(entry);
-
-    if (entry->name) {
-        g_string_free(entry->name, TRUE);
-    }
-
-    if (entry->value.str) {
-        g_string_free(entry->value.str, TRUE);
-    }
-}
-
-static inline struct ctf_clock_class *ctf_clock_class_create(void)
-{
-    struct ctf_clock_class *cc = g_new0(struct ctf_clock_class, 1);
-
-    BT_ASSERT(cc);
-    cc->name = g_string_new(NULL);
-    BT_ASSERT(cc->name);
-    cc->description = g_string_new(NULL);
-    BT_ASSERT(cc->description);
-    return cc;
-}
-
-static inline void ctf_clock_class_destroy(struct ctf_clock_class *cc)
-{
-    if (!cc) {
-        return;
-    }
-
-    if (cc->name) {
-        g_string_free(cc->name, TRUE);
-    }
-
-    if (cc->description) {
-        g_string_free(cc->description, TRUE);
-    }
-
-    bt_clock_class_put_ref(cc->ir_cc);
-    g_free(cc);
-}
-
-static inline struct ctf_trace_class *ctf_trace_class_create(void)
-{
-    struct ctf_trace_class *tc = g_new0(struct ctf_trace_class, 1);
-
-    BT_ASSERT(tc);
-    tc->default_byte_order = CTF_BYTE_ORDER_UNKNOWN;
-    tc->clock_classes = g_ptr_array_new_with_free_func((GDestroyNotify) ctf_clock_class_destroy);
-    BT_ASSERT(tc->clock_classes);
-    tc->stream_classes = g_ptr_array_new_with_free_func((GDestroyNotify) ctf_stream_class_destroy);
-    BT_ASSERT(tc->stream_classes);
-    tc->env_entries = g_array_new(FALSE, TRUE, sizeof(struct ctf_trace_class_env_entry));
-    return tc;
-}
-
-static inline void ctf_trace_class_destroy(struct ctf_trace_class *tc)
-{
-    if (!tc) {
-        return;
-    }
-
-    ctf_field_class_destroy(tc->packet_header_fc);
-
-    if (tc->clock_classes) {
-        g_ptr_array_free(tc->clock_classes, TRUE);
-    }
-
-    if (tc->stream_classes) {
-        g_ptr_array_free(tc->stream_classes, TRUE);
-    }
-
-    if (tc->env_entries) {
-        uint64_t i;
-
-        for (i = 0; i < tc->env_entries->len; i++) {
-            struct ctf_trace_class_env_entry *entry =
-                &g_array_index(tc->env_entries, struct ctf_trace_class_env_entry, i);
-
-            _ctf_trace_class_env_entry_fini(entry);
-        }
-
-        g_array_free(tc->env_entries, TRUE);
-    }
-
-    g_free(tc);
-}
-
-static inline void ctf_trace_class_append_env_entry(struct ctf_trace_class *tc, const char *name,
-                                                    enum ctf_trace_class_env_entry_type type,
-                                                    const char *str_value, int64_t i_value)
-{
-    struct ctf_trace_class_env_entry *entry;
-
-    BT_ASSERT(tc);
-    BT_ASSERT(name);
-    g_array_set_size(tc->env_entries, tc->env_entries->len + 1);
-
-    entry =
-        &g_array_index(tc->env_entries, struct ctf_trace_class_env_entry, tc->env_entries->len - 1);
-    entry->type = type;
-    _ctf_trace_class_env_entry_init(entry);
-    g_string_assign(entry->name, name);
-
-    if (str_value) {
-        g_string_assign(entry->value.str, str_value);
-    }
-
-    entry->value.i = i_value;
-}
-
-static inline struct ctf_stream_class *
-ctf_trace_class_borrow_stream_class_by_id(struct ctf_trace_class *tc, uint64_t id)
-{
-    uint64_t i;
-    struct ctf_stream_class *ret_sc = NULL;
-
-    BT_ASSERT_DBG(tc);
-
-    for (i = 0; i < tc->stream_classes->len; i++) {
-        struct ctf_stream_class *sc = (struct ctf_stream_class *) tc->stream_classes->pdata[i];
-
-        if (sc->id == id) {
-            ret_sc = sc;
-            goto end;
-        }
-    }
-
-end:
-    return ret_sc;
-}
-
-static inline struct ctf_clock_class *
-ctf_trace_class_borrow_clock_class_by_name(struct ctf_trace_class *tc, const char *name)
-{
-    uint64_t i;
-    struct ctf_clock_class *ret_cc = NULL;
-
-    BT_ASSERT_DBG(tc);
-    BT_ASSERT_DBG(name);
-
-    for (i = 0; i < tc->clock_classes->len; i++) {
-        struct ctf_clock_class *cc = (struct ctf_clock_class *) tc->clock_classes->pdata[i];
-
-        BT_ASSERT_DBG(cc->name);
-        if (strcmp(cc->name->str, name) == 0) {
-            ret_cc = cc;
-            goto end;
-        }
-    }
-
-end:
-    return ret_cc;
-}
-
-static inline struct ctf_trace_class_env_entry *
-ctf_trace_class_borrow_env_entry_by_index(struct ctf_trace_class *tc, uint64_t index)
-{
-    BT_ASSERT_DBG(tc);
-    BT_ASSERT_DBG(index < tc->env_entries->len);
-    return &g_array_index(tc->env_entries, struct ctf_trace_class_env_entry, index);
-}
-
-static inline struct ctf_trace_class_env_entry *
-ctf_trace_class_borrow_env_entry_by_name(struct ctf_trace_class *tc, const char *name)
-{
-    struct ctf_trace_class_env_entry *ret_entry = NULL;
-    uint64_t i;
-
-    BT_ASSERT_DBG(tc);
-    BT_ASSERT_DBG(name);
-
-    for (i = 0; i < tc->env_entries->len; i++) {
-        struct ctf_trace_class_env_entry *env_entry =
-            ctf_trace_class_borrow_env_entry_by_index(tc, i);
-
-        if (strcmp(env_entry->name->str, name) == 0) {
-            ret_entry = env_entry;
-            goto end;
-        }
-    }
-
-end:
-    return ret_entry;
-}
-
-#endif /* _CTF_META_H */
diff --git a/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.cpp b/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.cpp
deleted file mode 100644 (file)
index 3b7224a..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP       self_comp
-#define BT_COMP_LOG_SELF_COMP_CLASS self_comp_class
-#define BT_LOG_OUTPUT_LEVEL         log_level
-#define BT_LOG_TAG                  "PLUGIN/CTF/META/DECODER-DECODE-PACKET"
-#include "logging/comp-logging.h"
-
-#include "decoder-packetized-file-stream-to-buf.hpp"
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include "common/assert.h"
-#include "common/uuid.h"
-#include "compat/memstream.h"
-#include <babeltrace2/babeltrace.h>
-#include <glib.h>
-#include <string.h>
-
-#include "ast.hpp"
-#include "decoder.hpp"
-#include "scanner.hpp"
-#include "logging.hpp"
-
-#define TSDL_MAGIC 0x75d11d57
-
-struct ctf_metadata_decoder
-{
-    struct ctf_visitor_generate_ir *visitor;
-    bt_uuid_t uuid;
-    bool is_uuid_set;
-    int bo;
-    struct ctf_metadata_decoder_config config;
-};
-
-struct packet_header
-{
-    uint32_t magic;
-    bt_uuid_t uuid;
-    uint32_t checksum;
-    uint32_t content_size;
-    uint32_t packet_size;
-    uint8_t compression_scheme;
-    uint8_t encryption_scheme;
-    uint8_t checksum_scheme;
-    uint8_t major;
-    uint8_t minor;
-} __attribute__((__packed__));
-
-static int decode_packet(FILE *in_fp, FILE *out_fp, int byte_order, bool *is_uuid_set,
-                         uint8_t *uuid, bt_logging_level log_level, bt_self_component *self_comp,
-                         bt_self_component_class *self_comp_class)
-{
-    struct packet_header header;
-    size_t readlen, writelen, toread;
-    uint8_t buf[512 + 1]; /* + 1 for debug-mode \0 */
-    int ret = 0;
-    const long offset = ftell(in_fp);
-
-    if (offset < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE_ERRNO(BT_COMP_LOG_SELF_COMP,
-                                        "Failed to get current metadata file position", ".");
-        goto error;
-    }
-    BT_COMP_LOGD("Decoding metadata packet: offset=%ld", offset);
-    readlen = fread(&header, sizeof(header), 1, in_fp);
-    if (feof(in_fp) != 0) {
-        BT_COMP_LOGI("Reached end of file: offset=%ld", ftell(in_fp));
-        goto end;
-    }
-    if (readlen < 1) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot decode metadata packet: offset=%ld",
-                                                 offset);
-        goto error;
-    }
-
-    if (byte_order != BYTE_ORDER) {
-        header.magic = GUINT32_SWAP_LE_BE(header.magic);
-        header.checksum = GUINT32_SWAP_LE_BE(header.checksum);
-        header.content_size = GUINT32_SWAP_LE_BE(header.content_size);
-        header.packet_size = GUINT32_SWAP_LE_BE(header.packet_size);
-    }
-
-    if (header.compression_scheme) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Metadata packet compression is not supported as of this version: "
-            "compression-scheme=%u, offset=%ld",
-            (unsigned int) header.compression_scheme, offset);
-        goto error;
-    }
-
-    if (header.encryption_scheme) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Metadata packet encryption is not supported as of this version: "
-            "encryption-scheme=%u, offset=%ld",
-            (unsigned int) header.encryption_scheme, offset);
-        goto error;
-    }
-
-    if (header.checksum || header.checksum_scheme) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Metadata packet checksum verification is not supported as of this version: "
-            "checksum-scheme=%u, checksum=%x, offset=%ld",
-            (unsigned int) header.checksum_scheme, header.checksum, offset);
-        goto error;
-    }
-
-    if (!ctf_metadata_decoder_is_packet_version_valid(header.major, header.minor)) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid metadata packet version: "
-                                                 "version=%u.%u, offset=%ld",
-                                                 header.major, header.minor, offset);
-        goto error;
-    }
-
-    /* Set expected trace UUID if not set; otherwise validate it */
-    if (is_uuid_set) {
-        if (!*is_uuid_set) {
-            bt_uuid_copy(uuid, header.uuid);
-            *is_uuid_set = true;
-        } else if (bt_uuid_compare(header.uuid, uuid)) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Metadata UUID mismatch between packets of the same stream: "
-                "packet-uuid=\"" BT_UUID_FMT "\", "
-                "expected-uuid=\"" BT_UUID_FMT "\", "
-                "offset=%ld",
-                BT_UUID_FMT_VALUES(header.uuid), BT_UUID_FMT_VALUES(uuid), offset);
-            goto error;
-        }
-    }
-
-    if ((header.content_size / CHAR_BIT) < sizeof(header)) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Bad metadata packet content size: content-size=%u, "
-            "offset=%ld",
-            header.content_size, offset);
-        goto error;
-    }
-
-    toread = header.content_size / CHAR_BIT - sizeof(header);
-
-    for (;;) {
-        size_t loop_read;
-
-        loop_read = MIN(sizeof(buf) - 1, toread);
-        readlen = fread(buf, sizeof(uint8_t), loop_read, in_fp);
-        if (ferror(in_fp)) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot read metadata packet buffer: "
-                                                     "offset=%ld, read-size=%zu",
-                                                     ftell(in_fp), loop_read);
-            goto error;
-        }
-        if (readlen > loop_read) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("fread returned more byte than expected: "
-                                                     "read-size-asked=%zu, read-size-returned=%zu",
-                                                     loop_read, readlen);
-            goto error;
-        }
-
-        writelen = fwrite(buf, sizeof(uint8_t), readlen, out_fp);
-        if (writelen < readlen || ferror(out_fp)) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot write decoded metadata text to buffer: "
-                "read-offset=%ld, write-size=%zu",
-                ftell(in_fp), readlen);
-            goto error;
-        }
-
-        toread -= readlen;
-        if (toread == 0) {
-            int fseek_ret;
-
-            /* Read leftover padding */
-            toread = (header.packet_size - header.content_size) / CHAR_BIT;
-            fseek_ret = fseek(in_fp, toread, SEEK_CUR);
-            if (fseek_ret < 0) {
-                BT_COMP_LOGW_STR("Missing padding at the end of the metadata stream.");
-            }
-            break;
-        }
-    }
-
-    goto end;
-
-error:
-    ret = -1;
-
-end:
-    return ret;
-}
-
-BT_HIDDEN
-int ctf_metadata_decoder_packetized_file_stream_to_buf(FILE *fp, char **buf, int byte_order,
-                                                       bool *is_uuid_set, uint8_t *uuid,
-                                                       bt_logging_level log_level,
-                                                       bt_self_component *self_comp,
-                                                       bt_self_component_class *self_comp_class)
-{
-    FILE *out_fp;
-    size_t size;
-    int ret = 0;
-    int tret;
-    size_t packet_index = 0;
-
-    out_fp = bt_open_memstream(buf, &size);
-    if (!out_fp) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot open memory stream: %s.", strerror(errno));
-        goto error;
-    }
-
-    for (;;) {
-        if (feof(fp) != 0) {
-            break;
-        }
-
-        tret = decode_packet(fp, out_fp, byte_order, is_uuid_set, uuid, log_level, self_comp,
-                             self_comp_class);
-        if (tret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot decode packet: index=%zu",
-                                                     packet_index);
-            goto error;
-        }
-
-        packet_index++;
-    }
-
-    /* Make sure the whole string ends with a null character */
-    tret = fputc('\0', out_fp);
-    if (tret == EOF) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Cannot append '\\0' to the decoded metadata buffer.");
-        goto error;
-    }
-
-    /* Close stream, which also flushes the buffer */
-    ret = bt_close_memstream(buf, &size, out_fp);
-    /*
-     * See fclose(3). Further access to out_fp after both success
-     * and error, even through another bt_close_memstream(), results
-     * in undefined behavior. Nullify out_fp to ensure we don't
-     * fclose it twice on error.
-     */
-    out_fp = NULL;
-    if (ret < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE_ERRNO(BT_COMP_LOG_SELF_COMP, "Cannot close memory stream", ".");
-        goto error;
-    }
-
-    goto end;
-
-error:
-    ret = -1;
-
-    if (out_fp) {
-        if (bt_close_memstream(buf, &size, out_fp)) {
-            BT_COMP_LOGE_ERRNO("Cannot close memory stream", ".");
-        }
-    }
-
-    if (*buf) {
-        free(*buf);
-        *buf = NULL;
-    }
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.hpp b/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.hpp
deleted file mode 100644 (file)
index 9d17596..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Efficios Inc.
- */
-
-#ifndef SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF
-#define SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#include <babeltrace2/babeltrace.h>
-
-BT_HIDDEN
-int ctf_metadata_decoder_packetized_file_stream_to_buf(FILE *fp, char **buf, int byte_order,
-                                                       bool *is_uuid_set, uint8_t *uuid,
-                                                       bt_logging_level log_level,
-                                                       bt_self_component *self_comp,
-                                                       bt_self_component_class *self_comp_class);
-
-#endif /* SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF */
diff --git a/src/plugins/ctf/common/metadata/decoder.cpp b/src/plugins/ctf/common/metadata/decoder.cpp
deleted file mode 100644 (file)
index 740cbf0..0000000
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP       (mdec->config.self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (mdec->config.self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL         (mdec->config.log_level)
-#define BT_LOG_TAG                  "PLUGIN/CTF/META/DECODER"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include "common/assert.h"
-#include "common/uuid.h"
-#include "compat/memstream.h"
-#include <babeltrace2/babeltrace.h>
-#include <glib.h>
-#include <string.h>
-
-#include "ast.hpp"
-#include "decoder.hpp"
-#include "scanner.hpp"
-#include "logging.hpp"
-#include "parser-wrap.hpp"
-#include "decoder-packetized-file-stream-to-buf.hpp"
-
-#define TSDL_MAGIC 0x75d11d57
-
-struct ctf_metadata_decoder
-{
-    struct ctf_scanner *scanner;
-    GString *text;
-    struct ctf_visitor_generate_ir *visitor;
-    bt_uuid_t uuid;
-    bool is_uuid_set;
-    int bo;
-    struct ctf_metadata_decoder_config config;
-    struct meta_log_config log_cfg;
-    bool has_checked_plaintext_signature;
-};
-
-struct packet_header
-{
-    uint32_t magic;
-    bt_uuid_t uuid;
-    uint32_t checksum;
-    uint32_t content_size;
-    uint32_t packet_size;
-    uint8_t compression_scheme;
-    uint8_t encryption_scheme;
-    uint8_t checksum_scheme;
-    uint8_t major;
-    uint8_t minor;
-} __attribute__((__packed__));
-
-BT_HIDDEN
-int ctf_metadata_decoder_is_packetized(FILE *fp, bool *is_packetized, int *byte_order,
-                                       bt_logging_level log_level, bt_self_component *self_comp)
-{
-    uint32_t magic;
-    size_t len;
-    int ret = 0;
-
-    *is_packetized = false;
-    len = fread(&magic, sizeof(magic), 1, fp);
-    if (len != 1) {
-        BT_COMP_LOG_CUR_LVL(
-            BT_LOG_INFO, log_level, self_comp,
-            "Cannot read first metadata packet header: assuming the stream is not packetized.");
-        ret = -1;
-        goto end;
-    }
-
-    if (byte_order) {
-        if (magic == TSDL_MAGIC) {
-            *is_packetized = true;
-            *byte_order = BYTE_ORDER;
-        } else if (magic == GUINT32_SWAP_LE_BE(TSDL_MAGIC)) {
-            *is_packetized = true;
-            *byte_order = BYTE_ORDER == BIG_ENDIAN ? LITTLE_ENDIAN : BIG_ENDIAN;
-        }
-    }
-
-end:
-    rewind(fp);
-
-    return ret;
-}
-
-BT_HIDDEN
-struct ctf_metadata_decoder *
-ctf_metadata_decoder_create(const struct ctf_metadata_decoder_config *config)
-{
-    struct ctf_metadata_decoder *mdec = g_new0(struct ctf_metadata_decoder, 1);
-
-    BT_ASSERT(config);
-    BT_COMP_LOG_CUR_LVL(BT_LOG_DEBUG, config->log_level, config->self_comp,
-                        "Creating CTF metadata decoder: "
-                        "clock-class-offset-s=%" PRId64 ", "
-                        "clock-class-offset-ns=%" PRId64,
-                        config->clock_class_offset_s, config->clock_class_offset_ns);
-
-    if (!mdec) {
-        BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, config->log_level, config->self_comp,
-                            "Failed to allocate one CTF metadata decoder.");
-        goto end;
-    }
-
-    mdec->log_cfg.log_level = config->log_level;
-    mdec->log_cfg.self_comp = config->self_comp;
-    mdec->log_cfg.self_comp_class = config->self_comp_class;
-    mdec->scanner = ctf_scanner_alloc();
-    if (!mdec->scanner) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot allocate a metadata lexical scanner: "
-                                                 "mdec-addr=%p",
-                                                 mdec);
-        goto error;
-    }
-
-    mdec->text = g_string_new(NULL);
-    if (!mdec->text) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one GString: "
-                                                 "mdec-addr=%p",
-                                                 mdec);
-        goto error;
-    }
-
-    mdec->bo = -1;
-    mdec->config = *config;
-    mdec->visitor = ctf_visitor_generate_ir_create(config);
-    if (!mdec->visitor) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to create a CTF IR metadata AST visitor: "
-                                                 "mdec-addr=%p",
-                                                 mdec);
-        goto error;
-    }
-
-    BT_COMP_LOGD("Creating CTF metadata decoder: "
-                 "clock-class-offset-s=%" PRId64 ", "
-                 "clock-class-offset-ns=%" PRId64 ", addr=%p",
-                 config->clock_class_offset_s, config->clock_class_offset_ns, mdec);
-    goto end;
-
-error:
-    ctf_metadata_decoder_destroy(mdec);
-    mdec = NULL;
-
-end:
-    return mdec;
-}
-
-BT_HIDDEN
-void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder *mdec)
-{
-    if (!mdec) {
-        return;
-    }
-
-    if (mdec->scanner) {
-        ctf_scanner_free(mdec->scanner);
-    }
-
-    if (mdec->text) {
-        g_string_free(mdec->text, TRUE);
-    }
-
-    BT_COMP_LOGD("Destroying CTF metadata decoder: addr=%p", mdec);
-    ctf_visitor_generate_ir_destroy(mdec->visitor);
-    g_free(mdec);
-}
-
-BT_HIDDEN
-enum ctf_metadata_decoder_status
-ctf_metadata_decoder_append_content(struct ctf_metadata_decoder *mdec, FILE *fp)
-{
-    enum ctf_metadata_decoder_status status = CTF_METADATA_DECODER_STATUS_OK;
-    int ret;
-    char *buf = NULL;
-    bool close_fp = false;
-    long start_pos = -1;
-    bool is_packetized;
-
-    BT_ASSERT(mdec);
-    ret = ctf_metadata_decoder_is_packetized(fp, &is_packetized, &mdec->bo, mdec->config.log_level,
-                                             mdec->config.self_comp);
-    if (ret) {
-        status = CTF_METADATA_DECODER_STATUS_ERROR;
-        goto end;
-    }
-
-    if (is_packetized) {
-        BT_COMP_LOGI("Metadata stream is packetized: mdec-addr=%p", mdec);
-        ret = ctf_metadata_decoder_packetized_file_stream_to_buf(
-            fp, &buf, mdec->bo, &mdec->is_uuid_set, mdec->uuid, mdec->config.log_level,
-            mdec->config.self_comp, mdec->config.self_comp_class);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot decode packetized metadata packets to metadata text: "
-                "mdec-addr=%p, ret=%d",
-                mdec, ret);
-            status = CTF_METADATA_DECODER_STATUS_ERROR;
-            goto end;
-        }
-
-        if (strlen(buf) == 0) {
-            /* An empty metadata packet is OK. */
-            goto end;
-        }
-
-        /* Convert the real file pointer to a memory file pointer */
-        fp = bt_fmemopen(buf, strlen(buf), "rb");
-        close_fp = true;
-        if (!fp) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot memory-open metadata buffer: %s: "
-                                                     "mdec-addr=%p",
-                                                     strerror(errno), mdec);
-            status = CTF_METADATA_DECODER_STATUS_ERROR;
-            goto end;
-        }
-    } else if (!mdec->has_checked_plaintext_signature) {
-        unsigned int major, minor;
-        ssize_t nr_items;
-        const long init_pos = ftell(fp);
-
-        BT_COMP_LOGI("Metadata stream is plain text: mdec-addr=%p", mdec);
-
-        if (init_pos < 0) {
-            BT_COMP_LOGE_APPEND_CAUSE_ERRNO(BT_COMP_LOG_SELF_COMP,
-                                            "Failed to get current file position", ".");
-            status = CTF_METADATA_DECODER_STATUS_ERROR;
-            goto end;
-        }
-
-        /* Check text-only metadata header and version */
-        nr_items = fscanf(fp, "/* CTF %10u.%10u", &major, &minor);
-        if (nr_items < 2) {
-            BT_COMP_LOGW(
-                "Missing \"/* CTF major.minor\" signature in plain text metadata file stream: "
-                "mdec-addr=%p",
-                mdec);
-        }
-
-        BT_COMP_LOGI("Found metadata stream version in signature: version=%u.%u", major, minor);
-
-        if (!ctf_metadata_decoder_is_packet_version_valid(major, minor)) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Invalid metadata version found in plain text signature: "
-                "version=%u.%u, mdec-addr=%p",
-                major, minor, mdec);
-            status = CTF_METADATA_DECODER_STATUS_INVAL_VERSION;
-            goto end;
-        }
-
-        if (fseek(fp, init_pos, SEEK_SET)) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot seek metadata file stream to initial position: %s: "
-                "mdec-addr=%p",
-                strerror(errno), mdec);
-            status = CTF_METADATA_DECODER_STATUS_ERROR;
-            goto end;
-        }
-
-        mdec->has_checked_plaintext_signature = true;
-    }
-
-#if YYDEBUG
-    if (BT_LOG_ON_TRACE) {
-        yydebug = 1;
-    }
-#endif
-
-    /* Save the file's position: we'll seek back to append the plain text */
-    BT_ASSERT(fp);
-
-    if (mdec->config.keep_plain_text) {
-        start_pos = ftell(fp);
-    }
-
-    /* Append the metadata text content */
-    ret = ctf_scanner_append_ast(mdec->scanner, fp);
-    if (ret) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Cannot create the metadata AST out of the metadata text: "
-            "mdec-addr=%p",
-            mdec);
-        status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-        goto end;
-    }
-
-    /* We know it's complete: append plain text */
-    if (mdec->config.keep_plain_text) {
-        BT_ASSERT(start_pos != -1);
-        ret = fseek(fp, start_pos, SEEK_SET);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to seek file: ret=%d, mdec-addr=%p",
-                                                     ret, mdec);
-            status = CTF_METADATA_DECODER_STATUS_ERROR;
-            goto end;
-        }
-
-        ret = bt_common_append_file_content_to_g_string(mdec->text, fp);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to append to current plain text: "
-                                                     "ret=%d, mdec-addr=%p",
-                                                     ret, mdec);
-            status = CTF_METADATA_DECODER_STATUS_ERROR;
-            goto end;
-        }
-    }
-
-    ret = ctf_visitor_semantic_check(0, &mdec->scanner->ast->root, &mdec->log_cfg);
-    if (ret) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Validation of the metadata semantics failed: "
-                                                 "mdec-addr=%p",
-                                                 mdec);
-        status = CTF_METADATA_DECODER_STATUS_ERROR;
-        goto end;
-    }
-
-    if (mdec->config.create_trace_class) {
-        ret = ctf_visitor_generate_ir_visit_node(mdec->visitor, &mdec->scanner->ast->root);
-        switch (ret) {
-        case 0:
-            /* Success */
-            break;
-        case -EINCOMPLETE:
-            BT_COMP_LOGD("While visiting metadata AST: incomplete data: "
-                         "mdec-addr=%p",
-                         mdec);
-            status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-            goto end;
-        default:
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Failed to visit AST node to create CTF IR objects: "
-                "mdec-addr=%p, ret=%d",
-                mdec, ret);
-            status = CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR;
-            goto end;
-        }
-    }
-
-end:
-#if YYDEBUG
-    yydebug = 0;
-#endif
-
-    if (fp && close_fp) {
-        if (fclose(fp)) {
-            BT_COMP_LOGE("Cannot close metadata file stream: "
-                         "mdec-addr=%p",
-                         mdec);
-        }
-    }
-
-    free(buf);
-
-    return status;
-}
-
-BT_HIDDEN
-bt_trace_class *ctf_metadata_decoder_get_ir_trace_class(struct ctf_metadata_decoder *mdec)
-{
-    BT_ASSERT_DBG(mdec);
-    BT_ASSERT_DBG(mdec->config.create_trace_class);
-    return ctf_visitor_generate_ir_get_ir_trace_class(mdec->visitor);
-}
-
-BT_HIDDEN
-struct ctf_trace_class *
-ctf_metadata_decoder_borrow_ctf_trace_class(struct ctf_metadata_decoder *mdec)
-{
-    BT_ASSERT_DBG(mdec);
-    BT_ASSERT_DBG(mdec->config.create_trace_class);
-    return ctf_visitor_generate_ir_borrow_ctf_trace_class(mdec->visitor);
-}
-
-BT_HIDDEN
-const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder *mdec)
-{
-    BT_ASSERT_DBG(mdec);
-    BT_ASSERT_DBG(mdec->config.keep_plain_text);
-    return mdec->text->str;
-}
-
-BT_HIDDEN
-int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder *mdec)
-{
-    BT_ASSERT_DBG(mdec);
-    return mdec->bo;
-}
-
-BT_HIDDEN
-int ctf_metadata_decoder_get_uuid(struct ctf_metadata_decoder *mdec, bt_uuid_t uuid)
-{
-    int ret = 0;
-
-    BT_ASSERT_DBG(mdec);
-
-    if (!mdec->is_uuid_set) {
-        ret = -1;
-        goto end;
-    }
-
-    bt_uuid_copy(uuid, mdec->uuid);
-
-end:
-    return ret;
-}
-
-static enum ctf_metadata_decoder_status find_uuid_in_trace_decl(struct ctf_metadata_decoder *mdec,
-                                                                struct ctf_node *trace_node,
-                                                                bt_uuid_t uuid)
-{
-    enum ctf_metadata_decoder_status status = CTF_METADATA_DECODER_STATUS_OK;
-    struct ctf_node *entry_node;
-    struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
-    char *left = NULL;
-
-    bt_list_for_each_entry (entry_node, decl_list, siblings) {
-        if (entry_node->type == NODE_CTF_EXPRESSION) {
-            int ret;
-
-            left = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.left);
-            if (!left) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot concatenate unary strings.");
-                status = CTF_METADATA_DECODER_STATUS_ERROR;
-                goto end;
-            }
-
-            if (strcmp(left, "uuid") == 0) {
-                ret = ctf_ast_get_unary_uuid(&entry_node->u.ctf_expression.right, uuid,
-                                             mdec->config.log_level, mdec->config.self_comp);
-                if (ret) {
-                    _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid trace's `uuid` attribute.");
-                    status = CTF_METADATA_DECODER_STATUS_ERROR;
-                    goto end;
-                }
-
-                goto end;
-            }
-
-            g_free(left);
-            left = NULL;
-        }
-    }
-
-    status = CTF_METADATA_DECODER_STATUS_NONE;
-
-end:
-    g_free(left);
-    return status;
-}
-
-BT_HIDDEN
-enum ctf_metadata_decoder_status
-ctf_metadata_decoder_get_trace_class_uuid(struct ctf_metadata_decoder *mdec, bt_uuid_t uuid)
-{
-    enum ctf_metadata_decoder_status status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-    struct ctf_node *root_node = &mdec->scanner->ast->root;
-    struct ctf_node *trace_node;
-
-    if (!root_node) {
-        status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-        goto end;
-    }
-
-    trace_node = bt_list_entry(root_node->u.root.trace.next, struct ctf_node, siblings);
-    if (!trace_node) {
-        status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-        goto end;
-    }
-
-    status = find_uuid_in_trace_decl(mdec, trace_node, uuid);
-
-end:
-    return status;
-}
diff --git a/src/plugins/ctf/common/metadata/decoder.hpp b/src/plugins/ctf/common/metadata/decoder.hpp
deleted file mode 100644 (file)
index f96fc9d..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef _METADATA_DECODER_H
-#define _METADATA_DECODER_H
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdio.h>
-
-#include <babeltrace2/babeltrace.h>
-
-#include "common/macros.h"
-#include "common/uuid.h"
-
-struct ctf_trace_class;
-
-/* A CTF metadata decoder object */
-struct ctf_metadata_decoder;
-
-/* CTF metadata decoder status */
-enum ctf_metadata_decoder_status
-{
-    CTF_METADATA_DECODER_STATUS_OK = 0,
-    CTF_METADATA_DECODER_STATUS_NONE = 1,
-    CTF_METADATA_DECODER_STATUS_ERROR = -1,
-    CTF_METADATA_DECODER_STATUS_INCOMPLETE = -2,
-    CTF_METADATA_DECODER_STATUS_INVAL_VERSION = -3,
-    CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR = -4,
-};
-
-/* Decoding configuration */
-struct ctf_metadata_decoder_config
-{
-    /* Active log level to use */
-    bt_logging_level log_level;
-
-    /*
-     * Component or component class to use for logging (exactly one of
-     * them must be non-`NULL`); weak
-     */
-    bt_self_component *self_comp;
-    bt_self_component_class *self_comp_class;
-
-    /* Additional clock class offset to apply */
-    int64_t clock_class_offset_s;
-    int64_t clock_class_offset_ns;
-    bool force_clock_class_origin_unix_epoch;
-
-    /* True to create trace class objects */
-    bool create_trace_class;
-
-    /*
-     * True to keep the plain text when content is appended with
-     * ctf_metadata_decoder_append_content().
-     */
-    bool keep_plain_text;
-};
-
-/*
- * Creates a CTF metadata decoder.
- *
- * Returns `NULL` on error.
- */
-BT_HIDDEN
-struct ctf_metadata_decoder *
-ctf_metadata_decoder_create(const struct ctf_metadata_decoder_config *config);
-
-/*
- * Destroys a CTF metadata decoder that you created with
- * ctf_metadata_decoder_create().
- */
-BT_HIDDEN
-void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder *metadata_decoder);
-
-/*
- * Appends content to the metadata decoder.
- *
- * This function reads the metadata from the current position of `fp`
- * until the end of this file stream.
- *
- * The metadata can be packetized or not.
- *
- * The metadata chunk needs to be complete and lexically scannable, that
- * is, zero or more complete top-level blocks. If it's incomplete, this
- * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`. If this
- * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`, then you
- * need to call it again with the _same_ metadata and more to make it
- * complete. For example:
- *
- *     First call:  event { name = hell
- *     Second call: event { name = hello_world; ... };
- *
- * If everything goes as expected, this function returns
- * `CTF_METADATA_DECODER_STATUS_OK`.
- */
-BT_HIDDEN
-enum ctf_metadata_decoder_status
-ctf_metadata_decoder_append_content(struct ctf_metadata_decoder *metadata_decoder, FILE *fp);
-
-/*
- * Returns the trace IR trace class of this metadata decoder (new
- * reference).
- *
- * Returns `NULL` if there's none yet or if the metadata decoder is not
- * configured to create trace classes.
- */
-BT_HIDDEN
-bt_trace_class *ctf_metadata_decoder_get_ir_trace_class(struct ctf_metadata_decoder *mdec);
-
-/*
- * Returns the CTF IR trace class of this metadata decoder.
- *
- * Returns `NULL` if there's none yet or if the metadata decoder is not
- * configured to create trace classes.
- */
-BT_HIDDEN
-struct ctf_trace_class *
-ctf_metadata_decoder_borrow_ctf_trace_class(struct ctf_metadata_decoder *mdec);
-
-/*
- * Checks whether or not a given metadata file stream `fp` is
- * packetized, setting `is_packetized` accordingly on success. On
- * success, also sets `*byte_order` to the byte order of the first
- * packet.
- *
- * This function uses `log_level` and `self_comp` for logging purposes.
- * `self_comp` can be `NULL` if not available.
- */
-BT_HIDDEN
-int ctf_metadata_decoder_is_packetized(FILE *fp, bool *is_packetized, int *byte_order,
-                                       bt_logging_level log_level, bt_self_component *self_comp);
-
-/*
- * Returns the byte order of the decoder's metadata stream as set by the
- * last call to ctf_metadata_decoder_append_content().
- *
- * Returns -1 if unknown (plain text content).
- */
-BT_HIDDEN
-int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder *mdec);
-
-/*
- * Returns the UUID of the decoder's metadata stream as set by the last
- * call to ctf_metadata_decoder_append_content().
- */
-BT_HIDDEN
-int ctf_metadata_decoder_get_uuid(struct ctf_metadata_decoder *mdec, bt_uuid_t uuid);
-
-/*
- * Returns the UUID of the decoder's trace class, if available.
- *
- * Returns:
- *
- * * `CTF_METADATA_DECODER_STATUS_OK`: success.
- * * `CTF_METADATA_DECODER_STATUS_NONE`: no UUID.
- * * `CTF_METADATA_DECODER_STATUS_INCOMPLETE`: missing metadata content.
- */
-BT_HIDDEN
-enum ctf_metadata_decoder_status
-ctf_metadata_decoder_get_trace_class_uuid(struct ctf_metadata_decoder *mdec, bt_uuid_t uuid);
-
-/*
- * Returns the metadata decoder's current metadata text.
- */
-BT_HIDDEN
-const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder *mdec);
-
-static inline bool ctf_metadata_decoder_is_packet_version_valid(unsigned int major,
-                                                                unsigned int minor)
-{
-    return major == 1 && minor == 8;
-}
-
-#endif /* _METADATA_DECODER_H */
diff --git a/src/plugins/ctf/common/metadata/lexer.lpp b/src/plugins/ctf/common/metadata/lexer.lpp
deleted file mode 100644 (file)
index 27fea43..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-%{
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Formal Lexer
- */
-
-#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
-#define BT_LOG_TAG "PLUGIN-CTF-METADATA-LEXER"
-#include "logging.hpp"
-
-#include <stdio.h>
-#include <ctype.h>
-#include "scanner.hpp"
-#include "parser-wrap.hpp"
-#include "ast.hpp"
-
-#define YY_FATAL_ERROR(_msg)   BT_LOGF_STR(_msg)
-
-#define PARSE_INTEGER_LITERAL(base)                                    \
-       do {                                                            \
-               errno = 0;                                              \
-               yylval->ull = strtoull(yytext, NULL, base);             \
-               if (errno) {                                            \
-                       _BT_LOGE_APPEND_CAUSE_LINENO(yylineno,                  \
-                               "Cannot parser constant integer: "      \
-                               "base=%d, text=\"%s\"", base, yytext);  \
-                       return CTF_ERROR;                               \
-               }                                                       \
-       } while (0)
-%}
-
-%x comment_ml comment_sl string_lit char_const
-%option reentrant yylineno noyywrap bison-bridge
-%option extra-type="struct ctf_scanner *"
-       /* bison-locations */
-INTEGER_SUFFIX                 (U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu)
-DIGIT                          [0-9]
-NONDIGIT                       [a-zA-Z_]
-HEXDIGIT                       [0-9A-Fa-f]
-OCTALDIGIT                     [0-7]
-UCHARLOWERCASE                 \\u{HEXDIGIT}{4}
-UCHARUPPERCASE                 \\U{HEXDIGIT}{8}
-ID_NONDIGIT                    {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}
-IDENTIFIER                     {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})*
-%%
-
-                               /*
-                                * Using start conditions to deal with comments
-                                * and strings.
-                                */
-
-"/*"                           BEGIN(comment_ml);
-<comment_ml>[^*\n]*            /* eat anything that's not a '*' */
-<comment_ml>"*"+[^*/\n]*       /* eat up '*'s not followed by '/'s */
-<comment_ml>\n
-<comment_ml>"*"+"/"            BEGIN(INITIAL);
-
-"//"[^\n]*\n                   /* skip comment */
-
-L?\"(\\.|[^\\"])*\"            { if (import_string(yyextra, yylval, yytext, '\"') < 0) return CTF_ERROR; else return CTF_STRING_LITERAL; }
-L?\'(\\.|[^\\'])*\'            { if (import_string(yyextra, yylval, yytext, '\'') < 0) return CTF_ERROR; else return CTF_CHARACTER_LITERAL; }
-
-"["                            return CTF_LSBRAC;
-"]"                            return CTF_RSBRAC;
-"("                            return CTF_LPAREN;
-")"                            return CTF_RPAREN;
-"{"                            return CTF_LBRAC;
-"}"                            return CTF_RBRAC;
-"->"                           return CTF_RARROW;
-"*"                            return CTF_STAR;
-"+"                            return CTF_PLUS;
-"-"                            return CTF_MINUS;
-"<"                            return CTF_LT;
-">"                            return CTF_GT;
-:=                             return CTF_TYPEASSIGN;
-:                              return CTF_COLON;
-;                              return CTF_SEMICOLON;
-"..."                          return CTF_DOTDOTDOT;
-"."                            return CTF_DOT;
-=                              return CTF_EQUAL;
-","                            return CTF_COMMA;
-align                          setstring(yyextra, yylval, yytext); return CTF_TOK_ALIGN;
-const                          setstring(yyextra, yylval, yytext); return CTF_CONST;
-char                           setstring(yyextra, yylval, yytext); return CTF_CHAR;
-clock                          setstring(yyextra, yylval, yytext); return CTF_CLOCK;
-double                         setstring(yyextra, yylval, yytext); return CTF_DOUBLE;
-enum                           setstring(yyextra, yylval, yytext); return CTF_ENUM;
-env                            setstring(yyextra, yylval, yytext); return CTF_ENV;
-event                          setstring(yyextra, yylval, yytext); return CTF_EVENT;
-floating_point                 setstring(yyextra, yylval, yytext); return CTF_FLOATING_POINT;
-float                          setstring(yyextra, yylval, yytext); return CTF_FLOAT;
-integer                                setstring(yyextra, yylval, yytext); return CTF_INTEGER;
-int                            setstring(yyextra, yylval, yytext); return CTF_INT;
-long                           setstring(yyextra, yylval, yytext); return CTF_LONG;
-short                          setstring(yyextra, yylval, yytext); return CTF_SHORT;
-signed                         setstring(yyextra, yylval, yytext); return CTF_SIGNED;
-stream                         setstring(yyextra, yylval, yytext); return CTF_STREAM;
-string                         setstring(yyextra, yylval, yytext); return CTF_STRING;
-struct                         setstring(yyextra, yylval, yytext); return CTF_STRUCT;
-trace                          setstring(yyextra, yylval, yytext); return CTF_TRACE;
-callsite                       setstring(yyextra, yylval, yytext); return CTF_CALLSITE;
-typealias                      setstring(yyextra, yylval, yytext); return CTF_TYPEALIAS;
-typedef                                setstring(yyextra, yylval, yytext); return CTF_TYPEDEF;
-unsigned                       setstring(yyextra, yylval, yytext); return CTF_UNSIGNED;
-variant                                setstring(yyextra, yylval, yytext); return CTF_VARIANT;
-void                           setstring(yyextra, yylval, yytext); return CTF_VOID;
-_Bool                          setstring(yyextra, yylval, yytext); return CTF_BOOL;
-_Complex                       setstring(yyextra, yylval, yytext); return CTF_COMPLEX;
-_Imaginary                     setstring(yyextra, yylval, yytext); return CTF_IMAGINARY;
-[1-9]{DIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(10); return CTF_INTEGER_LITERAL;
-0{OCTALDIGIT}*{INTEGER_SUFFIX}?        PARSE_INTEGER_LITERAL(8); return CTF_INTEGER_LITERAL;
-0[xX]{HEXDIGIT}+{INTEGER_SUFFIX}?      PARSE_INTEGER_LITERAL(16); return CTF_INTEGER_LITERAL;
-
-{IDENTIFIER}                   BT_LOGT("Got identifier: id=\"%s\"", yytext); setstring(yyextra, yylval, yytext); if (is_type(yyextra, yytext)) return ID_TYPE; else return IDENTIFIER;
-[ \t\r\n]                      ; /* ignore */
-.                              _BT_LOGE_APPEND_CAUSE_LINENO(yylineno, "Invalid character: char=\"%c\", val=0x%02x", isprint((unsigned char) yytext[0]) ? yytext[0] : '\0', yytext[0]); return CTF_ERROR;
-%%
diff --git a/src/plugins/ctf/common/metadata/logging.cpp b/src/plugins/ctf/common/metadata/logging.cpp
deleted file mode 100644 (file)
index 6689e76..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- */
-
-#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
-#include "logging/log.h"
-
-BT_LOG_INIT_LOG_LEVEL(ctf_plugin_metadata_log_level, "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL");
diff --git a/src/plugins/ctf/common/metadata/logging.hpp b/src/plugins/ctf/common/metadata/logging.hpp
deleted file mode 100644 (file)
index a62f1e1..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- */
-
-#ifndef CTF_METADATA_LOGGING_H
-#define CTF_METADATA_LOGGING_H
-
-#include <babeltrace2/babeltrace.h>
-#include "logging/log.h"
-
-/*
- * This global log level is for the generated lexer and parser: we can't
- * use a contextual log level for their "tracing", so they rely on this.
- */
-BT_LOG_LEVEL_EXTERN_SYMBOL(ctf_plugin_metadata_log_level);
-
-/*
- * To be used by functions without a context structure to pass all the
- * logging configuration at once.
- */
-struct meta_log_config
-{
-    bt_logging_level log_level;
-
-    /* Weak, exactly one of these must be set */
-    bt_self_component *self_comp;
-    bt_self_component_class *self_comp_class;
-};
-
-#define _BT_LOGT_LINENO(_lineno, _msg, args...)                                                    \
-    BT_LOGT("At line %u in metadata stream: " _msg, _lineno, ##args)
-
-#define _BT_LOGW_LINENO(_lineno, _msg, args...)                                                    \
-    BT_LOGW("At line %u in metadata stream: " _msg, _lineno, ##args)
-
-#define _BT_LOGE_APPEND_CAUSE_LINENO(_lineno, _msg, args...)                                       \
-    do {                                                                                           \
-        BT_LOGE("At line %u in metadata stream: " _msg, _lineno, ##args);                          \
-        (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(                                  \
-            "CTF metadata parser", "At line %u in metadata stream: " _msg, _lineno, ##args);       \
-    } while (0)
-
-#define _BT_COMP_LOGT_LINENO(_lineno, _msg, args...)                                               \
-    BT_COMP_LOGT("At line %u in metadata stream: " _msg, _lineno, ##args)
-
-#define _BT_COMP_LOGW_LINENO(_lineno, _msg, args...)                                               \
-    BT_COMP_LOGW("At line %u in metadata stream: " _msg, _lineno, ##args)
-
-#define _BT_COMP_LOGE_LINENO(_lineno, _msg, args...)                                               \
-    BT_COMP_LOGE("At line %u in metadata stream: " _msg, _lineno, ##args)
-
-#define _BT_COMP_LOGE_APPEND_CAUSE_LINENO(_lineno, _msg, args...)                                  \
-    BT_COMP_LOGE_APPEND_CAUSE(BT_COMP_LOG_SELF_COMP, "At line %u in metadata stream: " _msg,       \
-                              _lineno, ##args)
-
-#define _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(_msg, args...)                                    \
-    BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(BT_COMP_LOG_SELF_COMP, BT_COMP_LOG_SELF_COMP_CLASS,    \
-                                            _msg, ##args)
-
-#endif /* CTF_METADATA_LOGGING_H */
diff --git a/src/plugins/ctf/common/metadata/objstack.cpp b/src/plugins/ctf/common/metadata/objstack.cpp
deleted file mode 100644 (file)
index 4828de9..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Object Stack.
- */
-
-#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
-#define BT_LOG_TAG          "PLUGIN/CTF/META/OBJSTACK"
-#include "logging.hpp"
-
-#include "objstack.hpp"
-
-#include <stdlib.h>
-#include "common/list.h"
-#include "common/macros.h"
-#include "common/align.h"
-
-#define OBJSTACK_ALIGN    8 /* Object stack alignment */
-#define OBJSTACK_INIT_LEN 128
-#define OBJSTACK_POISON   0xcc
-
-struct objstack
-{
-    struct bt_list_head head; /* list of struct objstack_node */
-};
-
-struct objstack_node
-{
-    struct bt_list_head node;
-    size_t len;
-    size_t used_len;
-    char __attribute__((aligned(OBJSTACK_ALIGN))) data[];
-};
-
-BT_HIDDEN
-struct objstack *objstack_create(void)
-{
-    struct objstack *objstack;
-    struct objstack_node *node;
-
-    objstack = (struct objstack *) calloc(1, sizeof(*objstack));
-    if (!objstack) {
-        BT_LOGE_STR("Failed to allocate one object stack.");
-        return NULL;
-    }
-    node = (objstack_node *) calloc(sizeof(struct objstack_node) + OBJSTACK_INIT_LEN, sizeof(char));
-    if (!node) {
-        BT_LOGE_STR("Failed to allocate one object stack node.");
-        free(objstack);
-        return NULL;
-    }
-    BT_INIT_LIST_HEAD(&objstack->head);
-    bt_list_add_tail(&node->node, &objstack->head);
-    node->len = OBJSTACK_INIT_LEN;
-    return objstack;
-}
-
-static void objstack_node_free(struct objstack_node *node)
-{
-    size_t offset, len;
-    char *p;
-
-    if (!node)
-        return;
-    p = (char *) node;
-    len = sizeof(*node) + node->len;
-    for (offset = 0; offset < len; offset++)
-        p[offset] = OBJSTACK_POISON;
-    free(node);
-}
-
-BT_HIDDEN
-void objstack_destroy(struct objstack *objstack)
-{
-    struct objstack_node *node, *p;
-
-    if (!objstack)
-        return;
-    bt_list_for_each_entry_safe (node, p, &objstack->head, node) {
-        bt_list_del(&node->node);
-        objstack_node_free(node);
-    }
-    free(objstack);
-}
-
-static struct objstack_node *objstack_append_node(struct objstack *objstack)
-{
-    struct objstack_node *last_node, *new_node;
-
-    /* Get last node */
-    last_node = bt_list_entry(objstack->head.prev, struct objstack_node, node);
-
-    /* Allocate new node with double of size of last node */
-    new_node = (objstack_node *) calloc(sizeof(struct objstack_node) + (last_node->len << 1),
-                                        sizeof(char));
-    if (!new_node) {
-        BT_LOGE_STR("Failed to allocate one object stack node.");
-        return NULL;
-    }
-    bt_list_add_tail(&new_node->node, &objstack->head);
-    new_node->len = last_node->len << 1;
-    return new_node;
-}
-
-BT_HIDDEN
-void *objstack_alloc(struct objstack *objstack, size_t len)
-{
-    struct objstack_node *last_node;
-    void *p;
-
-    len = BT_ALIGN(len, OBJSTACK_ALIGN);
-
-    /* Get last node */
-    last_node = bt_list_entry(objstack->head.prev, struct objstack_node, node);
-    while (last_node->len - last_node->used_len < len) {
-        last_node = objstack_append_node(objstack);
-        if (!last_node) {
-            return NULL;
-        }
-    }
-    p = &last_node->data[last_node->used_len];
-    last_node->used_len += len;
-    return p;
-}
diff --git a/src/plugins/ctf/common/metadata/objstack.hpp b/src/plugins/ctf/common/metadata/objstack.hpp
deleted file mode 100644 (file)
index f6c655f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Object Stack.
- */
-
-#ifndef _OBJSTACK_H
-#define _OBJSTACK_H
-
-#include "common/macros.h"
-
-struct objstack;
-
-BT_HIDDEN
-struct objstack *objstack_create(void);
-BT_HIDDEN
-void objstack_destroy(struct objstack *objstack);
-
-/*
- * Allocate len bytes of zeroed memory.
- * Return NULL on error.
- */
-BT_HIDDEN
-void *objstack_alloc(struct objstack *objstack, size_t len);
-
-#endif /* _OBJSTACK_H */
diff --git a/src/plugins/ctf/common/metadata/parser-wrap.hpp b/src/plugins/ctf/common/metadata/parser-wrap.hpp
deleted file mode 100644 (file)
index d43d391..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 EfficiOS Inc.
- */
-
-#ifndef BABELTRACE_PLUGINS_CTF_COMMON_METADATA_PARSER_WRAP_H
-#define BABELTRACE_PLUGINS_CTF_COMMON_METADATA_PARSER_WRAP_H
-
-/*
- * Small wrapper around the bison-generated parser.h to conditionally define
- * YYDEBUG (and therefore the yydebug declaration).
- */
-
-#include "logging/log.h"
-
-#if BT_LOG_ENABLED_TRACE
-#    define YYDEBUG                           1
-#    define YYFPRINTF(_stream, _fmt, args...) BT_LOGT(_fmt, ##args)
-#else
-#    define YYDEBUG 0
-#endif
-
-#define ALLOW_INCLUDE_PARSER_H
-#include "parser.hpp"
-#undef ALLOW_INCLUDE_PARSER_H
-
-#endif
diff --git a/src/plugins/ctf/common/metadata/parser.ypp b/src/plugins/ctf/common/metadata/parser.ypp
deleted file mode 100644 (file)
index e475436..0000000
+++ /dev/null
@@ -1,2615 +0,0 @@
-%{
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Metadata Grammar.
- */
-
-#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
-#define BT_LOG_TAG "PLUGIN/CTF/META/PARSER"
-#include "logging.hpp"
-
-#include <stdio.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <glib.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "common/list.h"
-#include "common/assert.h"
-#include "scanner.hpp"
-#include "ast.hpp"
-#include "objstack.hpp"
-
-#include "parser-wrap.hpp"
-
-/*
- * Avoid warning about "yynerrs" being unused, seen with bison 3.5.1 + clang 15
- * on Ubuntu 20.04.
- */
-BT_DIAG_IGNORE_UNUSED_BUT_SET_VARIABLE
-
-/* Join two lists, put "add" at the end of "head".  */
-static inline void
-_bt_list_splice_tail (struct bt_list_head *add, struct bt_list_head *head)
-{
-       /* Do nothing if the list which gets added is empty.  */
-       if (add != add->next) {
-               add->next->prev = head->prev;
-               add->prev->next = head;
-               head->prev->next = add->next;
-               head->prev = add->prev;
-       }
-}
-
-BT_HIDDEN
-int yylex(union YYSTYPE *yyval, yyscan_t yyscanner);
-BT_HIDDEN
-int yylex_init_extra(struct ctf_scanner *scanner, yyscan_t * ptr_yy_globals);
-BT_HIDDEN
-int yylex_destroy(yyscan_t yyscanner);
-BT_HIDDEN
-void yyrestart(FILE * in_str, yyscan_t yyscanner);
-BT_HIDDEN
-int yyget_lineno(yyscan_t yyscanner);
-BT_HIDDEN
-char *yyget_text(yyscan_t yyscanner);
-
-/*
- * Static node for out of memory errors. Only "type" is used. lineno is
- * always left at 0. The rest of the node content can be overwritten,
- * but is never used.
- */
-static struct ctf_node error_node = {
-       .parent = nullptr,
-       .siblings = {},
-       .tmp_head = {},
-       .lineno = 0,
-       .visited = 0,
-       .type = NODE_ERROR,
-};
-
-BT_HIDDEN
-const char *node_type(struct ctf_node *node)
-{
-       switch (node->type) {
-#define ENTRY(S) case S: return #S;
-       FOREACH_CTF_NODES(ENTRY)
-#undef ENTRY
-       };
-
-       bt_common_abort();
-}
-
-void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src)
-{
-       lvalp->s = (char *) objstack_alloc(scanner->objstack, strlen(src) + 1);
-       strcpy(lvalp->s, src);
-}
-
-static
-int str_check(size_t str_len, size_t offset, size_t len)
-{
-       /* check overflow */
-       if (offset + len < offset)
-               return -1;
-       if (offset + len > str_len)
-               return -1;
-       return 0;
-}
-
-static
-int bt_isodigit(int c)
-{
-       switch (c) {
-       case '0':
-       case '1':
-       case '2':
-       case '3':
-       case '4':
-       case '5':
-       case '6':
-       case '7':
-               return 1;
-       default:
-               return 0;
-       }
-}
-
-static
-int parse_base_sequence(const char *src, size_t len, size_t pos,
-               char *buffer, size_t *buf_len, int base)
-{
-       const size_t max_char = 3;
-       int nr_char = 0;
-
-       while (!str_check(len, pos, 1) && nr_char < max_char) {
-               char c = src[pos++];
-
-               if (base == 8) {
-                       if (bt_isodigit(c))
-                               buffer[nr_char++] = c;
-                       else
-                               break;
-               } else if (base == 16) {
-                       if (isxdigit(c))
-                               buffer[nr_char++] = c;
-                       else
-                               break;
-
-               } else {
-                       /* Unsupported base */
-                       return -1;
-               }
-       }
-       BT_ASSERT_DBG(nr_char > 0);
-       buffer[nr_char] = '\0';
-       *buf_len = nr_char;
-       return 0;
-}
-
-static
-int import_basic_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
-               size_t len, const char *src, char delim)
-{
-       size_t pos = 0, dpos = 0;
-
-       if (str_check(len, pos, 1))
-               return -1;
-       if (src[pos++] != delim)
-               return -1;
-
-       while (src[pos] != delim) {
-               char c;
-
-               if (str_check(len, pos, 1))
-                       return -1;
-               c = src[pos++];
-               if (c == '\\') {
-                       if (str_check(len, pos, 1))
-                               return -1;
-                       c = src[pos++];
-
-                       switch (c) {
-                       case 'a':
-                               c = '\a';
-                               break;
-                       case 'b':
-                               c = '\b';
-                               break;
-                       case 'f':
-                               c = '\f';
-                               break;
-                       case 'n':
-                               c = '\n';
-                               break;
-                       case 'r':
-                               c = '\r';
-                               break;
-                       case 't':
-                               c = '\t';
-                               break;
-                       case 'v':
-                               c = '\v';
-                               break;
-                       case '\\':
-                               c = '\\';
-                               break;
-                       case '\'':
-                               c = '\'';
-                               break;
-                       case '\"':
-                               c = '\"';
-                               break;
-                       case '?':
-                               c = '?';
-                               break;
-                       case '0':
-                       case '1':
-                       case '2':
-                       case '3':
-                       case '4':
-                       case '5':
-                       case '6':
-                       case '7':
-                       {
-                               char oct_buffer[4];
-                               size_t oct_len;
-
-                               if (parse_base_sequence(src, len, pos - 1,
-                                               oct_buffer, &oct_len, 8))
-                                       return -1;
-                               c = strtoul(&oct_buffer[0], NULL, 8);
-                               pos += oct_len - 1;
-                               break;
-                       }
-                       case 'x':
-                       {
-                               char hex_buffer[4];
-                               size_t hex_len;
-
-                               if (parse_base_sequence(src, len, pos,
-                                               hex_buffer, &hex_len, 16))
-                                       return -1;
-                               c = strtoul(&hex_buffer[0], NULL, 16);
-                               pos += hex_len;
-                               break;
-                       }
-                       default:
-                               return -1;
-                       }
-               }
-               if (str_check(len, dpos, 1))
-                       return -1;
-               lvalp->s[dpos++] = c;
-       }
-
-       if (str_check(len, dpos, 1))
-               return -1;
-       lvalp->s[dpos++] = '\0';
-
-       if (str_check(len, pos, 1))
-               return -1;
-       if (src[pos++] != delim)
-               return -1;
-
-       if (str_check(len, pos, 1))
-               return -1;
-       if (src[pos] != '\0')
-               return -1;
-       return 0;
-}
-
-int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
-               const char *src, char delim)
-{
-       size_t len;
-
-       len = strlen(src) + 1;
-       lvalp->s = (char *) objstack_alloc(scanner->objstack, len);
-       if (src[0] == 'L') {
-               // TODO: import wide string
-               _BT_LOGE_APPEND_CAUSE_LINENO(yyget_lineno(scanner),
-                       "wide characters are not supported as of this version: "
-                       "scanner-addr=%p", scanner);
-               return -1;
-       } else {
-               return import_basic_string(scanner, lvalp, len, src, delim);
-       }
-}
-
-static void init_scope(struct ctf_scanner_scope *scope,
-                      struct ctf_scanner_scope *parent)
-{
-       scope->parent = parent;
-       scope->classes = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                            NULL, NULL);
-}
-
-static void finalize_scope(struct ctf_scanner_scope *scope)
-{
-       g_hash_table_destroy(scope->classes);
-}
-
-static void push_scope(struct ctf_scanner *scanner)
-{
-       struct ctf_scanner_scope *ns;
-
-       BT_LOGT("Pushing scope: scanner-addr=%p", scanner);
-       ns = (ctf_scanner_scope *) malloc(sizeof(struct ctf_scanner_scope));
-       init_scope(ns, scanner->cs);
-       scanner->cs = ns;
-}
-
-static void pop_scope(struct ctf_scanner *scanner)
-{
-       struct ctf_scanner_scope *os;
-
-       BT_LOGT("Popping scope: scanner-addr=%p", scanner);
-       os = scanner->cs;
-       scanner->cs = os->parent;
-       finalize_scope(os);
-       free(os);
-}
-
-static int lookup_type(struct ctf_scanner_scope *s, const char *id)
-{
-       int ret;
-
-       ret = GPOINTER_TO_INT(g_hash_table_lookup(s->classes, id));
-       BT_LOGT("Looked up type: scanner-addr=%p, id=\"%s\", ret=%d",
-               s, id, ret);
-       return ret;
-}
-
-BT_HIDDEN
-int is_type(struct ctf_scanner *scanner, const char *id)
-{
-       struct ctf_scanner_scope *it;
-       int ret = 0;
-
-       for (it = scanner->cs; it; it = it->parent) {
-               if (lookup_type(it, id)) {
-                       ret = 1;
-                       break;
-               }
-       }
-       BT_LOGT("Found if ID is type: scanner-addr=%p, id=\"%s\", ret=%d",
-               scanner, id, ret);
-       return ret;
-}
-
-static void add_type(struct ctf_scanner *scanner, char *id)
-{
-       BT_LOGT("Adding type: scanner-addr=%p, id=\"%s\"",
-               scanner, id);
-       if (lookup_type(scanner->cs, id))
-               return;
-       g_hash_table_insert(scanner->cs->classes, id, id);
-}
-
-static struct ctf_node *make_node(struct ctf_scanner *scanner,
-                                 enum node_type type)
-{
-       struct ctf_node *node;
-
-       node = (ctf_node *) objstack_alloc(scanner->objstack, sizeof(*node));
-       if (!node) {
-               _BT_LOGE_APPEND_CAUSE_LINENO(yyget_lineno(scanner->scanner),
-                       "failed to allocate one stack entry: "
-                       "scanner-addr=%p", scanner);
-               return &error_node;
-       }
-       node->type = type;
-       node->lineno = yyget_lineno(scanner->scanner);
-       BT_INIT_LIST_HEAD(&node->tmp_head);
-       bt_list_add(&node->siblings, &node->tmp_head);
-
-       switch (type) {
-       case NODE_ROOT:
-               node->type = NODE_ERROR;
-               BT_LOGE("Trying to create root node: scanner-addr=%p",
-                       scanner);
-               break;
-       case NODE_EVENT:
-               BT_INIT_LIST_HEAD(&node->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               BT_INIT_LIST_HEAD(&node->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               BT_INIT_LIST_HEAD(&node->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               BT_INIT_LIST_HEAD(&node->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               BT_INIT_LIST_HEAD(&node->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               BT_INIT_LIST_HEAD(&node->u.callsite.declaration_list);
-               break;
-       case NODE_CTF_EXPRESSION:
-               BT_INIT_LIST_HEAD(&node->u.ctf_expression.left);
-               BT_INIT_LIST_HEAD(&node->u.ctf_expression.right);
-               break;
-       case NODE_UNARY_EXPRESSION:
-               break;
-       case NODE_TYPEDEF:
-               BT_INIT_LIST_HEAD(&node->u.field_class_def.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS_TARGET:
-               BT_INIT_LIST_HEAD(&node->u.field_class_alias_target.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS_ALIAS:
-               BT_INIT_LIST_HEAD(&node->u.field_class_alias_name.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS:
-               break;
-       case NODE_TYPE_SPECIFIER:
-               break;
-       case NODE_TYPE_SPECIFIER_LIST:
-               BT_INIT_LIST_HEAD(&node->u.field_class_specifier_list.head);
-               break;
-       case NODE_POINTER:
-               break;
-       case NODE_TYPE_DECLARATOR:
-               BT_INIT_LIST_HEAD(&node->u.field_class_declarator.pointers);
-               break;
-       case NODE_FLOATING_POINT:
-               BT_INIT_LIST_HEAD(&node->u.floating_point.expressions);
-               break;
-       case NODE_INTEGER:
-               BT_INIT_LIST_HEAD(&node->u.integer.expressions);
-               break;
-       case NODE_STRING:
-               BT_INIT_LIST_HEAD(&node->u.string.expressions);
-               break;
-       case NODE_ENUMERATOR:
-               BT_INIT_LIST_HEAD(&node->u.enumerator.values);
-               break;
-       case NODE_ENUM:
-               BT_INIT_LIST_HEAD(&node->u._enum.enumerator_list);
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               BT_INIT_LIST_HEAD(&node->u.struct_or_variant_declaration.field_class_declarators);
-               break;
-       case NODE_VARIANT:
-               BT_INIT_LIST_HEAD(&node->u.variant.declaration_list);
-               break;
-       case NODE_STRUCT:
-               BT_INIT_LIST_HEAD(&node->u._struct.declaration_list);
-               BT_INIT_LIST_HEAD(&node->u._struct.min_align);
-               break;
-       case NODE_UNKNOWN:
-       default:
-               node->type = NODE_ERROR;
-               BT_LOGE("Unknown node type: scanner-addr=%p, node-type=%d",
-                       scanner, type);
-               break;
-       }
-
-       return node;
-}
-
-static int reparent_ctf_expression(struct ctf_node *node,
-                                  struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_EVENT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
-               break;
-       case NODE_FLOATING_POINT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.floating_point.expressions);
-               break;
-       case NODE_INTEGER:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.integer.expressions);
-               break;
-       case NODE_STRING:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.string.expressions);
-               break;
-
-       case NODE_ROOT:
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPE_SPECIFIER_LIST:
-       case NODE_POINTER:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_ENUMERATOR:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_typedef(struct ctf_node *node, struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_ROOT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
-               break;
-       case NODE_EVENT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
-               break;
-       case NODE_VARIANT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
-               break;
-       case NODE_STRUCT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
-               break;
-
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPE_SPECIFIER_LIST:
-       case NODE_POINTER:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_ENUMERATOR:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_field_class_alias(struct ctf_node *node, struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_ROOT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
-               break;
-       case NODE_EVENT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
-               break;
-       case NODE_VARIANT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
-               break;
-       case NODE_STRUCT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
-               break;
-
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPE_SPECIFIER_LIST:
-       case NODE_POINTER:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_ENUMERATOR:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_field_class_specifier(struct ctf_node *node,
-                                  struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_TYPE_SPECIFIER_LIST:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_specifier_list.head);
-               break;
-
-       case NODE_TYPE_SPECIFIER:
-       case NODE_EVENT:
-       case NODE_STREAM:
-       case NODE_ENV:
-       case NODE_TRACE:
-       case NODE_CLOCK:
-       case NODE_CALLSITE:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_TYPEALIAS:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_POINTER:
-       case NODE_ENUMERATOR:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_field_class_specifier_list(struct ctf_node *node,
-                                       struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_ROOT:
-               bt_list_add_tail(&node->siblings, &parent->u.root.declaration_list);
-               break;
-       case NODE_EVENT:
-               bt_list_add_tail(&node->siblings, &parent->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               bt_list_add_tail(&node->siblings, &parent->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               bt_list_add_tail(&node->siblings, &parent->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               bt_list_add_tail(&node->siblings, &parent->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               bt_list_add_tail(&node->siblings, &parent->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               bt_list_add_tail(&node->siblings, &parent->u.callsite.declaration_list);
-               break;
-       case NODE_VARIANT:
-               bt_list_add_tail(&node->siblings, &parent->u.variant.declaration_list);
-               break;
-       case NODE_STRUCT:
-               bt_list_add_tail(&node->siblings, &parent->u._struct.declaration_list);
-               break;
-       case NODE_TYPEDEF:
-               parent->u.field_class_def.field_class_specifier_list = node;
-               break;
-       case NODE_TYPEALIAS_TARGET:
-               parent->u.field_class_alias_target.field_class_specifier_list = node;
-               break;
-       case NODE_TYPEALIAS_ALIAS:
-               parent->u.field_class_alias_name.field_class_specifier_list = node;
-               break;
-       case NODE_ENUM:
-               parent->u._enum.container_field_class = node;
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               parent->u.struct_or_variant_declaration.field_class_specifier_list = node;
-               break;
-       case NODE_TYPE_DECLARATOR:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPEALIAS:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_POINTER:
-       case NODE_ENUMERATOR:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_field_class_declarator(struct ctf_node *node,
-                                   struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_TYPE_DECLARATOR:
-               parent->u.field_class_declarator.type = TYPEDEC_NESTED;
-               parent->u.field_class_declarator.u.nested.field_class_declarator = node;
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.struct_or_variant_declaration.field_class_declarators);
-               break;
-       case NODE_TYPEDEF:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_def.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS_TARGET:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_target.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS_ALIAS:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_name.field_class_declarators);
-               break;
-
-       case NODE_ROOT:
-       case NODE_EVENT:
-       case NODE_STREAM:
-       case NODE_ENV:
-       case NODE_TRACE:
-       case NODE_CLOCK:
-       case NODE_CALLSITE:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-       case NODE_TYPEALIAS:
-       case NODE_ENUM:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPE_SPECIFIER_LIST:
-       case NODE_POINTER:
-       case NODE_ENUMERATOR:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-/*
- * set_parent_node
- *
- * Link node to parent. Returns 0 on success, -EPERM if it is not permitted to
- * create the link declared by the input, -ENOENT if node or parent is NULL,
- * -EINVAL if there is an internal structure problem.
- */
-static int set_parent_node(struct ctf_node *node,
-                        struct ctf_node *parent)
-{
-       if (!node || !parent)
-               return -ENOENT;
-
-       /* Note: Linking to parent will be done only by an external visitor */
-
-       switch (node->type) {
-       case NODE_ROOT:
-               BT_LOGE_STR("Trying to reparent root node.");
-               return -EINVAL;
-
-       case NODE_EVENT:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.event);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_STREAM:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.stream);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_ENV:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.env);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_TRACE:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.trace);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_CLOCK:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.clock);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_CALLSITE:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.callsite);
-               } else {
-                       return -EPERM;
-               }
-               break;
-
-       case NODE_CTF_EXPRESSION:
-               return reparent_ctf_expression(node, parent);
-       case NODE_UNARY_EXPRESSION:
-               if (parent->type == NODE_TYPE_DECLARATOR)
-                       parent->u.field_class_declarator.bitfield_len = node;
-               else
-                       return -EPERM;
-               break;
-
-       case NODE_TYPEDEF:
-               return reparent_typedef(node, parent);
-       case NODE_TYPEALIAS_TARGET:
-               if (parent->type == NODE_TYPEALIAS)
-                       parent->u.field_class_alias.target = node;
-               else
-                       return -EINVAL;
-               /* fall-through */
-       case NODE_TYPEALIAS_ALIAS:
-               if (parent->type == NODE_TYPEALIAS)
-                       parent->u.field_class_alias.alias = node;
-               else
-                       return -EINVAL;
-               /* fall-through */
-       case NODE_TYPEALIAS:
-               return reparent_field_class_alias(node, parent);
-
-       case NODE_POINTER:
-               if (parent->type == NODE_TYPE_DECLARATOR) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_declarator.pointers);
-               } else
-                       return -EPERM;
-               break;
-       case NODE_TYPE_DECLARATOR:
-               return reparent_field_class_declarator(node, parent);
-
-       case NODE_TYPE_SPECIFIER_LIST:
-               return reparent_field_class_specifier_list(node, parent);
-
-       case NODE_TYPE_SPECIFIER:
-               return reparent_field_class_specifier(node, parent);
-
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_ENUM:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-               return -EINVAL; /* Dealt with internally within grammar */
-
-       case NODE_ENUMERATOR:
-               if (parent->type == NODE_ENUM) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u._enum.enumerator_list);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               switch (parent->type) {
-               case NODE_STRUCT:
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
-                       break;
-               case NODE_VARIANT:
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
-                       break;
-               default:
-                       return -EINVAL;
-               }
-               break;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static
-void yyerror(struct ctf_scanner *scanner, yyscan_t yyscanner, const char *str)
-{
-       _BT_LOGE_APPEND_CAUSE_LINENO(yyget_lineno(scanner->scanner),
-               "%s: token=\"%s\"", str, yyget_text(scanner->scanner));
-}
-
-#define reparent_error(scanner, str)                           \
-do {                                                           \
-       yyerror(scanner, scanner->scanner, YY_("reparent_error: " str)); \
-       YYERROR;                                                \
-} while (0)
-
-static struct ctf_ast *ctf_ast_alloc(struct ctf_scanner *scanner)
-{
-       struct ctf_ast *ast;
-
-       ast = (ctf_ast *) objstack_alloc(scanner->objstack, sizeof(*ast));
-       if (!ast)
-               return NULL;
-       ast->root.type = NODE_ROOT;
-       BT_INIT_LIST_HEAD(&ast->root.tmp_head);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.declaration_list);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.trace);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.env);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.stream);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.event);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.clock);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.callsite);
-       return ast;
-}
-
-int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input)
-{
-       /* Start processing new stream */
-       yyrestart(input, scanner->scanner);
-       return yyparse(scanner, scanner->scanner);
-}
-
-struct ctf_scanner *ctf_scanner_alloc(void)
-{
-       struct ctf_scanner *scanner;
-       int ret;
-
-       scanner = (ctf_scanner *) malloc(sizeof(*scanner));
-       if (!scanner)
-               return NULL;
-       memset(scanner, 0, sizeof(*scanner));
-       ret = yylex_init_extra(scanner, &scanner->scanner);
-       if (ret) {
-               BT_LOGE("yylex_init_extra() failed: ret=%d", ret);
-               goto cleanup_scanner;
-       }
-       scanner->objstack = objstack_create();
-       if (!scanner->objstack)
-               goto cleanup_lexer;
-       scanner->ast = ctf_ast_alloc(scanner);
-       if (!scanner->ast)
-               goto cleanup_objstack;
-       init_scope(&scanner->root_scope, NULL);
-       scanner->cs = &scanner->root_scope;
-
-       return scanner;
-
-cleanup_objstack:
-       objstack_destroy(scanner->objstack);
-cleanup_lexer:
-       ret = yylex_destroy(scanner->scanner);
-       if (!ret)
-               BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d",
-                       scanner, ret);
-cleanup_scanner:
-       free(scanner);
-       return NULL;
-}
-
-void ctf_scanner_free(struct ctf_scanner *scanner)
-{
-       int ret;
-
-       if (!scanner)
-               return;
-
-       struct ctf_scanner_scope *scope = scanner->cs;
-
-       do {
-               struct ctf_scanner_scope *parent = scope->parent;
-               finalize_scope(scope);
-
-               /*
-                * The root scope is allocated within the ctf_scanner structure,
-                * do doesn't need freeing.  All others are allocated on their
-                * own.
-                */
-               if (scope != &scanner->root_scope)
-                       free(scope);
-
-               scope = parent;
-       } while (scope);
-
-       objstack_destroy(scanner->objstack);
-       ret = yylex_destroy(scanner->scanner);
-       if (ret)
-               BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d",
-                       scanner, ret);
-       free(scanner);
-}
-
-/*
- * The bison-provided version of strlen (yystrlen) generates a benign
- * -Wnull-dereference warning.  That version is used when building on cygwin,
- * for example, but you can also enable it by hand (to test) by removing the
- * preprocessor conditional around it.
- *
- * Define yystrlen such that it will always use strlen.  As far as we know,
- * strlen provided by all the platforms we use is reliable.
- */
-#define yystrlen strlen
-
-%}
-
-/*
- * This ends up in parser.h and makes sure those who want to include it pass
- * through parser-wrap.h.
- */
-%code requires {
-#ifndef ALLOW_INCLUDE_PARSER_H
-# error "Don't include parser.h directly, include parser-wrap.h instead."
-#endif
-}
-
-%code provides {
-       BT_HIDDEN
-       void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src);
-
-       BT_HIDDEN
-       int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src, char delim);
-}
-
-%define api.pure
-       /* %locations */
-%error-verbose
-%parse-param {struct ctf_scanner *scanner}
-%parse-param {yyscan_t yyscanner}
-%lex-param {yyscan_t yyscanner}
-/*
- * Expect two shift-reduce conflicts. Caused by enum name-opt : type {}
- * vs struct { int :value; } (unnamed bit-field). The default is to
- * shift, so whenever we encounter an enumeration, we are doing the
- * proper thing (shift). It is illegal to declare an enumeration
- * "bit-field", so it is OK if this situation ends up in a parsing
- * error.
- */
-%expect 2
-%start file
-%token CTF_INTEGER_LITERAL CTF_STRING_LITERAL CTF_CHARACTER_LITERAL CTF_LSBRAC CTF_RSBRAC CTF_LPAREN CTF_RPAREN CTF_LBRAC CTF_RBRAC CTF_RARROW CTF_STAR CTF_PLUS CTF_MINUS CTF_LT CTF_GT CTF_TYPEASSIGN CTF_COLON CTF_SEMICOLON CTF_DOTDOTDOT CTF_DOT CTF_EQUAL CTF_COMMA CTF_CONST CTF_CHAR CTF_DOUBLE CTF_ENUM CTF_ENV CTF_EVENT CTF_FLOATING_POINT CTF_FLOAT CTF_INTEGER CTF_INT CTF_LONG CTF_SHORT CTF_SIGNED CTF_STREAM CTF_STRING CTF_STRUCT CTF_TRACE CTF_CALLSITE CTF_CLOCK CTF_TYPEALIAS CTF_TYPEDEF CTF_UNSIGNED CTF_VARIANT CTF_VOID CTF_BOOL CTF_COMPLEX CTF_IMAGINARY CTF_TOK_ALIGN
-%token <s> IDENTIFIER ID_TYPE
-%token CTF_ERROR
-%union
-{
-       long long ll;
-       unsigned long long ull;
-       char c;
-       char *s;
-       struct ctf_node *n;
-}
-
-%type <s> CTF_STRING_LITERAL CTF_CHARACTER_LITERAL
-
-%type <s> keywords
-
-%type <ull> CTF_INTEGER_LITERAL
-%type <n> postfix_expression unary_expression unary_expression_or_range
-
-%type <n> declaration
-%type <n> event_declaration
-%type <n> stream_declaration
-%type <n> env_declaration
-%type <n> trace_declaration
-%type <n> clock_declaration
-%type <n> callsite_declaration
-%type <n> integer_declaration_specifiers
-%type <n> declaration_specifiers
-%type <n> alias_declaration_specifiers
-
-%type <n> field_class_declarator_list
-%type <n> integer_field_class_specifier
-%type <n> field_class_specifier
-%type <n> struct_class_specifier
-%type <n> variant_field_class_specifier
-%type <n> enum_field_class_specifier
-%type <n> struct_or_variant_declaration_list
-%type <n> struct_or_variant_declaration
-%type <n> struct_or_variant_declarator_list
-%type <n> struct_or_variant_declarator
-%type <n> enumerator_list
-%type <n> enumerator
-%type <n> abstract_declarator_list
-%type <n> abstract_declarator
-%type <n> direct_abstract_declarator
-%type <n> alias_abstract_declarator_list
-%type <n> alias_abstract_declarator
-%type <n> direct_alias_abstract_declarator
-%type <n> declarator
-%type <n> direct_declarator
-%type <n> field_class_declarator
-%type <n> direct_field_class_declarator
-%type <n> pointer
-%type <n> ctf_assignment_expression_list
-%type <n> ctf_assignment_expression
-
-%%
-
-file:
-               declaration
-               {
-                       if (set_parent_node($1, &ctf_scanner_get_ast(scanner)->root))
-                               reparent_error(scanner, "error reparenting to root");
-               }
-       |       file declaration
-               {
-                       if (set_parent_node($2, &ctf_scanner_get_ast(scanner)->root))
-                               reparent_error(scanner, "error reparenting to root");
-               }
-       ;
-
-keywords:
-               CTF_VOID
-               {       $$ = yylval.s;          }
-       |       CTF_CHAR
-               {       $$ = yylval.s;          }
-       |       CTF_SHORT
-               {       $$ = yylval.s;          }
-       |       CTF_INT
-               {       $$ = yylval.s;          }
-       |       CTF_LONG
-               {       $$ = yylval.s;          }
-       |       CTF_FLOAT
-               {       $$ = yylval.s;          }
-       |       CTF_DOUBLE
-               {       $$ = yylval.s;          }
-       |       CTF_SIGNED
-               {       $$ = yylval.s;          }
-       |       CTF_UNSIGNED
-               {       $$ = yylval.s;          }
-       |       CTF_BOOL
-               {       $$ = yylval.s;          }
-       |       CTF_COMPLEX
-               {       $$ = yylval.s;          }
-       |       CTF_IMAGINARY
-               {       $$ = yylval.s;          }
-       |       CTF_FLOATING_POINT
-               {       $$ = yylval.s;          }
-       |       CTF_INTEGER
-               {       $$ = yylval.s;          }
-       |       CTF_STRING
-               {       $$ = yylval.s;          }
-       |       CTF_ENUM
-               {       $$ = yylval.s;          }
-       |       CTF_VARIANT
-               {       $$ = yylval.s;          }
-       |       CTF_STRUCT
-               {       $$ = yylval.s;          }
-       |       CTF_CONST
-               {       $$ = yylval.s;          }
-       |       CTF_TYPEDEF
-               {       $$ = yylval.s;          }
-       |       CTF_EVENT
-               {       $$ = yylval.s;          }
-       |       CTF_STREAM
-               {       $$ = yylval.s;          }
-       |       CTF_ENV
-               {       $$ = yylval.s;          }
-       |       CTF_TRACE
-               {       $$ = yylval.s;          }
-       |       CTF_CLOCK
-               {       $$ = yylval.s;          }
-       |       CTF_CALLSITE
-               {       $$ = yylval.s;          }
-       |       CTF_TOK_ALIGN
-               {       $$ = yylval.s;          }
-       ;
-
-
-/* 2: Phrase structure grammar */
-
-postfix_expression:
-               IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-               }
-       |       keywords
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-               }
-       |       CTF_INTEGER_LITERAL
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_UNSIGNED_CONSTANT;
-                       $$->u.unary_expression.u.unsigned_constant = $1;
-               }
-       |       CTF_STRING_LITERAL
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = $1;
-               }
-       |       CTF_CHARACTER_LITERAL
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = $1;
-               }
-       |       CTF_LPAREN unary_expression CTF_RPAREN
-               {
-                       $$ = $2;
-               }
-       |       postfix_expression CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_SBRAC;
-                       $$->u.unary_expression.u.sbrac_exp = $3;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_DOT IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_DOTLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_DOT ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_DOTLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_DOT keywords
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_DOTLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_RARROW IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_ARROWLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_RARROW ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_ARROWLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-unary_expression:
-               postfix_expression
-               {       $$ = $1;                                }
-       |       CTF_PLUS postfix_expression
-               {
-                       $$ = $2;
-                       if ($$->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT
-                               && $$->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
-                               reparent_error(scanner, "expecting numeric constant");
-                       }
-               }
-       |       CTF_MINUS postfix_expression
-               {
-                       $$ = $2;
-                       if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) {
-                               $$->u.unary_expression.type = UNARY_SIGNED_CONSTANT;
-                               $$->u.unary_expression.u.signed_constant =
-                                       -($$->u.unary_expression.u.unsigned_constant);
-                       } else if ($$->u.unary_expression.type == UNARY_SIGNED_CONSTANT) {
-                               $$->u.unary_expression.u.signed_constant =
-                                       -($$->u.unary_expression.u.signed_constant);
-                       } else {
-                               reparent_error(scanner, "expecting numeric constant");
-                       }
-               }
-       ;
-
-unary_expression_or_range:
-               unary_expression CTF_DOTDOTDOT unary_expression
-               {
-                       $$ = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->tmp_head);
-                       $3->u.unary_expression.link = UNARY_DOTDOTDOT;
-               }
-       |       unary_expression
-               {       $$ = $1;                }
-       ;
-
-/* 2.2: Declarations */
-
-declaration:
-               declaration_specifiers CTF_SEMICOLON
-               {       $$ = $1;        }
-       |       event_declaration
-               {       $$ = $1;        }
-       |       stream_declaration
-               {       $$ = $1;        }
-       |       env_declaration
-               {       $$ = $1;        }
-       |       trace_declaration
-               {       $$ = $1;        }
-       |       clock_declaration
-               {       $$ = $1;        }
-       |       callsite_declaration
-               {       $$ = $1;        }
-       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEALIAS);
-                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
-                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
-               }
-       ;
-
-event_declaration:
-               event_declaration_begin event_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_EVENT);
-               }
-       |       event_declaration_begin ctf_assignment_expression_list event_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_EVENT);
-                       if (set_parent_node($2, $$))
-                               reparent_error(scanner, "event_declaration");
-               }
-       ;
-
-event_declaration_begin:
-               CTF_EVENT CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-event_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-
-stream_declaration:
-               stream_declaration_begin stream_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STREAM);
-               }
-       |       stream_declaration_begin ctf_assignment_expression_list stream_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STREAM);
-                       if (set_parent_node($2, $$))
-                               reparent_error(scanner, "stream_declaration");
-               }
-       ;
-
-stream_declaration_begin:
-               CTF_STREAM CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-stream_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-env_declaration:
-               env_declaration_begin env_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_ENV);
-               }
-       |       env_declaration_begin ctf_assignment_expression_list env_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_ENV);
-                       if (set_parent_node($2, $$))
-                               reparent_error(scanner, "env declaration");
-               }
-       ;
-
-env_declaration_begin:
-               CTF_ENV CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-env_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-trace_declaration:
-               trace_declaration_begin trace_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_TRACE);
-               }
-       |       trace_declaration_begin ctf_assignment_expression_list trace_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_TRACE);
-                       if (set_parent_node($2, $$))
-                               reparent_error(scanner, "trace_declaration");
-               }
-       ;
-
-trace_declaration_begin:
-               CTF_TRACE CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-trace_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-clock_declaration:
-               CTF_CLOCK clock_declaration_begin clock_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_CLOCK);
-               }
-       |       CTF_CLOCK clock_declaration_begin ctf_assignment_expression_list clock_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_CLOCK);
-                       if (set_parent_node($3, $$))
-                               reparent_error(scanner, "trace_declaration");
-               }
-       ;
-
-clock_declaration_begin:
-               CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-clock_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-callsite_declaration:
-               CTF_CALLSITE callsite_declaration_begin callsite_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_CALLSITE);
-               }
-       |       CTF_CALLSITE callsite_declaration_begin ctf_assignment_expression_list callsite_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_CALLSITE);
-                       if (set_parent_node($3, $$))
-                               reparent_error(scanner, "trace_declaration");
-               }
-       ;
-
-callsite_declaration_begin:
-               CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-callsite_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-integer_declaration_specifiers:
-               CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       integer_field_class_specifier
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = $1;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       integer_declaration_specifiers CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = $1;
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       integer_declaration_specifiers integer_field_class_specifier
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       ;
-
-declaration_specifiers:
-               CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       field_class_specifier
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = $1;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       declaration_specifiers CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = $1;
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       declaration_specifiers field_class_specifier
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       ;
-
-field_class_declarator_list:
-               field_class_declarator
-               {       $$ = $1;        }
-       |       field_class_declarator_list CTF_COMMA field_class_declarator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-integer_field_class_specifier:
-               CTF_CHAR
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_CHAR;
-               }
-       |       CTF_SHORT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_SHORT;
-               }
-       |       CTF_INT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INT;
-               }
-       |       CTF_LONG
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_LONG;
-               }
-       |       CTF_SIGNED
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_SIGNED;
-               }
-       |       CTF_UNSIGNED
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED;
-               }
-       |       CTF_BOOL
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_BOOL;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
-                       $$->u.field_class_specifier.id_type = yylval.s;
-               }
-       |       CTF_INTEGER CTF_LBRAC CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
-               }
-       |       CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
-                       if (set_parent_node($3, $$->u.field_class_specifier.node))
-                               reparent_error(scanner, "integer reparent error");
-               }
-       ;
-
-field_class_specifier:
-               CTF_VOID
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_VOID;
-               }
-       |       CTF_CHAR
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_CHAR;
-               }
-       |       CTF_SHORT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_SHORT;
-               }
-       |       CTF_INT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INT;
-               }
-       |       CTF_LONG
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_LONG;
-               }
-       |       CTF_FLOAT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_FLOAT;
-               }
-       |       CTF_DOUBLE
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_DOUBLE;
-               }
-       |       CTF_SIGNED
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_SIGNED;
-               }
-       |       CTF_UNSIGNED
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED;
-               }
-       |       CTF_BOOL
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_BOOL;
-               }
-       |       CTF_COMPLEX
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_COMPLEX;
-               }
-       |       CTF_IMAGINARY
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_IMAGINARY;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
-                       $$->u.field_class_specifier.id_type = yylval.s;
-               }
-       |       CTF_FLOATING_POINT CTF_LBRAC CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
-               }
-       |       CTF_FLOATING_POINT CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
-                       if (set_parent_node($3, $$->u.field_class_specifier.node))
-                               reparent_error(scanner, "floating point reparent error");
-               }
-       |       CTF_INTEGER CTF_LBRAC CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
-               }
-       |       CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
-                       if (set_parent_node($3, $$->u.field_class_specifier.node))
-                               reparent_error(scanner, "integer reparent error");
-               }
-       |       CTF_STRING
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
-               }
-       |       CTF_STRING CTF_LBRAC CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
-               }
-       |       CTF_STRING CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
-                       if (set_parent_node($3, $$->u.field_class_specifier.node))
-                               reparent_error(scanner, "string reparent error");
-               }
-       |       CTF_ENUM enum_field_class_specifier
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_ENUM;
-                       $$->u.field_class_specifier.node = $2;
-               }
-       |       CTF_VARIANT variant_field_class_specifier
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_VARIANT;
-                       $$->u.field_class_specifier.node = $2;
-               }
-       |       CTF_STRUCT struct_class_specifier
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_STRUCT;
-                       $$->u.field_class_specifier.node = $2;
-               }
-       ;
-
-struct_class_specifier:
-               struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       if ($2 && set_parent_node($2, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       $$->u._struct.name = $1;
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       $$->u._struct.name = $1;
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 0;
-                       $$->u._struct.name = $1;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 0;
-                       $$->u._struct.name = $1;
-               }
-       |       struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       bt_list_add_tail(&($6)->siblings, &$$->u._struct.min_align);
-                       if ($2 && set_parent_node($2, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       $$->u._struct.name = $1;
-                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       $$->u._struct.name = $1;
-                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       ;
-
-struct_declaration_begin:
-               CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-struct_declaration_end:
-               CTF_RBRAC
-               {       pop_scope(scanner);     }
-       ;
-
-variant_field_class_specifier:
-               variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       if ($2 && set_parent_node($2, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.choice = $2;
-                       if ($5 && set_parent_node($5, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.choice = $2;
-                       if ($5 && set_parent_node($5, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       IDENTIFIER variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       IDENTIFIER CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-                       if ($6 && set_parent_node($6, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       IDENTIFIER CTF_LT IDENTIFIER CTF_GT
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 0;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-               }
-       |       IDENTIFIER CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-                       if ($6 && set_parent_node($6, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       IDENTIFIER CTF_LT ID_TYPE CTF_GT
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 0;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-               }
-       |       ID_TYPE variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       ID_TYPE CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-                       if ($6 && set_parent_node($6, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       ID_TYPE CTF_LT IDENTIFIER CTF_GT
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 0;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-               }
-       |       ID_TYPE CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-                       if ($6 && set_parent_node($6, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       ID_TYPE CTF_LT ID_TYPE CTF_GT
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 0;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-               }
-       ;
-
-variant_declaration_begin:
-               CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-variant_declaration_end:
-               CTF_RBRAC
-               {       pop_scope(scanner);     }
-       ;
-
-enum_field_class_specifier:
-               CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       ($$)->u._enum.container_field_class = $2;
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       ($$)->u._enum.container_field_class = $3;
-                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       ID_TYPE CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       ($$)->u._enum.container_field_class = $3;
-                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       ($$)->u._enum.container_field_class = $2;
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       ($$)->u._enum.container_field_class = $3;
-                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 0;
-                       $$->u._enum.enum_id = $1;
-               }
-       |       ID_TYPE CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       ($$)->u._enum.container_field_class = $3;
-                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 0;
-                       $$->u._enum.enum_id = $1;
-               }
-       ;
-
-struct_or_variant_declaration_list:
-               /* empty */
-               {       $$ = NULL;      }
-       |       struct_or_variant_declaration_list struct_or_variant_declaration
-               {
-                       if ($1) {
-                               $$ = $1;
-                               bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
-                       } else {
-                               $$ = $2;
-                               bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-                       }
-               }
-       ;
-
-struct_or_variant_declaration:
-               declaration_specifiers struct_or_variant_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       $$ = make_node(scanner, NODE_STRUCT_OR_VARIANT_DECLARATION);
-                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u.struct_or_variant_declaration.field_class_declarators);
-               }
-       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEALIAS);
-                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
-                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
-               }
-       ;
-
-alias_declaration_specifiers:
-               CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       field_class_specifier
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = $1;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       IDENTIFIER
-               {
-                       struct ctf_node *node;
-
-                       add_type(scanner, $1);
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
-                       node->u.field_class_specifier.id_type = yylval.s;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       alias_declaration_specifiers CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = $1;
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       alias_declaration_specifiers field_class_specifier
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       alias_declaration_specifiers IDENTIFIER
-               {
-                       struct ctf_node *node;
-
-                       add_type(scanner, $2);
-                       $$ = $1;
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
-                       node->u.field_class_specifier.id_type = yylval.s;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       ;
-
-struct_or_variant_declarator_list:
-               struct_or_variant_declarator
-               {       $$ = $1;        }
-       |       struct_or_variant_declarator_list CTF_COMMA struct_or_variant_declarator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-struct_or_variant_declarator:
-               declarator
-               {       $$ = $1;        }
-       |       CTF_COLON unary_expression
-               {       $$ = $2;        }
-       |       declarator CTF_COLON unary_expression
-               {
-                       $$ = $1;
-                       if (set_parent_node($3, $1))
-                               reparent_error(scanner, "struct_or_variant_declarator");
-               }
-       ;
-
-enumerator_list:
-               enumerator
-               {       $$ = $1;        }
-       |       enumerator_list CTF_COMMA enumerator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-enumerator:
-               IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-               }
-       |       keywords
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-               }
-       |       CTF_STRING_LITERAL
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-               }
-       |       IDENTIFIER CTF_EQUAL unary_expression_or_range
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
-               }
-       |       ID_TYPE CTF_EQUAL unary_expression_or_range
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
-               }
-       |       keywords CTF_EQUAL unary_expression_or_range
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
-               }
-       |       CTF_STRING_LITERAL CTF_EQUAL unary_expression_or_range
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
-               }
-       ;
-
-abstract_declarator_list:
-               abstract_declarator
-               {       $$ = $1;        }
-       |       abstract_declarator_list CTF_COMMA abstract_declarator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-abstract_declarator:
-               direct_abstract_declarator
-               {       $$ = $1;        }
-       |       pointer direct_abstract_declarator
-               {
-                       $$ = $2;
-                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
-               }
-       ;
-
-direct_abstract_declarator:
-               /* empty */
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                        $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       /* id is NULL */
-               }
-       |       IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       $$->u.field_class_declarator.u.id = $1;
-               }
-       |       CTF_LPAREN abstract_declarator CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
-               }
-       |       direct_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
-               }
-       |       direct_abstract_declarator CTF_LSBRAC CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       $$->u.field_class_declarator.u.nested.abstract_array = 1;
-               }
-       ;
-
-alias_abstract_declarator_list:
-               alias_abstract_declarator
-               {       $$ = $1;        }
-       |       alias_abstract_declarator_list CTF_COMMA alias_abstract_declarator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-alias_abstract_declarator:
-               direct_alias_abstract_declarator
-               {       $$ = $1;        }
-       |       pointer direct_alias_abstract_declarator
-               {
-                       $$ = $2;
-                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
-               }
-       ;
-
-direct_alias_abstract_declarator:
-               /* empty */
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                        $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       /* id is NULL */
-               }
-       |       CTF_LPAREN alias_abstract_declarator CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
-               }
-       |       direct_alias_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
-               }
-       |       direct_alias_abstract_declarator CTF_LSBRAC CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       $$->u.field_class_declarator.u.nested.abstract_array = 1;
-               }
-       ;
-
-declarator:
-               direct_declarator
-               {       $$ = $1;        }
-       |       pointer direct_declarator
-               {
-                       $$ = $2;
-                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
-               }
-       ;
-
-direct_declarator:
-               IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       $$->u.field_class_declarator.u.id = $1;
-               }
-       |       CTF_LPAREN declarator CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
-               }
-       |       direct_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
-               }
-       ;
-
-field_class_declarator:
-               direct_field_class_declarator
-               {       $$ = $1;        }
-       |       pointer direct_field_class_declarator
-               {
-                       $$ = $2;
-                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
-               }
-       ;
-
-direct_field_class_declarator:
-               IDENTIFIER
-               {
-                       add_type(scanner, $1);
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       $$->u.field_class_declarator.u.id = $1;
-               }
-       |       CTF_LPAREN field_class_declarator CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
-               }
-       |       direct_field_class_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
-               }
-       ;
-
-pointer:
-               CTF_STAR
-               {
-                       $$ = make_node(scanner, NODE_POINTER);
-               }
-       |       CTF_STAR pointer
-               {
-                       $$ = make_node(scanner, NODE_POINTER);
-                       bt_list_splice(&($2)->tmp_head, &($$)->tmp_head);
-               }
-       |       CTF_STAR type_qualifier_list pointer
-               {
-                       $$ = make_node(scanner, NODE_POINTER);
-                       $$->u.pointer.const_qualifier = 1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->tmp_head);
-               }
-       ;
-
-type_qualifier_list:
-               /* pointer assumes only const type qualifier */
-               CTF_CONST
-       |       type_qualifier_list CTF_CONST
-       ;
-
-/* 2.3: CTF-specific declarations */
-
-ctf_assignment_expression_list:
-               ctf_assignment_expression CTF_SEMICOLON
-               {       $$ = $1;        }
-       |       ctf_assignment_expression_list ctf_assignment_expression CTF_SEMICOLON
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-ctf_assignment_expression:
-               unary_expression CTF_EQUAL unary_expression
-               {
-                       /*
-                        * Because we have left and right, cannot use
-                        * set_parent_node.
-                        */
-                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
-                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
-                       if ($1->u.unary_expression.type != UNARY_STRING)
-                               reparent_error(scanner, "ctf_assignment_expression left expects string");
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.ctf_expression.right);
-               }
-       |       unary_expression CTF_TYPEASSIGN declaration_specifiers  /* Only allow struct */
-               {
-                       /*
-                        * Because we have left and right, cannot use
-                        * set_parent_node.
-                        */
-                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
-                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
-                       if ($1->u.unary_expression.type != UNARY_STRING)
-                               reparent_error(scanner, "ctf_assignment_expression left expects string");
-                       bt_list_add_tail(&($3)->siblings, &($$)->u.ctf_expression.right);
-               }
-       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list
-               {
-                       struct ctf_node *list;
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list
-               {
-                       struct ctf_node *list;
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEALIAS);
-                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
-                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
-               }
-       ;
diff --git a/src/plugins/ctf/common/metadata/scanner-symbols.hpp b/src/plugins/ctf/common/metadata/scanner-symbols.hpp
deleted file mode 100644 (file)
index 1f6f144..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#ifndef _CTF_SCANNER_SYMBOLS
-#define _CTF_SCANNER_SYMBOLS
-
-#define yy_create_buffer    bt_yy_create_buffer
-#define yy_delete_buffer    bt_yy_delete_buffer
-#define yy_flush_buffer     bt_yy_flush_buffer
-#define yy_scan_buffer      bt_yy_scan_buffer
-#define yy_scan_bytes       bt_yy_scan_bytes
-#define yy_scan_string      bt_yy_scan_string
-#define yy_switch_to_buffer bt_yy_switch_to_buffer
-#define yyalloc             bt_yyalloc
-#define yyfree              bt_yyfree
-#define yyget_column        bt_yyget_column
-#define yyget_debug         bt_yyget_debug
-#define yyget_extra         bt_yyget_extra
-#define yyget_in            bt_yyget_in
-#define yyget_leng          bt_yyget_leng
-#define yyget_lineno        bt_yyget_lineno
-#define yyget_lval          bt_yyget_lval
-#define yyget_out           bt_yyget_out
-#define yyget_text          bt_yyget_text
-#define yylex_init          bt_yylex_init
-#define yypop_buffer_state  bt_yypop_buffer_state
-#define yypush_buffer_state bt_yypush_buffer_state
-#define yyrealloc           bt_yyrealloc
-#define yyset_column        bt_yyset_column
-#define yyset_debug         bt_yyset_debug
-#define yyset_extra         bt_yyset_extra
-#define yyset_in            bt_yyset_in
-#define yyset_lineno        bt_yyset_lineno
-#define yyset_lval          bt_yyset_lval
-#define yyset_out           bt_yyset_out
-
-#endif /* _CTF_SCANNER_SYMBOLS */
diff --git a/src/plugins/ctf/common/metadata/scanner.hpp b/src/plugins/ctf/common/metadata/scanner.hpp
deleted file mode 100644 (file)
index d924ec5..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#ifndef _CTF_SCANNER_H
-#define _CTF_SCANNER_H
-
-#include <stdio.h>
-#include "common/macros.h"
-#include "ast.hpp"
-
-#ifndef YY_TYPEDEF_YY_SCANNER_T
-#    define YY_TYPEDEF_YY_SCANNER_T
-typedef void *yyscan_t;
-#endif
-
-struct ctf_scanner_scope;
-struct ctf_scanner_scope
-{
-    struct ctf_scanner_scope *parent;
-    GHashTable *classes;
-};
-
-struct ctf_scanner
-{
-    yyscan_t scanner;
-    struct ctf_ast *ast;
-    struct ctf_scanner_scope root_scope;
-    struct ctf_scanner_scope *cs;
-    struct objstack *objstack;
-};
-
-BT_HIDDEN
-struct ctf_scanner *ctf_scanner_alloc(void);
-
-BT_HIDDEN
-void ctf_scanner_free(struct ctf_scanner *scanner);
-
-BT_HIDDEN
-int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input);
-
-static inline struct ctf_ast *ctf_scanner_get_ast(struct ctf_scanner *scanner)
-{
-    return scanner->ast;
-}
-
-BT_HIDDEN
-int is_type(struct ctf_scanner *scanner, const char *id);
-
-#endif /* _CTF_SCANNER_H */
diff --git a/src/plugins/ctf/common/metadata/visitor-generate-ir.cpp b/src/plugins/ctf/common/metadata/visitor-generate-ir.cpp
deleted file mode 100644 (file)
index 50d8d44..0000000
+++ /dev/null
@@ -1,4785 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright 2015-2018 Philippe Proulx <philippe.proulx@efficios.com>
- *
- * Common Trace Format metadata visitor (generates CTF IR objects).
- */
-
-#define BT_COMP_LOG_SELF_COMP       (ctx->log_cfg.self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (ctx->log_cfg.self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL         (ctx->log_cfg.log_level)
-#define BT_LOG_TAG                  "PLUGIN/CTF/META/IR-VISITOR"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include "common/assert.h"
-#include <glib.h>
-#include <inttypes.h>
-#include <errno.h>
-#include "common/common.h"
-#include "common/uuid.h"
-#include "compat/endian.h"
-#include <babeltrace2/babeltrace.h>
-
-#include "logging.hpp"
-#include "scanner.hpp"
-#include "ast.hpp"
-#include "decoder.hpp"
-#include "ctf-meta.hpp"
-#include "ctf-meta-visitors.hpp"
-
-/* Bit value (left shift) */
-#define _BV(_val) (1 << (_val))
-
-/* Bit is set in a set of bits */
-#define _IS_SET(_set, _mask) (*(_set) & (_mask))
-
-/* Set bit in a set of bits */
-#define _SET(_set, _mask) (*(_set) |= (_mask))
-
-/* Try to push scope, or go to the `error` label */
-#define _TRY_PUSH_SCOPE_OR_GOTO_ERROR()                                                            \
-    do {                                                                                           \
-        ret = ctx_push_scope(ctx);                                                                 \
-        if (ret) {                                                                                 \
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot push scope.");                        \
-            goto error;                                                                            \
-        }                                                                                          \
-    } while (0)
-
-/* Bits for verifying existing attributes in various declarations */
-enum
-{
-    _CLOCK_NAME_SET = _BV(0),
-    _CLOCK_UUID_SET = _BV(1),
-    _CLOCK_FREQ_SET = _BV(2),
-    _CLOCK_PRECISION_SET = _BV(3),
-    _CLOCK_OFFSET_S_SET = _BV(4),
-    _CLOCK_OFFSET_SET = _BV(5),
-    _CLOCK_ABSOLUTE_SET = _BV(6),
-    _CLOCK_DESCRIPTION_SET = _BV(7),
-};
-
-enum
-{
-    _INTEGER_ALIGN_SET = _BV(0),
-    _INTEGER_SIZE_SET = _BV(1),
-    _INTEGER_BASE_SET = _BV(2),
-    _INTEGER_ENCODING_SET = _BV(3),
-    _INTEGER_BYTE_ORDER_SET = _BV(4),
-    _INTEGER_SIGNED_SET = _BV(5),
-    _INTEGER_MAP_SET = _BV(6),
-};
-
-enum
-{
-    _FLOAT_ALIGN_SET = _BV(0),
-    _FLOAT_MANT_DIG_SET = _BV(1),
-    _FLOAT_EXP_DIG_SET = _BV(2),
-    _FLOAT_BYTE_ORDER_SET = _BV(3),
-};
-
-enum
-{
-    _STRING_ENCODING_SET = _BV(0),
-};
-
-enum
-{
-    _TRACE_MINOR_SET = _BV(0),
-    _TRACE_MAJOR_SET = _BV(1),
-    _TRACE_BYTE_ORDER_SET = _BV(2),
-    _TRACE_UUID_SET = _BV(3),
-    _TRACE_PACKET_HEADER_SET = _BV(4),
-};
-
-enum
-{
-    _STREAM_ID_SET = _BV(0),
-    _STREAM_PACKET_CONTEXT_SET = _BV(1),
-    _STREAM_EVENT_HEADER_SET = _BV(2),
-    _STREAM_EVENT_CONTEXT_SET = _BV(3),
-};
-
-enum
-{
-    _EVENT_NAME_SET = _BV(0),
-    _EVENT_ID_SET = _BV(1),
-    _EVENT_MODEL_EMF_URI_SET = _BV(2),
-    _EVENT_STREAM_ID_SET = _BV(3),
-    _EVENT_LOG_LEVEL_SET = _BV(4),
-    _EVENT_CONTEXT_SET = _BV(5),
-    _EVENT_FIELDS_SET = _BV(6),
-};
-
-enum loglevel
-{
-    LOG_LEVEL_EMERG = 0,
-    LOG_LEVEL_ALERT = 1,
-    LOG_LEVEL_CRIT = 2,
-    LOG_LEVEL_ERR = 3,
-    LOG_LEVEL_WARNING = 4,
-    LOG_LEVEL_NOTICE = 5,
-    LOG_LEVEL_INFO = 6,
-    LOG_LEVEL_DEBUG_SYSTEM = 7,
-    LOG_LEVEL_DEBUG_PROGRAM = 8,
-    LOG_LEVEL_DEBUG_PROCESS = 9,
-    LOG_LEVEL_DEBUG_MODULE = 10,
-    LOG_LEVEL_DEBUG_UNIT = 11,
-    LOG_LEVEL_DEBUG_FUNCTION = 12,
-    LOG_LEVEL_DEBUG_LINE = 13,
-    LOG_LEVEL_DEBUG = 14,
-    _NR_LOGLEVELS = 15,
-};
-
-/* Prefixes of class aliases */
-#define _PREFIX_ALIAS   'a'
-#define _PREFIX_ENUM    'e'
-#define _PREFIX_STRUCT  's'
-#define _PREFIX_VARIANT 'v'
-
-/* First entry in a BT list */
-#define _BT_LIST_FIRST_ENTRY(_ptr, _class, _member) bt_list_entry((_ptr)->next, _class, _member)
-
-#define _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(_node, _attr, _entity)                                 \
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(                                                             \
-        (_node)->lineno, "Duplicate attribute in %s: attr-name=\"%s\"", _entity, _attr)
-
-#define _BT_COMP_LOGE_NODE(_node, _msg, args...) _BT_COMP_LOGE_LINENO((_node)->lineno, _msg, ##args)
-
-#define _BT_COMP_LOGE_APPEND_CAUSE_NODE(_node, _msg, args...)                                      \
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO((_node)->lineno, _msg, ##args)
-
-#define _BT_COMP_LOGW_NODE(_node, _msg, args...) _BT_COMP_LOGW_LINENO((_node)->lineno, _msg, ##args)
-
-#define _BT_COMP_LOGT_NODE(_node, _msg, args...) _BT_COMP_LOGT_LINENO((_node)->lineno, _msg, ##args)
-
-/*
- * Declaration scope of a visitor context. This represents a TSDL
- * lexical scope, so that aliases and named structures, variants,
- * and enumerations may be registered and looked up hierarchically.
- */
-struct ctx_decl_scope
-{
-    /*
-     * Alias name to field class.
-     *
-     * GQuark -> struct ctf_field_class * (owned by this)
-     */
-    GHashTable *decl_map;
-
-    /* Parent scope; NULL if this is the root declaration scope */
-    struct ctx_decl_scope *parent_scope;
-};
-
-/*
- * Visitor context (private).
- */
-struct ctf_visitor_generate_ir
-{
-    struct meta_log_config log_cfg;
-
-    /* Trace IR trace class being filled (owned by this) */
-    bt_trace_class *trace_class;
-
-    /* CTF meta trace being filled (owned by this) */
-    struct ctf_trace_class *ctf_tc;
-
-    /* Current declaration scope (top of the stack) (owned by this) */
-    struct ctx_decl_scope *current_scope;
-
-    /* True if trace declaration is visited */
-    bool is_trace_visited;
-
-    /* True if this is an LTTng trace */
-    bool is_lttng;
-
-    /* Config passed by the user */
-    struct ctf_metadata_decoder_config decoder_config;
-};
-
-/*
- * Visitor (public).
- */
-struct ctf_visitor_generate_ir;
-
-/**
- * Creates a new declaration scope.
- *
- * @param par_scope    Parent scope (NULL if creating a root scope)
- * @returns            New declaration scope, or NULL on error
- */
-static struct ctx_decl_scope *ctx_decl_scope_create(struct ctf_visitor_generate_ir *ctx,
-                                                    struct ctx_decl_scope *par_scope)
-{
-    struct ctx_decl_scope *scope;
-
-    scope = g_new(struct ctx_decl_scope, 1);
-    if (!scope) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one declaration scope.");
-        goto end;
-    }
-
-    scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
-                                            (GDestroyNotify) ctf_field_class_destroy);
-    scope->parent_scope = par_scope;
-
-end:
-    return scope;
-}
-
-/**
- * Destroys a declaration scope.
- *
- * This function does not destroy the parent scope.
- *
- * @param scope        Scope to destroy
- */
-static void ctx_decl_scope_destroy(struct ctx_decl_scope *scope)
-{
-    if (!scope) {
-        goto end;
-    }
-
-    g_hash_table_destroy(scope->decl_map);
-    g_free(scope);
-
-end:
-    return;
-}
-
-/**
- * Returns the GQuark of a prefixed alias.
- *
- * @param prefix       Prefix character
- * @param name         Name
- * @returns            Associated GQuark, or 0 on error
- */
-static GQuark get_prefixed_named_quark(struct ctf_visitor_generate_ir *ctx, char prefix,
-                                       const char *name)
-{
-    GQuark qname = 0;
-
-    BT_ASSERT(name);
-
-    /* Prefix character + original string + '\0' */
-    char *prname = g_new(char, strlen(name) + 2);
-    if (!prname) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate a string.");
-        goto end;
-    }
-
-    sprintf(prname, "%c%s", prefix, name);
-    qname = g_quark_from_string(prname);
-    g_free(prname);
-
-end:
-    return qname;
-}
-
-/**
- * Looks up a prefixed class alias within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param prefix       Prefix character
- * @param name         Alias name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static struct ctf_field_class *
-ctx_decl_scope_lookup_prefix_alias(struct ctf_visitor_generate_ir *ctx,
-                                   struct ctx_decl_scope *scope, char prefix, const char *name,
-                                   int levels, bool copy)
-{
-    GQuark qname = 0;
-    int cur_levels = 0;
-    struct ctf_field_class *decl = NULL;
-    struct ctx_decl_scope *cur_scope = scope;
-
-    BT_ASSERT(scope);
-    BT_ASSERT(name);
-    qname = get_prefixed_named_quark(ctx, prefix, name);
-    if (!qname) {
-        goto end;
-    }
-
-    if (levels < 0) {
-        levels = INT_MAX;
-    }
-
-    while (cur_scope && cur_levels < levels) {
-        decl = (ctf_field_class *) g_hash_table_lookup(cur_scope->decl_map,
-                                                       (gconstpointer) GUINT_TO_POINTER(qname));
-        if (decl) {
-            /* Caller's reference */
-            if (copy) {
-                decl = ctf_field_class_copy(decl);
-                BT_ASSERT(decl);
-            }
-
-            goto end;
-        }
-
-        cur_scope = cur_scope->parent_scope;
-        cur_levels++;
-    }
-
-end:
-    return decl;
-}
-
-/**
- * Looks up a class alias within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param name         Alias name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static struct ctf_field_class *ctx_decl_scope_lookup_alias(struct ctf_visitor_generate_ir *ctx,
-                                                           struct ctx_decl_scope *scope,
-                                                           const char *name, int levels, bool copy)
-{
-    return ctx_decl_scope_lookup_prefix_alias(ctx, scope, _PREFIX_ALIAS, name, levels, copy);
-}
-
-/**
- * Looks up an enumeration within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param name         Enumeration name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static struct ctf_field_class_enum *ctx_decl_scope_lookup_enum(struct ctf_visitor_generate_ir *ctx,
-                                                               struct ctx_decl_scope *scope,
-                                                               const char *name, int levels,
-                                                               bool copy)
-{
-    return ctf_field_class_as_enum(
-        ctx_decl_scope_lookup_prefix_alias(ctx, scope, _PREFIX_ENUM, name, levels, copy));
-}
-
-/**
- * Looks up a structure within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param name         Structure name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static struct ctf_field_class_struct *
-ctx_decl_scope_lookup_struct(struct ctf_visitor_generate_ir *ctx, struct ctx_decl_scope *scope,
-                             const char *name, int levels, bool copy)
-{
-    return ctf_field_class_as_struct(
-        ctx_decl_scope_lookup_prefix_alias(ctx, scope, _PREFIX_STRUCT, name, levels, copy));
-}
-
-/**
- * Looks up a variant within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param name         Variant name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static struct ctf_field_class_variant *
-ctx_decl_scope_lookup_variant(struct ctf_visitor_generate_ir *ctx, struct ctx_decl_scope *scope,
-                              const char *name, int levels, bool copy)
-{
-    return ctf_field_class_as_variant(
-        ctx_decl_scope_lookup_prefix_alias(ctx, scope, _PREFIX_VARIANT, name, levels, copy));
-}
-
-/**
- * Registers a prefixed class alias within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param prefix       Prefix character
- * @param name         Alias name (non-NULL)
- * @param decl         Field class to register (copied)
- * @returns            0 if registration went okay, negative value otherwise
- */
-static int ctx_decl_scope_register_prefix_alias(struct ctf_visitor_generate_ir *ctx,
-                                                struct ctx_decl_scope *scope, char prefix,
-                                                const char *name, struct ctf_field_class *decl)
-{
-    int ret = 0;
-    GQuark qname = 0;
-
-    BT_ASSERT(scope);
-    BT_ASSERT(name);
-    BT_ASSERT(decl);
-    qname = get_prefixed_named_quark(ctx, prefix, name);
-    if (!qname) {
-        ret = -ENOMEM;
-        goto end;
-    }
-
-    /* Make sure alias does not exist in local scope */
-    if (ctx_decl_scope_lookup_prefix_alias(ctx, scope, prefix, name, 1, false)) {
-        ret = -EEXIST;
-        goto end;
-    }
-
-    decl = ctf_field_class_copy(decl);
-    BT_ASSERT(decl);
-    g_hash_table_insert(scope->decl_map, GUINT_TO_POINTER(qname), decl);
-
-end:
-    return ret;
-}
-
-/**
- * Registers a class alias within a declaration scope.
- *
- * @param scope        Declaration scope
- * @param name Alias name (non-NULL)
- * @param decl Field class to register (copied)
- * @returns    0 if registration went okay, negative value otherwise
- */
-static int ctx_decl_scope_register_alias(struct ctf_visitor_generate_ir *ctx,
-                                         struct ctx_decl_scope *scope, const char *name,
-                                         struct ctf_field_class *decl)
-{
-    return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_ALIAS, name, decl);
-}
-
-/**
- * Registers an enumeration declaration within a declaration scope.
- *
- * @param scope        Declaration scope
- * @param name Enumeration name (non-NULL)
- * @param decl Enumeration field class to register (copied)
- * @returns    0 if registration went okay, negative value otherwise
- */
-static int ctx_decl_scope_register_enum(struct ctf_visitor_generate_ir *ctx,
-                                        struct ctx_decl_scope *scope, const char *name,
-                                        struct ctf_field_class_enum *decl)
-{
-    return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_ENUM, name,
-                                                &decl->base.base.base);
-}
-
-/**
- * Registers a structure declaration within a declaration scope.
- *
- * @param scope        Declaration scope
- * @param name Structure name (non-NULL)
- * @param decl Structure field class to register (copied)
- * @returns    0 if registration went okay, negative value otherwise
- */
-static int ctx_decl_scope_register_struct(struct ctf_visitor_generate_ir *ctx,
-                                          struct ctx_decl_scope *scope, const char *name,
-                                          struct ctf_field_class_struct *decl)
-{
-    return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_STRUCT, name, &decl->base);
-}
-
-/**
- * Registers a variant declaration within a declaration scope.
- *
- * @param scope        Declaration scope
- * @param name Variant name (non-NULL)
- * @param decl Variant field class to register
- * @returns    0 if registration went okay, negative value otherwise
- */
-static int ctx_decl_scope_register_variant(struct ctf_visitor_generate_ir *ctx,
-                                           struct ctx_decl_scope *scope, const char *name,
-                                           struct ctf_field_class_variant *decl)
-{
-    return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_VARIANT, name, &decl->base);
-}
-
-/**
- * Destroys a visitor context.
- *
- * @param ctx  Visitor context to destroy
- */
-static void ctx_destroy(struct ctf_visitor_generate_ir *ctx)
-{
-    struct ctx_decl_scope *scope;
-
-    if (!ctx) {
-        goto end;
-    }
-
-    scope = ctx->current_scope;
-
-    /*
-     * Destroy all scopes, from current one to the root scope.
-     */
-    while (scope) {
-        struct ctx_decl_scope *parent_scope = scope->parent_scope;
-
-        ctx_decl_scope_destroy(scope);
-        scope = parent_scope;
-    }
-
-    bt_trace_class_put_ref(ctx->trace_class);
-
-    if (ctx->ctf_tc) {
-        ctf_trace_class_destroy(ctx->ctf_tc);
-    }
-
-    g_free(ctx);
-
-end:
-    return;
-}
-
-/**
- * Creates a new visitor context.
- *
- * @param trace        Associated trace
- * @returns    New visitor context, or NULL on error
- */
-static struct ctf_visitor_generate_ir *
-ctx_create(const struct ctf_metadata_decoder_config *decoder_config)
-{
-    struct ctf_visitor_generate_ir *ctx = NULL;
-
-    BT_ASSERT(decoder_config);
-
-    ctx = g_new0(struct ctf_visitor_generate_ir, 1);
-    if (!ctx) {
-        BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, decoder_config->log_level, decoder_config->self_comp,
-                            "Failed to allocate one visitor context.");
-        goto error;
-    }
-
-    ctx->log_cfg.log_level = decoder_config->log_level;
-    ctx->log_cfg.self_comp = decoder_config->self_comp;
-    ctx->log_cfg.self_comp_class = decoder_config->self_comp_class;
-
-    if (decoder_config->self_comp) {
-        ctx->trace_class = bt_trace_class_create(decoder_config->self_comp);
-        if (!ctx->trace_class) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create empty trace class.");
-            goto error;
-        }
-    }
-
-    ctx->ctf_tc = ctf_trace_class_create();
-    if (!ctx->ctf_tc) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create CTF trace class.");
-        goto error;
-    }
-
-    /* Root declaration scope */
-    ctx->current_scope = ctx_decl_scope_create(ctx, NULL);
-    if (!ctx->current_scope) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create declaration scope.");
-        goto error;
-    }
-
-    ctx->decoder_config = *decoder_config;
-    goto end;
-
-error:
-    ctx_destroy(ctx);
-    ctx = NULL;
-
-end:
-    return ctx;
-}
-
-/**
- * Pushes a new declaration scope on top of a visitor context's
- * declaration scope stack.
- *
- * @param ctx  Visitor context
- * @returns    0 on success, or a negative value on error
- */
-static int ctx_push_scope(struct ctf_visitor_generate_ir *ctx)
-{
-    int ret = 0;
-    struct ctx_decl_scope *new_scope;
-
-    BT_ASSERT(ctx);
-    new_scope = ctx_decl_scope_create(ctx, ctx->current_scope);
-    if (!new_scope) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create declaration scope.");
-        ret = -ENOMEM;
-        goto end;
-    }
-
-    ctx->current_scope = new_scope;
-
-end:
-    return ret;
-}
-
-static void ctx_pop_scope(struct ctf_visitor_generate_ir *ctx)
-{
-    struct ctx_decl_scope *parent_scope = NULL;
-
-    BT_ASSERT(ctx);
-
-    if (!ctx->current_scope) {
-        goto end;
-    }
-
-    parent_scope = ctx->current_scope->parent_scope;
-    ctx_decl_scope_destroy(ctx->current_scope);
-    ctx->current_scope = parent_scope;
-
-end:
-    return;
-}
-
-static int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
-                                            struct ctf_node *ts_list,
-                                            struct ctf_field_class **decl);
-
-static int is_unary_string(struct bt_list_head *head)
-{
-    int ret = TRUE;
-    struct ctf_node *node;
-
-    bt_list_for_each_entry (node, head, siblings) {
-        if (node->type != NODE_UNARY_EXPRESSION) {
-            ret = FALSE;
-        }
-
-        if (node->u.unary_expression.type != UNARY_STRING) {
-            ret = FALSE;
-        }
-    }
-
-    return ret;
-}
-
-static const char *get_map_clock_name_value(struct bt_list_head *head)
-{
-    int i = 0;
-    struct ctf_node *node;
-    const char *name = NULL;
-
-    bt_list_for_each_entry (node, head, siblings) {
-        char *src_string;
-        int uexpr_type = node->u.unary_expression.type;
-        int uexpr_link = node->u.unary_expression.link;
-        int cond = node->type != NODE_UNARY_EXPRESSION || uexpr_type != UNARY_STRING ||
-                   !((uexpr_link != UNARY_LINK_UNKNOWN) ^ (i == 0));
-        if (cond) {
-            goto error;
-        }
-
-        /* Needs to be chained with . */
-        switch (node->u.unary_expression.link) {
-        case UNARY_DOTLINK:
-            break;
-        case UNARY_ARROWLINK:
-        case UNARY_DOTDOTDOT:
-            goto error;
-        default:
-            break;
-        }
-
-        src_string = node->u.unary_expression.u.string;
-
-        switch (i) {
-        case 0:
-            if (strcmp("clock", src_string)) {
-                goto error;
-            }
-            break;
-        case 1:
-            name = src_string;
-            break;
-        case 2:
-            if (strcmp("value", src_string)) {
-                goto error;
-            }
-            break;
-        default:
-            /* Extra identifier, unknown */
-            goto error;
-        }
-
-        i++;
-    }
-
-    return name;
-
-error:
-    return NULL;
-}
-
-static int is_unary_unsigned(struct bt_list_head *head)
-{
-    int ret = TRUE;
-    struct ctf_node *node;
-
-    bt_list_for_each_entry (node, head, siblings) {
-        if (node->type != NODE_UNARY_EXPRESSION) {
-            ret = FALSE;
-        }
-
-        if (node->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
-            ret = FALSE;
-        }
-    }
-
-    return ret;
-}
-
-static int get_unary_unsigned(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
-                              uint64_t *value)
-{
-    int i = 0;
-    int ret = 0;
-    struct ctf_node *node;
-
-    *value = 0;
-
-    if (bt_list_empty(head)) {
-        ret = -1;
-        goto end;
-    }
-
-    bt_list_for_each_entry (node, head, siblings) {
-        int uexpr_type = node->u.unary_expression.type;
-        int uexpr_link = node->u.unary_expression.link;
-        int cond = node->type != NODE_UNARY_EXPRESSION || uexpr_type != UNARY_UNSIGNED_CONSTANT ||
-                   uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
-        if (cond) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Invalid constant unsigned integer.");
-            ret = -EINVAL;
-            goto end;
-        }
-
-        *value = node->u.unary_expression.u.unsigned_constant;
-        i++;
-    }
-
-end:
-    return ret;
-}
-
-static int is_unary_signed(struct bt_list_head *head)
-{
-    int ret = TRUE;
-    struct ctf_node *node;
-
-    bt_list_for_each_entry (node, head, siblings) {
-        if (node->type != NODE_UNARY_EXPRESSION) {
-            ret = FALSE;
-        }
-
-        if (node->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
-            ret = FALSE;
-        }
-    }
-
-    return ret;
-}
-
-static int get_unary_signed(struct bt_list_head *head, int64_t *value)
-{
-    int i = 0;
-    int ret = 0;
-    struct ctf_node *node;
-
-    bt_list_for_each_entry (node, head, siblings) {
-        int uexpr_type = node->u.unary_expression.type;
-        int uexpr_link = node->u.unary_expression.link;
-        int cond = node->type != NODE_UNARY_EXPRESSION ||
-                   (uexpr_type != UNARY_UNSIGNED_CONSTANT && uexpr_type != UNARY_SIGNED_CONSTANT) ||
-                   uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
-        if (cond) {
-            ret = -EINVAL;
-            goto end;
-        }
-
-        switch (uexpr_type) {
-        case UNARY_UNSIGNED_CONSTANT:
-            *value = (int64_t) node->u.unary_expression.u.unsigned_constant;
-            break;
-        case UNARY_SIGNED_CONSTANT:
-            *value = node->u.unary_expression.u.signed_constant;
-            break;
-        default:
-            ret = -EINVAL;
-            goto end;
-        }
-
-        i++;
-    }
-
-end:
-    return ret;
-}
-
-static int get_unary_uuid(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
-                          bt_uuid_t uuid)
-{
-    return ctf_ast_get_unary_uuid(head, uuid, ctx->log_cfg.log_level, ctx->log_cfg.self_comp);
-}
-
-static int get_boolean(struct ctf_visitor_generate_ir *ctx, struct ctf_node *unary_expr)
-{
-    int ret = 0;
-
-    if (unary_expr->type != NODE_UNARY_EXPRESSION) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr, "Expecting unary expression: node-type=%d",
-                                        unary_expr->type);
-        ret = -EINVAL;
-        goto end;
-    }
-
-    switch (unary_expr->u.unary_expression.type) {
-    case UNARY_UNSIGNED_CONSTANT:
-        ret = (unary_expr->u.unary_expression.u.unsigned_constant != 0);
-        break;
-    case UNARY_SIGNED_CONSTANT:
-        ret = (unary_expr->u.unary_expression.u.signed_constant != 0);
-        break;
-    case UNARY_STRING:
-    {
-        const char *str = unary_expr->u.unary_expression.u.string;
-
-        if (strcmp(str, "true") == 0 || strcmp(str, "TRUE") == 0) {
-            ret = TRUE;
-        } else if (strcmp(str, "false") == 0 || strcmp(str, "FALSE") == 0) {
-            ret = FALSE;
-        } else {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr, "Unexpected boolean value: value=\"%s\"",
-                                            str);
-            ret = -EINVAL;
-            goto end;
-        }
-        break;
-    }
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
-                                        "Unexpected unary expression type: node-type=%d",
-                                        unary_expr->u.unary_expression.type);
-        ret = -EINVAL;
-        goto end;
-    }
-
-end:
-    return ret;
-}
-
-static enum ctf_byte_order byte_order_from_unary_expr(struct ctf_visitor_generate_ir *ctx,
-                                                      struct ctf_node *unary_expr)
-{
-    const char *str;
-    enum ctf_byte_order bo = CTF_BYTE_ORDER_UNKNOWN;
-
-    if (unary_expr->u.unary_expression.type != UNARY_STRING) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-            unary_expr, "\"byte_order\" attribute: expecting `be`, `le`, `network`, or `native`.");
-        goto end;
-    }
-
-    str = unary_expr->u.unary_expression.u.string;
-
-    if (strcmp(str, "be") == 0 || strcmp(str, "network") == 0) {
-        bo = CTF_BYTE_ORDER_BIG;
-    } else if (strcmp(str, "le") == 0) {
-        bo = CTF_BYTE_ORDER_LITTLE;
-    } else if (strcmp(str, "native") == 0) {
-        bo = CTF_BYTE_ORDER_DEFAULT;
-    } else {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-            unary_expr,
-            "Unexpected \"byte_order\" attribute value: "
-            "expecting `be`, `le`, `network`, or `native`: value=\"%s\"",
-            str);
-        goto end;
-    }
-
-end:
-    return bo;
-}
-
-static enum ctf_byte_order get_real_byte_order(struct ctf_visitor_generate_ir *ctx,
-                                               struct ctf_node *uexpr)
-{
-    enum ctf_byte_order bo = byte_order_from_unary_expr(ctx, uexpr);
-
-    if (bo == CTF_BYTE_ORDER_DEFAULT) {
-        bo = ctx->ctf_tc->default_byte_order;
-    }
-
-    return bo;
-}
-
-static int is_align_valid(uint64_t align)
-{
-    return (align != 0) && !(align & (align - UINT64_C(1)));
-}
-
-static int get_class_specifier_name(struct ctf_visitor_generate_ir *ctx,
-                                    struct ctf_node *cls_specifier, GString *str)
-{
-    int ret = 0;
-
-    if (cls_specifier->type != NODE_TYPE_SPECIFIER) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier, "Unexpected node type: node-type=%d",
-                                        cls_specifier->type);
-        ret = -EINVAL;
-        goto end;
-    }
-
-    switch (cls_specifier->u.field_class_specifier.type) {
-    case TYPESPEC_VOID:
-        g_string_append(str, "void");
-        break;
-    case TYPESPEC_CHAR:
-        g_string_append(str, "char");
-        break;
-    case TYPESPEC_SHORT:
-        g_string_append(str, "short");
-        break;
-    case TYPESPEC_INT:
-        g_string_append(str, "int");
-        break;
-    case TYPESPEC_LONG:
-        g_string_append(str, "long");
-        break;
-    case TYPESPEC_FLOAT:
-        g_string_append(str, "float");
-        break;
-    case TYPESPEC_DOUBLE:
-        g_string_append(str, "double");
-        break;
-    case TYPESPEC_SIGNED:
-        g_string_append(str, "signed");
-        break;
-    case TYPESPEC_UNSIGNED:
-        g_string_append(str, "unsigned");
-        break;
-    case TYPESPEC_BOOL:
-        g_string_append(str, "bool");
-        break;
-    case TYPESPEC_COMPLEX:
-        g_string_append(str, "_Complex");
-        break;
-    case TYPESPEC_IMAGINARY:
-        g_string_append(str, "_Imaginary");
-        break;
-    case TYPESPEC_CONST:
-        g_string_append(str, "const");
-        break;
-    case TYPESPEC_ID_TYPE:
-        if (cls_specifier->u.field_class_specifier.id_type) {
-            g_string_append(str, cls_specifier->u.field_class_specifier.id_type);
-        }
-        break;
-    case TYPESPEC_STRUCT:
-    {
-        struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
-
-        if (!node->u._struct.name) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unexpected empty structure field class name.");
-            ret = -EINVAL;
-            goto end;
-        }
-
-        g_string_append(str, "struct ");
-        g_string_append(str, node->u._struct.name);
-        break;
-    }
-    case TYPESPEC_VARIANT:
-    {
-        struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
-
-        if (!node->u.variant.name) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unexpected empty variant field class name.");
-            ret = -EINVAL;
-            goto end;
-        }
-
-        g_string_append(str, "variant ");
-        g_string_append(str, node->u.variant.name);
-        break;
-    }
-    case TYPESPEC_ENUM:
-    {
-        struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
-
-        if (!node->u._enum.enum_id) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                node, "Unexpected empty enumeration field class (`enum`) name.");
-            ret = -EINVAL;
-            goto end;
-        }
-
-        g_string_append(str, "enum ");
-        g_string_append(str, node->u._enum.enum_id);
-        break;
-    }
-    case TYPESPEC_FLOATING_POINT:
-    case TYPESPEC_INTEGER:
-    case TYPESPEC_STRING:
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier->u.field_class_specifier.node,
-                                        "Unexpected field class specifier type: %d",
-                                        cls_specifier->u.field_class_specifier.type);
-        ret = -EINVAL;
-        goto end;
-    }
-
-end:
-    return ret;
-}
-
-static int get_class_specifier_list_name(struct ctf_visitor_generate_ir *ctx,
-                                         struct ctf_node *cls_specifier_list, GString *str)
-{
-    int ret = 0;
-    struct ctf_node *iter;
-    int alias_item_nr = 0;
-    struct bt_list_head *head = &cls_specifier_list->u.field_class_specifier_list.head;
-
-    bt_list_for_each_entry (iter, head, siblings) {
-        if (alias_item_nr != 0) {
-            g_string_append(str, " ");
-        }
-
-        alias_item_nr++;
-        ret = get_class_specifier_name(ctx, iter, str);
-        if (ret) {
-            goto end;
-        }
-    }
-
-end:
-    return ret;
-}
-
-static GQuark create_class_alias_identifier(struct ctf_visitor_generate_ir *ctx,
-                                            struct ctf_node *cls_specifier_list,
-                                            struct ctf_node *node_field_class_declarator)
-{
-    int ret;
-    char *str_c;
-    GString *str;
-    GQuark qalias = 0;
-    struct ctf_node *iter;
-    struct bt_list_head *pointers = &node_field_class_declarator->u.field_class_declarator.pointers;
-
-    str = g_string_new("");
-    ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
-    if (ret) {
-        g_string_free(str, TRUE);
-        goto end;
-    }
-
-    bt_list_for_each_entry (iter, pointers, siblings) {
-        g_string_append(str, " *");
-
-        if (iter->u.pointer.const_qualifier) {
-            g_string_append(str, " const");
-        }
-    }
-
-    str_c = g_string_free(str, FALSE);
-    qalias = g_quark_from_string(str_c);
-    g_free(str_c);
-
-end:
-    return qalias;
-}
-
-static int visit_field_class_declarator(struct ctf_visitor_generate_ir *ctx,
-                                        struct ctf_node *cls_specifier_list, GQuark *field_name,
-                                        struct ctf_node *node_field_class_declarator,
-                                        struct ctf_field_class **field_decl,
-                                        struct ctf_field_class *nested_decl)
-{
-    /*
-     * During this whole function, nested_decl is always OURS,
-     * whereas field_decl is an output which we create, but
-     * belongs to the caller (it is moved).
-     */
-    int ret = 0;
-    *field_decl = NULL;
-
-    /* Validate field class declarator node */
-    if (node_field_class_declarator) {
-        if (node_field_class_declarator->u.field_class_declarator.type == TYPEDEC_UNKNOWN) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                node_field_class_declarator, "Unexpected field class declarator type: type=%d",
-                node_field_class_declarator->u.field_class_declarator.type);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        /* TODO: GCC bitfields not supported yet */
-        if (node_field_class_declarator->u.field_class_declarator.bitfield_len != NULL) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                            "GCC bitfields are not supported as of this version.");
-            ret = -EPERM;
-            goto error;
-        }
-    }
-
-    /* Find the right nested declaration if not provided */
-    if (!nested_decl) {
-        struct bt_list_head *pointers =
-            &node_field_class_declarator->u.field_class_declarator.pointers;
-
-        if (node_field_class_declarator && !bt_list_empty(pointers)) {
-            GQuark qalias;
-
-            /*
-             * If we have a pointer declarator, it HAS to
-                        * be present in the field class aliases (else
-                        * fail).
-             */
-            qalias =
-                create_class_alias_identifier(ctx, cls_specifier_list, node_field_class_declarator);
-            nested_decl = ctx_decl_scope_lookup_alias(ctx, ctx->current_scope,
-                                                      g_quark_to_string(qalias), -1, true);
-            if (!nested_decl) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                                "Cannot find class alias: name=\"%s\"",
-                                                g_quark_to_string(qalias));
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (nested_decl->type == CTF_FIELD_CLASS_TYPE_INT) {
-                /* Pointer: force integer's base to 16 */
-                struct ctf_field_class_int *int_fc = ctf_field_class_as_int(nested_decl);
-
-                int_fc->disp_base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
-            }
-        } else {
-            ret = visit_field_class_specifier_list(ctx, cls_specifier_list, &nested_decl);
-            if (ret) {
-                BT_ASSERT(!nested_decl);
-                goto error;
-            }
-        }
-    }
-
-    BT_ASSERT(nested_decl);
-
-    if (!node_field_class_declarator) {
-        *field_decl = nested_decl;
-        nested_decl = NULL;
-        goto end;
-    }
-
-    if (node_field_class_declarator->u.field_class_declarator.type == TYPEDEC_ID) {
-        if (node_field_class_declarator->u.field_class_declarator.u.id) {
-            const char *id = node_field_class_declarator->u.field_class_declarator.u.id;
-
-            *field_name = g_quark_from_string(id);
-        } else {
-            *field_name = 0;
-        }
-
-        *field_decl = nested_decl;
-        nested_decl = NULL;
-        goto end;
-    } else {
-        struct ctf_node *first;
-        struct ctf_field_class *decl = NULL;
-        struct ctf_field_class *outer_field_decl = NULL;
-        struct bt_list_head *length =
-            &node_field_class_declarator->u.field_class_declarator.u.nested.length;
-
-        /* Create array/sequence, pass nested_decl as child */
-        if (bt_list_empty(length)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                            "Expecting length field reference or value.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        first = _BT_LIST_FIRST_ENTRY(length, struct ctf_node, siblings);
-        if (first->type != NODE_UNARY_EXPRESSION) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(first, "Unexpected node type: node-type=%d",
-                                            first->type);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        switch (first->u.unary_expression.type) {
-        case UNARY_UNSIGNED_CONSTANT:
-        {
-            struct ctf_field_class_array *array_decl = NULL;
-
-            array_decl = ctf_field_class_array_create();
-            BT_ASSERT(array_decl);
-            array_decl->length = first->u.unary_expression.u.unsigned_constant;
-            array_decl->base.elem_fc = nested_decl;
-            nested_decl = NULL;
-            decl = &array_decl->base.base;
-            break;
-        }
-        case UNARY_STRING:
-        {
-            /* Lookup unsigned integer definition, create seq. */
-            struct ctf_field_class_sequence *seq_decl = NULL;
-            char *length_name = ctf_ast_concatenate_unary_strings(length);
-
-            if (!length_name) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                                "Cannot concatenate unary strings.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (strncmp(length_name, "env.", 4) == 0) {
-                /* This is, in fact, an array */
-                const char *env_entry_name = &length_name[4];
-                struct ctf_trace_class_env_entry *env_entry =
-                    ctf_trace_class_borrow_env_entry_by_name(ctx->ctf_tc, env_entry_name);
-                struct ctf_field_class_array *array_decl;
-
-                if (!env_entry) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                                    "Cannot find environment entry: "
-                                                    "name=\"%s\"",
-                                                    env_entry_name);
-                    ret = -EINVAL;
-                    goto error;
-                }
-
-                if (env_entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                                    "Wrong environment entry type "
-                                                    "(expecting integer): "
-                                                    "name=\"%s\"",
-                                                    env_entry_name);
-                    ret = -EINVAL;
-                    goto error;
-                }
-
-                if (env_entry->value.i < 0) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                                    "Invalid, negative array length: "
-                                                    "env-entry-name=\"%s\", "
-                                                    "value=%" PRId64,
-                                                    env_entry_name, env_entry->value.i);
-                    ret = -EINVAL;
-                    goto error;
-                }
-
-                array_decl = ctf_field_class_array_create();
-                BT_ASSERT(array_decl);
-                array_decl->length = (uint64_t) env_entry->value.i;
-                array_decl->base.elem_fc = nested_decl;
-                nested_decl = NULL;
-                decl = &array_decl->base.base;
-            } else {
-                seq_decl = ctf_field_class_sequence_create();
-                BT_ASSERT(seq_decl);
-                seq_decl->base.elem_fc = nested_decl;
-                nested_decl = NULL;
-                g_string_assign(seq_decl->length_ref, length_name);
-                decl = &seq_decl->base.base;
-            }
-
-            g_free(length_name);
-            break;
-        }
-        default:
-            ret = -EINVAL;
-            goto error;
-        }
-
-        BT_ASSERT(!nested_decl);
-        BT_ASSERT(decl);
-        BT_ASSERT(!*field_decl);
-
-        /*
-         * At this point, we found the next nested declaration.
-         * We currently own this (and lost the ownership of
-         * nested_decl in the meantime). Pass this next
-         * nested declaration as the content of the outer
-         * container, MOVING its ownership.
-         */
-        ret = visit_field_class_declarator(
-            ctx, cls_specifier_list, field_name,
-            node_field_class_declarator->u.field_class_declarator.u.nested.field_class_declarator,
-            &outer_field_decl, decl);
-        decl = NULL;
-        if (ret) {
-            BT_ASSERT(!outer_field_decl);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        BT_ASSERT(outer_field_decl);
-        *field_decl = outer_field_decl;
-        outer_field_decl = NULL;
-    }
-
-    BT_ASSERT(*field_decl);
-    goto end;
-
-error:
-    ctf_field_class_destroy(*field_decl);
-    *field_decl = NULL;
-
-    if (ret >= 0) {
-        ret = -1;
-    }
-
-end:
-    ctf_field_class_destroy(nested_decl);
-    nested_decl = NULL;
-    return ret;
-}
-
-static int visit_struct_decl_field(struct ctf_visitor_generate_ir *ctx,
-                                   struct ctf_field_class_struct *struct_decl,
-                                   struct ctf_node *cls_specifier_list,
-                                   struct bt_list_head *field_class_declarators)
-{
-    int ret = 0;
-    struct ctf_node *iter;
-    struct ctf_field_class *field_decl = NULL;
-
-    bt_list_for_each_entry (iter, field_class_declarators, siblings) {
-        field_decl = NULL;
-        GQuark qfield_name;
-        const char *field_name;
-
-        ret = visit_field_class_declarator(ctx, cls_specifier_list, &qfield_name, iter, &field_decl,
-                                           NULL);
-        if (ret) {
-            BT_ASSERT(!field_decl);
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                                            "Cannot visit field class declarator: ret=%d", ret);
-            goto error;
-        }
-
-        BT_ASSERT(field_decl);
-        field_name = g_quark_to_string(qfield_name);
-
-        /* Check if field with same name already exists */
-        if (ctf_field_class_struct_borrow_member_by_name(struct_decl, field_name)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                                            "Duplicate field in structure field class: "
-                                            "field-name=\"%s\"",
-                                            field_name);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        /* Add field to structure */
-        ctf_field_class_struct_append_member(struct_decl, field_name, field_decl);
-        field_decl = NULL;
-    }
-
-    return 0;
-
-error:
-    ctf_field_class_destroy(field_decl);
-    field_decl = NULL;
-    return ret;
-}
-
-static int visit_variant_decl_field(struct ctf_visitor_generate_ir *ctx,
-                                    struct ctf_field_class_variant *variant_decl,
-                                    struct ctf_node *cls_specifier_list,
-                                    struct bt_list_head *field_class_declarators)
-{
-    int ret = 0;
-    struct ctf_node *iter;
-    struct ctf_field_class *field_decl = NULL;
-
-    bt_list_for_each_entry (iter, field_class_declarators, siblings) {
-        field_decl = NULL;
-        GQuark qfield_name;
-        const char *field_name;
-
-        ret = visit_field_class_declarator(ctx, cls_specifier_list, &qfield_name, iter, &field_decl,
-                                           NULL);
-        if (ret) {
-            BT_ASSERT(!field_decl);
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                                            "Cannot visit field class declarator: ret=%d", ret);
-            goto error;
-        }
-
-        BT_ASSERT(field_decl);
-        field_name = g_quark_to_string(qfield_name);
-
-        /* Check if field with same name already exists */
-        if (ctf_field_class_variant_borrow_option_by_name(variant_decl, field_name)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                                            "Duplicate field in variant field class: "
-                                            "field-name=\"%s\"",
-                                            field_name);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        /* Add field to structure */
-        ctf_field_class_variant_append_option(variant_decl, field_name, field_decl);
-        field_decl = NULL;
-    }
-
-    return 0;
-
-error:
-    ctf_field_class_destroy(field_decl);
-    field_decl = NULL;
-    return ret;
-}
-
-static int visit_field_class_def(struct ctf_visitor_generate_ir *ctx,
-                                 struct ctf_node *cls_specifier_list,
-                                 struct bt_list_head *field_class_declarators)
-{
-    int ret = 0;
-    GQuark qidentifier;
-    struct ctf_node *iter;
-    struct ctf_field_class *class_decl = NULL;
-
-    bt_list_for_each_entry (iter, field_class_declarators, siblings) {
-        ret = visit_field_class_declarator(ctx, cls_specifier_list, &qidentifier, iter, &class_decl,
-                                           NULL);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit field class declarator: ret=%d",
-                                            ret);
-            ret = -EINVAL;
-            goto end;
-        }
-
-        /* Do not allow field class def and alias of untagged variants */
-        if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
-            struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(class_decl);
-
-            if (var_fc->tag_path.path->len == 0) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    iter, "Type definition of untagged variant field class is not allowed.");
-                ret = -EPERM;
-                goto end;
-            }
-        }
-
-        ret = ctx_decl_scope_register_alias(ctx, ctx->current_scope, g_quark_to_string(qidentifier),
-                                            class_decl);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot register field class alias: name=\"%s\"",
-                                            g_quark_to_string(qidentifier));
-            goto end;
-        }
-    }
-
-end:
-    ctf_field_class_destroy(class_decl);
-    class_decl = NULL;
-    return ret;
-}
-
-static int visit_field_class_alias(struct ctf_visitor_generate_ir *ctx, struct ctf_node *target,
-                                   struct ctf_node *alias)
-{
-    int ret = 0;
-    GQuark qalias;
-    struct ctf_node *node;
-    GQuark qdummy_field_name;
-    struct ctf_field_class *class_decl = NULL;
-
-    /* Create target field class */
-    if (bt_list_empty(&target->u.field_class_alias_target.field_class_declarators)) {
-        node = NULL;
-    } else {
-        node = _BT_LIST_FIRST_ENTRY(&target->u.field_class_alias_target.field_class_declarators,
-                                    struct ctf_node, siblings);
-    }
-
-    ret = visit_field_class_declarator(
-        ctx, target->u.field_class_alias_target.field_class_specifier_list, &qdummy_field_name,
-        node, &class_decl, NULL);
-    if (ret) {
-        BT_ASSERT(!class_decl);
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot visit field class declarator: ret=%d", ret);
-        goto end;
-    }
-
-    /* Do not allow field class def and alias of untagged variants */
-    if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
-        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(class_decl);
-
-        if (var_fc->tag_path.path->len == 0) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                target, "Type definition of untagged variant field class is not allowed.");
-            ret = -EPERM;
-            goto end;
-        }
-    }
-
-    /*
-     * The semantic validator does not check whether the target is
-     * abstract or not (if it has an identifier). Check it here.
-     */
-    if (qdummy_field_name != 0) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(target, "Expecting empty identifier: id=\"%s\"",
-                                        g_quark_to_string(qdummy_field_name));
-        ret = -EINVAL;
-        goto end;
-    }
-
-    /* Create alias identifier */
-    node = _BT_LIST_FIRST_ENTRY(&alias->u.field_class_alias_name.field_class_declarators,
-                                struct ctf_node, siblings);
-    qalias = create_class_alias_identifier(
-        ctx, alias->u.field_class_alias_name.field_class_specifier_list, node);
-    ret = ctx_decl_scope_register_alias(ctx, ctx->current_scope, g_quark_to_string(qalias),
-                                        class_decl);
-    if (ret) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot register class alias: name=\"%s\"",
-                                        g_quark_to_string(qalias));
-        goto end;
-    }
-
-end:
-    ctf_field_class_destroy(class_decl);
-    class_decl = NULL;
-    return ret;
-}
-
-static int visit_struct_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
-                                   struct ctf_field_class_struct *struct_decl)
-{
-    int ret = 0;
-
-    switch (entry_node->type) {
-    case NODE_TYPEDEF:
-        ret = visit_field_class_def(ctx, entry_node->u.field_class_def.field_class_specifier_list,
-                                    &entry_node->u.field_class_def.field_class_declarators);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Cannot add field class found in structure field class: ret=%d", ret);
-            goto end;
-        }
-        break;
-    case NODE_TYPEALIAS:
-        ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
-                                      entry_node->u.field_class_alias.alias);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Cannot add field class alias found in structure field class: ret=%d",
-                ret);
-            goto end;
-        }
-        break;
-    case NODE_STRUCT_OR_VARIANT_DECLARATION:
-        /* Field */
-        ret = visit_struct_decl_field(
-            ctx, struct_decl,
-            entry_node->u.struct_or_variant_declaration.field_class_specifier_list,
-            &entry_node->u.struct_or_variant_declaration.field_class_declarators);
-        if (ret) {
-            goto end;
-        }
-        break;
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Unexpected node type: node-type=%d",
-                                        entry_node->type);
-        ret = -EINVAL;
-        goto end;
-    }
-
-end:
-    return ret;
-}
-
-static int visit_variant_decl_entry(struct ctf_visitor_generate_ir *ctx,
-                                    struct ctf_node *entry_node,
-                                    struct ctf_field_class_variant *variant_decl)
-{
-    int ret = 0;
-
-    switch (entry_node->type) {
-    case NODE_TYPEDEF:
-        ret = visit_field_class_def(ctx, entry_node->u.field_class_def.field_class_specifier_list,
-                                    &entry_node->u.field_class_def.field_class_declarators);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Cannot add field class found in variant field class: ret=%d", ret);
-            goto end;
-        }
-        break;
-    case NODE_TYPEALIAS:
-        ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
-                                      entry_node->u.field_class_alias.alias);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Cannot add field class alias found in variant field class: ret=%d",
-                ret);
-            goto end;
-        }
-        break;
-    case NODE_STRUCT_OR_VARIANT_DECLARATION:
-        /* Field */
-        ret = visit_variant_decl_field(
-            ctx, variant_decl,
-            entry_node->u.struct_or_variant_declaration.field_class_specifier_list,
-            &entry_node->u.struct_or_variant_declaration.field_class_declarators);
-        if (ret) {
-            goto end;
-        }
-        break;
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Unexpected node type: node-type=%d",
-                                        entry_node->type);
-        ret = -EINVAL;
-        goto end;
-    }
-
-end:
-    return ret;
-}
-
-static int visit_struct_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
-                             struct bt_list_head *decl_list, int has_body,
-                             struct bt_list_head *min_align,
-                             struct ctf_field_class_struct **struct_decl)
-{
-    int ret = 0;
-
-    BT_ASSERT(struct_decl);
-    *struct_decl = NULL;
-
-    /* For named struct (without body), lookup in declaration scope */
-    if (!has_body) {
-        if (!name) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Bodyless structure field class: missing name.");
-            ret = -EPERM;
-            goto error;
-        }
-
-        *struct_decl = ctx_decl_scope_lookup_struct(ctx, ctx->current_scope, name, -1, true);
-        if (!*struct_decl) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot find structure field class: name=\"struct %s\"", name);
-            ret = -EINVAL;
-            goto error;
-        }
-    } else {
-        struct ctf_node *entry_node;
-        uint64_t min_align_value = 0;
-
-        if (name) {
-            if (ctx_decl_scope_lookup_struct(ctx, ctx->current_scope, name, 1, false)) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Structure field class already declared in local scope: "
-                    "name=\"struct %s\"",
-                    name);
-                ret = -EINVAL;
-                goto error;
-            }
-        }
-
-        if (!bt_list_empty(min_align)) {
-            ret = get_unary_unsigned(ctx, min_align, &min_align_value);
-            if (ret) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Unexpected unary expression for structure field class's `align` attribute: "
-                    "ret=%d",
-                    ret);
-                goto error;
-            }
-        }
-
-        *struct_decl = ctf_field_class_struct_create();
-        BT_ASSERT(*struct_decl);
-
-        if (min_align_value != 0) {
-            (*struct_decl)->base.alignment = min_align_value;
-        }
-
-        _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-
-        bt_list_for_each_entry (entry_node, decl_list, siblings) {
-            ret = visit_struct_decl_entry(ctx, entry_node, *struct_decl);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                                                "Cannot visit structure field class entry: "
-                                                "ret=%d",
-                                                ret);
-                ctx_pop_scope(ctx);
-                goto error;
-            }
-        }
-
-        ctx_pop_scope(ctx);
-
-        if (name) {
-            ret = ctx_decl_scope_register_struct(ctx, ctx->current_scope, name, *struct_decl);
-            if (ret) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Cannot register structure field class in declaration scope: "
-                    "name=\"struct %s\", ret=%d",
-                    name, ret);
-                goto error;
-            }
-        }
-    }
-
-    return 0;
-
-error:
-    ctf_field_class_destroy(&(*struct_decl)->base);
-    *struct_decl = NULL;
-    return ret;
-}
-
-static int visit_variant_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
-                              const char *tag, struct bt_list_head *decl_list, int has_body,
-                              struct ctf_field_class_variant **variant_decl)
-{
-    int ret = 0;
-    struct ctf_field_class_variant *untagged_variant_decl = NULL;
-
-    BT_ASSERT(variant_decl);
-    *variant_decl = NULL;
-
-    /* For named variant (without body), lookup in declaration scope */
-    if (!has_body) {
-        if (!name) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless variant field class: missing name.");
-            ret = -EPERM;
-            goto error;
-        }
-
-        untagged_variant_decl =
-            ctx_decl_scope_lookup_variant(ctx, ctx->current_scope, name, -1, true);
-        if (!untagged_variant_decl) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot find variant field class: name=\"variant %s\"", name);
-            ret = -EINVAL;
-            goto error;
-        }
-    } else {
-        struct ctf_node *entry_node;
-
-        if (name) {
-            if (ctx_decl_scope_lookup_variant(ctx, ctx->current_scope, name, 1, false)) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Variant field class already declared in local scope: "
-                    "name=\"variant %s\"",
-                    name);
-                ret = -EINVAL;
-                goto error;
-            }
-        }
-
-        untagged_variant_decl = ctf_field_class_variant_create();
-        BT_ASSERT(untagged_variant_decl);
-        _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-
-        bt_list_for_each_entry (entry_node, decl_list, siblings) {
-            ret = visit_variant_decl_entry(ctx, entry_node, untagged_variant_decl);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                                                "Cannot visit variant field class entry: "
-                                                "ret=%d",
-                                                ret);
-                ctx_pop_scope(ctx);
-                goto error;
-            }
-        }
-
-        ctx_pop_scope(ctx);
-
-        if (name) {
-            ret = ctx_decl_scope_register_variant(ctx, ctx->current_scope, name,
-                                                  untagged_variant_decl);
-            if (ret) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Cannot register variant field class in declaration scope: "
-                    "name=\"variant %s\", ret=%d",
-                    name, ret);
-                goto error;
-            }
-        }
-    }
-
-    /*
-     * If tagged, create tagged variant and return; otherwise
-     * return untagged variant.
-     */
-    if (!tag) {
-        *variant_decl = untagged_variant_decl;
-        untagged_variant_decl = NULL;
-    } else {
-        /*
-         * At this point, we have a fresh untagged variant; nobody
-         * else owns it. Set its tag now.
-         */
-        g_string_assign(untagged_variant_decl->tag_ref, tag);
-        *variant_decl = untagged_variant_decl;
-        untagged_variant_decl = NULL;
-    }
-
-    BT_ASSERT(!untagged_variant_decl);
-    BT_ASSERT(*variant_decl);
-    return 0;
-
-error:
-    ctf_field_class_destroy(&untagged_variant_decl->base);
-    untagged_variant_decl = NULL;
-    ctf_field_class_destroy(&(*variant_decl)->base);
-    *variant_decl = NULL;
-    return ret;
-}
-
-struct uori
-{
-    bool is_signed;
-    union
-    {
-        uint64_t u;
-        uint64_t i;
-    } value;
-};
-
-static int visit_enum_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *enumerator,
-                                 struct ctf_field_class_enum *enum_decl, struct uori *last)
-{
-    int ret = 0;
-    int nr_vals = 0;
-    struct ctf_node *iter;
-    struct uori start = {
-        .is_signed = false,
-        .value =
-            {
-                .u = 0,
-            },
-    };
-    struct uori end = {
-        .is_signed = false,
-        .value =
-            {
-                .u = 0,
-            },
-    };
-    const char *label = enumerator->u.enumerator.id;
-    struct bt_list_head *values = &enumerator->u.enumerator.values;
-
-    bt_list_for_each_entry (iter, values, siblings) {
-        struct uori *target;
-
-        if (iter->type != NODE_UNARY_EXPRESSION) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                            "Wrong expression for enumeration field class label: "
-                                            "node-type=%d, label=\"%s\"",
-                                            iter->type, label);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (nr_vals == 0) {
-            target = &start;
-        } else {
-            target = &end;
-        }
-
-        switch (iter->u.unary_expression.type) {
-        case UNARY_SIGNED_CONSTANT:
-            target->is_signed = true;
-            target->value.i = iter->u.unary_expression.u.signed_constant;
-            break;
-        case UNARY_UNSIGNED_CONSTANT:
-            target->is_signed = false;
-            target->value.u = iter->u.unary_expression.u.unsigned_constant;
-            break;
-        default:
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                            "Invalid enumeration field class entry: "
-                                            "expecting constant signed or unsigned integer: "
-                                            "node-type=%d, label=\"%s\"",
-                                            iter->u.unary_expression.type, label);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (nr_vals > 1) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                iter, "Invalid enumeration field class entry: label=\"%s\"", label);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        nr_vals++;
-    }
-
-    if (nr_vals == 0) {
-        start = *last;
-    }
-
-    if (nr_vals <= 1) {
-        end = start;
-    }
-
-    if (end.is_signed) {
-        last->value.i = end.value.i + 1;
-    } else {
-        last->value.u = end.value.u + 1;
-    }
-
-    ctf_field_class_enum_map_range(enum_decl, label, start.value.u, end.value.u);
-    return 0;
-
-error:
-    return ret;
-}
-
-static int visit_enum_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
-                           struct ctf_node *container_cls, struct bt_list_head *enumerator_list,
-                           int has_body, struct ctf_field_class_enum **enum_decl)
-{
-    int ret = 0;
-    GQuark qdummy_id;
-    struct ctf_field_class_int *integer_decl = NULL;
-
-    BT_ASSERT(enum_decl);
-    *enum_decl = NULL;
-
-    /* For named enum (without body), lookup in declaration scope */
-    if (!has_body) {
-        if (!name) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Bodyless enumeration field class: missing name.");
-            ret = -EPERM;
-            goto error;
-        }
-
-        *enum_decl = ctx_decl_scope_lookup_enum(ctx, ctx->current_scope, name, -1, true);
-        if (!*enum_decl) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find enumeration field class: "
-                                                     "name=\"enum %s\"",
-                                                     name);
-            ret = -EINVAL;
-            goto error;
-        }
-    } else {
-        struct ctf_node *iter;
-        struct uori last_value = {
-            .is_signed = false,
-            .value =
-                {
-                    .u = 0,
-                },
-        };
-
-        if (name) {
-            if (ctx_decl_scope_lookup_enum(ctx, ctx->current_scope, name, 1, false)) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Enumeration field class already declared in local scope: "
-                    "name=\"enum %s\"",
-                    name);
-                ret = -EINVAL;
-                goto error;
-            }
-        }
-
-        if (!container_cls) {
-            integer_decl = ctf_field_class_as_int(
-                ctx_decl_scope_lookup_alias(ctx, ctx->current_scope, "int", -1, true));
-            if (!integer_decl) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Cannot find implicit `int` field class alias for enumeration field class.");
-                ret = -EINVAL;
-                goto error;
-            }
-        } else {
-            ctf_field_class *decl;
-
-            ret = visit_field_class_declarator(ctx, container_cls, &qdummy_id, NULL, &decl, NULL);
-            if (ret) {
-                BT_ASSERT(!decl);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            integer_decl = ctf_field_class_as_int(decl);
-        }
-
-        BT_ASSERT(integer_decl);
-
-        if (integer_decl->base.base.type != CTF_FIELD_CLASS_TYPE_INT) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Container field class for enumeration field class is not an integer field class: "
-                "fc-type=%d",
-                integer_decl->base.base.type);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        *enum_decl = ctf_field_class_enum_create();
-        BT_ASSERT(*enum_decl);
-        (*enum_decl)->base.base.base.alignment = integer_decl->base.base.alignment;
-        ctf_field_class_int_copy_content(&(*enum_decl)->base, integer_decl);
-        last_value.is_signed = (*enum_decl)->base.is_signed;
-
-        bt_list_for_each_entry (iter, enumerator_list, siblings) {
-            ret = visit_enum_decl_entry(ctx, iter, *enum_decl, &last_value);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                                "Cannot visit enumeration field class entry: "
-                                                "ret=%d",
-                                                ret);
-                goto error;
-            }
-        }
-
-        if (name) {
-            ret = ctx_decl_scope_register_enum(ctx, ctx->current_scope, name, *enum_decl);
-            if (ret) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Cannot register enumeration field class in declaration scope: "
-                    "ret=%d",
-                    ret);
-                goto error;
-            }
-        }
-    }
-
-    goto end;
-
-error:
-    ctf_field_class_destroy(&(*enum_decl)->base.base.base);
-    *enum_decl = NULL;
-
-end:
-    ctf_field_class_destroy(&integer_decl->base.base);
-    integer_decl = NULL;
-    return ret;
-}
-
-static int visit_field_class_specifier(struct ctf_visitor_generate_ir *ctx,
-                                       struct ctf_node *cls_specifier_list,
-                                       struct ctf_field_class **decl)
-{
-    int ret = 0;
-    GString *str = NULL;
-
-    *decl = NULL;
-    str = g_string_new("");
-    ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
-    if (ret) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-            cls_specifier_list, "Cannot get field class specifier list's name: ret=%d", ret);
-        goto error;
-    }
-
-    *decl = ctx_decl_scope_lookup_alias(ctx, ctx->current_scope, str->str, -1, true);
-    if (!*decl) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                                        "Cannot find field class alias: name=\"%s\"", str->str);
-        ret = -EINVAL;
-        goto error;
-    }
-
-    goto end;
-
-error:
-    ctf_field_class_destroy(*decl);
-    *decl = NULL;
-
-end:
-    if (str) {
-        g_string_free(str, TRUE);
-    }
-
-    return ret;
-}
-
-static int visit_integer_decl(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *expressions,
-                              struct ctf_field_class_int **integer_decl)
-{
-    int set = 0;
-    int ret = 0;
-    int signedness = 0;
-    struct ctf_node *expression;
-    uint64_t alignment = 0, size = 0;
-    struct ctf_clock_class *mapped_clock_class = NULL;
-    enum ctf_encoding encoding = CTF_ENCODING_NONE;
-    bt_field_class_integer_preferred_display_base base =
-        BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
-    enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
-
-    *integer_decl = NULL;
-
-    bt_list_for_each_entry (expression, expressions, siblings) {
-        struct ctf_node *left, *right;
-
-        left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, struct ctf_node, siblings);
-        right =
-            _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.right, struct ctf_node, siblings);
-
-        if (left->u.unary_expression.type != UNARY_STRING) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(left, "Unexpected unary expression type: type=%d",
-                                            left->u.unary_expression.type);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (strcmp(left->u.unary_expression.u.string, "signed") == 0) {
-            if (_IS_SET(&set, _INTEGER_SIGNED_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "signed", "integer field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            signedness = get_boolean(ctx, right);
-            if (signedness < 0) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid boolean value for integer field class's `signed` attribute: "
-                    "ret=%d",
-                    ret);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            _SET(&set, _INTEGER_SIGNED_SET);
-        } else if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
-            if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order", "integer field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            byte_order = get_real_byte_order(ctx, right);
-            if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `byte_order` attribute in integer field class: "
-                    "ret=%d",
-                    ret);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            _SET(&set, _INTEGER_BYTE_ORDER_SET);
-        } else if (strcmp(left->u.unary_expression.u.string, "size") == 0) {
-            if (_IS_SET(&set, _INTEGER_SIZE_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "size", "integer field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                                "Invalid `size` attribute in integer field class: "
-                                                "expecting unsigned constant integer: "
-                                                "node-type=%d",
-                                                right->u.unary_expression.type);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            size = right->u.unary_expression.u.unsigned_constant;
-            if (size == 0) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                                "Invalid `size` attribute in integer field class: "
-                                                "expecting positive constant integer: "
-                                                "size=%" PRIu64,
-                                                size);
-                ret = -EINVAL;
-                goto error;
-            } else if (size > 64) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `size` attribute in integer field class: "
-                    "integer fields over 64 bits are not supported as of this version: "
-                    "size=%" PRIu64,
-                    size);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            _SET(&set, _INTEGER_SIZE_SET);
-        } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
-            if (_IS_SET(&set, _INTEGER_ALIGN_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "align", "integer field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                                "Invalid `align` attribute in integer field class: "
-                                                "expecting unsigned constant integer: "
-                                                "node-type=%d",
-                                                right->u.unary_expression.type);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            alignment = right->u.unary_expression.u.unsigned_constant;
-            if (!is_align_valid(alignment)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                                "Invalid `align` attribute in integer field class: "
-                                                "expecting power of two: "
-                                                "align=%" PRIu64,
-                                                alignment);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            _SET(&set, _INTEGER_ALIGN_SET);
-        } else if (strcmp(left->u.unary_expression.u.string, "base") == 0) {
-            if (_IS_SET(&set, _INTEGER_BASE_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "base", "integer field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            switch (right->u.unary_expression.type) {
-            case UNARY_UNSIGNED_CONSTANT:
-            {
-                uint64_t constant = right->u.unary_expression.u.unsigned_constant;
-
-                switch (constant) {
-                case 2:
-                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
-                    break;
-                case 8:
-                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
-                    break;
-                case 10:
-                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
-                    break;
-                case 16:
-                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
-                    break;
-                default:
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                        right,
-                        "Invalid `base` attribute in integer field class: "
-                        "base=%" PRIu64,
-                        right->u.unary_expression.u.unsigned_constant);
-                    ret = -EINVAL;
-                    goto error;
-                }
-                break;
-            }
-            case UNARY_STRING:
-            {
-                char *s_right =
-                    ctf_ast_concatenate_unary_strings(&expression->u.ctf_expression.right);
-                if (!s_right) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                        right,
-                        "Unexpected unary expression for integer field class's `base` attribute.");
-                    ret = -EINVAL;
-                    goto error;
-                }
-
-                if (strcmp(s_right, "decimal") == 0 || strcmp(s_right, "dec") == 0 ||
-                    strcmp(s_right, "d") == 0 || strcmp(s_right, "i") == 0 ||
-                    strcmp(s_right, "u") == 0) {
-                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
-                } else if (strcmp(s_right, "hexadecimal") == 0 || strcmp(s_right, "hex") == 0 ||
-                           strcmp(s_right, "x") == 0 || strcmp(s_right, "X") == 0 ||
-                           strcmp(s_right, "p") == 0) {
-                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
-                } else if (strcmp(s_right, "octal") == 0 || strcmp(s_right, "oct") == 0 ||
-                           strcmp(s_right, "o") == 0) {
-                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
-                } else if (strcmp(s_right, "binary") == 0 || strcmp(s_right, "b") == 0) {
-                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
-                } else {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                        right,
-                        "Unexpected unary expression for integer field class's `base` attribute: "
-                        "base=\"%s\"",
-                        s_right);
-                    g_free(s_right);
-                    ret = -EINVAL;
-                    goto error;
-                }
-
-                g_free(s_right);
-                break;
-            }
-            default:
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right, "Invalid `base` attribute in integer field class: "
-                           "expecting unsigned constant integer or unary string.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            _SET(&set, _INTEGER_BASE_SET);
-        } else if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
-            char *s_right;
-
-            if (_IS_SET(&set, _INTEGER_ENCODING_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding", "integer field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            if (right->u.unary_expression.type != UNARY_STRING) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right, "Invalid `encoding` attribute in integer field class: "
-                           "expecting unary string.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            s_right = ctf_ast_concatenate_unary_strings(&expression->u.ctf_expression.right);
-            if (!s_right) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Unexpected unary expression for integer field class's `encoding` attribute.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (strcmp(s_right, "UTF8") == 0 || strcmp(s_right, "utf8") == 0 ||
-                strcmp(s_right, "utf-8") == 0 || strcmp(s_right, "UTF-8") == 0 ||
-                strcmp(s_right, "ASCII") == 0 || strcmp(s_right, "ascii") == 0) {
-                encoding = CTF_ENCODING_UTF8;
-            } else if (strcmp(s_right, "none") == 0) {
-                encoding = CTF_ENCODING_NONE;
-            } else {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `encoding` attribute in integer field class: "
-                    "unknown encoding: encoding=\"%s\"",
-                    s_right);
-                g_free(s_right);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            g_free(s_right);
-            _SET(&set, _INTEGER_ENCODING_SET);
-        } else if (strcmp(left->u.unary_expression.u.string, "map") == 0) {
-            const char *clock_name;
-
-            if (_IS_SET(&set, _INTEGER_MAP_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "map", "integer field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            if (right->u.unary_expression.type != UNARY_STRING) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                                "Invalid `map` attribute in integer field class: "
-                                                "expecting unary string.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            clock_name = get_map_clock_name_value(&expression->u.ctf_expression.right);
-            if (!clock_name) {
-                char *s_right =
-                    ctf_ast_concatenate_unary_strings(&expression->u.ctf_expression.right);
-
-                if (!s_right) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                        right,
-                        "Unexpected unary expression for integer field class's `map` attribute.");
-                    ret = -EINVAL;
-                    goto error;
-                }
-
-                _BT_COMP_LOGE_NODE(right,
-                                   "Invalid `map` attribute in integer field class: "
-                                   "cannot find clock class at this point: name=\"%s\"",
-                                   s_right);
-                _SET(&set, _INTEGER_MAP_SET);
-                g_free(s_right);
-                continue;
-            }
-
-            mapped_clock_class =
-                ctf_trace_class_borrow_clock_class_by_name(ctx->ctf_tc, clock_name);
-            if (!mapped_clock_class) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `map` attribute in integer field class: "
-                    "cannot find clock class at this point: name=\"%s\"",
-                    clock_name);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            _SET(&set, _INTEGER_MAP_SET);
-        } else {
-            _BT_COMP_LOGW_NODE(left,
-                               "Unknown attribute in integer field class: "
-                               "attr-name=\"%s\"",
-                               left->u.unary_expression.u.string);
-        }
-    }
-
-    if (!_IS_SET(&set, _INTEGER_SIZE_SET)) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Missing `size` attribute in integer field class.");
-        ret = -EPERM;
-        goto error;
-    }
-
-    if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
-        if (size % CHAR_BIT) {
-            /* Bit-packed alignment */
-            alignment = 1;
-        } else {
-            /* Byte-packed alignment */
-            alignment = CHAR_BIT;
-        }
-    }
-
-    *integer_decl = ctf_field_class_int_create();
-    BT_ASSERT(*integer_decl);
-    (*integer_decl)->base.base.alignment = alignment;
-    (*integer_decl)->base.byte_order = byte_order;
-    (*integer_decl)->base.size = size;
-    (*integer_decl)->is_signed = (signedness > 0);
-    (*integer_decl)->disp_base = base;
-    (*integer_decl)->encoding = encoding;
-    (*integer_decl)->mapped_clock_class = mapped_clock_class;
-    return 0;
-
-error:
-    ctf_field_class_destroy(&(*integer_decl)->base.base);
-    *integer_decl = NULL;
-    return ret;
-}
-
-static int visit_floating_point_number_decl(struct ctf_visitor_generate_ir *ctx,
-                                            struct bt_list_head *expressions,
-                                            struct ctf_field_class_float **float_decl)
-{
-    int set = 0;
-    int ret = 0;
-    struct ctf_node *expression;
-    uint64_t alignment = 1, exp_dig = 0, mant_dig = 0;
-    enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
-
-    *float_decl = NULL;
-
-    bt_list_for_each_entry (expression, expressions, siblings) {
-        struct ctf_node *left, *right;
-
-        left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, struct ctf_node, siblings);
-        right =
-            _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.right, struct ctf_node, siblings);
-
-        if (left->u.unary_expression.type != UNARY_STRING) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(left, "Unexpected unary expression type: type=%d",
-                                            left->u.unary_expression.type);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
-            if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order",
-                                                    "floating point number field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            byte_order = get_real_byte_order(ctx, right);
-            if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `byte_order` attribute in floating point number field class: "
-                    "ret=%d",
-                    ret);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            _SET(&set, _FLOAT_BYTE_ORDER_SET);
-        } else if (strcmp(left->u.unary_expression.u.string, "exp_dig") == 0) {
-            if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "exp_dig",
-                                                    "floating point number field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `exp_dig` attribute in floating point number field class: "
-                    "expecting unsigned constant integer: "
-                    "node-type=%d",
-                    right->u.unary_expression.type);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            exp_dig = right->u.unary_expression.u.unsigned_constant;
-            _SET(&set, _FLOAT_EXP_DIG_SET);
-        } else if (strcmp(left->u.unary_expression.u.string, "mant_dig") == 0) {
-            if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "mant_dig",
-                                                    "floating point number field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `mant_dig` attribute in floating point number field class: "
-                    "expecting unsigned constant integer: "
-                    "node-type=%d",
-                    right->u.unary_expression.type);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            mant_dig = right->u.unary_expression.u.unsigned_constant;
-            _SET(&set, _FLOAT_MANT_DIG_SET);
-        } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
-            if (_IS_SET(&set, _FLOAT_ALIGN_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "align",
-                                                    "floating point number field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `align` attribute in floating point number field class: "
-                    "expecting unsigned constant integer: "
-                    "node-type=%d",
-                    right->u.unary_expression.type);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            alignment = right->u.unary_expression.u.unsigned_constant;
-
-            if (!is_align_valid(alignment)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `align` attribute in floating point number field class: "
-                    "expecting power of two: "
-                    "align=%" PRIu64,
-                    alignment);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            _SET(&set, _FLOAT_ALIGN_SET);
-        } else {
-            _BT_COMP_LOGW_NODE(left,
-                               "Unknown attribute in floating point number field class: "
-                               "attr-name=\"%s\"",
-                               left->u.unary_expression.u.string);
-        }
-    }
-
-    if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Missing `mant_dig` attribute in floating point number field class.");
-        ret = -EPERM;
-        goto error;
-    }
-
-    if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Missing `exp_dig` attribute in floating point number field class.");
-        ret = -EPERM;
-        goto error;
-    }
-
-    if (mant_dig != 24 && mant_dig != 53) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`mant_dig` attribute: expecting 24 or 53.");
-        ret = -EPERM;
-        goto error;
-    }
-
-    if (mant_dig == 24 && exp_dig != 8) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "`exp_dig` attribute: expecting 8 because `mant_dig` is 24.");
-        ret = -EPERM;
-        goto error;
-    }
-
-    if (mant_dig == 53 && exp_dig != 11) {
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "`exp_dig` attribute: expecting 11 because `mant_dig` is 53.");
-        ret = -EPERM;
-        goto error;
-    }
-
-    if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
-        if ((mant_dig + exp_dig) % CHAR_BIT) {
-            /* Bit-packed alignment */
-            alignment = 1;
-        } else {
-            /* Byte-packed alignment */
-            alignment = CHAR_BIT;
-        }
-    }
-
-    *float_decl = ctf_field_class_float_create();
-    BT_ASSERT(*float_decl);
-    (*float_decl)->base.base.alignment = alignment;
-    (*float_decl)->base.byte_order = byte_order;
-    (*float_decl)->base.size = mant_dig + exp_dig;
-    return 0;
-
-error:
-    ctf_field_class_destroy(&(*float_decl)->base.base);
-    *float_decl = NULL;
-    return ret;
-}
-
-static int visit_string_decl(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *expressions,
-                             struct ctf_field_class_string **string_decl)
-{
-    int set = 0;
-    int ret = 0;
-    struct ctf_node *expression;
-    enum ctf_encoding encoding = CTF_ENCODING_UTF8;
-
-    *string_decl = NULL;
-
-    bt_list_for_each_entry (expression, expressions, siblings) {
-        struct ctf_node *left, *right;
-
-        left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, struct ctf_node, siblings);
-        right =
-            _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.right, struct ctf_node, siblings);
-
-        if (left->u.unary_expression.type != UNARY_STRING) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(left, "Unexpected unary expression type: type=%d",
-                                            left->u.unary_expression.type);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
-            char *s_right;
-
-            if (_IS_SET(&set, _STRING_ENCODING_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding", "string field class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            if (right->u.unary_expression.type != UNARY_STRING) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right, "Invalid `encoding` attribute in string field class: "
-                           "expecting unary string.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            s_right = ctf_ast_concatenate_unary_strings(&expression->u.ctf_expression.right);
-            if (!s_right) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Unexpected unary expression for string field class's `encoding` attribute.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (strcmp(s_right, "UTF8") == 0 || strcmp(s_right, "utf8") == 0 ||
-                strcmp(s_right, "utf-8") == 0 || strcmp(s_right, "UTF-8") == 0 ||
-                strcmp(s_right, "ASCII") == 0 || strcmp(s_right, "ascii") == 0) {
-                encoding = CTF_ENCODING_UTF8;
-            } else if (strcmp(s_right, "none") == 0) {
-                encoding = CTF_ENCODING_NONE;
-            } else {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    right,
-                    "Invalid `encoding` attribute in string field class: "
-                    "unknown encoding: encoding=\"%s\"",
-                    s_right);
-                g_free(s_right);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            g_free(s_right);
-            _SET(&set, _STRING_ENCODING_SET);
-        } else {
-            _BT_COMP_LOGW_NODE(left,
-                               "Unknown attribute in string field class: "
-                               "attr-name=\"%s\"",
-                               left->u.unary_expression.u.string);
-        }
-    }
-
-    *string_decl = ctf_field_class_string_create();
-    BT_ASSERT(*string_decl);
-    (*string_decl)->encoding = encoding;
-    return 0;
-
-error:
-    ctf_field_class_destroy(&(*string_decl)->base);
-    *string_decl = NULL;
-    return ret;
-}
-
-static int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
-                                            struct ctf_node *ts_list, struct ctf_field_class **decl)
-{
-    int ret = 0;
-    struct ctf_node *first, *node;
-
-    *decl = NULL;
-
-    if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(ts_list, "Unexpected node type: node-type=%d",
-                                        ts_list->type);
-        ret = -EINVAL;
-        goto error;
-    }
-
-    first = _BT_LIST_FIRST_ENTRY(&ts_list->u.field_class_specifier_list.head, struct ctf_node,
-                                 siblings);
-    if (first->type != NODE_TYPE_SPECIFIER) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(first, "Unexpected node type: node-type=%d", first->type);
-        ret = -EINVAL;
-        goto error;
-    }
-
-    node = first->u.field_class_specifier.node;
-
-    switch (first->u.field_class_specifier.type) {
-    case TYPESPEC_INTEGER:
-    {
-        ctf_field_class_int *int_decl;
-
-        ret = visit_integer_decl(ctx, &node->u.integer.expressions, &int_decl);
-        if (ret) {
-            BT_ASSERT(!int_decl);
-            goto error;
-        }
-
-        *decl = &int_decl->base.base;
-        break;
-    }
-    case TYPESPEC_FLOATING_POINT:
-    {
-        ctf_field_class_float *float_decl;
-
-        ret =
-            visit_floating_point_number_decl(ctx, &node->u.floating_point.expressions, &float_decl);
-        if (ret) {
-            BT_ASSERT(!float_decl);
-            goto error;
-        }
-
-        *decl = &float_decl->base.base;
-        break;
-    }
-    case TYPESPEC_STRING:
-    {
-        ctf_field_class_string *string_decl;
-
-        ret = visit_string_decl(ctx, &node->u.string.expressions, &string_decl);
-        if (ret) {
-            BT_ASSERT(!string_decl);
-            goto error;
-        }
-
-        *decl = &string_decl->base;
-        break;
-    }
-    case TYPESPEC_STRUCT:
-    {
-        ctf_field_class_struct *struct_decl;
-
-        ret = visit_struct_decl(ctx, node->u._struct.name, &node->u._struct.declaration_list,
-                                node->u._struct.has_body, &node->u._struct.min_align, &struct_decl);
-        if (ret) {
-            BT_ASSERT(!struct_decl);
-            goto error;
-        }
-
-        *decl = &struct_decl->base;
-        break;
-    }
-    case TYPESPEC_VARIANT:
-    {
-        ctf_field_class_variant *variant_decl;
-
-        ret = visit_variant_decl(ctx, node->u.variant.name, node->u.variant.choice,
-                                 &node->u.variant.declaration_list, node->u.variant.has_body,
-                                 &variant_decl);
-        if (ret) {
-            BT_ASSERT(!variant_decl);
-            goto error;
-        }
-
-        *decl = &variant_decl->base;
-        break;
-    }
-    case TYPESPEC_ENUM:
-    {
-        ctf_field_class_enum *enum_decl;
-
-        ret = visit_enum_decl(ctx, node->u._enum.enum_id, node->u._enum.container_field_class,
-                              &node->u._enum.enumerator_list, node->u._enum.has_body, &enum_decl);
-        if (ret) {
-            BT_ASSERT(!enum_decl);
-            goto error;
-        }
-
-        *decl = &enum_decl->base.base.base;
-        break;
-    }
-    case TYPESPEC_VOID:
-    case TYPESPEC_CHAR:
-    case TYPESPEC_SHORT:
-    case TYPESPEC_INT:
-    case TYPESPEC_LONG:
-    case TYPESPEC_FLOAT:
-    case TYPESPEC_DOUBLE:
-    case TYPESPEC_SIGNED:
-    case TYPESPEC_UNSIGNED:
-    case TYPESPEC_BOOL:
-    case TYPESPEC_COMPLEX:
-    case TYPESPEC_IMAGINARY:
-    case TYPESPEC_CONST:
-    case TYPESPEC_ID_TYPE:
-        ret = visit_field_class_specifier(ctx, ts_list, decl);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(first, "Cannot visit field class specifier: ret=%d",
-                                            ret);
-            BT_ASSERT(!*decl);
-            goto error;
-        }
-        break;
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
-                                        "Unexpected field class specifier type: node-type=%d",
-                                        first->u.field_class_specifier.type);
-        ret = -EINVAL;
-        goto error;
-    }
-
-    BT_ASSERT(*decl);
-    return 0;
-
-error:
-    ctf_field_class_destroy(*decl);
-    *decl = NULL;
-    return ret;
-}
-
-static int visit_event_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
-                                  struct ctf_event_class *event_class, uint64_t *stream_id,
-                                  int *set)
-{
-    int ret = 0;
-    char *left = NULL;
-
-    switch (node->type) {
-    case NODE_TYPEDEF:
-        ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
-                                    &node->u.field_class_def.field_class_declarators);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot add field class found in event class.");
-            goto error;
-        }
-        break;
-    case NODE_TYPEALIAS:
-        ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
-                                      node->u.field_class_alias.alias);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                            "Cannot add field class alias found in event class.");
-            goto error;
-        }
-        break;
-    case NODE_CTF_EXPRESSION:
-    {
-        left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
-        if (!left) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (strcmp(left, "name") == 0) {
-            /* This is already known at this stage */
-            if (_IS_SET(set, _EVENT_NAME_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "name", "event class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            _SET(set, _EVENT_NAME_SET);
-        } else if (strcmp(left, "id") == 0) {
-            int64_t id = -1;
-
-            if (_IS_SET(set, _EVENT_ID_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "id", "event class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, (uint64_t *) &id);
-            /* Only read "id" if get_unary_unsigned() succeeded. */
-            if (ret || (!ret && id < 0)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Unexpected unary expression for event class's `id` attribute.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            event_class->id = id;
-            _SET(set, _EVENT_ID_SET);
-        } else if (strcmp(left, "stream_id") == 0) {
-            if (_IS_SET(set, _EVENT_STREAM_ID_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "stream_id", "event class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, stream_id);
-
-            /*
-             * Only read "stream_id" if get_unary_unsigned()
-             * succeeded.
-             */
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Unexpected unary expression for event class's `stream_id` attribute.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            _SET(set, _EVENT_STREAM_ID_SET);
-        } else if (strcmp(left, "context") == 0) {
-            if (_IS_SET(set, _EVENT_CONTEXT_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Duplicate `context` entry in event class.");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = visit_field_class_specifier_list(
-                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
-                &event_class->spec_context_fc);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                                "Cannot create event class's context field class.");
-                goto error;
-            }
-
-            BT_ASSERT(event_class->spec_context_fc);
-            _SET(set, _EVENT_CONTEXT_SET);
-        } else if (strcmp(left, "fields") == 0) {
-            if (_IS_SET(set, _EVENT_FIELDS_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Duplicate `fields` entry in event class.");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = visit_field_class_specifier_list(
-                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
-                &event_class->payload_fc);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                                "Cannot create event class's payload field class.");
-                goto error;
-            }
-
-            BT_ASSERT(event_class->payload_fc);
-            _SET(set, _EVENT_FIELDS_SET);
-        } else if (strcmp(left, "loglevel") == 0) {
-            uint64_t loglevel_value;
-            bool is_log_level_known = true;
-            bt_event_class_log_level log_level;
-
-            if (_IS_SET(set, _EVENT_LOG_LEVEL_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "loglevel", "event class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, &loglevel_value);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Unexpected unary expression for event class's `loglevel` attribute.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            switch (loglevel_value) {
-            case 0:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY;
-                break;
-            case 1:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_ALERT;
-                break;
-            case 2:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_CRITICAL;
-                break;
-            case 3:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_ERROR;
-                break;
-            case 4:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_WARNING;
-                break;
-            case 5:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_NOTICE;
-                break;
-            case 6:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_INFO;
-                break;
-            case 7:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM;
-                break;
-            case 8:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM;
-                break;
-            case 9:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS;
-                break;
-            case 10:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE;
-                break;
-            case 11:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT;
-                break;
-            case 12:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION;
-                break;
-            case 13:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE;
-                break;
-            case 14:
-                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG;
-                break;
-            default:
-                is_log_level_known = false;
-                _BT_COMP_LOGW_NODE(
-                    node,
-                    "Not setting event class's log level because its value is unknown: "
-                    "log-level=%" PRIu64,
-                    loglevel_value);
-            }
-
-            if (is_log_level_known) {
-                ctf_event_class_set_log_level(event_class, log_level);
-            }
-
-            _SET(set, _EVENT_LOG_LEVEL_SET);
-        } else if (strcmp(left, "model.emf.uri") == 0) {
-            char *right;
-
-            if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "model.emf.uri", "event class");
-                ret = -EPERM;
-                goto error;
-            }
-
-            right = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.right);
-            if (!right) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node,
-                    "Unexpected unary expression for event class's `model.emf.uri` attribute.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (strlen(right) == 0) {
-                _BT_COMP_LOGW_NODE(node, "Not setting event class's EMF URI because it's empty.");
-            } else {
-                g_string_assign(event_class->emf_uri, right);
-            }
-
-            g_free(right);
-            _SET(set, _EVENT_MODEL_EMF_URI_SET);
-        } else {
-            _BT_COMP_LOGW_NODE(node,
-                               "Unknown attribute in event class: "
-                               "attr-name=\"%s\"",
-                               left);
-        }
-
-        g_free(left);
-        left = NULL;
-        break;
-    }
-    default:
-        ret = -EPERM;
-        goto error;
-    }
-
-    goto end;
-
-error:
-    g_free(left);
-
-end:
-    return ret;
-}
-
-static char *get_event_decl_name(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-    char *left = NULL;
-    char *name = NULL;
-    struct ctf_node *iter;
-    struct bt_list_head *decl_list = &node->u.event.declaration_list;
-
-    bt_list_for_each_entry (iter, decl_list, siblings) {
-        if (iter->type != NODE_CTF_EXPRESSION) {
-            continue;
-        }
-
-        left = ctf_ast_concatenate_unary_strings(&iter->u.ctf_expression.left);
-        if (!left) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot concatenate unary strings.");
-            goto error;
-        }
-
-        if (strcmp(left, "name") == 0) {
-            name = ctf_ast_concatenate_unary_strings(&iter->u.ctf_expression.right);
-            if (!name) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    iter, "Unexpected unary expression for event class's `name` attribute.");
-                goto error;
-            }
-        }
-
-        g_free(left);
-        left = NULL;
-
-        if (name) {
-            break;
-        }
-    }
-
-    return name;
-
-error:
-    g_free(left);
-    return NULL;
-}
-
-static int visit_event_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-    int ret = 0;
-    int set = 0;
-    struct ctf_node *iter;
-    uint64_t stream_id = 0;
-    char *event_name = NULL;
-    struct ctf_event_class *event_class = NULL;
-    struct ctf_stream_class *stream_class = NULL;
-    struct bt_list_head *decl_list = &node->u.event.declaration_list;
-    bool pop_scope = false;
-
-    if (node->visited) {
-        goto end;
-    }
-
-    node->visited = TRUE;
-    event_name = get_event_decl_name(ctx, node);
-    if (!event_name) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Missing `name` attribute in event class.");
-        ret = -EPERM;
-        goto error;
-    }
-
-    event_class = ctf_event_class_create();
-    BT_ASSERT(event_class);
-    g_string_assign(event_class->name, event_name);
-    _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-    pop_scope = true;
-
-    bt_list_for_each_entry (iter, decl_list, siblings) {
-        ret = visit_event_decl_entry(ctx, iter, event_class, &stream_id, &set);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                            "Cannot visit event class's entry: "
-                                            "ret=%d",
-                                            ret);
-            goto error;
-        }
-    }
-
-    if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) {
-        /*
-         * Allow missing stream_id if there is only a single
-         * stream class.
-         */
-        switch (ctx->ctf_tc->stream_classes->len) {
-        case 0:
-            /* Create implicit stream class if there's none */
-            stream_id = 0;
-            stream_class = ctf_stream_class_create();
-            BT_ASSERT(stream_class);
-            stream_class->id = stream_id;
-            g_ptr_array_add(ctx->ctf_tc->stream_classes, stream_class);
-            break;
-        case 1:
-            /* Single stream class: get its ID */
-            stream_class = (ctf_stream_class *) ctx->ctf_tc->stream_classes->pdata[0];
-            stream_id = stream_class->id;
-            break;
-        default:
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Missing `stream_id` attribute in event class.");
-            ret = -EPERM;
-            goto error;
-        }
-    }
-
-    /* We have the stream ID now; get the stream class if found */
-    if (!stream_class) {
-        stream_class = ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc, stream_id);
-        if (!stream_class) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                            "Cannot find stream class at this point: "
-                                            "id=%" PRId64,
-                                            stream_id);
-            ret = -EINVAL;
-            goto error;
-        }
-    }
-
-    BT_ASSERT(stream_class);
-
-    if (!_IS_SET(&set, _EVENT_ID_SET)) {
-        /* Allow only one event without ID per stream */
-        if (stream_class->event_classes->len != 0) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Missing `id` attribute in event class.");
-            ret = -EPERM;
-            goto error;
-        }
-
-        /* Automatic ID */
-        event_class->id = 0;
-    }
-
-    if (ctf_stream_class_borrow_event_class_by_id(stream_class, event_class->id)) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                        "Duplicate event class (same ID) in the same stream class: "
-                                        "id=%" PRId64,
-                                        event_class->id);
-        ret = -EEXIST;
-        goto error;
-    }
-
-    ctf_stream_class_append_event_class(stream_class, event_class);
-    event_class = NULL;
-    goto end;
-
-error:
-    ctf_event_class_destroy(event_class);
-    event_class = NULL;
-
-    if (ret >= 0) {
-        ret = -1;
-    }
-
-end:
-    if (pop_scope) {
-        ctx_pop_scope(ctx);
-    }
-
-    g_free(event_name);
-
-    return ret;
-}
-
-static int auto_map_field_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
-                                               struct ctf_field_class *fc)
-{
-    struct ctf_clock_class *clock_class_to_map_to = NULL;
-    struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
-    int ret = 0;
-    uint64_t clock_class_count;
-
-    if (!fc) {
-        goto end;
-    }
-
-    if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-        goto end;
-    }
-
-    if (int_fc->mapped_clock_class) {
-        /* Already mapped */
-        goto end;
-    }
-
-    clock_class_count = ctx->ctf_tc->clock_classes->len;
-
-    switch (clock_class_count) {
-    case 0:
-        /*
-         * No clock class exists in the trace at this point. Create an
-         * implicit one at 1 GHz, named `default`, and use this clock
-         * class.
-         */
-        clock_class_to_map_to = ctf_clock_class_create();
-        BT_ASSERT(clock_class_to_map_to);
-        clock_class_to_map_to->frequency = UINT64_C(1000000000);
-        g_string_assign(clock_class_to_map_to->name, "default");
-        BT_ASSERT(ret == 0);
-        g_ptr_array_add(ctx->ctf_tc->clock_classes, clock_class_to_map_to);
-        break;
-    case 1:
-        /*
-         * Only one clock class exists in the trace at this point: use
-         * this one.
-         */
-        clock_class_to_map_to = (ctf_clock_class *) ctx->ctf_tc->clock_classes->pdata[0];
-        break;
-    default:
-        /*
-         * Timestamp field not mapped to a clock class and there's more
-         * than one clock class in the trace: this is an error.
-         */
-        _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            "Timestamp field found with no mapped clock class, "
-            "but there's more than one clock class in the trace at this point.");
-        ret = -1;
-        goto end;
-    }
-
-    BT_ASSERT(clock_class_to_map_to);
-    int_fc->mapped_clock_class = clock_class_to_map_to;
-
-end:
-    return ret;
-}
-
-static int auto_map_fields_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
-                                                struct ctf_field_class *root_fc,
-                                                const char *field_name)
-{
-    int ret = 0;
-    uint64_t i, count;
-    struct ctf_field_class_struct *struct_fc = (ctf_field_class_struct *) root_fc;
-    struct ctf_field_class_variant *var_fc = (ctf_field_class_variant *) root_fc;
-
-    if (!root_fc) {
-        goto end;
-    }
-
-    if (root_fc->type != CTF_FIELD_CLASS_TYPE_STRUCT &&
-        root_fc->type != CTF_FIELD_CLASS_TYPE_VARIANT) {
-        goto end;
-    }
-
-    if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
-        count = struct_fc->members->len;
-    } else {
-        count = var_fc->options->len;
-    }
-
-    for (i = 0; i < count; i++) {
-        struct ctf_named_field_class *named_fc = NULL;
-
-        if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
-            named_fc = ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
-        } else if (root_fc->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
-            named_fc = ctf_field_class_variant_borrow_option_by_index(var_fc, i);
-        } else {
-            bt_common_abort();
-        }
-
-        if (strcmp(named_fc->name->str, field_name) == 0) {
-            ret = auto_map_field_to_trace_clock_class(ctx, named_fc->fc);
-            if (ret) {
-                _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    "Cannot automatically map field to trace's clock class: "
-                    "field-name=\"%s\"",
-                    field_name);
-                goto end;
-            }
-        }
-
-        ret = auto_map_fields_to_trace_clock_class(ctx, named_fc->fc, field_name);
-        if (ret) {
-            _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                "Cannot automatically map structure or variant field class's fields to trace's clock class: "
-                "field-name=\"%s\", root-field-name=\"%s\"",
-                field_name, named_fc->name->str);
-            goto end;
-        }
-    }
-
-end:
-    return ret;
-}
-
-static int visit_stream_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
-                                   struct ctf_stream_class *stream_class, int *set)
-{
-    int ret = 0;
-    char *left = NULL;
-
-    switch (node->type) {
-    case NODE_TYPEDEF:
-        ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
-                                    &node->u.field_class_def.field_class_declarators);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot add field class found in stream class.");
-            goto error;
-        }
-        break;
-    case NODE_TYPEALIAS:
-        ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
-                                      node->u.field_class_alias.alias);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                            "Cannot add field class alias found in stream class.");
-            goto error;
-        }
-        break;
-    case NODE_CTF_EXPRESSION:
-    {
-        left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
-        if (!left) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (strcmp(left, "id") == 0) {
-            int64_t id;
-
-            if (_IS_SET(set, _STREAM_ID_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "id", "stream declaration");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, (uint64_t *) &id);
-
-            /* Only read "id" if get_unary_unsigned() succeeded. */
-            if (ret || (!ret && id < 0)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Unexpected unary expression for stream class's `id` attribute.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc, id)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Duplicate stream class (same ID): id=%" PRId64, id);
-                ret = -EEXIST;
-                goto error;
-            }
-
-            stream_class->id = id;
-            _SET(set, _STREAM_ID_SET);
-        } else if (strcmp(left, "event.header") == 0) {
-            if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                                "Duplicate `event.header` entry in stream class.");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = visit_field_class_specifier_list(
-                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
-                &stream_class->event_header_fc);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Cannot create stream class's event header field class.");
-                goto error;
-            }
-
-            BT_ASSERT(stream_class->event_header_fc);
-            ret = auto_map_fields_to_trace_clock_class(ctx, stream_class->event_header_fc,
-                                                       "timestamp");
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node,
-                    "Cannot automatically map specific event header field class fields named `timestamp` to trace's clock class.");
-                goto error;
-            }
-
-            _SET(set, _STREAM_EVENT_HEADER_SET);
-        } else if (strcmp(left, "event.context") == 0) {
-            if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                                "Duplicate `event.context` entry in stream class.");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = visit_field_class_specifier_list(
-                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
-                &stream_class->event_common_context_fc);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Cannot create stream class's event context field class.");
-                goto error;
-            }
-
-            BT_ASSERT(stream_class->event_common_context_fc);
-            _SET(set, _STREAM_EVENT_CONTEXT_SET);
-        } else if (strcmp(left, "packet.context") == 0) {
-            if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Duplicate `packet.context` entry in stream class.");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = visit_field_class_specifier_list(
-                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
-                &stream_class->packet_context_fc);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Cannot create stream class's packet context field class.");
-                goto error;
-            }
-
-            BT_ASSERT(stream_class->packet_context_fc);
-            ret = auto_map_fields_to_trace_clock_class(ctx, stream_class->packet_context_fc,
-                                                       "timestamp_begin");
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node,
-                    "Cannot automatically map specific packet context field class fields named `timestamp_begin` to trace's clock class.");
-                goto error;
-            }
-
-            ret = auto_map_fields_to_trace_clock_class(ctx, stream_class->packet_context_fc,
-                                                       "timestamp_end");
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node,
-                    "Cannot automatically map specific packet context field class fields named `timestamp_end` to trace's clock class.");
-                goto error;
-            }
-
-            _SET(set, _STREAM_PACKET_CONTEXT_SET);
-        } else {
-            _BT_COMP_LOGW_NODE(node,
-                               "Unknown attribute in stream class: "
-                               "attr-name=\"%s\"",
-                               left);
-        }
-
-        g_free(left);
-        left = NULL;
-        break;
-    }
-
-    default:
-        ret = -EPERM;
-        goto error;
-    }
-
-    return 0;
-
-error:
-    g_free(left);
-    return ret;
-}
-
-static int visit_stream_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-    int set = 0;
-    int ret = 0;
-    struct ctf_node *iter;
-    struct ctf_stream_class *stream_class = NULL;
-    struct bt_list_head *decl_list = &node->u.stream.declaration_list;
-
-    if (node->visited) {
-        goto end;
-    }
-
-    node->visited = TRUE;
-    stream_class = ctf_stream_class_create();
-    BT_ASSERT(stream_class);
-    _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-
-    bt_list_for_each_entry (iter, decl_list, siblings) {
-        ret = visit_stream_decl_entry(ctx, iter, stream_class, &set);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                            "Cannot visit stream class's entry: "
-                                            "ret=%d",
-                                            ret);
-            ctx_pop_scope(ctx);
-            goto error;
-        }
-    }
-
-    ctx_pop_scope(ctx);
-
-    if (_IS_SET(&set, _STREAM_ID_SET)) {
-        /* Check that packet header has `stream_id` field */
-        struct ctf_named_field_class *named_fc = NULL;
-
-        if (!ctx->ctf_tc->packet_header_fc) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Stream class has a `id` attribute, "
-                                                  "but trace has no packet header field class.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        named_fc = ctf_field_class_struct_borrow_member_by_name(
-            ctf_field_class_as_struct(ctx->ctf_tc->packet_header_fc), "stream_id");
-        if (!named_fc) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                node, "Stream class has a `id` attribute, "
-                      "but trace's packet header field class has no `stream_id` field.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (named_fc->fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-            named_fc->fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                node,
-                "Stream class has a `id` attribute, "
-                "but trace's packet header field class's `stream_id` field is not an integer field class.");
-            ret = -EINVAL;
-            goto error;
-        }
-    } else {
-        /* Allow only _one_ ID-less stream */
-        if (ctx->ctf_tc->stream_classes->len != 0) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                node,
-                "Missing `id` attribute in stream class as there's more than one stream class in the trace.");
-            ret = -EPERM;
-            goto error;
-        }
-
-        /* Automatic ID: 0 */
-        stream_class->id = 0;
-    }
-
-    /*
-     * Make sure that this stream class's ID is currently unique in
-     * the trace.
-     */
-    if (ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc, stream_class->id)) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Duplicate stream class (same ID): id=%" PRId64,
-                                        stream_class->id);
-        ret = -EINVAL;
-        goto error;
-    }
-
-    g_ptr_array_add(ctx->ctf_tc->stream_classes, stream_class);
-    stream_class = NULL;
-    goto end;
-
-error:
-    ctf_stream_class_destroy(stream_class);
-    stream_class = NULL;
-
-end:
-    return ret;
-}
-
-static int visit_trace_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
-                                  int *set)
-{
-    int ret = 0;
-    char *left = NULL;
-    uint64_t val;
-
-    switch (node->type) {
-    case NODE_TYPEDEF:
-        ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
-                                    &node->u.field_class_def.field_class_declarators);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                node, "Cannot add field class found in trace (`trace` block).");
-            goto error;
-        }
-        break;
-    case NODE_TYPEALIAS:
-        ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
-                                      node->u.field_class_alias.alias);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                node, "Cannot add field class alias found in trace (`trace` block).");
-            goto error;
-        }
-        break;
-    case NODE_CTF_EXPRESSION:
-    {
-        left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
-        if (!left) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (strcmp(left, "major") == 0) {
-            if (_IS_SET(set, _TRACE_MAJOR_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "major", "trace");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, &val);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Unexpected unary expression for trace's `major` attribute.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (val != 1) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                                "Invalid trace's `minor` attribute: expecting 1.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            ctx->ctf_tc->major = val;
-            _SET(set, _TRACE_MAJOR_SET);
-        } else if (strcmp(left, "minor") == 0) {
-            if (_IS_SET(set, _TRACE_MINOR_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "minor", "trace");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, &val);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    node, "Unexpected unary expression for trace's `minor` attribute.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (val != 8) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                                "Invalid trace's `minor` attribute: expecting 8.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            ctx->ctf_tc->minor = val;
-            _SET(set, _TRACE_MINOR_SET);
-        } else if (strcmp(left, "uuid") == 0) {
-            if (_IS_SET(set, _TRACE_UUID_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "uuid", "trace");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = get_unary_uuid(ctx, &node->u.ctf_expression.right, ctx->ctf_tc->uuid);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Invalid trace's `uuid` attribute.");
-                goto error;
-            }
-
-            ctx->ctf_tc->is_uuid_set = true;
-            _SET(set, _TRACE_UUID_SET);
-        } else if (strcmp(left, "byte_order") == 0) {
-            /* Default byte order is already known at this stage */
-            if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order", "trace");
-                ret = -EPERM;
-                goto error;
-            }
-
-            BT_ASSERT(ctx->ctf_tc->default_byte_order != CTF_BYTE_ORDER_UNKNOWN);
-            _SET(set, _TRACE_BYTE_ORDER_SET);
-        } else if (strcmp(left, "packet.header") == 0) {
-            if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Duplicate `packet.header` entry in trace.");
-                ret = -EPERM;
-                goto error;
-            }
-
-            ret = visit_field_class_specifier_list(
-                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
-                &ctx->ctf_tc->packet_header_fc);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                                "Cannot create trace's packet header field class.");
-                goto error;
-            }
-
-            BT_ASSERT(ctx->ctf_tc->packet_header_fc);
-            _SET(set, _TRACE_PACKET_HEADER_SET);
-        } else {
-            _BT_COMP_LOGW_NODE(node,
-                               "Unknown attribute in stream class: "
-                               "attr-name=\"%s\"",
-                               left);
-        }
-
-        g_free(left);
-        left = NULL;
-        break;
-    }
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unknown expression in trace.");
-        ret = -EINVAL;
-        goto error;
-    }
-
-    return 0;
-
-error:
-    g_free(left);
-    return ret;
-}
-
-static int visit_trace_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-    int ret = 0;
-    int set = 0;
-    struct ctf_node *iter;
-    struct bt_list_head *decl_list = &node->u.trace.declaration_list;
-
-    if (node->visited) {
-        goto end;
-    }
-
-    node->visited = TRUE;
-
-    if (ctx->is_trace_visited) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Duplicate trace (`trace` block).");
-        ret = -EEXIST;
-        goto error;
-    }
-
-    _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-
-    bt_list_for_each_entry (iter, decl_list, siblings) {
-        ret = visit_trace_decl_entry(ctx, iter, &set);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                            "Cannot visit trace's entry (`trace` block): "
-                                            "ret=%d",
-                                            ret);
-            ctx_pop_scope(ctx);
-            goto error;
-        }
-    }
-
-    ctx_pop_scope(ctx);
-
-    if (!_IS_SET(&set, _TRACE_MAJOR_SET)) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                        "Missing `major` attribute in trace (`trace` block).");
-        ret = -EPERM;
-        goto error;
-    }
-
-    if (!_IS_SET(&set, _TRACE_MINOR_SET)) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                        "Missing `minor` attribute in trace (`trace` block).");
-        ret = -EPERM;
-        goto error;
-    }
-
-    if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                        "Missing `byte_order` attribute in trace (`trace` block).");
-        ret = -EPERM;
-        goto error;
-    }
-
-    ctx->is_trace_visited = true;
-
-end:
-    return 0;
-
-error:
-    return ret;
-}
-
-static int visit_env(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-    int ret = 0;
-    char *left = NULL;
-    struct ctf_node *entry_node;
-    struct bt_list_head *decl_list = &node->u.env.declaration_list;
-
-    if (node->visited) {
-        goto end;
-    }
-
-    node->visited = TRUE;
-
-    bt_list_for_each_entry (entry_node, decl_list, siblings) {
-        struct bt_list_head *right_head = &entry_node->u.ctf_expression.right;
-
-        if (entry_node->type != NODE_CTF_EXPRESSION) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                                            "Wrong expression in environment entry: "
-                                            "node-type=%d",
-                                            entry_node->type);
-            ret = -EPERM;
-            goto error;
-        }
-
-        left = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.left);
-        if (!left) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Cannot get environment entry's name.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (is_unary_string(right_head)) {
-            char *right = ctf_ast_concatenate_unary_strings(right_head);
-
-            if (!right) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    entry_node,
-                    "Unexpected unary expression for environment entry's value: "
-                    "name=\"%s\"",
-                    left);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (strcmp(left, "tracer_name") == 0) {
-                if (strncmp(right, "lttng", 5) == 0) {
-                    BT_COMP_LOGI("Detected LTTng trace from `%s` environment value: "
-                                 "tracer-name=\"%s\"",
-                                 left, right);
-                    ctx->is_lttng = true;
-                }
-            }
-
-            ctf_trace_class_append_env_entry(ctx->ctf_tc, left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR,
-                                             right, 0);
-            g_free(right);
-        } else if (is_unary_unsigned(right_head) || is_unary_signed(right_head)) {
-            int64_t v;
-
-            if (is_unary_unsigned(right_head)) {
-                ret = get_unary_unsigned(ctx, right_head, (uint64_t *) &v);
-            } else {
-                ret = get_unary_signed(right_head, &v);
-            }
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    entry_node,
-                    "Unexpected unary expression for environment entry's value: "
-                    "name=\"%s\"",
-                    left);
-                ret = -EINVAL;
-                goto error;
-            }
-
-            ctf_trace_class_append_env_entry(ctx->ctf_tc, left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT,
-                                             NULL, v);
-        } else {
-            _BT_COMP_LOGW_NODE(entry_node,
-                               "Environment entry has unknown type: "
-                               "name=\"%s\"",
-                               left);
-        }
-
-        g_free(left);
-        left = NULL;
-    }
-
-end:
-    return 0;
-
-error:
-    g_free(left);
-    return ret;
-}
-
-static int set_trace_byte_order(struct ctf_visitor_generate_ir *ctx, struct ctf_node *trace_node)
-{
-    int ret = 0;
-    int set = 0;
-    char *left = NULL;
-    struct ctf_node *node;
-    struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
-
-    bt_list_for_each_entry (node, decl_list, siblings) {
-        if (node->type == NODE_CTF_EXPRESSION) {
-            struct ctf_node *right_node;
-
-            left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
-            if (!left) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
-                ret = -EINVAL;
-                goto error;
-            }
-
-            if (strcmp(left, "byte_order") == 0) {
-                enum ctf_byte_order bo;
-
-                if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order", "trace");
-                    ret = -EPERM;
-                    goto error;
-                }
-
-                _SET(&set, _TRACE_BYTE_ORDER_SET);
-                right_node =
-                    _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings);
-                bo = byte_order_from_unary_expr(ctx, right_node);
-                if (bo == CTF_BYTE_ORDER_UNKNOWN) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                        node, "Invalid `byte_order` attribute in trace (`trace` block): "
-                              "expecting `le`, `be`, or `network`.");
-                    ret = -EINVAL;
-                    goto error;
-                } else if (bo == CTF_BYTE_ORDER_DEFAULT) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                        node, "Invalid `byte_order` attribute in trace (`trace` block): "
-                              "cannot be set to `native` here.");
-                    ret = -EPERM;
-                    goto error;
-                }
-
-                ctx->ctf_tc->default_byte_order = bo;
-            }
-
-            g_free(left);
-            left = NULL;
-        }
-    }
-
-    if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(trace_node,
-                                        "Missing `byte_order` attribute in trace (`trace` block).");
-        ret = -EINVAL;
-        goto error;
-    }
-
-    return 0;
-
-error:
-    g_free(left);
-    return ret;
-}
-
-static int visit_clock_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
-                                  struct ctf_clock_class *clock, int *set, int64_t *offset_seconds,
-                                  uint64_t *offset_cycles)
-{
-    int ret = 0;
-    char *left = NULL;
-
-    if (entry_node->type != NODE_CTF_EXPRESSION) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Unexpected node type: node-type=%d",
-                                        entry_node->type);
-        ret = -EPERM;
-        goto error;
-    }
-
-    left = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.left);
-    if (!left) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Cannot concatenate unary strings.");
-        ret = -EINVAL;
-        goto error;
-    }
-
-    if (strcmp(left, "name") == 0) {
-        char *right;
-
-        if (_IS_SET(set, _CLOCK_NAME_SET)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "name", "clock class");
-            ret = -EPERM;
-            goto error;
-        }
-
-        right = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.right);
-        if (!right) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Unexpected unary expression for clock class's `name` attribute.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        g_string_assign(clock->name, right);
-        g_free(right);
-        _SET(set, _CLOCK_NAME_SET);
-    } else if (strcmp(left, "uuid") == 0) {
-        bt_uuid_t uuid;
-
-        if (_IS_SET(set, _CLOCK_UUID_SET)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "uuid", "clock class");
-            ret = -EPERM;
-            goto error;
-        }
-
-        ret = get_unary_uuid(ctx, &entry_node->u.ctf_expression.right, uuid);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Invalid clock class's `uuid` attribute.");
-            goto error;
-        }
-
-        clock->has_uuid = true;
-        bt_uuid_copy(clock->uuid, uuid);
-        _SET(set, _CLOCK_UUID_SET);
-    } else if (strcmp(left, "description") == 0) {
-        char *right;
-
-        if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "description", "clock class");
-            ret = -EPERM;
-            goto error;
-        }
-
-        right = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.right);
-        if (!right) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node,
-                "Unexpected unary expression for clock class's `description` attribute.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        g_string_assign(clock->description, right);
-        g_free(right);
-        _SET(set, _CLOCK_DESCRIPTION_SET);
-    } else if (strcmp(left, "freq") == 0) {
-        uint64_t freq = UINT64_C(-1);
-
-        if (_IS_SET(set, _CLOCK_FREQ_SET)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "freq", "clock class");
-            ret = -EPERM;
-            goto error;
-        }
-
-        ret = get_unary_unsigned(ctx, &entry_node->u.ctf_expression.right, &freq);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Unexpected unary expression for clock class's `freq` attribute.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        if (freq == UINT64_C(-1) || freq == 0) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                                            "Invalid clock class frequency: freq=%" PRIu64, freq);
-            ret = -EINVAL;
-            goto error;
-        }
-
-        clock->frequency = freq;
-        _SET(set, _CLOCK_FREQ_SET);
-    } else if (strcmp(left, "precision") == 0) {
-        uint64_t precision;
-
-        if (_IS_SET(set, _CLOCK_PRECISION_SET)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "precision", "clock class");
-            ret = -EPERM;
-            goto error;
-        }
-
-        ret = get_unary_unsigned(ctx, &entry_node->u.ctf_expression.right, &precision);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Unexpected unary expression for clock class's `precision` attribute.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        clock->precision = precision;
-        _SET(set, _CLOCK_PRECISION_SET);
-    } else if (strcmp(left, "offset_s") == 0) {
-        if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset_s", "clock class");
-            ret = -EPERM;
-            goto error;
-        }
-
-        ret = get_unary_signed(&entry_node->u.ctf_expression.right, offset_seconds);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Unexpected unary expression for clock class's `offset_s` attribute.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        _SET(set, _CLOCK_OFFSET_S_SET);
-    } else if (strcmp(left, "offset") == 0) {
-        if (_IS_SET(set, _CLOCK_OFFSET_SET)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset", "clock class");
-            ret = -EPERM;
-            goto error;
-        }
-
-        ret = get_unary_unsigned(ctx, &entry_node->u.ctf_expression.right, offset_cycles);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Unexpected unary expression for clock class's `offset` attribute.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        _SET(set, _CLOCK_OFFSET_SET);
-    } else if (strcmp(left, "absolute") == 0) {
-        struct ctf_node *right;
-
-        if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) {
-            _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "absolute", "clock class");
-            ret = -EPERM;
-            goto error;
-        }
-
-        right =
-            _BT_LIST_FIRST_ENTRY(&entry_node->u.ctf_expression.right, struct ctf_node, siblings);
-        ret = get_boolean(ctx, right);
-        if (ret < 0) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                entry_node, "Unexpected unary expression for clock class's `absolute` attribute.");
-            ret = -EINVAL;
-            goto error;
-        }
-
-        clock->is_absolute = ret;
-        _SET(set, _CLOCK_ABSOLUTE_SET);
-    } else {
-        _BT_COMP_LOGW_NODE(entry_node, "Unknown attribute in clock class: attr-name=\"%s\"", left);
-    }
-
-    g_free(left);
-    left = NULL;
-    return 0;
-
-error:
-    g_free(left);
-    return ret;
-}
-
-static inline uint64_t cycles_from_ns(uint64_t frequency, uint64_t ns)
-{
-    uint64_t cycles;
-
-    /* 1GHz */
-    if (frequency == UINT64_C(1000000000)) {
-        cycles = ns;
-    } else {
-        cycles = (uint64_t) (((double) ns * (double) frequency) / 1e9);
-    }
-
-    return cycles;
-}
-
-static void calibrate_clock_class_offsets(int64_t *offset_seconds, uint64_t *offset_cycles,
-                                          uint64_t freq)
-{
-    if (*offset_cycles >= freq) {
-        const uint64_t s_in_offset_cycles = *offset_cycles / freq;
-
-        *offset_seconds += (int64_t) s_in_offset_cycles;
-        *offset_cycles -= (s_in_offset_cycles * freq);
-    }
-}
-
-static void apply_clock_class_is_absolute(struct ctf_visitor_generate_ir *ctx,
-                                          struct ctf_clock_class *clock)
-{
-    if (ctx->decoder_config.force_clock_class_origin_unix_epoch) {
-        clock->is_absolute = true;
-    }
-
-    return;
-}
-
-static void apply_clock_class_offset(struct ctf_visitor_generate_ir *ctx,
-                                     struct ctf_clock_class *clock)
-{
-    uint64_t freq;
-    int64_t offset_s_to_apply = ctx->decoder_config.clock_class_offset_s;
-    uint64_t offset_ns_to_apply;
-    int64_t cur_offset_s;
-    uint64_t cur_offset_cycles;
-
-    if (ctx->decoder_config.clock_class_offset_s == 0 &&
-        ctx->decoder_config.clock_class_offset_ns == 0) {
-        goto end;
-    }
-
-    /* Transfer nanoseconds to seconds as much as possible */
-    if (ctx->decoder_config.clock_class_offset_ns < 0) {
-        const int64_t abs_ns = -ctx->decoder_config.clock_class_offset_ns;
-        const int64_t abs_extra_s = abs_ns / INT64_C(1000000000) + 1;
-        const int64_t extra_s = -abs_extra_s;
-        const int64_t offset_ns =
-            ctx->decoder_config.clock_class_offset_ns - (extra_s * INT64_C(1000000000));
-
-        BT_ASSERT(offset_ns > 0);
-        offset_ns_to_apply = (uint64_t) offset_ns;
-        offset_s_to_apply += extra_s;
-    } else {
-        const int64_t extra_s = ctx->decoder_config.clock_class_offset_ns / INT64_C(1000000000);
-        const int64_t offset_ns =
-            ctx->decoder_config.clock_class_offset_ns - (extra_s * INT64_C(1000000000));
-
-        BT_ASSERT(offset_ns >= 0);
-        offset_ns_to_apply = (uint64_t) offset_ns;
-        offset_s_to_apply += extra_s;
-    }
-
-    freq = clock->frequency;
-    cur_offset_s = clock->offset_seconds;
-    cur_offset_cycles = clock->offset_cycles;
-
-    /* Apply offsets */
-    cur_offset_s += offset_s_to_apply;
-    cur_offset_cycles += cycles_from_ns(freq, offset_ns_to_apply);
-
-    /*
-     * Recalibrate offsets because the part in cycles can be greater
-     * than the frequency at this point.
-     */
-    calibrate_clock_class_offsets(&cur_offset_s, &cur_offset_cycles, freq);
-
-    /* Set final offsets */
-    clock->offset_seconds = cur_offset_s;
-    clock->offset_cycles = cur_offset_cycles;
-
-end:
-    return;
-}
-
-static int visit_clock_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *clock_node)
-{
-    int ret = 0;
-    int set = 0;
-    struct ctf_clock_class *clock;
-    struct ctf_node *entry_node;
-    struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list;
-    const char *clock_class_name;
-    int64_t offset_seconds = 0;
-    uint64_t offset_cycles = 0;
-    uint64_t freq;
-
-    if (clock_node->visited) {
-        return 0;
-    }
-
-    clock_node->visited = TRUE;
-
-    /* CTF 1.8's default frequency for a clock class is 1 GHz */
-    clock = ctf_clock_class_create();
-    if (!clock) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(clock_node, "Cannot create default clock class.");
-        ret = -ENOMEM;
-        goto end;
-    }
-
-    bt_list_for_each_entry (entry_node, decl_list, siblings) {
-        ret = visit_clock_decl_entry(ctx, entry_node, clock, &set, &offset_seconds, &offset_cycles);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Cannot visit clock class's entry: ret=%d",
-                                            ret);
-            goto end;
-        }
-    }
-
-    if (!_IS_SET(&set, _CLOCK_NAME_SET)) {
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(clock_node, "Missing `name` attribute in clock class.");
-        ret = -EPERM;
-        goto end;
-    }
-
-    clock_class_name = clock->name->str;
-    BT_ASSERT(clock_class_name);
-    if (ctx->is_lttng && strcmp(clock_class_name, "monotonic") == 0) {
-        /*
-         * Old versions of LTTng forgot to set its clock class
-         * as absolute, even if it is. This is important because
-         * it's a condition to be able to sort messages
-         * from different sources.
-         */
-        clock->is_absolute = true;
-    }
-
-    /*
-     * Adjust offsets so that the part in cycles is less than the
-     * frequency (move to the part in seconds).
-     */
-    freq = clock->frequency;
-    calibrate_clock_class_offsets(&offset_seconds, &offset_cycles, freq);
-    BT_ASSERT(offset_cycles < clock->frequency);
-    clock->offset_seconds = offset_seconds;
-    clock->offset_cycles = offset_cycles;
-    apply_clock_class_offset(ctx, clock);
-    apply_clock_class_is_absolute(ctx, clock);
-    g_ptr_array_add(ctx->ctf_tc->clock_classes, clock);
-    clock = NULL;
-
-end:
-    if (clock) {
-        ctf_clock_class_destroy(clock);
-    }
-
-    return ret;
-}
-
-static int visit_root_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *root_decl_node)
-{
-    int ret = 0;
-
-    if (root_decl_node->visited) {
-        goto end;
-    }
-
-    root_decl_node->visited = TRUE;
-
-    switch (root_decl_node->type) {
-    case NODE_TYPEDEF:
-        ret =
-            visit_field_class_def(ctx, root_decl_node->u.field_class_def.field_class_specifier_list,
-                                  &root_decl_node->u.field_class_def.field_class_declarators);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
-                                            "Cannot add field class found in root scope.");
-            goto end;
-        }
-        break;
-    case NODE_TYPEALIAS:
-        ret = visit_field_class_alias(ctx, root_decl_node->u.field_class_alias.target,
-                                      root_decl_node->u.field_class_alias.alias);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
-                                            "Cannot add field class alias found in root scope.");
-            goto end;
-        }
-        break;
-    case NODE_TYPE_SPECIFIER_LIST:
-    {
-        struct ctf_field_class *decl = NULL;
-
-        /*
-         * Just add the field class specifier to the root
-         * declaration scope. Put local reference.
-         */
-        ret = visit_field_class_specifier_list(ctx, root_decl_node, &decl);
-        if (ret) {
-            _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
-                                            "Cannot visit root scope's field class: "
-                                            "ret=%d",
-                                            ret);
-            BT_ASSERT(!decl);
-            goto end;
-        }
-
-        ctf_field_class_destroy(decl);
-        decl = NULL;
-        break;
-    }
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node, "Unexpected node type: node-type=%d",
-                                        root_decl_node->type);
-        ret = -EPERM;
-        goto end;
-    }
-
-end:
-    return ret;
-}
-
-BT_HIDDEN
-struct ctf_visitor_generate_ir *
-ctf_visitor_generate_ir_create(const struct ctf_metadata_decoder_config *decoder_config)
-{
-    struct ctf_visitor_generate_ir *ctx = NULL;
-
-    /* Create visitor's context */
-    ctx = ctx_create(decoder_config);
-    if (!ctx) {
-        BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, decoder_config->log_level, decoder_config->self_comp,
-                            "Cannot create visitor's context.");
-        goto error;
-    }
-
-    goto end;
-
-error:
-    ctx_destroy(ctx);
-    ctx = NULL;
-
-end:
-    return ctx;
-}
-
-BT_HIDDEN
-void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor)
-{
-    ctx_destroy(visitor);
-}
-
-BT_HIDDEN
-bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class(struct ctf_visitor_generate_ir *ctx)
-{
-    BT_ASSERT_DBG(ctx);
-
-    if (ctx->trace_class) {
-        bt_trace_class_get_ref(ctx->trace_class);
-    }
-
-    return ctx->trace_class;
-}
-
-BT_HIDDEN
-struct ctf_trace_class *
-ctf_visitor_generate_ir_borrow_ctf_trace_class(struct ctf_visitor_generate_ir *ctx)
-{
-    BT_ASSERT_DBG(ctx);
-    BT_ASSERT_DBG(ctx->ctf_tc);
-    return ctx->ctf_tc;
-}
-
-BT_HIDDEN
-int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-    int ret = 0;
-
-    BT_COMP_LOGI_STR("Visiting metadata's AST to generate CTF IR objects.");
-
-    switch (node->type) {
-    case NODE_ROOT:
-    {
-        struct ctf_node *iter;
-        bool got_trace_decl = false;
-
-        /*
-         * The first thing we need is the native byte order of
-         * the trace block, because early class aliases can have
-         * a `byte_order` attribute set to `native`. If we don't
-         * have the native byte order yet, and we don't have any
-         * trace block yet, then fail with EINCOMPLETE.
-         */
-        if (ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_UNKNOWN) {
-            bt_list_for_each_entry (iter, &node->u.root.trace, siblings) {
-                if (got_trace_decl) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Duplicate trace (`trace` block).");
-                    ret = -1;
-                    goto end;
-                }
-
-                ret = set_trace_byte_order(ctx, iter);
-                if (ret) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                                    "Cannot set trace's native byte order: "
-                                                    "ret=%d",
-                                                    ret);
-                    goto end;
-                }
-
-                got_trace_decl = true;
-            }
-
-            if (!got_trace_decl) {
-                BT_COMP_LOGD_STR("Incomplete AST: need trace (`trace` block).");
-                ret = -EINCOMPLETE;
-                goto end;
-            }
-        }
-
-        BT_ASSERT(ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_LITTLE ||
-                  ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_BIG);
-        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
-
-        /* Environment */
-        bt_list_for_each_entry (iter, &node->u.root.env, siblings) {
-            ret = visit_env(ctx, iter);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(
-                    iter,
-                    "Cannot visit trace's environment (`env` block) entry: "
-                    "ret=%d",
-                    ret);
-                goto end;
-            }
-        }
-
-        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
-
-        /*
-         * Visit clock blocks.
-         */
-        bt_list_for_each_entry (iter, &node->u.root.clock, siblings) {
-            ret = visit_clock_decl(ctx, iter);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit clock class: ret=%d", ret);
-                goto end;
-            }
-        }
-
-        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
-
-        /*
-         * Visit root declarations next, as they can be used by any
-         * following entity.
-         */
-        bt_list_for_each_entry (iter, &node->u.root.declaration_list, siblings) {
-            ret = visit_root_decl(ctx, iter);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit root entry: ret=%d", ret);
-                goto end;
-            }
-        }
-
-        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
-
-        /* Callsite blocks are not supported */
-        bt_list_for_each_entry (iter, &node->u.root.callsite, siblings) {
-            _BT_COMP_LOGW_NODE(iter, "\"callsite\" blocks are not supported as of this version.");
-        }
-
-        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
-
-        /* Trace */
-        bt_list_for_each_entry (iter, &node->u.root.trace, siblings) {
-            ret = visit_trace_decl(ctx, iter);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                                "Cannot visit trace (`trace` block): "
-                                                "ret=%d",
-                                                ret);
-                goto end;
-            }
-        }
-
-        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
-
-        /* Streams */
-        bt_list_for_each_entry (iter, &node->u.root.stream, siblings) {
-            ret = visit_stream_decl(ctx, iter);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit stream class: ret=%d", ret);
-                goto end;
-            }
-        }
-
-        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
-
-        /* Events */
-        bt_list_for_each_entry (iter, &node->u.root.event, siblings) {
-            ret = visit_event_decl(ctx, iter);
-            if (ret) {
-                _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit event class: ret=%d", ret);
-                goto end;
-            }
-        }
-
-        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
-        break;
-    }
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unexpected node type: node-type=%d", node->type);
-        ret = -EINVAL;
-        goto end;
-    }
-
-    /* Update default clock classes */
-    ret = ctf_trace_class_update_default_clock_classes(ctx->ctf_tc, &ctx->log_cfg);
-    if (ret) {
-        ret = -EINVAL;
-        goto end;
-    }
-
-    /* Update trace class meanings */
-    ret = ctf_trace_class_update_meanings(ctx->ctf_tc);
-    if (ret) {
-        ret = -EINVAL;
-        goto end;
-    }
-
-    /* Update stream class configuration */
-    ret = ctf_trace_class_update_stream_class_config(ctx->ctf_tc);
-    if (ret) {
-        ret = -EINVAL;
-        goto end;
-    }
-
-    /* Update text arrays and sequences */
-    ret = ctf_trace_class_update_text_array_sequence(ctx->ctf_tc);
-    if (ret) {
-        ret = -EINVAL;
-        goto end;
-    }
-
-    /* Update structure/array/sequence alignments */
-    ret = ctf_trace_class_update_alignments(ctx->ctf_tc);
-    if (ret) {
-        ret = -EINVAL;
-        goto end;
-    }
-
-    /* Resolve sequence lengths and variant tags */
-    ret = ctf_trace_class_resolve_field_classes(ctx->ctf_tc, &ctx->log_cfg);
-    if (ret) {
-        ret = -EINVAL;
-        goto end;
-    }
-
-    if (ctx->trace_class) {
-        /*
-         * Update "in IR" for field classes.
-         *
-         * If we have no IR trace class, then we'll have no way
-         * to create IR fields anyway, so we leave all the
-         * `in_ir` members false.
-         */
-        ret = ctf_trace_class_update_in_ir(ctx->ctf_tc);
-        if (ret) {
-            ret = -EINVAL;
-            goto end;
-        }
-    }
-
-    /* Update saved value indexes */
-    ret = ctf_trace_class_update_value_storing_indexes(ctx->ctf_tc);
-    if (ret) {
-        ret = -EINVAL;
-        goto end;
-    }
-
-    /* Validate what we have so far */
-    ret = ctf_trace_class_validate(ctx->ctf_tc, &ctx->log_cfg);
-    if (ret) {
-        ret = -EINVAL;
-        goto end;
-    }
-
-    /*
-     * If there are fields which are not related to the CTF format
-     * itself in the packet header and in event header field
-     * classes, warn about it because they are never translated.
-     */
-    ctf_trace_class_warn_meaningless_header_fields(ctx->ctf_tc, &ctx->log_cfg);
-
-    if (ctx->trace_class) {
-        /* Copy new CTF metadata -> new IR metadata */
-        ret = ctf_trace_class_translate(ctx->log_cfg.self_comp, ctx->trace_class, ctx->ctf_tc);
-        if (ret) {
-            ret = -EINVAL;
-            goto end;
-        }
-    }
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/visitor-parent-links.cpp b/src/plugins/ctf/common/metadata/visitor-parent-links.cpp
deleted file mode 100644 (file)
index 724ada6..0000000
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Metadata Parent Link Creator.
- */
-
-#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (log_cfg->log_level)
-#define BT_LOG_TAG            "PLUGIN/CTF/META/PARENT-LINKS-VISITOR"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include "common/assert.h"
-#include <glib.h>
-#include <inttypes.h>
-#include <errno.h>
-#include "common/macros.h"
-#include "common/list.h"
-#include "scanner.hpp"
-#include "ast.hpp"
-#include "logging.hpp"
-
-static int ctf_visitor_unary_expression(int depth, struct ctf_node *node,
-                                        struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-
-    switch (node->u.unary_expression.link) {
-    case UNARY_LINK_UNKNOWN:
-    case UNARY_DOTLINK:
-    case UNARY_ARROWLINK:
-    case UNARY_DOTDOTDOT:
-        break;
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno, "Unknown expression link type: type=%d\n",
-                                          node->u.unary_expression.link);
-        return -EINVAL;
-    }
-
-    switch (node->u.unary_expression.type) {
-    case UNARY_STRING:
-    case UNARY_SIGNED_CONSTANT:
-    case UNARY_UNSIGNED_CONSTANT:
-        break;
-    case UNARY_SBRAC:
-        node->u.unary_expression.u.sbrac_exp->parent = node;
-        ret =
-            ctf_visitor_unary_expression(depth + 1, node->u.unary_expression.u.sbrac_exp, log_cfg);
-        if (ret)
-            return ret;
-        break;
-
-    case UNARY_UNKNOWN:
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno, "Unknown expression link type: type=%d\n",
-                                          node->u.unary_expression.link);
-        return -EINVAL;
-    }
-    return 0;
-}
-
-static int ctf_visitor_type_specifier(int depth, struct ctf_node *node,
-                                      struct meta_log_config *log_cfg)
-{
-    int ret;
-
-    switch (node->u.field_class_specifier.type) {
-    case TYPESPEC_VOID:
-    case TYPESPEC_CHAR:
-    case TYPESPEC_SHORT:
-    case TYPESPEC_INT:
-    case TYPESPEC_LONG:
-    case TYPESPEC_FLOAT:
-    case TYPESPEC_DOUBLE:
-    case TYPESPEC_SIGNED:
-    case TYPESPEC_UNSIGNED:
-    case TYPESPEC_BOOL:
-    case TYPESPEC_COMPLEX:
-    case TYPESPEC_IMAGINARY:
-    case TYPESPEC_CONST:
-    case TYPESPEC_ID_TYPE:
-        break;
-    case TYPESPEC_FLOATING_POINT:
-    case TYPESPEC_INTEGER:
-    case TYPESPEC_STRING:
-    case TYPESPEC_STRUCT:
-    case TYPESPEC_VARIANT:
-    case TYPESPEC_ENUM:
-        node->u.field_class_specifier.node->parent = node;
-        ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_specifier.node, log_cfg);
-        if (ret)
-            return ret;
-        break;
-
-    case TYPESPEC_UNKNOWN:
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno, "Unknown type specifier: type=%d\n",
-                                          node->u.field_class_specifier.type);
-        return -EINVAL;
-    }
-    return 0;
-}
-
-static int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node,
-                                              struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-    struct ctf_node *iter;
-
-    depth++;
-
-    bt_list_for_each_entry (iter, &node->u.field_class_declarator.pointers, siblings) {
-        iter->parent = node;
-        ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-        if (ret)
-            return ret;
-    }
-
-    switch (node->u.field_class_declarator.type) {
-    case TYPEDEC_ID:
-        break;
-    case TYPEDEC_NESTED:
-        if (node->u.field_class_declarator.u.nested.field_class_declarator) {
-            node->u.field_class_declarator.u.nested.field_class_declarator->parent = node;
-            ret = ctf_visitor_parent_links(
-                depth + 1, node->u.field_class_declarator.u.nested.field_class_declarator, log_cfg);
-            if (ret)
-                return ret;
-        }
-        if (!node->u.field_class_declarator.u.nested.abstract_array) {
-            bt_list_for_each_entry (iter, &node->u.field_class_declarator.u.nested.length,
-                                    siblings) {
-                iter->parent = node;
-                ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-                if (ret)
-                    return ret;
-            }
-        }
-        if (node->u.field_class_declarator.bitfield_len) {
-            node->u.field_class_declarator.bitfield_len = node;
-            ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_declarator.bitfield_len,
-                                           log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case TYPEDEC_UNKNOWN:
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno, "Unknown type declarator: type=%d\n",
-                                          node->u.field_class_declarator.type);
-        return -EINVAL;
-    }
-    depth--;
-    return 0;
-}
-
-int ctf_visitor_parent_links(int depth, struct ctf_node *node, struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-    struct ctf_node *iter;
-
-    if (node->visited)
-        return 0;
-
-    switch (node->type) {
-    case NODE_ROOT:
-        bt_list_for_each_entry (iter, &node->u.root.declaration_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.root.trace, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.root.stream, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.root.event, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.root.clock, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.root.callsite, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-
-    case NODE_EVENT:
-        bt_list_for_each_entry (iter, &node->u.event.declaration_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_STREAM:
-        bt_list_for_each_entry (iter, &node->u.stream.declaration_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_ENV:
-        bt_list_for_each_entry (iter, &node->u.env.declaration_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_TRACE:
-        bt_list_for_each_entry (iter, &node->u.trace.declaration_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_CLOCK:
-        bt_list_for_each_entry (iter, &node->u.clock.declaration_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_CALLSITE:
-        bt_list_for_each_entry (iter, &node->u.callsite.declaration_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-
-    case NODE_CTF_EXPRESSION:
-        depth++;
-        bt_list_for_each_entry (iter, &node->u.ctf_expression.left, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.ctf_expression.right, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        depth--;
-        break;
-    case NODE_UNARY_EXPRESSION:
-        return ctf_visitor_unary_expression(depth, node, log_cfg);
-
-    case NODE_TYPEDEF:
-        depth++;
-        node->u.field_class_def.field_class_specifier_list->parent = node;
-        ret = ctf_visitor_parent_links(depth + 1,
-                                       node->u.field_class_def.field_class_specifier_list, log_cfg);
-        if (ret)
-            return ret;
-        bt_list_for_each_entry (iter, &node->u.field_class_def.field_class_declarators, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        depth--;
-        break;
-    case NODE_TYPEALIAS_TARGET:
-        depth++;
-        node->u.field_class_alias_target.field_class_specifier_list->parent = node;
-        ret = ctf_visitor_parent_links(
-            depth + 1, node->u.field_class_alias_target.field_class_specifier_list, log_cfg);
-        if (ret)
-            return ret;
-        bt_list_for_each_entry (iter, &node->u.field_class_alias_target.field_class_declarators,
-                                siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        depth--;
-        break;
-    case NODE_TYPEALIAS_ALIAS:
-        depth++;
-        node->u.field_class_alias_name.field_class_specifier_list->parent = node;
-        ret = ctf_visitor_parent_links(
-            depth + 1, node->u.field_class_alias_name.field_class_specifier_list, log_cfg);
-        if (ret)
-            return ret;
-        bt_list_for_each_entry (iter, &node->u.field_class_alias_name.field_class_declarators,
-                                siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        depth--;
-        break;
-    case NODE_TYPEALIAS:
-        node->u.field_class_alias.target->parent = node;
-        ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias.target, log_cfg);
-        if (ret)
-            return ret;
-        node->u.field_class_alias.alias->parent = node;
-        ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias.alias, log_cfg);
-        if (ret)
-            return ret;
-        break;
-
-    case NODE_TYPE_SPECIFIER_LIST:
-        bt_list_for_each_entry (iter, &node->u.field_class_specifier_list.head, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-
-    case NODE_TYPE_SPECIFIER:
-        ret = ctf_visitor_type_specifier(depth, node, log_cfg);
-        if (ret)
-            return ret;
-        break;
-    case NODE_POINTER:
-        break;
-    case NODE_TYPE_DECLARATOR:
-        ret = ctf_visitor_field_class_declarator(depth, node, log_cfg);
-        if (ret)
-            return ret;
-        break;
-
-    case NODE_FLOATING_POINT:
-        bt_list_for_each_entry (iter, &node->u.floating_point.expressions, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_INTEGER:
-        bt_list_for_each_entry (iter, &node->u.integer.expressions, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_STRING:
-        bt_list_for_each_entry (iter, &node->u.string.expressions, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_ENUMERATOR:
-        bt_list_for_each_entry (iter, &node->u.enumerator.values, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_ENUM:
-        depth++;
-        if (node->u._enum.container_field_class) {
-            ret = ctf_visitor_parent_links(depth + 1, node->u._enum.container_field_class, log_cfg);
-            if (ret)
-                return ret;
-        }
-
-        bt_list_for_each_entry (iter, &node->u._enum.enumerator_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        depth--;
-        break;
-    case NODE_STRUCT_OR_VARIANT_DECLARATION:
-        node->u.struct_or_variant_declaration.field_class_specifier_list->parent = node;
-        ret = ctf_visitor_parent_links(
-            depth + 1, node->u.struct_or_variant_declaration.field_class_specifier_list, log_cfg);
-        if (ret)
-            return ret;
-        bt_list_for_each_entry (
-            iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_VARIANT:
-        bt_list_for_each_entry (iter, &node->u.variant.declaration_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_STRUCT:
-        bt_list_for_each_entry (iter, &node->u._struct.declaration_list, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u._struct.min_align, siblings) {
-            iter->parent = node;
-            ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-
-    case NODE_UNKNOWN:
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno, "Unknown node type: type=%d\n", node->type);
-        return -EINVAL;
-    }
-    return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/visitor-semantic-validator.cpp b/src/plugins/ctf/common/metadata/visitor-semantic-validator.cpp
deleted file mode 100644 (file)
index d2d084d..0000000
+++ /dev/null
@@ -1,1004 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Metadata Semantic Validator.
- */
-
-#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (log_cfg->log_level)
-#define BT_LOG_TAG            "PLUGIN/CTF/META/SEMANTIC-VALIDATOR-VISITOR"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include "common/assert.h"
-#include <glib.h>
-#include <inttypes.h>
-#include <errno.h>
-#include "common/list.h"
-#include "scanner.hpp"
-#include "ast.hpp"
-#include "logging.hpp"
-
-#define _bt_list_first_entry(ptr, type, member) bt_list_entry((ptr)->next, type, member)
-
-static int _ctf_visitor_semantic_check(int depth, struct ctf_node *node,
-                                       struct meta_log_config *log_cfg);
-
-static int ctf_visitor_unary_expression(int depth, struct ctf_node *node,
-                                        struct meta_log_config *log_cfg)
-{
-    struct ctf_node *iter;
-    int is_ctf_exp = 0, is_ctf_exp_left = 0;
-
-    switch (node->parent->type) {
-    case NODE_CTF_EXPRESSION:
-        is_ctf_exp = 1;
-        bt_list_for_each_entry (iter, &node->parent->u.ctf_expression.left, siblings) {
-            if (iter == node) {
-                is_ctf_exp_left = 1;
-                /*
-                 * We are a left child of a ctf expression.
-                 * We are only allowed to be a string.
-                 */
-                if (node->u.unary_expression.type != UNARY_STRING) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                        node->lineno,
-                        "Left child of a CTF expression is only allowed to be a string.");
-                    goto errperm;
-                }
-                break;
-            }
-        }
-        /* Right child of a ctf expression can be any type of unary exp. */
-        break; /* OK */
-    case NODE_TYPE_DECLARATOR:
-        /*
-         * We are the length of a type declarator.
-         */
-        switch (node->u.unary_expression.type) {
-        case UNARY_UNSIGNED_CONSTANT:
-        case UNARY_STRING:
-            break;
-        default:
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                node->lineno,
-                "Children of field class declarator and `enum` can only be unsigned numeric constants or references to fields (e.g., `a.b.c`).");
-            goto errperm;
-        }
-        break; /* OK */
-
-    case NODE_STRUCT:
-        /*
-         * We are the size of a struct align attribute.
-         */
-        switch (node->u.unary_expression.type) {
-        case UNARY_UNSIGNED_CONSTANT:
-            break;
-        default:
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                node->lineno,
-                "Structure alignment attribute can only be an unsigned numeric constant.");
-            goto errperm;
-        }
-        break;
-
-    case NODE_ENUMERATOR:
-        /* The enumerator's parent has validated its validity already. */
-        break; /* OK */
-
-    case NODE_UNARY_EXPRESSION:
-        /*
-         * We disallow nested unary expressions and "sbrac" unary
-         * expressions.
-         */
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                          "Nested unary expressions not allowed (`()` and `[]`).");
-        goto errperm;
-
-    case NODE_ROOT:
-    case NODE_EVENT:
-    case NODE_STREAM:
-    case NODE_ENV:
-    case NODE_TRACE:
-    case NODE_CLOCK:
-    case NODE_CALLSITE:
-    case NODE_TYPEDEF:
-    case NODE_TYPEALIAS_TARGET:
-    case NODE_TYPEALIAS_ALIAS:
-    case NODE_TYPEALIAS:
-    case NODE_TYPE_SPECIFIER:
-    case NODE_POINTER:
-    case NODE_FLOATING_POINT:
-    case NODE_INTEGER:
-    case NODE_STRING:
-    case NODE_ENUM:
-    case NODE_STRUCT_OR_VARIANT_DECLARATION:
-    case NODE_VARIANT:
-    default:
-        goto errinval;
-    }
-
-    switch (node->u.unary_expression.link) {
-    case UNARY_LINK_UNKNOWN:
-        /* We don't allow empty link except on the first node of the list */
-        if (is_ctf_exp &&
-            _bt_list_first_entry(is_ctf_exp_left ? &node->parent->u.ctf_expression.left :
-                                                   &node->parent->u.ctf_expression.right,
-                                 struct ctf_node, siblings) != node) {
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                node->lineno,
-                "Empty link is not allowed except on first node of unary expression (need to separate nodes with `.` or `->`).");
-            goto errperm;
-        }
-        break; /* OK */
-    case UNARY_DOTLINK:
-    case UNARY_ARROWLINK:
-        /* We only allow -> and . links between children of ctf_expression. */
-        if (node->parent->type != NODE_CTF_EXPRESSION) {
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                node->lineno, "Links `.` and `->` are only allowed as children of CTF expression.");
-            goto errperm;
-        }
-        /*
-         * Only strings can be separated linked by . or ->.
-         * This includes "", '' and non-quoted identifiers.
-         */
-        if (node->u.unary_expression.type != UNARY_STRING) {
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                node->lineno,
-                "Links `.` and `->` are only allowed to separate strings and identifiers.");
-            goto errperm;
-        }
-        /* We don't allow link on the first node of the list */
-        if (is_ctf_exp &&
-            _bt_list_first_entry(is_ctf_exp_left ? &node->parent->u.ctf_expression.left :
-                                                   &node->parent->u.ctf_expression.right,
-                                 struct ctf_node, siblings) == node) {
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                node->lineno,
-                "Links `.` and `->` are not allowed before first node of the unary expression list.");
-            goto errperm;
-        }
-        break;
-    case UNARY_DOTDOTDOT:
-        /* We only allow ... link between children of enumerator. */
-        if (node->parent->type != NODE_ENUMERATOR) {
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                              "Link `...` is only allowed within enumerator.");
-            goto errperm;
-        }
-        /* We don't allow link on the first node of the list */
-        if (_bt_list_first_entry(&node->parent->u.enumerator.values, struct ctf_node, siblings) ==
-            node) {
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                node->lineno,
-                "Link `...` is not allowed on the first node of the unary expression list.");
-            goto errperm;
-        }
-        break;
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno, "Unknown expression link type: type=%d",
-                                          node->u.unary_expression.link);
-        return -EINVAL;
-    }
-    return 0;
-
-errinval:
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-        node->lineno, "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-        node_type(node), node_type(node->parent));
-    return -EINVAL; /* Incoherent structure */
-
-errperm:
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                      "Semantic error: node-type=%s, parent-node-type=%s",
-                                      node_type(node), node_type(node->parent));
-    return -EPERM; /* Structure not allowed */
-}
-
-static int ctf_visitor_field_class_specifier_list(int depth, struct ctf_node *node,
-                                                  struct meta_log_config *log_cfg)
-{
-    switch (node->parent->type) {
-    case NODE_CTF_EXPRESSION:
-    case NODE_TYPE_DECLARATOR:
-    case NODE_TYPEDEF:
-    case NODE_TYPEALIAS_TARGET:
-    case NODE_TYPEALIAS_ALIAS:
-    case NODE_ENUM:
-    case NODE_STRUCT_OR_VARIANT_DECLARATION:
-    case NODE_ROOT:
-        break; /* OK */
-
-    case NODE_EVENT:
-    case NODE_STREAM:
-    case NODE_ENV:
-    case NODE_TRACE:
-    case NODE_CLOCK:
-    case NODE_CALLSITE:
-    case NODE_UNARY_EXPRESSION:
-    case NODE_TYPEALIAS:
-    case NODE_TYPE_SPECIFIER:
-    case NODE_TYPE_SPECIFIER_LIST:
-    case NODE_POINTER:
-    case NODE_FLOATING_POINT:
-    case NODE_INTEGER:
-    case NODE_STRING:
-    case NODE_ENUMERATOR:
-    case NODE_VARIANT:
-    case NODE_STRUCT:
-    default:
-        goto errinval;
-    }
-    return 0;
-errinval:
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-        node->lineno, "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-        node_type(node), node_type(node->parent));
-    return -EINVAL; /* Incoherent structure */
-}
-
-static int ctf_visitor_field_class_specifier(int depth, struct ctf_node *node,
-                                             struct meta_log_config *log_cfg)
-{
-    switch (node->parent->type) {
-    case NODE_TYPE_SPECIFIER_LIST:
-        break; /* OK */
-
-    case NODE_CTF_EXPRESSION:
-    case NODE_TYPE_DECLARATOR:
-    case NODE_TYPEDEF:
-    case NODE_TYPEALIAS_TARGET:
-    case NODE_TYPEALIAS_ALIAS:
-    case NODE_ENUM:
-    case NODE_STRUCT_OR_VARIANT_DECLARATION:
-    case NODE_ROOT:
-    case NODE_EVENT:
-    case NODE_STREAM:
-    case NODE_ENV:
-    case NODE_TRACE:
-    case NODE_CLOCK:
-    case NODE_CALLSITE:
-    case NODE_UNARY_EXPRESSION:
-    case NODE_TYPEALIAS:
-    case NODE_TYPE_SPECIFIER:
-    case NODE_POINTER:
-    case NODE_FLOATING_POINT:
-    case NODE_INTEGER:
-    case NODE_STRING:
-    case NODE_ENUMERATOR:
-    case NODE_VARIANT:
-    case NODE_STRUCT:
-    default:
-        goto errinval;
-    }
-    return 0;
-errinval:
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-        node->lineno, "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-        node_type(node), node_type(node->parent));
-    return -EINVAL; /* Incoherent structure */
-}
-
-static int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node,
-                                              struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-    struct ctf_node *iter;
-
-    depth++;
-
-    switch (node->parent->type) {
-    case NODE_TYPE_DECLARATOR:
-        /*
-         * A nested field class declarator is not allowed to
-         * contain pointers.
-         */
-        if (!bt_list_empty(&node->u.field_class_declarator.pointers))
-            goto errperm;
-        break; /* OK */
-    case NODE_TYPEALIAS_TARGET:
-        break; /* OK */
-    case NODE_TYPEALIAS_ALIAS:
-        /*
-         * Only accept alias name containing:
-         * - identifier
-         * - identifier *   (any number of pointers)
-         * NOT accepting alias names containing [] (would otherwise
-         * cause semantic clash for later declarations of
-         * arrays/sequences of elements, where elements could be
-         * arrays/sequences themselves (if allowed in field class alias).
-         * NOT accepting alias with identifier. The declarator should
-         * be either empty or contain pointer(s).
-         */
-        if (node->u.field_class_declarator.type == TYPEDEC_NESTED)
-            goto errperm;
-        bt_list_for_each_entry (iter,
-                                &node->parent->u.field_class_alias_name.field_class_specifier_list
-                                     ->u.field_class_specifier_list.head,
-                                siblings) {
-            switch (iter->u.field_class_specifier.type) {
-            case TYPESPEC_FLOATING_POINT:
-            case TYPESPEC_INTEGER:
-            case TYPESPEC_STRING:
-            case TYPESPEC_STRUCT:
-            case TYPESPEC_VARIANT:
-            case TYPESPEC_ENUM:
-                if (bt_list_empty(&node->u.field_class_declarator.pointers))
-                    goto errperm;
-                break;
-            default:
-                break;
-            }
-        }
-        if (node->u.field_class_declarator.type == TYPEDEC_ID &&
-            node->u.field_class_declarator.u.id)
-            goto errperm;
-        break; /* OK */
-    case NODE_TYPEDEF:
-    case NODE_STRUCT_OR_VARIANT_DECLARATION:
-        break; /* OK */
-
-    case NODE_ROOT:
-    case NODE_EVENT:
-    case NODE_STREAM:
-    case NODE_ENV:
-    case NODE_TRACE:
-    case NODE_CLOCK:
-    case NODE_CALLSITE:
-    case NODE_CTF_EXPRESSION:
-    case NODE_UNARY_EXPRESSION:
-    case NODE_TYPEALIAS:
-    case NODE_TYPE_SPECIFIER:
-    case NODE_POINTER:
-    case NODE_FLOATING_POINT:
-    case NODE_INTEGER:
-    case NODE_STRING:
-    case NODE_ENUMERATOR:
-    case NODE_ENUM:
-    case NODE_VARIANT:
-    case NODE_STRUCT:
-    default:
-        goto errinval;
-    }
-
-    bt_list_for_each_entry (iter, &node->u.field_class_declarator.pointers, siblings) {
-        ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-        if (ret)
-            return ret;
-    }
-
-    switch (node->u.field_class_declarator.type) {
-    case TYPEDEC_ID:
-        break;
-    case TYPEDEC_NESTED:
-    {
-        if (node->u.field_class_declarator.u.nested.field_class_declarator) {
-            ret = _ctf_visitor_semantic_check(
-                depth + 1, node->u.field_class_declarator.u.nested.field_class_declarator, log_cfg);
-            if (ret)
-                return ret;
-        }
-        if (!node->u.field_class_declarator.u.nested.abstract_array) {
-            bt_list_for_each_entry (iter, &node->u.field_class_declarator.u.nested.length,
-                                    siblings) {
-                if (iter->type != NODE_UNARY_EXPRESSION) {
-                    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                        node->lineno, "Expecting unary expression as length: node-type=%s",
-                        node_type(iter));
-                    return -EINVAL;
-                }
-                ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-                if (ret)
-                    return ret;
-            }
-        } else {
-            if (node->parent->type == NODE_TYPEALIAS_TARGET) {
-                _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                    node->lineno,
-                    "Abstract array declarator not permitted as target of field class alias.");
-                return -EINVAL;
-            }
-        }
-        if (node->u.field_class_declarator.bitfield_len) {
-            ret = _ctf_visitor_semantic_check(depth + 1,
-                                              node->u.field_class_declarator.bitfield_len, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    }
-    case TYPEDEC_UNKNOWN:
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno, "Unknown field class declarator: type=%d",
-                                          node->u.field_class_declarator.type);
-        return -EINVAL;
-    }
-    depth--;
-    return 0;
-
-errinval:
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-        node->lineno, "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-        node_type(node), node_type(node->parent));
-    return -EINVAL; /* Incoherent structure */
-
-errperm:
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                      "Semantic error: node-type=%s, parent-node-type=%s",
-                                      node_type(node), node_type(node->parent));
-    return -EPERM; /* Structure not allowed */
-}
-
-static int _ctf_visitor_semantic_check(int depth, struct ctf_node *node,
-                                       struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-    struct ctf_node *iter;
-
-    if (node->visited)
-        return 0;
-
-    switch (node->type) {
-    case NODE_ROOT:
-        bt_list_for_each_entry (iter, &node->u.root.declaration_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.root.trace, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.root.stream, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.root.event, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-
-    case NODE_EVENT:
-        switch (node->parent->type) {
-        case NODE_ROOT:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-
-        bt_list_for_each_entry (iter, &node->u.event.declaration_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_STREAM:
-        switch (node->parent->type) {
-        case NODE_ROOT:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-
-        bt_list_for_each_entry (iter, &node->u.stream.declaration_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_ENV:
-        switch (node->parent->type) {
-        case NODE_ROOT:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-
-        bt_list_for_each_entry (iter, &node->u.env.declaration_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_TRACE:
-        switch (node->parent->type) {
-        case NODE_ROOT:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-
-        bt_list_for_each_entry (iter, &node->u.trace.declaration_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_CLOCK:
-        switch (node->parent->type) {
-        case NODE_ROOT:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-
-        bt_list_for_each_entry (iter, &node->u.clock.declaration_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_CALLSITE:
-        switch (node->parent->type) {
-        case NODE_ROOT:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-
-        bt_list_for_each_entry (iter, &node->u.callsite.declaration_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-
-    case NODE_CTF_EXPRESSION:
-        switch (node->parent->type) {
-        case NODE_ROOT:
-        case NODE_EVENT:
-        case NODE_STREAM:
-        case NODE_ENV:
-        case NODE_TRACE:
-        case NODE_CLOCK:
-        case NODE_CALLSITE:
-        case NODE_FLOATING_POINT:
-        case NODE_INTEGER:
-        case NODE_STRING:
-            break; /* OK */
-
-        case NODE_CTF_EXPRESSION:
-        case NODE_UNARY_EXPRESSION:
-        case NODE_TYPEDEF:
-        case NODE_TYPEALIAS_TARGET:
-        case NODE_TYPEALIAS_ALIAS:
-        case NODE_STRUCT_OR_VARIANT_DECLARATION:
-        case NODE_TYPEALIAS:
-        case NODE_TYPE_SPECIFIER:
-        case NODE_TYPE_SPECIFIER_LIST:
-        case NODE_POINTER:
-        case NODE_TYPE_DECLARATOR:
-        case NODE_ENUMERATOR:
-        case NODE_ENUM:
-        case NODE_VARIANT:
-        case NODE_STRUCT:
-        default:
-            goto errinval;
-        }
-
-        depth++;
-        bt_list_for_each_entry (iter, &node->u.ctf_expression.left, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        bt_list_for_each_entry (iter, &node->u.ctf_expression.right, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        depth--;
-        break;
-    case NODE_UNARY_EXPRESSION:
-        return ctf_visitor_unary_expression(depth, node, log_cfg);
-
-    case NODE_TYPEDEF:
-        switch (node->parent->type) {
-        case NODE_ROOT:
-        case NODE_EVENT:
-        case NODE_STREAM:
-        case NODE_TRACE:
-        case NODE_VARIANT:
-        case NODE_STRUCT:
-            break; /* OK */
-
-        case NODE_CTF_EXPRESSION:
-        case NODE_UNARY_EXPRESSION:
-        case NODE_TYPEDEF:
-        case NODE_TYPEALIAS_TARGET:
-        case NODE_TYPEALIAS_ALIAS:
-        case NODE_TYPEALIAS:
-        case NODE_STRUCT_OR_VARIANT_DECLARATION:
-        case NODE_TYPE_SPECIFIER:
-        case NODE_TYPE_SPECIFIER_LIST:
-        case NODE_POINTER:
-        case NODE_TYPE_DECLARATOR:
-        case NODE_FLOATING_POINT:
-        case NODE_INTEGER:
-        case NODE_STRING:
-        case NODE_ENUMERATOR:
-        case NODE_ENUM:
-        case NODE_CLOCK:
-        case NODE_CALLSITE:
-        case NODE_ENV:
-        default:
-            goto errinval;
-        }
-
-        depth++;
-        ret = _ctf_visitor_semantic_check(
-            depth + 1, node->u.field_class_def.field_class_specifier_list, log_cfg);
-        if (ret)
-            return ret;
-        bt_list_for_each_entry (iter, &node->u.field_class_def.field_class_declarators, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        depth--;
-        break;
-    case NODE_TYPEALIAS_TARGET:
-    {
-        int nr_declarators;
-
-        switch (node->parent->type) {
-        case NODE_TYPEALIAS:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-
-        depth++;
-        ret = _ctf_visitor_semantic_check(
-            depth + 1, node->u.field_class_alias_target.field_class_specifier_list, log_cfg);
-        if (ret)
-            return ret;
-        nr_declarators = 0;
-        bt_list_for_each_entry (iter, &node->u.field_class_alias_target.field_class_declarators,
-                                siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-            nr_declarators++;
-        }
-        if (nr_declarators > 1) {
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                node->lineno,
-                "Too many declarators in field class alias's name (maximum is 1): count=%d",
-                nr_declarators);
-            return -EINVAL;
-        }
-        depth--;
-        break;
-    }
-    case NODE_TYPEALIAS_ALIAS:
-    {
-        int nr_declarators;
-
-        switch (node->parent->type) {
-        case NODE_TYPEALIAS:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-
-        depth++;
-        ret = _ctf_visitor_semantic_check(
-            depth + 1, node->u.field_class_alias_name.field_class_specifier_list, log_cfg);
-        if (ret)
-            return ret;
-        nr_declarators = 0;
-        bt_list_for_each_entry (iter, &node->u.field_class_alias_name.field_class_declarators,
-                                siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-            nr_declarators++;
-        }
-        if (nr_declarators > 1) {
-            _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                node->lineno,
-                "Too many declarators in field class alias's name (maximum is 1): count=%d",
-                nr_declarators);
-            return -EINVAL;
-        }
-        depth--;
-        break;
-    }
-    case NODE_TYPEALIAS:
-        switch (node->parent->type) {
-        case NODE_ROOT:
-        case NODE_EVENT:
-        case NODE_STREAM:
-        case NODE_TRACE:
-        case NODE_VARIANT:
-        case NODE_STRUCT:
-            break; /* OK */
-
-        case NODE_CTF_EXPRESSION:
-        case NODE_UNARY_EXPRESSION:
-        case NODE_TYPEDEF:
-        case NODE_TYPEALIAS_TARGET:
-        case NODE_TYPEALIAS_ALIAS:
-        case NODE_TYPEALIAS:
-        case NODE_STRUCT_OR_VARIANT_DECLARATION:
-        case NODE_TYPE_SPECIFIER:
-        case NODE_TYPE_SPECIFIER_LIST:
-        case NODE_POINTER:
-        case NODE_TYPE_DECLARATOR:
-        case NODE_FLOATING_POINT:
-        case NODE_INTEGER:
-        case NODE_STRING:
-        case NODE_ENUMERATOR:
-        case NODE_ENUM:
-        case NODE_CLOCK:
-        case NODE_CALLSITE:
-        case NODE_ENV:
-        default:
-            goto errinval;
-        }
-
-        ret = _ctf_visitor_semantic_check(depth + 1, node->u.field_class_alias.target, log_cfg);
-        if (ret)
-            return ret;
-        ret = _ctf_visitor_semantic_check(depth + 1, node->u.field_class_alias.alias, log_cfg);
-        if (ret)
-            return ret;
-        break;
-
-    case NODE_TYPE_SPECIFIER_LIST:
-        ret = ctf_visitor_field_class_specifier_list(depth, node, log_cfg);
-        if (ret)
-            return ret;
-        break;
-    case NODE_TYPE_SPECIFIER:
-        ret = ctf_visitor_field_class_specifier(depth, node, log_cfg);
-        if (ret)
-            return ret;
-        break;
-    case NODE_POINTER:
-        switch (node->parent->type) {
-        case NODE_TYPE_DECLARATOR:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-        break;
-    case NODE_TYPE_DECLARATOR:
-        ret = ctf_visitor_field_class_declarator(depth, node, log_cfg);
-        if (ret)
-            return ret;
-        break;
-
-    case NODE_FLOATING_POINT:
-        switch (node->parent->type) {
-        case NODE_TYPE_SPECIFIER:
-            break; /* OK */
-        default:
-            goto errinval;
-
-        case NODE_UNARY_EXPRESSION:
-            goto errperm;
-        }
-        bt_list_for_each_entry (iter, &node->u.floating_point.expressions, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_INTEGER:
-        switch (node->parent->type) {
-        case NODE_TYPE_SPECIFIER:
-            break; /* OK */
-        default:
-            goto errinval;
-        }
-
-        bt_list_for_each_entry (iter, &node->u.integer.expressions, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_STRING:
-        switch (node->parent->type) {
-        case NODE_TYPE_SPECIFIER:
-            break; /* OK */
-        default:
-            goto errinval;
-
-        case NODE_UNARY_EXPRESSION:
-            goto errperm;
-        }
-
-        bt_list_for_each_entry (iter, &node->u.string.expressions, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_ENUMERATOR:
-        switch (node->parent->type) {
-        case NODE_ENUM:
-            break;
-        default:
-            goto errinval;
-        }
-        /*
-         * Enumerators are only allows to contain:
-         *    numeric unary expression
-         * or num. unary exp. ... num. unary exp
-         */
-        {
-            int count = 0;
-
-            bt_list_for_each_entry (iter, &node->u.enumerator.values, siblings) {
-                switch (count++) {
-                case 0:
-                    if (iter->type != NODE_UNARY_EXPRESSION ||
-                        (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT &&
-                         iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) ||
-                        iter->u.unary_expression.link != UNARY_LINK_UNKNOWN) {
-                        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                            iter->lineno, "First unary expression of enumerator is unexpected.");
-                        goto errperm;
-                    }
-                    break;
-                case 1:
-                    if (iter->type != NODE_UNARY_EXPRESSION ||
-                        (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT &&
-                         iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) ||
-                        iter->u.unary_expression.link != UNARY_DOTDOTDOT) {
-                        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-                            iter->lineno, "Second unary expression of enumerator is unexpected.");
-                        goto errperm;
-                    }
-                    break;
-                default:
-                    goto errperm;
-                }
-            }
-        }
-
-        bt_list_for_each_entry (iter, &node->u.enumerator.values, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_ENUM:
-        switch (node->parent->type) {
-        case NODE_TYPE_SPECIFIER:
-            break; /* OK */
-        default:
-            goto errinval;
-
-        case NODE_UNARY_EXPRESSION:
-            goto errperm;
-        }
-
-        depth++;
-        ret = _ctf_visitor_semantic_check(depth + 1, node->u._enum.container_field_class, log_cfg);
-        if (ret)
-            return ret;
-
-        bt_list_for_each_entry (iter, &node->u._enum.enumerator_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        depth--;
-        break;
-    case NODE_STRUCT_OR_VARIANT_DECLARATION:
-        switch (node->parent->type) {
-        case NODE_STRUCT:
-        case NODE_VARIANT:
-            break;
-        default:
-            goto errinval;
-        }
-        ret = _ctf_visitor_semantic_check(
-            depth + 1, node->u.struct_or_variant_declaration.field_class_specifier_list, log_cfg);
-        if (ret)
-            return ret;
-        bt_list_for_each_entry (
-            iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-    case NODE_VARIANT:
-        switch (node->parent->type) {
-        case NODE_TYPE_SPECIFIER:
-            break; /* OK */
-        default:
-            goto errinval;
-
-        case NODE_UNARY_EXPRESSION:
-            goto errperm;
-        }
-        bt_list_for_each_entry (iter, &node->u.variant.declaration_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-
-    case NODE_STRUCT:
-        switch (node->parent->type) {
-        case NODE_TYPE_SPECIFIER:
-            break; /* OK */
-        default:
-            goto errinval;
-
-        case NODE_UNARY_EXPRESSION:
-            goto errperm;
-        }
-        bt_list_for_each_entry (iter, &node->u._struct.declaration_list, siblings) {
-            ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-            if (ret)
-                return ret;
-        }
-        break;
-
-    case NODE_UNKNOWN:
-    default:
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno, "Unknown node type: type=%d", node->type);
-        return -EINVAL;
-    }
-    return ret;
-
-errinval:
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(
-        node->lineno, "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-        node_type(node), node_type(node->parent));
-    return -EINVAL; /* Incoherent structure */
-
-errperm:
-    _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                      "Semantic error: node-type=%s, parent-node-type=%s",
-                                      node_type(node), node_type(node->parent));
-    return -EPERM; /* Structure not allowed */
-}
-
-int ctf_visitor_semantic_check(int depth, struct ctf_node *node, struct meta_log_config *log_cfg)
-{
-    int ret = 0;
-
-    /*
-     * First make sure we create the parent links for all children. Let's
-     * take the safe route and recreate them at each validation, just in
-     * case the structure has changed.
-     */
-    ret = ctf_visitor_parent_links(depth, node, log_cfg);
-    if (ret) {
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                          "Cannot create parent links in metadata's AST: "
-                                          "ret=%d",
-                                          ret);
-        goto end;
-    }
-
-    ret = _ctf_visitor_semantic_check(depth, node, log_cfg);
-    if (ret) {
-        _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                          "Cannot check metadata's AST semantics: "
-                                          "ret=%d",
-                                          ret);
-        goto end;
-    }
-
-end:
-    return ret;
-}
diff --git a/src/plugins/ctf/common/msg-iter/Makefile.am b/src/plugins/ctf/common/msg-iter/Makefile.am
deleted file mode 100644 (file)
index d963df7..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libctf-msg-iter.la
-
-libctf_msg_iter_la_SOURCES = \
-       msg-iter.cpp \
-       msg-iter.hpp
diff --git a/src/plugins/ctf/common/msg-iter/msg-iter.cpp b/src/plugins/ctf/common/msg-iter/msg-iter.cpp
deleted file mode 100644 (file)
index e419072..0000000
+++ /dev/null
@@ -1,3000 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2015-2018 EfficiOS Inc. and Linux Foundation
- * Copyright (c) 2015-2018 Philippe Proulx <pproulx@efficios.com>
- *
- * Babeltrace - CTF message iterator
- */
-
-#define BT_COMP_LOG_SELF_COMP (msg_it->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (msg_it->log_level)
-#define BT_LOG_TAG            "PLUGIN/CTF/MSG-ITER"
-#include "logging/comp-logging.h"
-
-#include <stdint.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include "common/assert.h"
-#include <string.h>
-#include <babeltrace2/babeltrace.h>
-#include "common/common.h"
-#include <glib.h>
-#include <stdlib.h>
-
-#include "msg-iter.hpp"
-#include "../bfcr/bfcr.hpp"
-
-struct ctf_msg_iter;
-
-/* A visit stack entry */
-struct stack_entry
-{
-    /*
-     * Current base field, one of:
-     *
-     *   * string
-     *   * structure
-     *   * array
-     *   * sequence
-     *   * variant
-     *
-     * Field is borrowed.
-     */
-    bt_field *base;
-
-    /* Index of next field to set */
-    size_t index;
-};
-
-struct ctf_msg_iter;
-
-/* Visit stack */
-struct stack
-{
-    struct ctf_msg_iter *msg_it;
-
-    /* Entries (struct stack_entry) */
-    GArray *entries;
-
-    /* Number of active entries */
-    size_t size;
-};
-
-/* State */
-enum state
-{
-    STATE_INIT,
-    STATE_SWITCH_PACKET,
-    STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN,
-    STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE,
-    STATE_AFTER_TRACE_PACKET_HEADER,
-    STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN,
-    STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
-    STATE_AFTER_STREAM_PACKET_CONTEXT,
-    STATE_EMIT_MSG_STREAM_BEGINNING,
-    STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS,
-    STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS,
-    STATE_EMIT_MSG_DISCARDED_EVENTS,
-    STATE_EMIT_MSG_DISCARDED_PACKETS,
-    STATE_EMIT_MSG_PACKET_BEGINNING,
-    STATE_DSCOPE_EVENT_HEADER_BEGIN,
-    STATE_DSCOPE_EVENT_HEADER_CONTINUE,
-    STATE_AFTER_EVENT_HEADER,
-    STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN,
-    STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE,
-    STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN,
-    STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE,
-    STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
-    STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE,
-    STATE_EMIT_MSG_EVENT,
-    STATE_EMIT_QUEUED_MSG_EVENT,
-    STATE_SKIP_PACKET_PADDING,
-    STATE_EMIT_MSG_PACKET_END_MULTI,
-    STATE_EMIT_MSG_PACKET_END_SINGLE,
-    STATE_EMIT_QUEUED_MSG_PACKET_END,
-    STATE_CHECK_EMIT_MSG_STREAM_END,
-    STATE_EMIT_MSG_STREAM_END,
-    STATE_DONE,
-};
-
-struct end_of_packet_snapshots
-{
-    uint64_t discarded_events;
-    uint64_t packets;
-    uint64_t beginning_clock;
-    uint64_t end_clock;
-};
-
-/* CTF message iterator */
-struct ctf_msg_iter
-{
-    /* Visit stack */
-    struct stack *stack;
-
-    /* Current message iterator to create messages (weak) */
-    bt_self_message_iterator *self_msg_iter;
-
-    /*
-     * True if library objects are unavailable during the decoding and
-     * should not be created/used.
-     */
-    bool dry_run;
-
-    /*
-     * Current dynamic scope field pointer.
-     *
-     * This is set by read_dscope_begin_state() and contains the
-     * value of one of the pointers in `dscopes` below.
-     */
-    bt_field *cur_dscope_field;
-
-    /*
-     * True if we're done filling a string field from a text
-     * array/sequence payload.
-     */
-    bool done_filling_string;
-
-    /* Trace and classes */
-    /* True to set IR fields */
-    bool set_ir_fields;
-
-    struct
-    {
-        struct ctf_trace_class *tc;
-        struct ctf_stream_class *sc;
-        struct ctf_event_class *ec;
-    } meta;
-
-    /* Current packet (NULL if not created yet) */
-    bt_packet *packet;
-
-    /* Current stream (NULL if not set yet) */
-    bt_stream *stream;
-
-    /* Current event (NULL if not created yet) */
-    bt_event *event;
-
-    /* Current event message (NULL if not created yet) */
-    bt_message *event_msg;
-
-    /*
-     * True if we need to emit a packet beginning message before we emit
-     * the next event message or the packet end message.
-     */
-    bool emit_delayed_packet_beginning_msg;
-
-    /*
-     * True if this is the first packet we are reading, and therefore if we
-     * should emit a stream beginning message.
-     */
-    bool emit_stream_beginning_message;
-
-    /*
-     * True if we need to emit a stream end message at the end of the
-     * current stream. A live stream may never receive any data and thus
-     * never send a stream beginning message which removes the need to emit
-     * a stream end message.
-     */
-    bool emit_stream_end_message;
-
-    /* Database of current dynamic scopes */
-    struct
-    {
-        bt_field *stream_packet_context;
-        bt_field *event_common_context;
-        bt_field *event_spec_context;
-        bt_field *event_payload;
-    } dscopes;
-
-    /* Current state */
-    enum state state;
-
-    /* Current medium buffer data */
-    struct
-    {
-        /* Last address provided by medium */
-        const uint8_t *addr;
-
-        /* Buffer size provided by medium (bytes) */
-        size_t sz;
-
-        /* Offset within whole packet of addr (bits) */
-        size_t packet_offset;
-
-        /* Current position from addr (bits) */
-        size_t at;
-
-        /* Position of the last event header from addr (bits) */
-        size_t last_eh_at;
-    } buf;
-
-    /* Binary type reader */
-    struct bt_bfcr *bfcr;
-
-    /* Current medium data */
-    struct
-    {
-        struct ctf_msg_iter_medium_ops medops;
-        size_t max_request_sz;
-        void *data;
-    } medium;
-
-    /* Current packet size (bits) (-1 if unknown) */
-    int64_t cur_exp_packet_total_size;
-
-    /* Current content size (bits) (-1 if unknown) */
-    int64_t cur_exp_packet_content_size;
-
-    /* Current stream class ID */
-    int64_t cur_stream_class_id;
-
-    /* Current event class ID */
-    int64_t cur_event_class_id;
-
-    /* Current data stream ID */
-    int64_t cur_data_stream_id;
-
-    /*
-     * Offset, in the underlying media, of the current packet's
-     * start (-1 if unknown).
-     */
-    off_t cur_packet_offset;
-
-    /* Default clock's current value */
-    uint64_t default_clock_snapshot;
-
-    /* End of current packet snapshots */
-    struct end_of_packet_snapshots snapshots;
-
-    /* End of previous packet snapshots */
-    struct end_of_packet_snapshots prev_packet_snapshots;
-
-    /* Stored values (for sequence lengths, variant tags) */
-    GArray *stored_values;
-
-    /* Iterator's current log level */
-    bt_logging_level log_level;
-
-    /* Iterator's owning self component, or `NULL` if none (query) */
-    bt_self_component *self_comp;
-};
-
-static inline const char *state_string(enum state state)
-{
-    switch (state) {
-    case STATE_INIT:
-        return "INIT";
-    case STATE_SWITCH_PACKET:
-        return "SWITCH_PACKET";
-    case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
-        return "DSCOPE_TRACE_PACKET_HEADER_BEGIN";
-    case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
-        return "DSCOPE_TRACE_PACKET_HEADER_CONTINUE";
-    case STATE_AFTER_TRACE_PACKET_HEADER:
-        return "AFTER_TRACE_PACKET_HEADER";
-    case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
-        return "DSCOPE_STREAM_PACKET_CONTEXT_BEGIN";
-    case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
-        return "DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE";
-    case STATE_AFTER_STREAM_PACKET_CONTEXT:
-        return "AFTER_STREAM_PACKET_CONTEXT";
-    case STATE_EMIT_MSG_STREAM_BEGINNING:
-        return "EMIT_MSG_STREAM_BEGINNING";
-    case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
-        return "CHECK_EMIT_MSG_DISCARDED_EVENTS";
-    case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
-        return "CHECK_EMIT_MSG_DISCARDED_PACKETS";
-    case STATE_EMIT_MSG_PACKET_BEGINNING:
-        return "EMIT_MSG_PACKET_BEGINNING";
-    case STATE_EMIT_MSG_DISCARDED_EVENTS:
-        return "EMIT_MSG_DISCARDED_EVENTS";
-    case STATE_EMIT_MSG_DISCARDED_PACKETS:
-        return "EMIT_MSG_DISCARDED_PACKETS";
-    case STATE_DSCOPE_EVENT_HEADER_BEGIN:
-        return "DSCOPE_EVENT_HEADER_BEGIN";
-    case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
-        return "DSCOPE_EVENT_HEADER_CONTINUE";
-    case STATE_AFTER_EVENT_HEADER:
-        return "AFTER_EVENT_HEADER";
-    case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
-        return "DSCOPE_EVENT_COMMON_CONTEXT_BEGIN";
-    case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
-        return "DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE";
-    case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
-        return "DSCOPE_EVENT_SPEC_CONTEXT_BEGIN";
-    case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
-        return "DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE";
-    case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
-        return "DSCOPE_EVENT_PAYLOAD_BEGIN";
-    case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
-        return "DSCOPE_EVENT_PAYLOAD_CONTINUE";
-    case STATE_EMIT_MSG_EVENT:
-        return "EMIT_MSG_EVENT";
-    case STATE_EMIT_QUEUED_MSG_EVENT:
-        return "EMIT_QUEUED_MSG_EVENT";
-    case STATE_SKIP_PACKET_PADDING:
-        return "SKIP_PACKET_PADDING";
-    case STATE_EMIT_MSG_PACKET_END_MULTI:
-        return "EMIT_MSG_PACKET_END_MULTI";
-    case STATE_EMIT_MSG_PACKET_END_SINGLE:
-        return "EMIT_MSG_PACKET_END_SINGLE";
-    case STATE_EMIT_QUEUED_MSG_PACKET_END:
-        return "EMIT_QUEUED_MSG_PACKET_END";
-    case STATE_CHECK_EMIT_MSG_STREAM_END:
-        return "CHECK_EMIT_MSG_STREAM_END";
-    case STATE_EMIT_MSG_STREAM_END:
-        return "EMIT_MSG_STREAM_END";
-    case STATE_DONE:
-        return "DONE";
-    }
-
-    bt_common_abort();
-}
-
-static struct stack *stack_new(struct ctf_msg_iter *msg_it)
-{
-    bt_self_component *self_comp = msg_it->self_comp;
-    struct stack *stack = NULL;
-
-    stack = g_new0(struct stack, 1);
-    if (!stack) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate one stack.");
-        goto error;
-    }
-
-    stack->msg_it = msg_it;
-    stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry));
-    if (!stack->entries) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate a GArray.");
-        goto error;
-    }
-
-    BT_COMP_LOGD("Created stack: msg-it-addr=%p, stack-addr=%p", msg_it, stack);
-    goto end;
-
-error:
-    g_free(stack);
-    stack = NULL;
-
-end:
-    return stack;
-}
-
-static void stack_destroy(struct stack *stack)
-{
-    struct ctf_msg_iter *msg_it;
-
-    BT_ASSERT_DBG(stack);
-    msg_it = stack->msg_it;
-    BT_COMP_LOGD("Destroying stack: addr=%p", stack);
-
-    if (stack->entries) {
-        g_array_free(stack->entries, TRUE);
-    }
-
-    g_free(stack);
-}
-
-static void stack_push(struct stack *stack, bt_field *base)
-{
-    struct stack_entry *entry;
-    struct ctf_msg_iter *msg_it;
-
-    BT_ASSERT_DBG(stack);
-    msg_it = stack->msg_it;
-    BT_ASSERT_DBG(base);
-    BT_COMP_LOGT("Pushing base field on stack: stack-addr=%p, "
-                 "stack-size-before=%zu, stack-size-after=%zu",
-                 stack, stack->size, stack->size + 1);
-
-    if (stack->entries->len == stack->size) {
-        g_array_set_size(stack->entries, stack->size + 1);
-    }
-
-    entry = &g_array_index(stack->entries, struct stack_entry, stack->size);
-    entry->base = base;
-    entry->index = 0;
-    stack->size++;
-}
-
-static inline unsigned int stack_size(struct stack *stack)
-{
-    BT_ASSERT_DBG(stack);
-    return stack->size;
-}
-
-static void stack_pop(struct stack *stack)
-{
-    struct ctf_msg_iter *msg_it;
-
-    BT_ASSERT_DBG(stack);
-    BT_ASSERT_DBG(stack_size(stack));
-    msg_it = stack->msg_it;
-    BT_COMP_LOGT("Popping from stack: "
-                 "stack-addr=%p, stack-size-before=%zu, stack-size-after=%zu",
-                 stack, stack->size, stack->size - 1);
-    stack->size--;
-}
-
-static inline struct stack_entry *stack_top(struct stack *stack)
-{
-    BT_ASSERT_DBG(stack);
-    BT_ASSERT_DBG(stack_size(stack));
-    return &g_array_index(stack->entries, struct stack_entry, stack->size - 1);
-}
-
-static inline bool stack_empty(struct stack *stack)
-{
-    return stack_size(stack) == 0;
-}
-
-static void stack_clear(struct stack *stack)
-{
-    BT_ASSERT_DBG(stack);
-    stack->size = 0;
-}
-
-static inline enum ctf_msg_iter_status
-msg_iter_status_from_m_status(enum ctf_msg_iter_medium_status m_status)
-{
-    /* They are the same */
-    return (ctf_msg_iter_status) m_status;
-}
-
-static inline size_t buf_size_bits(struct ctf_msg_iter *msg_it)
-{
-    return msg_it->buf.sz * 8;
-}
-
-static inline size_t buf_available_bits(struct ctf_msg_iter *msg_it)
-{
-    return buf_size_bits(msg_it) - msg_it->buf.at;
-}
-
-static inline size_t packet_at(struct ctf_msg_iter *msg_it)
-{
-    return msg_it->buf.packet_offset + msg_it->buf.at;
-}
-
-static inline void buf_consume_bits(struct ctf_msg_iter *msg_it, size_t incr)
-{
-    BT_COMP_LOGT("Advancing cursor: msg-it-addr=%p, cur-before=%zu, cur-after=%zu", msg_it,
-                 msg_it->buf.at, msg_it->buf.at + incr);
-    msg_it->buf.at += incr;
-}
-
-static enum ctf_msg_iter_status request_medium_bytes(struct ctf_msg_iter *msg_it)
-{
-    bt_self_component *self_comp = msg_it->self_comp;
-    uint8_t *buffer_addr = NULL;
-    size_t buffer_sz = 0;
-    enum ctf_msg_iter_medium_status m_status;
-
-    BT_COMP_LOGD("Calling user function (request bytes): msg-it-addr=%p, "
-                 "request-size=%zu",
-                 msg_it, msg_it->medium.max_request_sz);
-    m_status = msg_it->medium.medops.request_bytes(msg_it->medium.max_request_sz, &buffer_addr,
-                                                   &buffer_sz, msg_it->medium.data);
-    BT_COMP_LOGD("User function returned: status=%s, buf-addr=%p, buf-size=%zu",
-                 ctf_msg_iter_medium_status_string(m_status), buffer_addr, buffer_sz);
-    if (m_status == CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-        BT_ASSERT(buffer_sz != 0);
-
-        /* New packet offset is old one + old size (in bits) */
-        msg_it->buf.packet_offset += buf_size_bits(msg_it);
-
-        /* Restart at the beginning of the new medium buffer */
-        msg_it->buf.at = 0;
-        msg_it->buf.last_eh_at = SIZE_MAX;
-
-        /* New medium buffer size */
-        msg_it->buf.sz = buffer_sz;
-
-        /* New medium buffer address */
-        msg_it->buf.addr = buffer_addr;
-
-        BT_COMP_LOGD("User function returned new bytes: "
-                     "packet-offset=%zu, cur=%zu, size=%zu, addr=%p",
-                     msg_it->buf.packet_offset, msg_it->buf.at, msg_it->buf.sz, msg_it->buf.addr);
-        BT_COMP_LOGT_MEM(buffer_addr, buffer_sz, "Returned bytes at %p:", buffer_addr);
-    } else if (m_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
-        /*
-         * User returned end of stream: validate that we're not
-         * in the middle of a packet header, packet context, or
-         * event.
-         */
-        if (msg_it->cur_exp_packet_total_size >= 0) {
-            if (packet_at(msg_it) == msg_it->cur_exp_packet_total_size) {
-                goto end;
-            }
-        } else {
-            if (packet_at(msg_it) == 0) {
-                goto end;
-            }
-
-            if (msg_it->buf.last_eh_at != SIZE_MAX && msg_it->buf.at == msg_it->buf.last_eh_at) {
-                goto end;
-            }
-        }
-
-        /* All other states are invalid */
-        BT_COMP_LOGE_APPEND_CAUSE(
-            self_comp,
-            "User function returned %s, but message iterator is in an unexpected state: "
-            "state=%s, cur-packet-size=%" PRId64 ", cur=%zu, "
-            "packet-cur=%zu, last-eh-at=%zu",
-            ctf_msg_iter_medium_status_string(m_status), state_string(msg_it->state),
-            msg_it->cur_exp_packet_total_size, msg_it->buf.at, packet_at(msg_it),
-            msg_it->buf.last_eh_at);
-        m_status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-    } else if (m_status < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "User function failed: "
-                                  "status=%s",
-                                  ctf_msg_iter_medium_status_string(m_status));
-    }
-
-end:
-    return msg_iter_status_from_m_status(m_status);
-}
-
-static inline enum ctf_msg_iter_status buf_ensure_available_bits(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-
-    if (G_UNLIKELY(buf_available_bits(msg_it) == 0)) {
-        /*
-         * This _cannot_ return CTF_MSG_ITER_STATUS_OK
-         * _and_ no bits.
-         */
-        status = request_medium_bytes(msg_it);
-    }
-
-    return status;
-}
-
-static enum ctf_msg_iter_status
-read_dscope_begin_state(struct ctf_msg_iter *msg_it, struct ctf_field_class *dscope_fc,
-                        enum state done_state, enum state continue_state, bt_field *dscope_field)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    enum bt_bfcr_status bfcr_status;
-    size_t consumed_bits;
-
-    msg_it->cur_dscope_field = dscope_field;
-    BT_COMP_LOGT("Starting BFCR: msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p", msg_it, msg_it->bfcr,
-                 dscope_fc);
-    consumed_bits = bt_bfcr_start(msg_it->bfcr, dscope_fc, msg_it->buf.addr, msg_it->buf.at,
-                                  packet_at(msg_it), msg_it->buf.sz, &bfcr_status);
-    BT_COMP_LOGT("BFCR consumed bits: size=%zu", consumed_bits);
-
-    switch (bfcr_status) {
-    case BT_BFCR_STATUS_OK:
-        /* Field class was read completely */
-        BT_COMP_LOGT_STR("Field was completely decoded.");
-        msg_it->state = done_state;
-        break;
-    case BT_BFCR_STATUS_EOF:
-        BT_COMP_LOGT_STR("BFCR needs more data to decode field completely.");
-        msg_it->state = continue_state;
-        break;
-    default:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "BFCR failed to start: msg-it-addr=%p, bfcr-addr=%p, "
-                                  "status=%s",
-                                  msg_it, msg_it->bfcr, bt_bfcr_status_string(bfcr_status));
-        status = CTF_MSG_ITER_STATUS_ERROR;
-        goto end;
-    }
-
-    /* Consume bits now since we know we're not in an error state */
-    buf_consume_bits(msg_it, consumed_bits);
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_dscope_continue_state(struct ctf_msg_iter *msg_it,
-                                                           enum state done_state)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    enum bt_bfcr_status bfcr_status;
-    size_t consumed_bits;
-
-    BT_COMP_LOGT("Continuing BFCR: msg-it-addr=%p, bfcr-addr=%p", msg_it, msg_it->bfcr);
-
-    status = buf_ensure_available_bits(msg_it);
-    if (status != CTF_MSG_ITER_STATUS_OK) {
-        if (status < 0) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Cannot ensure that buffer has at least one byte: "
-                                      "msg-addr=%p, status=%s",
-                                      msg_it, ctf_msg_iter_status_string(status));
-        } else {
-            BT_COMP_LOGT("Cannot ensure that buffer has at least one byte: "
-                         "msg-addr=%p, status=%s",
-                         msg_it, ctf_msg_iter_status_string(status));
-        }
-
-        goto end;
-    }
-
-    consumed_bits = bt_bfcr_continue(msg_it->bfcr, msg_it->buf.addr, msg_it->buf.sz, &bfcr_status);
-    BT_COMP_LOGT("BFCR consumed bits: size=%zu", consumed_bits);
-
-    switch (bfcr_status) {
-    case BT_BFCR_STATUS_OK:
-        /* Type was read completely. */
-        BT_COMP_LOGT_STR("Field was completely decoded.");
-        msg_it->state = done_state;
-        break;
-    case BT_BFCR_STATUS_EOF:
-        /* Stay in this continue state. */
-        BT_COMP_LOGT_STR("BFCR needs more data to decode field completely.");
-        break;
-    default:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "BFCR failed to continue: msg-it-addr=%p, bfcr-addr=%p, "
-                                  "status=%s",
-                                  msg_it, msg_it->bfcr, bt_bfcr_status_string(bfcr_status));
-        status = CTF_MSG_ITER_STATUS_ERROR;
-        goto end;
-    }
-
-    /* Consume bits now since we know we're not in an error state. */
-    buf_consume_bits(msg_it, consumed_bits);
-end:
-    return status;
-}
-
-static void release_event_dscopes(struct ctf_msg_iter *msg_it)
-{
-    msg_it->dscopes.event_common_context = NULL;
-    msg_it->dscopes.event_spec_context = NULL;
-    msg_it->dscopes.event_payload = NULL;
-}
-
-static void release_all_dscopes(struct ctf_msg_iter *msg_it)
-{
-    msg_it->dscopes.stream_packet_context = NULL;
-
-    release_event_dscopes(msg_it);
-}
-
-static enum ctf_msg_iter_status switch_packet_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status;
-    bt_self_component *self_comp = msg_it->self_comp;
-
-    /*
-     * We don't put the stream class here because we need to make
-     * sure that all the packets processed by the same message
-     * iterator refer to the same stream class (the first one).
-     */
-    BT_ASSERT(msg_it);
-
-    if (msg_it->cur_exp_packet_total_size != -1) {
-        msg_it->cur_packet_offset += msg_it->cur_exp_packet_total_size;
-    }
-
-    BT_COMP_LOGD("Switching packet: msg-it-addr=%p, cur=%zu, "
-                 "packet-offset=%" PRId64,
-                 msg_it, msg_it->buf.at, msg_it->cur_packet_offset);
-    stack_clear(msg_it->stack);
-    msg_it->meta.ec = NULL;
-    BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
-    BT_MESSAGE_PUT_REF_AND_RESET(msg_it->event_msg);
-    release_all_dscopes(msg_it);
-    msg_it->cur_dscope_field = NULL;
-
-    if (msg_it->medium.medops.switch_packet) {
-        enum ctf_msg_iter_medium_status medium_status;
-
-        medium_status = msg_it->medium.medops.switch_packet(msg_it->medium.data);
-        if (medium_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
-            /* No more packets. */
-            msg_it->state = STATE_CHECK_EMIT_MSG_STREAM_END;
-            status = CTF_MSG_ITER_STATUS_OK;
-            goto end;
-        } else if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-            status = msg_iter_status_from_m_status(medium_status);
-            goto end;
-        }
-
-        /*
-         * After the packet switch, the medium might want to give us a
-         * different buffer for the new packet.
-         */
-        status = request_medium_bytes(msg_it);
-        if (status != CTF_MSG_ITER_STATUS_OK) {
-            goto end;
-        }
-    }
-
-    /*
-     * Adjust current buffer so that addr points to the beginning of the new
-     * packet.
-     */
-    if (msg_it->buf.addr) {
-        size_t consumed_bytes = (size_t) (msg_it->buf.at / CHAR_BIT);
-
-        /* Packets are assumed to start on a byte frontier. */
-        if (msg_it->buf.at % CHAR_BIT) {
-            BT_COMP_LOGE_APPEND_CAUSE(
-                self_comp,
-                "Cannot switch packet: current position is not a multiple of 8: "
-                "msg-it-addr=%p, cur=%zu",
-                msg_it, msg_it->buf.at);
-            status = CTF_MSG_ITER_STATUS_ERROR;
-            goto end;
-        }
-
-        msg_it->buf.addr += consumed_bytes;
-        msg_it->buf.sz -= consumed_bytes;
-        msg_it->buf.at = 0;
-        msg_it->buf.packet_offset = 0;
-        BT_COMP_LOGD("Adjusted buffer: addr=%p, size=%zu", msg_it->buf.addr, msg_it->buf.sz);
-    }
-
-    msg_it->cur_exp_packet_content_size = -1;
-    msg_it->cur_exp_packet_total_size = -1;
-    msg_it->cur_stream_class_id = -1;
-    msg_it->cur_event_class_id = -1;
-    msg_it->cur_data_stream_id = -1;
-    msg_it->prev_packet_snapshots = msg_it->snapshots;
-    msg_it->snapshots.discarded_events = UINT64_C(-1);
-    msg_it->snapshots.packets = UINT64_C(-1);
-    msg_it->snapshots.beginning_clock = UINT64_C(-1);
-    msg_it->snapshots.end_clock = UINT64_C(-1);
-    msg_it->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN;
-
-    status = CTF_MSG_ITER_STATUS_OK;
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_packet_header_begin_state(struct ctf_msg_iter *msg_it)
-{
-    struct ctf_field_class *packet_header_fc = NULL;
-    bt_self_component *self_comp = msg_it->self_comp;
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-
-    /*
-     * Make sure at least one bit is available for this packet. An
-     * empty packet is impossible. If we reach the end of the medium
-     * at this point, then it's considered the end of the stream.
-     */
-    status = buf_ensure_available_bits(msg_it);
-    switch (status) {
-    case CTF_MSG_ITER_STATUS_OK:
-        break;
-    case CTF_MSG_ITER_STATUS_EOF:
-        status = CTF_MSG_ITER_STATUS_OK;
-        msg_it->state = STATE_CHECK_EMIT_MSG_STREAM_END;
-        goto end;
-    default:
-        goto end;
-    }
-
-    /* Packet header class is common to the whole trace class. */
-    packet_header_fc = msg_it->meta.tc->packet_header_fc;
-    if (!packet_header_fc) {
-        msg_it->state = STATE_AFTER_TRACE_PACKET_HEADER;
-        goto end;
-    }
-
-    msg_it->cur_stream_class_id = -1;
-    msg_it->cur_event_class_id = -1;
-    msg_it->cur_data_stream_id = -1;
-    BT_COMP_LOGD("Decoding packet header field: "
-                 "msg-it-addr=%p, trace-class-addr=%p, fc-addr=%p",
-                 msg_it, msg_it->meta.tc, packet_header_fc);
-    status = read_dscope_begin_state(msg_it, packet_header_fc, STATE_AFTER_TRACE_PACKET_HEADER,
-                                     STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, NULL);
-    if (status < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot decode packet header field: "
-                                  "msg-it-addr=%p, trace-class-addr=%p, "
-                                  "fc-addr=%p",
-                                  msg_it, msg_it->meta.tc, packet_header_fc);
-    }
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_packet_header_continue_state(struct ctf_msg_iter *msg_it)
-{
-    return read_dscope_continue_state(msg_it, STATE_AFTER_TRACE_PACKET_HEADER);
-}
-
-static inline enum ctf_msg_iter_status set_current_stream_class(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    struct ctf_stream_class *new_stream_class = NULL;
-
-    if (msg_it->cur_stream_class_id == -1) {
-        /*
-         * No current stream class ID field, therefore only one
-         * stream class.
-         */
-        if (msg_it->meta.tc->stream_classes->len != 1) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Need exactly one stream class since there's "
-                                      "no stream class ID field: "
-                                      "msg-it-addr=%p",
-                                      msg_it);
-            status = CTF_MSG_ITER_STATUS_ERROR;
-            goto end;
-        }
-
-        new_stream_class = (ctf_stream_class *) msg_it->meta.tc->stream_classes->pdata[0];
-        msg_it->cur_stream_class_id = new_stream_class->id;
-    }
-
-    new_stream_class =
-        ctf_trace_class_borrow_stream_class_by_id(msg_it->meta.tc, msg_it->cur_stream_class_id);
-    if (!new_stream_class) {
-        BT_COMP_LOGE_APPEND_CAUSE(
-            self_comp,
-            "No stream class with ID of stream class ID to use in trace class: "
-            "msg-it-addr=%p, stream-class-id=%" PRIu64 ", "
-            "trace-class-addr=%p",
-            msg_it, msg_it->cur_stream_class_id, msg_it->meta.tc);
-        status = CTF_MSG_ITER_STATUS_ERROR;
-        goto end;
-    }
-
-    if (msg_it->meta.sc) {
-        if (new_stream_class != msg_it->meta.sc) {
-            BT_COMP_LOGE_APPEND_CAUSE(
-                self_comp,
-                "Two packets refer to two different stream classes within the same packet sequence: "
-                "msg-it-addr=%p, prev-stream-class-addr=%p, "
-                "prev-stream-class-id=%" PRId64 ", "
-                "next-stream-class-addr=%p, "
-                "next-stream-class-id=%" PRId64 ", "
-                "trace-addr=%p",
-                msg_it, msg_it->meta.sc, msg_it->meta.sc->id, new_stream_class,
-                new_stream_class->id, msg_it->meta.tc);
-            status = CTF_MSG_ITER_STATUS_ERROR;
-            goto end;
-        }
-    } else {
-        msg_it->meta.sc = new_stream_class;
-    }
-
-    BT_COMP_LOGD("Set current stream class: "
-                 "msg-it-addr=%p, stream-class-addr=%p, "
-                 "stream-class-id=%" PRId64,
-                 msg_it, msg_it->meta.sc, msg_it->meta.sc->id);
-
-end:
-    return status;
-}
-
-static inline enum ctf_msg_iter_status set_current_stream(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    bt_stream *stream = NULL;
-
-    BT_COMP_LOGD("Calling user function (get stream): msg-it-addr=%p, "
-                 "stream-class-addr=%p, stream-class-id=%" PRId64,
-                 msg_it, msg_it->meta.sc, msg_it->meta.sc->id);
-    stream = msg_it->medium.medops.borrow_stream(msg_it->meta.sc->ir_sc, msg_it->cur_data_stream_id,
-                                                 msg_it->medium.data);
-    bt_stream_get_ref(stream);
-    BT_COMP_LOGD("User function returned: stream-addr=%p", stream);
-    if (!stream) {
-        BT_COMP_LOGE_APPEND_CAUSE(
-            self_comp,
-            "User function failed to return a stream object for the given stream class.");
-        status = CTF_MSG_ITER_STATUS_ERROR;
-        goto end;
-    }
-
-    if (msg_it->stream && stream != msg_it->stream) {
-        BT_COMP_LOGE_APPEND_CAUSE(
-            self_comp,
-            "User function returned a different stream than the previous one for the same sequence of packets.");
-        status = CTF_MSG_ITER_STATUS_ERROR;
-        goto end;
-    }
-
-    BT_STREAM_MOVE_REF(msg_it->stream, stream);
-
-end:
-    bt_stream_put_ref(stream);
-    return status;
-}
-
-static inline enum ctf_msg_iter_status set_current_packet(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    bt_packet *packet = NULL;
-
-    BT_COMP_LOGD("Creating packet from stream: "
-                 "msg-it-addr=%p, stream-addr=%p, "
-                 "stream-class-addr=%p, "
-                 "stream-class-id=%" PRId64,
-                 msg_it, msg_it->stream, msg_it->meta.sc, msg_it->meta.sc->id);
-
-    /* Create packet */
-    BT_ASSERT(msg_it->stream);
-    packet = bt_packet_create(msg_it->stream);
-    if (!packet) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot create packet from stream: "
-                                  "msg-it-addr=%p, stream-addr=%p, "
-                                  "stream-class-addr=%p, "
-                                  "stream-class-id=%" PRId64,
-                                  msg_it, msg_it->stream, msg_it->meta.sc, msg_it->meta.sc->id);
-        goto error;
-    }
-
-    goto end;
-
-error:
-    BT_PACKET_PUT_REF_AND_RESET(packet);
-    status = CTF_MSG_ITER_STATUS_ERROR;
-
-end:
-    BT_PACKET_MOVE_REF(msg_it->packet, packet);
-    return status;
-}
-
-static enum ctf_msg_iter_status after_packet_header_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status;
-
-    status = set_current_stream_class(msg_it);
-    if (status != CTF_MSG_ITER_STATUS_OK) {
-        goto end;
-    }
-
-    if (!msg_it->dry_run) {
-        status = set_current_stream(msg_it);
-        if (status != CTF_MSG_ITER_STATUS_OK) {
-            goto end;
-        }
-
-        status = set_current_packet(msg_it);
-        if (status != CTF_MSG_ITER_STATUS_OK) {
-            goto end;
-        }
-    }
-
-    msg_it->state = STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN;
-
-    status = CTF_MSG_ITER_STATUS_OK;
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_packet_context_begin_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    struct ctf_field_class *packet_context_fc;
-
-    BT_ASSERT(msg_it->meta.sc);
-    packet_context_fc = msg_it->meta.sc->packet_context_fc;
-    if (!packet_context_fc) {
-        BT_COMP_LOGD("No packet packet context field class in stream class: continuing: "
-                     "msg-it-addr=%p, stream-class-addr=%p, "
-                     "stream-class-id=%" PRId64,
-                     msg_it, msg_it->meta.sc, msg_it->meta.sc->id);
-        msg_it->state = STATE_AFTER_STREAM_PACKET_CONTEXT;
-        goto end;
-    }
-
-    if (packet_context_fc->in_ir && !msg_it->dry_run) {
-        BT_ASSERT(!msg_it->dscopes.stream_packet_context);
-        BT_ASSERT(msg_it->packet);
-        msg_it->dscopes.stream_packet_context = bt_packet_borrow_context_field(msg_it->packet);
-        BT_ASSERT(msg_it->dscopes.stream_packet_context);
-    }
-
-    BT_COMP_LOGD("Decoding packet context field: "
-                 "msg-it-addr=%p, stream-class-addr=%p, "
-                 "stream-class-id=%" PRId64 ", fc-addr=%p",
-                 msg_it, msg_it->meta.sc, msg_it->meta.sc->id, packet_context_fc);
-    status = read_dscope_begin_state(msg_it, packet_context_fc, STATE_AFTER_STREAM_PACKET_CONTEXT,
-                                     STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
-                                     msg_it->dscopes.stream_packet_context);
-    if (status < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot decode packet context field: "
-                                  "msg-it-addr=%p, stream-class-addr=%p, "
-                                  "stream-class-id=%" PRId64 ", fc-addr=%p",
-                                  msg_it, msg_it->meta.sc, msg_it->meta.sc->id, packet_context_fc);
-    }
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_packet_context_continue_state(struct ctf_msg_iter *msg_it)
-{
-    return read_dscope_continue_state(msg_it, STATE_AFTER_STREAM_PACKET_CONTEXT);
-}
-
-static enum ctf_msg_iter_status set_current_packet_content_sizes(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-
-    if (msg_it->cur_exp_packet_total_size == -1) {
-        if (msg_it->cur_exp_packet_content_size != -1) {
-            msg_it->cur_exp_packet_total_size = msg_it->cur_exp_packet_content_size;
-        }
-    } else {
-        if (msg_it->cur_exp_packet_content_size == -1) {
-            msg_it->cur_exp_packet_content_size = msg_it->cur_exp_packet_total_size;
-        }
-    }
-
-    BT_ASSERT(
-        (msg_it->cur_exp_packet_total_size >= 0 && msg_it->cur_exp_packet_content_size >= 0) ||
-        (msg_it->cur_exp_packet_total_size < 0 && msg_it->cur_exp_packet_content_size < 0));
-
-    if (msg_it->cur_exp_packet_content_size > msg_it->cur_exp_packet_total_size) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Invalid packet or content size: "
-                                  "content size is greater than packet size: "
-                                  "msg-it-addr=%p, packet-context-field-addr=%p, "
-                                  "packet-size=%" PRId64 ", content-size=%" PRId64,
-                                  msg_it, msg_it->dscopes.stream_packet_context,
-                                  msg_it->cur_exp_packet_total_size,
-                                  msg_it->cur_exp_packet_content_size);
-        status = CTF_MSG_ITER_STATUS_ERROR;
-        goto end;
-    }
-
-    BT_COMP_LOGD("Set current packet and content sizes: "
-                 "msg-it-addr=%p, packet-size=%" PRIu64 ", content-size=%" PRIu64,
-                 msg_it, msg_it->cur_exp_packet_total_size, msg_it->cur_exp_packet_content_size);
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status after_packet_context_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status;
-
-    status = set_current_packet_content_sizes(msg_it);
-    if (status != CTF_MSG_ITER_STATUS_OK) {
-        goto end;
-    }
-
-    if (msg_it->emit_stream_beginning_message) {
-        msg_it->state = STATE_EMIT_MSG_STREAM_BEGINNING;
-    } else {
-        msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS;
-    }
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_event_header_begin_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    struct ctf_field_class *event_header_fc = NULL;
-
-    /* Reset the position of the last event header */
-    msg_it->buf.last_eh_at = msg_it->buf.at;
-    msg_it->cur_event_class_id = -1;
-
-    /* Check if we have some content left */
-    if (msg_it->cur_exp_packet_content_size >= 0) {
-        if (G_UNLIKELY(packet_at(msg_it) == msg_it->cur_exp_packet_content_size)) {
-            /* No more events! */
-            BT_COMP_LOGD("Reached end of packet: msg-it-addr=%p, "
-                         "cur=%zu",
-                         msg_it, packet_at(msg_it));
-            msg_it->state = STATE_EMIT_MSG_PACKET_END_MULTI;
-            goto end;
-        } else if (G_UNLIKELY(packet_at(msg_it) > msg_it->cur_exp_packet_content_size)) {
-            /* That's not supposed to happen */
-            BT_COMP_LOGD(
-                "Before decoding event header field: cursor is passed the packet's content: "
-                "msg-it-addr=%p, content-size=%" PRId64 ", "
-                "cur=%zu",
-                msg_it, msg_it->cur_exp_packet_content_size, packet_at(msg_it));
-            status = CTF_MSG_ITER_STATUS_ERROR;
-            goto end;
-        }
-    } else {
-        /*
-         * "Infinite" content: we're done when the medium has
-         * nothing else for us.
-         */
-        status = buf_ensure_available_bits(msg_it);
-        switch (status) {
-        case CTF_MSG_ITER_STATUS_OK:
-            break;
-        case CTF_MSG_ITER_STATUS_EOF:
-            status = CTF_MSG_ITER_STATUS_OK;
-            msg_it->state = STATE_EMIT_MSG_PACKET_END_SINGLE;
-            goto end;
-        default:
-            goto end;
-        }
-    }
-
-    release_event_dscopes(msg_it);
-    BT_ASSERT(msg_it->meta.sc);
-    event_header_fc = msg_it->meta.sc->event_header_fc;
-    if (!event_header_fc) {
-        msg_it->state = STATE_AFTER_EVENT_HEADER;
-        goto end;
-    }
-
-    BT_COMP_LOGD("Decoding event header field: "
-                 "msg-it-addr=%p, stream-class-addr=%p, "
-                 "stream-class-id=%" PRId64 ", "
-                 "fc-addr=%p",
-                 msg_it, msg_it->meta.sc, msg_it->meta.sc->id, event_header_fc);
-    status = read_dscope_begin_state(msg_it, event_header_fc, STATE_AFTER_EVENT_HEADER,
-                                     STATE_DSCOPE_EVENT_HEADER_CONTINUE, NULL);
-    if (status < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot decode event header field: "
-                                  "msg-it-addr=%p, stream-class-addr=%p, "
-                                  "stream-class-id=%" PRId64 ", fc-addr=%p",
-                                  msg_it, msg_it->meta.sc, msg_it->meta.sc->id, event_header_fc);
-    }
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_event_header_continue_state(struct ctf_msg_iter *msg_it)
-{
-    return read_dscope_continue_state(msg_it, STATE_AFTER_EVENT_HEADER);
-}
-
-static inline enum ctf_msg_iter_status set_current_event_class(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-
-    struct ctf_event_class *new_event_class = NULL;
-
-    if (msg_it->cur_event_class_id == -1) {
-        /*
-         * No current event class ID field, therefore only one
-         * event class.
-         */
-        if (msg_it->meta.sc->event_classes->len != 1) {
-            BT_COMP_LOGE_APPEND_CAUSE(
-                self_comp,
-                "Need exactly one event class since there's no event class ID field: "
-                "msg-it-addr=%p",
-                msg_it);
-            status = CTF_MSG_ITER_STATUS_ERROR;
-            goto end;
-        }
-
-        new_event_class = (ctf_event_class *) msg_it->meta.sc->event_classes->pdata[0];
-        msg_it->cur_event_class_id = new_event_class->id;
-    }
-
-    new_event_class =
-        ctf_stream_class_borrow_event_class_by_id(msg_it->meta.sc, msg_it->cur_event_class_id);
-    if (!new_event_class) {
-        BT_COMP_LOGE_APPEND_CAUSE(
-            self_comp,
-            "No event class with ID of event class ID to use in stream class: "
-            "msg-it-addr=%p, stream-class-id=%" PRIu64 ", "
-            "event-class-id=%" PRIu64 ", "
-            "trace-class-addr=%p",
-            msg_it, msg_it->meta.sc->id, msg_it->cur_event_class_id, msg_it->meta.tc);
-        status = CTF_MSG_ITER_STATUS_ERROR;
-        goto end;
-    }
-
-    msg_it->meta.ec = new_event_class;
-    BT_COMP_LOGD("Set current event class: "
-                 "msg-it-addr=%p, event-class-addr=%p, "
-                 "event-class-id=%" PRId64 ", "
-                 "event-class-name=\"%s\"",
-                 msg_it, msg_it->meta.ec, msg_it->meta.ec->id, msg_it->meta.ec->name->str);
-
-end:
-    return status;
-}
-
-static inline enum ctf_msg_iter_status set_current_event_message(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    bt_message *msg = NULL;
-
-    BT_ASSERT_DBG(msg_it->meta.ec);
-    BT_ASSERT_DBG(msg_it->packet);
-    BT_COMP_LOGD("Creating event message from event class and packet: "
-                 "msg-it-addr=%p, ec-addr=%p, ec-name=\"%s\", packet-addr=%p",
-                 msg_it, msg_it->meta.ec, msg_it->meta.ec->name->str, msg_it->packet);
-    BT_ASSERT_DBG(msg_it->self_msg_iter);
-    BT_ASSERT_DBG(msg_it->meta.sc);
-
-    if (bt_stream_class_borrow_default_clock_class(msg_it->meta.sc->ir_sc)) {
-        msg = bt_message_event_create_with_packet_and_default_clock_snapshot(
-            msg_it->self_msg_iter, msg_it->meta.ec->ir_ec, msg_it->packet,
-            msg_it->default_clock_snapshot);
-    } else {
-        msg = bt_message_event_create_with_packet(msg_it->self_msg_iter, msg_it->meta.ec->ir_ec,
-                                                  msg_it->packet);
-    }
-
-    if (!msg) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot create event message: "
-                                  "msg-it-addr=%p, ec-addr=%p, ec-name=\"%s\", "
-                                  "packet-addr=%p",
-                                  msg_it, msg_it->meta.ec, msg_it->meta.ec->name->str,
-                                  msg_it->packet);
-        goto error;
-    }
-
-    goto end;
-
-error:
-    BT_MESSAGE_PUT_REF_AND_RESET(msg);
-    status = CTF_MSG_ITER_STATUS_ERROR;
-
-end:
-    BT_MESSAGE_MOVE_REF(msg_it->event_msg, msg);
-    return status;
-}
-
-static enum ctf_msg_iter_status after_event_header_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status;
-
-    status = set_current_event_class(msg_it);
-    if (status != CTF_MSG_ITER_STATUS_OK) {
-        goto end;
-    }
-
-    if (G_UNLIKELY(msg_it->dry_run)) {
-        goto next_state;
-    }
-
-    status = set_current_event_message(msg_it);
-    if (status != CTF_MSG_ITER_STATUS_OK) {
-        goto end;
-    }
-
-    msg_it->event = bt_message_event_borrow_event(msg_it->event_msg);
-    BT_ASSERT_DBG(msg_it->event);
-
-next_state:
-    msg_it->state = STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN;
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_event_common_context_begin_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    struct ctf_field_class *event_common_context_fc;
-
-    event_common_context_fc = msg_it->meta.sc->event_common_context_fc;
-    if (!event_common_context_fc) {
-        msg_it->state = STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN;
-        goto end;
-    }
-
-    if (event_common_context_fc->in_ir && !msg_it->dry_run) {
-        BT_ASSERT_DBG(!msg_it->dscopes.event_common_context);
-        msg_it->dscopes.event_common_context = bt_event_borrow_common_context_field(msg_it->event);
-        BT_ASSERT_DBG(msg_it->dscopes.event_common_context);
-    }
-
-    BT_COMP_LOGT("Decoding event common context field: "
-                 "msg-it-addr=%p, stream-class-addr=%p, "
-                 "stream-class-id=%" PRId64 ", "
-                 "fc-addr=%p",
-                 msg_it, msg_it->meta.sc, msg_it->meta.sc->id, event_common_context_fc);
-    status = read_dscope_begin_state(
-        msg_it, event_common_context_fc, STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN,
-        STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE, msg_it->dscopes.event_common_context);
-    if (status < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot decode event common context field: "
-                                  "msg-it-addr=%p, stream-class-addr=%p, "
-                                  "stream-class-id=%" PRId64 ", fc-addr=%p",
-                                  msg_it, msg_it->meta.sc, msg_it->meta.sc->id,
-                                  event_common_context_fc);
-    }
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status
-read_event_common_context_continue_state(struct ctf_msg_iter *msg_it)
-{
-    return read_dscope_continue_state(msg_it, STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN);
-}
-
-static enum ctf_msg_iter_status read_event_spec_context_begin_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    struct ctf_field_class *event_spec_context_fc;
-
-    event_spec_context_fc = msg_it->meta.ec->spec_context_fc;
-    if (!event_spec_context_fc) {
-        msg_it->state = STATE_DSCOPE_EVENT_PAYLOAD_BEGIN;
-        goto end;
-    }
-
-    if (event_spec_context_fc->in_ir && !msg_it->dry_run) {
-        BT_ASSERT_DBG(!msg_it->dscopes.event_spec_context);
-        msg_it->dscopes.event_spec_context = bt_event_borrow_specific_context_field(msg_it->event);
-        BT_ASSERT_DBG(msg_it->dscopes.event_spec_context);
-    }
-
-    BT_COMP_LOGT("Decoding event specific context field: "
-                 "msg-it-addr=%p, event-class-addr=%p, "
-                 "event-class-name=\"%s\", event-class-id=%" PRId64 ", "
-                 "fc-addr=%p",
-                 msg_it, msg_it->meta.ec, msg_it->meta.ec->name->str, msg_it->meta.ec->id,
-                 event_spec_context_fc);
-    status = read_dscope_begin_state(
-        msg_it, event_spec_context_fc, STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
-        STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE, msg_it->dscopes.event_spec_context);
-    if (status < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot decode event specific context field: "
-                                  "msg-it-addr=%p, event-class-addr=%p, "
-                                  "event-class-name=\"%s\", "
-                                  "event-class-id=%" PRId64 ", fc-addr=%p",
-                                  msg_it, msg_it->meta.ec, msg_it->meta.ec->name->str,
-                                  msg_it->meta.ec->id, event_spec_context_fc);
-    }
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_event_spec_context_continue_state(struct ctf_msg_iter *msg_it)
-{
-    return read_dscope_continue_state(msg_it, STATE_DSCOPE_EVENT_PAYLOAD_BEGIN);
-}
-
-static enum ctf_msg_iter_status read_event_payload_begin_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-    struct ctf_field_class *event_payload_fc;
-
-    event_payload_fc = msg_it->meta.ec->payload_fc;
-    if (!event_payload_fc) {
-        msg_it->state = STATE_EMIT_MSG_EVENT;
-        goto end;
-    }
-
-    if (event_payload_fc->in_ir && !msg_it->dry_run) {
-        BT_ASSERT_DBG(!msg_it->dscopes.event_payload);
-        msg_it->dscopes.event_payload = bt_event_borrow_payload_field(msg_it->event);
-        BT_ASSERT_DBG(msg_it->dscopes.event_payload);
-    }
-
-    BT_COMP_LOGT("Decoding event payload field: "
-                 "msg-it-addr=%p, event-class-addr=%p, "
-                 "event-class-name=\"%s\", event-class-id=%" PRId64 ", "
-                 "fc-addr=%p",
-                 msg_it, msg_it->meta.ec, msg_it->meta.ec->name->str, msg_it->meta.ec->id,
-                 event_payload_fc);
-    status =
-        read_dscope_begin_state(msg_it, event_payload_fc, STATE_EMIT_MSG_EVENT,
-                                STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, msg_it->dscopes.event_payload);
-    if (status < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot decode event payload field: "
-                                  "msg-it-addr=%p, event-class-addr=%p, "
-                                  "event-class-name=\"%s\", "
-                                  "event-class-id=%" PRId64 ", fc-addr=%p",
-                                  msg_it, msg_it->meta.ec, msg_it->meta.ec->name->str,
-                                  msg_it->meta.ec->id, event_payload_fc);
-    }
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_event_payload_continue_state(struct ctf_msg_iter *msg_it)
-{
-    return read_dscope_continue_state(msg_it, STATE_EMIT_MSG_EVENT);
-}
-
-static enum ctf_msg_iter_status skip_packet_padding_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    size_t bits_to_skip;
-    const enum state next_state = STATE_SWITCH_PACKET;
-
-    BT_ASSERT(msg_it->cur_exp_packet_total_size > 0);
-    bits_to_skip = msg_it->cur_exp_packet_total_size - packet_at(msg_it);
-    if (bits_to_skip == 0) {
-        msg_it->state = next_state;
-        goto end;
-    } else {
-        size_t bits_to_consume;
-
-        BT_COMP_LOGD("Trying to skip %zu bits of padding: msg-it-addr=%p, size=%zu", bits_to_skip,
-                     msg_it, bits_to_skip);
-        status = buf_ensure_available_bits(msg_it);
-        if (status != CTF_MSG_ITER_STATUS_OK) {
-            goto end;
-        }
-
-        bits_to_consume = MIN(buf_available_bits(msg_it), bits_to_skip);
-        BT_COMP_LOGD("Skipping %zu bits of padding: msg-it-addr=%p, size=%zu", bits_to_consume,
-                     msg_it, bits_to_consume);
-        buf_consume_bits(msg_it, bits_to_consume);
-        bits_to_skip = msg_it->cur_exp_packet_total_size - packet_at(msg_it);
-        if (bits_to_skip == 0) {
-            msg_it->state = next_state;
-            goto end;
-        }
-    }
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status check_emit_msg_discarded_events(struct ctf_msg_iter *msg_it)
-{
-    msg_it->state = STATE_EMIT_MSG_DISCARDED_EVENTS;
-
-    if (!msg_it->meta.sc->has_discarded_events) {
-        msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
-        goto end;
-    }
-
-    if (msg_it->prev_packet_snapshots.discarded_events == UINT64_C(-1)) {
-        if (msg_it->snapshots.discarded_events == 0 ||
-            msg_it->snapshots.discarded_events == UINT64_C(-1)) {
-            /*
-             * Stream's first packet with no discarded
-             * events or no information about discarded
-             * events: do not emit.
-             */
-            msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
-        }
-    } else {
-        /*
-         * If the previous packet has a value for this counter,
-         * then this counter is defined for the whole stream.
-         */
-        BT_ASSERT(msg_it->snapshots.discarded_events != UINT64_C(-1));
-
-        if (msg_it->snapshots.discarded_events - msg_it->prev_packet_snapshots.discarded_events ==
-            0) {
-            /*
-             * No discarded events since previous packet: do
-             * not emit.
-             */
-            msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
-        }
-    }
-
-end:
-    return CTF_MSG_ITER_STATUS_OK;
-}
-
-static enum ctf_msg_iter_status check_emit_msg_discarded_packets(struct ctf_msg_iter *msg_it)
-{
-    msg_it->state = STATE_EMIT_MSG_DISCARDED_PACKETS;
-
-    if (!msg_it->meta.sc->has_discarded_packets) {
-        msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
-        goto end;
-    }
-
-    if (msg_it->prev_packet_snapshots.packets == UINT64_C(-1)) {
-        /*
-         * Stream's first packet or no information about
-         * discarded packets: do not emit. In other words, if
-         * this is the first packet and its sequence number is
-         * not 0, do not consider that packets were previously
-         * lost: we might be reading a partial stream (LTTng
-         * snapshot for example).
-         */
-        msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
-    } else {
-        /*
-         * If the previous packet has a value for this counter,
-         * then this counter is defined for the whole stream.
-         */
-        BT_ASSERT(msg_it->snapshots.packets != UINT64_C(-1));
-
-        if (msg_it->snapshots.packets - msg_it->prev_packet_snapshots.packets <= 1) {
-            /*
-             * No discarded packets since previous packet:
-             * do not emit.
-             */
-            msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
-        }
-    }
-
-end:
-    return CTF_MSG_ITER_STATUS_OK;
-}
-
-static inline enum state check_emit_msg_stream_end(struct ctf_msg_iter *msg_it)
-{
-    enum state next_state;
-
-    if (msg_it->emit_stream_end_message) {
-        next_state = STATE_EMIT_MSG_STREAM_END;
-    } else {
-        next_state = STATE_DONE;
-    }
-
-    return next_state;
-}
-
-static inline enum ctf_msg_iter_status handle_state(struct ctf_msg_iter *msg_it)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    const enum state state = msg_it->state;
-
-    BT_COMP_LOGT("Handling state: msg-it-addr=%p, state=%s", msg_it, state_string(state));
-
-    // TODO: optimalize!
-    switch (state) {
-    case STATE_INIT:
-        msg_it->state = STATE_SWITCH_PACKET;
-        break;
-    case STATE_SWITCH_PACKET:
-        status = switch_packet_state(msg_it);
-        break;
-    case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
-        status = read_packet_header_begin_state(msg_it);
-        break;
-    case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
-        status = read_packet_header_continue_state(msg_it);
-        break;
-    case STATE_AFTER_TRACE_PACKET_HEADER:
-        status = after_packet_header_state(msg_it);
-        break;
-    case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
-        status = read_packet_context_begin_state(msg_it);
-        break;
-    case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
-        status = read_packet_context_continue_state(msg_it);
-        break;
-    case STATE_AFTER_STREAM_PACKET_CONTEXT:
-        status = after_packet_context_state(msg_it);
-        break;
-    case STATE_EMIT_MSG_STREAM_BEGINNING:
-        msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS;
-        break;
-    case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
-        status = check_emit_msg_discarded_events(msg_it);
-        break;
-    case STATE_EMIT_MSG_DISCARDED_EVENTS:
-        msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
-        break;
-    case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
-        status = check_emit_msg_discarded_packets(msg_it);
-        break;
-    case STATE_EMIT_MSG_DISCARDED_PACKETS:
-        msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
-        break;
-    case STATE_EMIT_MSG_PACKET_BEGINNING:
-        msg_it->state = STATE_DSCOPE_EVENT_HEADER_BEGIN;
-        break;
-    case STATE_DSCOPE_EVENT_HEADER_BEGIN:
-        status = read_event_header_begin_state(msg_it);
-        break;
-    case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
-        status = read_event_header_continue_state(msg_it);
-        break;
-    case STATE_AFTER_EVENT_HEADER:
-        status = after_event_header_state(msg_it);
-        break;
-    case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
-        status = read_event_common_context_begin_state(msg_it);
-        break;
-    case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
-        status = read_event_common_context_continue_state(msg_it);
-        break;
-    case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
-        status = read_event_spec_context_begin_state(msg_it);
-        break;
-    case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
-        status = read_event_spec_context_continue_state(msg_it);
-        break;
-    case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
-        status = read_event_payload_begin_state(msg_it);
-        break;
-    case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
-        status = read_event_payload_continue_state(msg_it);
-        break;
-    case STATE_EMIT_MSG_EVENT:
-        msg_it->state = STATE_DSCOPE_EVENT_HEADER_BEGIN;
-        break;
-    case STATE_EMIT_QUEUED_MSG_EVENT:
-        msg_it->state = STATE_EMIT_MSG_EVENT;
-        break;
-    case STATE_SKIP_PACKET_PADDING:
-        status = skip_packet_padding_state(msg_it);
-        break;
-    case STATE_EMIT_MSG_PACKET_END_MULTI:
-        msg_it->state = STATE_SKIP_PACKET_PADDING;
-        break;
-    case STATE_EMIT_MSG_PACKET_END_SINGLE:
-        msg_it->state = STATE_EMIT_MSG_STREAM_END;
-        break;
-    case STATE_EMIT_QUEUED_MSG_PACKET_END:
-        msg_it->state = STATE_EMIT_MSG_PACKET_END_SINGLE;
-        break;
-    case STATE_CHECK_EMIT_MSG_STREAM_END:
-        msg_it->state = check_emit_msg_stream_end(msg_it);
-        break;
-    case STATE_EMIT_MSG_STREAM_END:
-        msg_it->state = STATE_DONE;
-        break;
-    case STATE_DONE:
-        break;
-    default:
-        BT_COMP_LOGF("Unknown CTF plugin message iterator state: "
-                     "msg-it-addr=%p, state=%d",
-                     msg_it, msg_it->state);
-        bt_common_abort();
-    }
-
-    BT_COMP_LOGT("Handled state: msg-it-addr=%p, status=%s, "
-                 "prev-state=%s, cur-state=%s",
-                 msg_it, ctf_msg_iter_status_string(status), state_string(state),
-                 state_string(msg_it->state));
-    return status;
-}
-
-BT_HIDDEN
-void ctf_msg_iter_reset_for_next_stream_file(struct ctf_msg_iter *msg_it)
-{
-    BT_ASSERT(msg_it);
-    BT_COMP_LOGD("Resetting message iterator: addr=%p", msg_it);
-    stack_clear(msg_it->stack);
-    msg_it->meta.sc = NULL;
-    msg_it->meta.ec = NULL;
-    BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
-    BT_STREAM_PUT_REF_AND_RESET(msg_it->stream);
-    BT_MESSAGE_PUT_REF_AND_RESET(msg_it->event_msg);
-    release_all_dscopes(msg_it);
-    msg_it->cur_dscope_field = NULL;
-
-    msg_it->buf.addr = NULL;
-    msg_it->buf.sz = 0;
-    msg_it->buf.at = 0;
-    msg_it->buf.last_eh_at = SIZE_MAX;
-    msg_it->buf.packet_offset = 0;
-    msg_it->state = STATE_INIT;
-    msg_it->cur_exp_packet_content_size = -1;
-    msg_it->cur_exp_packet_total_size = -1;
-    msg_it->cur_packet_offset = -1;
-    msg_it->cur_event_class_id = -1;
-    msg_it->snapshots.beginning_clock = UINT64_C(-1);
-    msg_it->snapshots.end_clock = UINT64_C(-1);
-}
-
-/**
- * Resets the internal state of a CTF message iterator.
- */
-BT_HIDDEN
-void ctf_msg_iter_reset(struct ctf_msg_iter *msg_it)
-{
-    ctf_msg_iter_reset_for_next_stream_file(msg_it);
-    msg_it->cur_stream_class_id = -1;
-    msg_it->cur_data_stream_id = -1;
-    msg_it->snapshots.discarded_events = UINT64_C(-1);
-    msg_it->snapshots.packets = UINT64_C(-1);
-    msg_it->prev_packet_snapshots.discarded_events = UINT64_C(-1);
-    msg_it->prev_packet_snapshots.packets = UINT64_C(-1);
-    msg_it->prev_packet_snapshots.beginning_clock = UINT64_C(-1);
-    msg_it->prev_packet_snapshots.end_clock = UINT64_C(-1);
-    msg_it->emit_stream_beginning_message = true;
-    msg_it->emit_stream_end_message = false;
-}
-
-static bt_field *borrow_next_field(struct ctf_msg_iter *msg_it)
-{
-    bt_field *next_field = NULL;
-    bt_field *base_field;
-    const bt_field_class *base_fc;
-    bt_field_class_type base_fc_type;
-    size_t index;
-
-    BT_ASSERT_DBG(!stack_empty(msg_it->stack));
-    index = stack_top(msg_it->stack)->index;
-    base_field = stack_top(msg_it->stack)->base;
-    BT_ASSERT_DBG(base_field);
-    base_fc = bt_field_borrow_class_const(base_field);
-    BT_ASSERT_DBG(base_fc);
-    base_fc_type = bt_field_class_get_type(base_fc);
-
-    if (base_fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
-        BT_ASSERT_DBG(index < bt_field_class_structure_get_member_count(
-                                  bt_field_borrow_class_const(base_field)));
-        next_field = bt_field_structure_borrow_member_field_by_index(base_field, index);
-    } else if (bt_field_class_type_is(base_fc_type, BT_FIELD_CLASS_TYPE_ARRAY)) {
-        BT_ASSERT_DBG(index < bt_field_array_get_length(base_field));
-        next_field = bt_field_array_borrow_element_field_by_index(base_field, index);
-    } else if (bt_field_class_type_is(base_fc_type, BT_FIELD_CLASS_TYPE_VARIANT)) {
-        BT_ASSERT_DBG(index == 0);
-        next_field = bt_field_variant_borrow_selected_option_field(base_field);
-    } else {
-        bt_common_abort();
-    }
-
-    BT_ASSERT_DBG(next_field);
-    return next_field;
-}
-
-static void update_default_clock(struct ctf_msg_iter *msg_it, uint64_t new_val,
-                                 uint64_t new_val_size)
-{
-    uint64_t new_val_mask;
-    uint64_t cur_value_masked;
-
-    BT_ASSERT_DBG(new_val_size > 0);
-
-    /*
-     * Special case for a 64-bit new value, which is the limit
-     * of a clock value as of this version: overwrite the
-     * current value directly.
-     */
-    if (new_val_size == 64) {
-        msg_it->default_clock_snapshot = new_val;
-        goto end;
-    }
-
-    new_val_mask = (1ULL << new_val_size) - 1;
-    cur_value_masked = msg_it->default_clock_snapshot & new_val_mask;
-
-    if (new_val < cur_value_masked) {
-        /*
-         * It looks like a wrap happened on the number of bits
-         * of the requested new value. Assume that the clock
-         * value wrapped only one time.
-         */
-        msg_it->default_clock_snapshot += new_val_mask + 1;
-    }
-
-    /* Clear the low bits of the current clock value. */
-    msg_it->default_clock_snapshot &= ~new_val_mask;
-
-    /* Set the low bits of the current clock value. */
-    msg_it->default_clock_snapshot |= new_val;
-
-end:
-    BT_COMP_LOGT("Updated default clock's value from integer field's value: "
-                 "value=%" PRIu64,
-                 msg_it->default_clock_snapshot);
-}
-
-static enum bt_bfcr_status bfcr_unsigned_int_cb(uint64_t value, struct ctf_field_class *fc,
-                                                void *data)
-{
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-    bt_self_component *self_comp = msg_it->self_comp;
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-    bt_field *field = NULL;
-    ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
-
-    BT_COMP_LOGT("Unsigned integer function called from BFCR: "
-                 "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-                 "fc-type=%d, fc-in-ir=%d, value=%" PRIu64,
-                 msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
-
-    if (G_LIKELY(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE)) {
-        goto update_def_clock;
-    }
-
-    switch (int_fc->meaning) {
-    case CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID:
-        msg_it->cur_event_class_id = value;
-        break;
-    case CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID:
-        msg_it->cur_data_stream_id = value;
-        break;
-    case CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME:
-        msg_it->snapshots.beginning_clock = value;
-        break;
-    case CTF_FIELD_CLASS_MEANING_PACKET_END_TIME:
-        msg_it->snapshots.end_clock = value;
-        break;
-    case CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID:
-        msg_it->cur_stream_class_id = value;
-        break;
-    case CTF_FIELD_CLASS_MEANING_MAGIC:
-        if (value != 0xc1fc1fc1) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Invalid CTF magic number: msg-it-addr=%p, "
-                                      "magic=%" PRIx64,
-                                      msg_it, value);
-            status = BT_BFCR_STATUS_ERROR;
-            goto end;
-        }
-
-        break;
-    case CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT:
-        msg_it->snapshots.packets = value;
-        break;
-    case CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT:
-        msg_it->snapshots.discarded_events = value;
-        break;
-    case CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE:
-        msg_it->cur_exp_packet_total_size = value;
-        break;
-    case CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE:
-        msg_it->cur_exp_packet_content_size = value;
-        break;
-    default:
-        bt_common_abort();
-    }
-
-update_def_clock:
-    if (G_UNLIKELY(int_fc->mapped_clock_class)) {
-        update_default_clock(msg_it, value, int_fc->base.size);
-    }
-
-    if (G_UNLIKELY(int_fc->storing_index >= 0)) {
-        g_array_index(msg_it->stored_values, uint64_t, (uint64_t) int_fc->storing_index) = value;
-    }
-
-    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-        goto end;
-    }
-
-    field = borrow_next_field(msg_it);
-    BT_ASSERT_DBG(field);
-    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-    BT_ASSERT_DBG(bt_field_class_type_is(bt_field_get_class_type(field),
-                                         BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER));
-    bt_field_integer_unsigned_set_value(field, value);
-    stack_top(msg_it->stack)->index++;
-
-end:
-    return status;
-}
-
-static enum bt_bfcr_status bfcr_unsigned_int_char_cb(uint64_t value, struct ctf_field_class *fc,
-                                                     void *data)
-{
-    int ret;
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-    bt_self_component *self_comp = msg_it->self_comp;
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-    bt_field *string_field = NULL;
-    ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
-    char str[2] = {'\0', '\0'};
-
-    BT_COMP_LOGT("Unsigned integer character function called from BFCR: "
-                 "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-                 "fc-type=%d, fc-in-ir=%d, value=%" PRIu64,
-                 msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
-    BT_ASSERT_DBG(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE);
-    BT_ASSERT_DBG(!int_fc->mapped_clock_class);
-    BT_ASSERT_DBG(int_fc->storing_index < 0);
-
-    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-        goto end;
-    }
-
-    if (msg_it->done_filling_string) {
-        goto end;
-    }
-
-    if (value == 0) {
-        msg_it->done_filling_string = true;
-        goto end;
-    }
-
-    string_field = stack_top(msg_it->stack)->base;
-    BT_ASSERT_DBG(bt_field_get_class_type(string_field) == BT_FIELD_CLASS_TYPE_STRING);
-
-    /* Append character */
-    str[0] = (char) value;
-    ret = bt_field_string_append_with_length(string_field, str, 1);
-    if (ret) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot append character to string field's value: "
-                                  "msg-it-addr=%p, field-addr=%p, ret=%d",
-                                  msg_it, string_field, ret);
-        status = BT_BFCR_STATUS_ERROR;
-        goto end;
-    }
-
-end:
-    return status;
-}
-
-static enum bt_bfcr_status bfcr_signed_int_cb(int64_t value, struct ctf_field_class *fc, void *data)
-{
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-    bt_field *field = NULL;
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-    ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
-
-    BT_COMP_LOGT("Signed integer function called from BFCR: "
-                 "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-                 "fc-type=%d, fc-in-ir=%d, value=%" PRId64,
-                 msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
-    BT_ASSERT_DBG(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE);
-
-    if (G_UNLIKELY(int_fc->storing_index >= 0)) {
-        g_array_index(msg_it->stored_values, uint64_t, (uint64_t) int_fc->storing_index) =
-            (uint64_t) value;
-    }
-
-    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-        goto end;
-    }
-
-    field = borrow_next_field(msg_it);
-    BT_ASSERT_DBG(field);
-    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-    BT_ASSERT_DBG(
-        bt_field_class_type_is(bt_field_get_class_type(field), BT_FIELD_CLASS_TYPE_SIGNED_INTEGER));
-    bt_field_integer_signed_set_value(field, value);
-    stack_top(msg_it->stack)->index++;
-
-end:
-    return status;
-}
-
-static enum bt_bfcr_status bfcr_floating_point_cb(double value, struct ctf_field_class *fc,
-                                                  void *data)
-{
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-    bt_field *field = NULL;
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-    bt_field_class_type type;
-
-    BT_COMP_LOGT("Floating point number function called from BFCR: "
-                 "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-                 "fc-type=%d, fc-in-ir=%d, value=%f",
-                 msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
-
-    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-        goto end;
-    }
-
-    field = borrow_next_field(msg_it);
-    type = bt_field_get_class_type(field);
-    BT_ASSERT_DBG(field);
-    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-    BT_ASSERT_DBG(bt_field_class_type_is(type, BT_FIELD_CLASS_TYPE_REAL));
-
-    if (type == BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL) {
-        bt_field_real_single_precision_set_value(field, (float) value);
-    } else {
-        bt_field_real_double_precision_set_value(field, value);
-    }
-    stack_top(msg_it->stack)->index++;
-
-end:
-    return status;
-}
-
-static enum bt_bfcr_status bfcr_string_begin_cb(struct ctf_field_class *fc, void *data)
-{
-    bt_field *field = NULL;
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-
-    BT_COMP_LOGT("String (beginning) function called from BFCR: "
-                 "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-                 "fc-type=%d, fc-in-ir=%d",
-                 msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
-
-    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-        goto end;
-    }
-
-    field = borrow_next_field(msg_it);
-    BT_ASSERT_DBG(field);
-    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-    BT_ASSERT_DBG(bt_field_get_class_type(field) == BT_FIELD_CLASS_TYPE_STRING);
-    bt_field_string_clear(field);
-
-    /*
-     * Push on stack. Not a compound class per se, but we know that
-     * only bfcr_string_cb() may be called between this call and a
-     * subsequent call to bfcr_string_end_cb().
-     */
-    stack_push(msg_it->stack, field);
-
-end:
-    return BT_BFCR_STATUS_OK;
-}
-
-static enum bt_bfcr_status bfcr_string_cb(const char *value, size_t len, struct ctf_field_class *fc,
-                                          void *data)
-{
-    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-    bt_field *field = NULL;
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-    bt_self_component *self_comp = msg_it->self_comp;
-    int ret;
-
-    BT_COMP_LOGT("String (substring) function called from BFCR: "
-                 "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-                 "fc-type=%d, fc-in-ir=%d, string-length=%zu",
-                 msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, len);
-
-    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-        goto end;
-    }
-
-    field = stack_top(msg_it->stack)->base;
-    BT_ASSERT_DBG(field);
-
-    /* Append current substring */
-    ret = bt_field_string_append_with_length(field, value, len);
-    if (ret) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot append substring to string field's value: "
-                                  "msg-it-addr=%p, field-addr=%p, string-length=%zu, "
-                                  "ret=%d",
-                                  msg_it, field, len, ret);
-        status = BT_BFCR_STATUS_ERROR;
-        goto end;
-    }
-
-end:
-    return status;
-}
-
-static enum bt_bfcr_status bfcr_string_end_cb(struct ctf_field_class *fc, void *data)
-{
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-
-    BT_COMP_LOGT("String (end) function called from BFCR: "
-                 "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-                 "fc-type=%d, fc-in-ir=%d",
-                 msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
-
-    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-        goto end;
-    }
-
-    /* Pop string field */
-    stack_pop(msg_it->stack);
-
-    /* Go to next field */
-    stack_top(msg_it->stack)->index++;
-
-end:
-    return BT_BFCR_STATUS_OK;
-}
-
-static enum bt_bfcr_status bfcr_compound_begin_cb(struct ctf_field_class *fc, void *data)
-{
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-    bt_field *field;
-
-    BT_COMP_LOGT("Compound (beginning) function called from BFCR: "
-                 "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-                 "fc-type=%d, fc-in-ir=%d",
-                 msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
-
-    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-        goto end;
-    }
-
-    /* Borrow field */
-    if (stack_empty(msg_it->stack)) {
-        /* Root: already set by read_dscope_begin_state() */
-        field = msg_it->cur_dscope_field;
-    } else {
-        field = borrow_next_field(msg_it);
-        BT_ASSERT_DBG(field);
-    }
-
-    /* Push field */
-    BT_ASSERT_DBG(field);
-    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-    stack_push(msg_it->stack, field);
-
-    /*
-     * Change BFCR "unsigned int" callback if it's a text
-     * array/sequence.
-     */
-    if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-        ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        if (array_fc->is_text) {
-            BT_ASSERT_DBG(bt_field_get_class_type(field) == BT_FIELD_CLASS_TYPE_STRING);
-            msg_it->done_filling_string = false;
-            bt_field_string_clear(field);
-            bt_bfcr_set_unsigned_int_cb(msg_it->bfcr, bfcr_unsigned_int_char_cb);
-        }
-    }
-
-end:
-    return BT_BFCR_STATUS_OK;
-}
-
-static enum bt_bfcr_status bfcr_compound_end_cb(struct ctf_field_class *fc, void *data)
-{
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-
-    BT_COMP_LOGT("Compound (end) function called from BFCR: "
-                 "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-                 "fc-type=%d, fc-in-ir=%d",
-                 msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
-
-    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-        goto end;
-    }
-
-    BT_ASSERT_DBG(!stack_empty(msg_it->stack));
-    BT_ASSERT_DBG(bt_field_borrow_class_const(stack_top(msg_it->stack)->base) == fc->ir_fc);
-
-    /*
-     * Reset BFCR "unsigned int" callback if it's a text
-     * array/sequence.
-     */
-    if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-        ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
-
-        if (array_fc->is_text) {
-            BT_ASSERT_DBG(bt_field_get_class_type(stack_top(msg_it->stack)->base) ==
-                          BT_FIELD_CLASS_TYPE_STRING);
-            bt_bfcr_set_unsigned_int_cb(msg_it->bfcr, bfcr_unsigned_int_cb);
-        }
-    }
-
-    /* Pop stack */
-    stack_pop(msg_it->stack);
-
-    /* If the stack is not empty, increment the base's index */
-    if (!stack_empty(msg_it->stack)) {
-        stack_top(msg_it->stack)->index++;
-    }
-
-end:
-    return BT_BFCR_STATUS_OK;
-}
-
-static int64_t bfcr_get_sequence_length_cb(struct ctf_field_class *fc, void *data)
-{
-    bt_field *seq_field;
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-    bt_self_component *self_comp = msg_it->self_comp;
-    struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
-    int64_t length;
-    int ret;
-
-    length = (uint64_t) g_array_index(msg_it->stored_values, uint64_t, seq_fc->stored_length_index);
-
-    if (G_UNLIKELY(msg_it->dry_run)) {
-        goto end;
-    }
-
-    seq_field = stack_top(msg_it->stack)->base;
-    BT_ASSERT_DBG(seq_field);
-
-    /*
-     * bfcr_get_sequence_length_cb() also gets called back for a
-     * text sequence, but the destination field is a string field.
-     * Only set the field's sequence length if the destination field
-     * is a sequence field.
-     */
-    if (!seq_fc->base.is_text) {
-        BT_ASSERT_DBG(bt_field_class_type_is(bt_field_get_class_type(seq_field),
-                                             BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY));
-        ret = bt_field_array_dynamic_set_length(seq_field, (uint64_t) length);
-        if (ret) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Cannot set dynamic array field's length field: "
-                                      "msg-it-addr=%p, field-addr=%p, "
-                                      "length=%" PRIu64,
-                                      msg_it, seq_field, length);
-            length = -1;
-        }
-    }
-
-end:
-    return length;
-}
-
-static struct ctf_field_class *
-bfcr_borrow_variant_selected_field_class_cb(struct ctf_field_class *fc, void *data)
-{
-    int ret;
-    uint64_t i;
-    int64_t option_index = -1;
-    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
-    ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
-    struct ctf_named_field_class *selected_option = NULL;
-    bt_self_component *self_comp = msg_it->self_comp;
-    struct ctf_field_class *ret_fc = NULL;
-    union
-    {
-        uint64_t u;
-        int64_t i;
-    } tag;
-
-    /* Get variant's tag */
-    tag.u = g_array_index(msg_it->stored_values, uint64_t, var_fc->stored_tag_index);
-
-    /*
-     * Check each range to find the selected option's index.
-     */
-    if (var_fc->tag_fc->base.is_signed) {
-        for (i = 0; i < var_fc->ranges->len; i++) {
-            struct ctf_field_class_variant_range *range =
-                ctf_field_class_variant_borrow_range_by_index(var_fc, i);
-
-            if (tag.i >= range->range.lower.i && tag.i <= range->range.upper.i) {
-                option_index = (int64_t) range->option_index;
-                break;
-            }
-        }
-    } else {
-        for (i = 0; i < var_fc->ranges->len; i++) {
-            struct ctf_field_class_variant_range *range =
-                ctf_field_class_variant_borrow_range_by_index(var_fc, i);
-
-            if (tag.u >= range->range.lower.u && tag.u <= range->range.upper.u) {
-                option_index = (int64_t) range->option_index;
-                break;
-            }
-        }
-    }
-
-    if (option_index < 0) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot find variant field class's option: "
-                                  "msg-it-addr=%p, var-fc-addr=%p, u-tag=%" PRIu64 ", "
-                                  "i-tag=%" PRId64,
-                                  msg_it, var_fc, tag.u, tag.i);
-        ret_fc = NULL;
-        goto end;
-    }
-
-    selected_option =
-        ctf_field_class_variant_borrow_option_by_index(var_fc, (uint64_t) option_index);
-
-    if (selected_option->fc->in_ir && !msg_it->dry_run) {
-        bt_field *var_field = stack_top(msg_it->stack)->base;
-
-        ret = bt_field_variant_select_option_by_index(var_field, option_index);
-        if (ret) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Cannot select variant field's option field: "
-                                      "msg-it-addr=%p, var-field-addr=%p, "
-                                      "opt-index=%" PRId64,
-                                      msg_it, var_field, option_index);
-            ret_fc = NULL;
-            goto end;
-        }
-    }
-
-    ret_fc = selected_option->fc;
-
-end:
-    return ret_fc;
-}
-
-static bt_message *create_msg_stream_beginning(struct ctf_msg_iter *msg_it)
-{
-    bt_self_component *self_comp = msg_it->self_comp;
-    bt_message *msg;
-
-    BT_ASSERT(msg_it->stream);
-    BT_ASSERT(msg_it->self_msg_iter);
-    msg = bt_message_stream_beginning_create(msg_it->self_msg_iter, msg_it->stream);
-    if (!msg) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot create stream beginning message: "
-                                  "msg-it-addr=%p, stream-addr=%p",
-                                  msg_it, msg_it->stream);
-    }
-
-    return msg;
-}
-
-static bt_message *create_msg_stream_end(struct ctf_msg_iter *msg_it)
-{
-    bt_self_component *self_comp = msg_it->self_comp;
-    bt_message *msg;
-
-    if (!msg_it->stream) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot create stream end message because stream is NULL: "
-                                  "msg-it-addr=%p",
-                                  msg_it);
-        msg = NULL;
-        goto end;
-    }
-
-    BT_ASSERT(msg_it->self_msg_iter);
-    msg = bt_message_stream_end_create(msg_it->self_msg_iter, msg_it->stream);
-    if (!msg) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot create stream end message: "
-                                  "msg-it-addr=%p, stream-addr=%p",
-                                  msg_it, msg_it->stream);
-    }
-
-end:
-    return msg;
-}
-
-static bt_message *create_msg_packet_beginning(struct ctf_msg_iter *msg_it, bool use_default_cs)
-{
-    bt_self_component *self_comp = msg_it->self_comp;
-    bt_message *msg;
-    const bt_stream_class *sc = msg_it->meta.sc->ir_sc;
-
-    BT_ASSERT(msg_it->packet);
-    BT_ASSERT(sc);
-    BT_ASSERT(msg_it->self_msg_iter);
-
-    if (msg_it->meta.sc->packets_have_ts_begin) {
-        BT_ASSERT(msg_it->snapshots.beginning_clock != UINT64_C(-1));
-        uint64_t raw_cs_value;
-
-        /*
-         * Either use the decoded packet `timestamp_begin` field or the
-         * current stream's default clock_snapshot.
-         */
-        if (use_default_cs) {
-            raw_cs_value = msg_it->default_clock_snapshot;
-        } else {
-            raw_cs_value = msg_it->snapshots.beginning_clock;
-        }
-
-        msg = bt_message_packet_beginning_create_with_default_clock_snapshot(
-            msg_it->self_msg_iter, msg_it->packet, raw_cs_value);
-    } else {
-        msg = bt_message_packet_beginning_create(msg_it->self_msg_iter, msg_it->packet);
-    }
-
-    if (!msg) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot create packet beginning message: "
-                                  "msg-it-addr=%p, packet-addr=%p",
-                                  msg_it, msg_it->packet);
-        goto end;
-    }
-
-end:
-    return msg;
-}
-
-static bt_message *emit_delayed_packet_beg_msg(struct ctf_msg_iter *msg_it)
-{
-    bool packet_beg_ts_need_fix_up;
-
-    msg_it->emit_delayed_packet_beginning_msg = false;
-
-    /*
-     * Only fix the packet's timestamp_begin if it's larger than the first
-     * event of the packet. If there was no event in the packet, the
-     * `default_clock_snapshot` field will be either equal or greater than
-     * `snapshots.beginning_clock` so there is not fix needed.
-     */
-    packet_beg_ts_need_fix_up = msg_it->default_clock_snapshot < msg_it->snapshots.beginning_clock;
-
-    /* create_msg_packet_beginning() logs errors */
-    return create_msg_packet_beginning(msg_it, packet_beg_ts_need_fix_up);
-}
-
-static bt_message *create_msg_packet_end(struct ctf_msg_iter *msg_it)
-{
-    bt_message *msg;
-    bool update_default_cs = true;
-    bt_self_component *self_comp = msg_it->self_comp;
-
-    if (!msg_it->packet) {
-        msg = NULL;
-        goto end;
-    }
-
-    /*
-     * Check if we need to emit the delayed packet
-     * beginning message instead of the packet end message.
-     */
-    if (G_UNLIKELY(msg_it->emit_delayed_packet_beginning_msg)) {
-        msg = emit_delayed_packet_beg_msg(msg_it);
-        /* Don't forget to emit the packet end message. */
-        msg_it->state = STATE_EMIT_QUEUED_MSG_PACKET_END;
-        goto end;
-    }
-
-    /* Check if may be affected by lttng-crash timestamp_end quirk. */
-    if (G_UNLIKELY(msg_it->meta.tc->quirks.lttng_crash)) {
-        /*
-         * Check if the `timestamp_begin` field is non-zero but
-         * `timestamp_end` is zero. It means the trace is affected by
-         * the lttng-crash packet `timestamp_end` quirk and must be
-         * fixed up by omitting to update the default clock snapshot to
-         * the `timestamp_end` as is typically done.
-         */
-        if (msg_it->snapshots.beginning_clock != 0 && msg_it->snapshots.end_clock == 0) {
-            update_default_cs = false;
-        }
-    }
-
-    /*
-     * Check if may be affected by lttng event-after-packet `timestamp_end`
-     * quirk.
-     */
-    if (msg_it->meta.tc->quirks.lttng_event_after_packet) {
-        /*
-         * Check if `timestamp_end` is smaller then the current
-         * default_clock_snapshot (which is set to the last event
-         * decoded). It means the trace is affected by the lttng
-         * `event-after-packet` packet `timestamp_end` quirk and must
-         * be fixed up by omitting to update the default clock snapshot
-         * to the `timestamp_end` as is typically done.
-         */
-        if (msg_it->snapshots.end_clock < msg_it->default_clock_snapshot) {
-            update_default_cs = false;
-        }
-    }
-
-    /* Update default clock from packet's end time. */
-    if (msg_it->snapshots.end_clock != UINT64_C(-1) && update_default_cs) {
-        msg_it->default_clock_snapshot = msg_it->snapshots.end_clock;
-    }
-
-    BT_ASSERT(msg_it->self_msg_iter);
-
-    if (msg_it->meta.sc->packets_have_ts_end) {
-        BT_ASSERT(msg_it->snapshots.end_clock != UINT64_C(-1));
-        msg = bt_message_packet_end_create_with_default_clock_snapshot(
-            msg_it->self_msg_iter, msg_it->packet, msg_it->default_clock_snapshot);
-    } else {
-        msg = bt_message_packet_end_create(msg_it->self_msg_iter, msg_it->packet);
-    }
-
-    if (!msg) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot create packet end message: "
-                                  "msg-it-addr=%p, packet-addr=%p",
-                                  msg_it, msg_it->packet);
-        goto end;
-    }
-
-    BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
-
-end:
-    return msg;
-}
-
-static bt_message *create_msg_discarded_events(struct ctf_msg_iter *msg_it)
-{
-    bt_message *msg;
-    bt_self_component *self_comp = msg_it->self_comp;
-    uint64_t beginning_raw_value = UINT64_C(-1);
-    uint64_t end_raw_value = UINT64_C(-1);
-
-    BT_ASSERT(msg_it->self_msg_iter);
-    BT_ASSERT(msg_it->stream);
-    BT_ASSERT(msg_it->meta.sc->has_discarded_events);
-
-    if (msg_it->meta.sc->discarded_events_have_default_cs) {
-        if (msg_it->prev_packet_snapshots.discarded_events == UINT64_C(-1)) {
-            /*
-             * We discarded events, but before (and possibly
-             * including) the current packet: use this packet's time
-             * range, and do not have a specific count.
-             */
-            beginning_raw_value = msg_it->snapshots.beginning_clock;
-            end_raw_value = msg_it->snapshots.end_clock;
-        } else {
-            beginning_raw_value = msg_it->prev_packet_snapshots.end_clock;
-            end_raw_value = msg_it->snapshots.end_clock;
-        }
-
-        BT_ASSERT(beginning_raw_value != UINT64_C(-1));
-        BT_ASSERT(end_raw_value != UINT64_C(-1));
-        msg = bt_message_discarded_events_create_with_default_clock_snapshots(
-            msg_it->self_msg_iter, msg_it->stream, beginning_raw_value, end_raw_value);
-    } else {
-        msg = bt_message_discarded_events_create(msg_it->self_msg_iter, msg_it->stream);
-    }
-
-    if (!msg) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot create discarded events message: "
-                                  "msg-it-addr=%p, stream-addr=%p",
-                                  msg_it, msg_it->stream);
-        goto end;
-    }
-
-    if (msg_it->prev_packet_snapshots.discarded_events != UINT64_C(-1)) {
-        bt_message_discarded_events_set_count(msg,
-                                              msg_it->snapshots.discarded_events -
-                                                  msg_it->prev_packet_snapshots.discarded_events);
-    }
-
-end:
-    return msg;
-}
-
-static bt_message *create_msg_discarded_packets(struct ctf_msg_iter *msg_it)
-{
-    bt_message *msg;
-    bt_self_component *self_comp = msg_it->self_comp;
-
-    BT_ASSERT(msg_it->self_msg_iter);
-    BT_ASSERT(msg_it->stream);
-    BT_ASSERT(msg_it->meta.sc->has_discarded_packets);
-    BT_ASSERT(msg_it->prev_packet_snapshots.packets != UINT64_C(-1));
-
-    if (msg_it->meta.sc->discarded_packets_have_default_cs) {
-        BT_ASSERT(msg_it->prev_packet_snapshots.end_clock != UINT64_C(-1));
-        BT_ASSERT(msg_it->snapshots.beginning_clock != UINT64_C(-1));
-        msg = bt_message_discarded_packets_create_with_default_clock_snapshots(
-            msg_it->self_msg_iter, msg_it->stream, msg_it->prev_packet_snapshots.end_clock,
-            msg_it->snapshots.beginning_clock);
-    } else {
-        msg = bt_message_discarded_packets_create(msg_it->self_msg_iter, msg_it->stream);
-    }
-
-    if (!msg) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot create discarded packets message: "
-                                  "msg-it-addr=%p, stream-addr=%p",
-                                  msg_it, msg_it->stream);
-        goto end;
-    }
-
-    bt_message_discarded_packets_set_count(msg, msg_it->snapshots.packets -
-                                                    msg_it->prev_packet_snapshots.packets - 1);
-
-end:
-    return msg;
-}
-
-BT_HIDDEN
-struct ctf_msg_iter *ctf_msg_iter_create(struct ctf_trace_class *tc, size_t max_request_sz,
-                                         struct ctf_msg_iter_medium_ops medops, void *data,
-                                         bt_logging_level log_level, bt_self_component *self_comp,
-                                         bt_self_message_iterator *self_msg_iter)
-{
-    struct ctf_msg_iter *msg_it = NULL;
-    struct bt_bfcr_cbs cbs = {
-        .classes =
-            {
-                .signed_int = bfcr_signed_int_cb,
-                .unsigned_int = bfcr_unsigned_int_cb,
-                .floating_point = bfcr_floating_point_cb,
-                .string_begin = bfcr_string_begin_cb,
-                .string = bfcr_string_cb,
-                .string_end = bfcr_string_end_cb,
-                .compound_begin = bfcr_compound_begin_cb,
-                .compound_end = bfcr_compound_end_cb,
-            },
-        .query =
-            {
-                .get_sequence_length = bfcr_get_sequence_length_cb,
-                .borrow_variant_selected_field_class = bfcr_borrow_variant_selected_field_class_cb,
-            },
-    };
-
-    BT_ASSERT(tc);
-    BT_ASSERT(medops.request_bytes);
-    BT_ASSERT(medops.borrow_stream);
-    BT_ASSERT(max_request_sz > 0);
-
-    BT_COMP_LOG_CUR_LVL(BT_LOG_DEBUG, log_level, self_comp,
-                        "Creating CTF plugin message iterator: "
-                        "trace-addr=%p, max-request-size=%zu, "
-                        "data=%p, log-level=%s",
-                        tc, max_request_sz, data, bt_common_logging_level_string(log_level));
-    msg_it = g_new0(struct ctf_msg_iter, 1);
-    if (!msg_it) {
-        BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                            "Failed to allocate one CTF plugin message iterator.");
-        goto end;
-    }
-    msg_it->self_comp = self_comp;
-    msg_it->self_msg_iter = self_msg_iter;
-    msg_it->log_level = log_level;
-    msg_it->meta.tc = tc;
-    msg_it->medium.medops = medops;
-    msg_it->medium.max_request_sz = max_request_sz;
-    msg_it->medium.data = data;
-    msg_it->stack = stack_new(msg_it);
-    msg_it->stored_values = g_array_new(FALSE, TRUE, sizeof(uint64_t));
-    g_array_set_size(msg_it->stored_values, tc->stored_value_count);
-
-    if (!msg_it->stack) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create field stack.");
-        goto error;
-    }
-
-    msg_it->bfcr = bt_bfcr_create(cbs, msg_it, log_level, NULL);
-    if (!msg_it->bfcr) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create binary class reader (BFCR).");
-        goto error;
-    }
-
-    ctf_msg_iter_reset(msg_it);
-    BT_COMP_LOGD("Created CTF plugin message iterator: "
-                 "trace-addr=%p, max-request-size=%zu, "
-                 "data=%p, msg-it-addr=%p, log-level=%s",
-                 tc, max_request_sz, data, msg_it, bt_common_logging_level_string(log_level));
-    msg_it->cur_packet_offset = 0;
-
-end:
-    return msg_it;
-
-error:
-    ctf_msg_iter_destroy(msg_it);
-    msg_it = NULL;
-    goto end;
-}
-
-void ctf_msg_iter_destroy(struct ctf_msg_iter *msg_it)
-{
-    BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
-    BT_STREAM_PUT_REF_AND_RESET(msg_it->stream);
-    release_all_dscopes(msg_it);
-
-    BT_COMP_LOGD("Destroying CTF plugin message iterator: addr=%p", msg_it);
-
-    if (msg_it->stack) {
-        BT_COMP_LOGD_STR("Destroying field stack.");
-        stack_destroy(msg_it->stack);
-    }
-
-    if (msg_it->bfcr) {
-        BT_COMP_LOGD("Destroying BFCR: bfcr-addr=%p", msg_it->bfcr);
-        bt_bfcr_destroy(msg_it->bfcr);
-    }
-
-    if (msg_it->stored_values) {
-        g_array_free(msg_it->stored_values, TRUE);
-    }
-
-    g_free(msg_it);
-}
-
-enum ctf_msg_iter_status ctf_msg_iter_get_next_message(struct ctf_msg_iter *msg_it,
-                                                       const bt_message **message)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-
-    BT_ASSERT_DBG(msg_it);
-    BT_ASSERT_DBG(message);
-    BT_COMP_LOGD("Getting next message: msg-it-addr=%p", msg_it);
-
-    while (true) {
-        status = handle_state(msg_it);
-        if (G_UNLIKELY(status == CTF_MSG_ITER_STATUS_AGAIN)) {
-            BT_COMP_LOGD_STR("Medium returned CTF_MSG_ITER_STATUS_AGAIN.");
-            goto end;
-        } else if (G_UNLIKELY(status != CTF_MSG_ITER_STATUS_OK)) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot handle state: msg-it-addr=%p, state=%s",
-                                      msg_it, state_string(msg_it->state));
-            goto end;
-        }
-
-        switch (msg_it->state) {
-        case STATE_EMIT_MSG_EVENT:
-            BT_ASSERT_DBG(msg_it->event_msg);
-
-            /*
-             * Check if we need to emit the delayed packet
-             * beginning message instead of the event message.
-             */
-            if (G_UNLIKELY(msg_it->emit_delayed_packet_beginning_msg)) {
-                *message = emit_delayed_packet_beg_msg(msg_it);
-                if (!*message) {
-                    status = CTF_MSG_ITER_STATUS_ERROR;
-                }
-
-                /*
-                 * Don't forget to emit the event message of
-                 * the event record that was just decoded.
-                 */
-                msg_it->state = STATE_EMIT_QUEUED_MSG_EVENT;
-
-            } else {
-                *message = msg_it->event_msg;
-                msg_it->event_msg = NULL;
-            }
-            goto end;
-        case STATE_EMIT_MSG_DISCARDED_EVENTS:
-            /* create_msg_discared_events() logs errors */
-            *message = create_msg_discarded_events(msg_it);
-
-            if (!*message) {
-                status = CTF_MSG_ITER_STATUS_ERROR;
-            }
-
-            goto end;
-        case STATE_EMIT_MSG_DISCARDED_PACKETS:
-            /* create_msg_discared_packets() logs errors */
-            *message = create_msg_discarded_packets(msg_it);
-
-            if (!*message) {
-                status = CTF_MSG_ITER_STATUS_ERROR;
-            }
-
-            goto end;
-        case STATE_EMIT_MSG_PACKET_BEGINNING:
-            if (G_UNLIKELY(msg_it->meta.tc->quirks.barectf_event_before_packet)) {
-                msg_it->emit_delayed_packet_beginning_msg = true;
-                /*
-                 * There is no message to return yet as this
-                 * packet beginning message is delayed until we
-                 * decode the first event message of the
-                 * packet.
-                 */
-                break;
-            } else {
-                /* create_msg_packet_beginning() logs errors */
-                *message = create_msg_packet_beginning(msg_it, false);
-                if (!*message) {
-                    status = CTF_MSG_ITER_STATUS_ERROR;
-                }
-            }
-
-            goto end;
-        case STATE_EMIT_MSG_PACKET_END_SINGLE:
-        case STATE_EMIT_MSG_PACKET_END_MULTI:
-            /* create_msg_packet_end() logs errors */
-            *message = create_msg_packet_end(msg_it);
-
-            if (!*message) {
-                status = CTF_MSG_ITER_STATUS_ERROR;
-            }
-
-            goto end;
-        case STATE_EMIT_MSG_STREAM_BEGINNING:
-            /* create_msg_stream_beginning() logs errors */
-            *message = create_msg_stream_beginning(msg_it);
-            msg_it->emit_stream_beginning_message = false;
-            msg_it->emit_stream_end_message = true;
-
-            if (!*message) {
-                status = CTF_MSG_ITER_STATUS_ERROR;
-            }
-
-            goto end;
-        case STATE_EMIT_MSG_STREAM_END:
-            /* create_msg_stream_end() logs errors */
-            *message = create_msg_stream_end(msg_it);
-            msg_it->emit_stream_end_message = false;
-
-            if (!*message) {
-                status = CTF_MSG_ITER_STATUS_ERROR;
-            }
-
-            goto end;
-        case STATE_DONE:
-            status = CTF_MSG_ITER_STATUS_EOF;
-            goto end;
-        default:
-            /* Non-emitting state: continue */
-            break;
-        }
-    }
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status decode_until_state(struct ctf_msg_iter *msg_it,
-                                                   enum state target_state_1,
-                                                   enum state target_state_2)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    bt_self_component *self_comp = msg_it->self_comp;
-
-    BT_ASSERT_DBG(msg_it);
-
-    do {
-        /*
-         * Check if we reached the state at which we want to stop
-         * decoding.
-         */
-        if (msg_it->state == target_state_1 || msg_it->state == target_state_2) {
-            goto end;
-        }
-
-        status = handle_state(msg_it);
-        if (G_UNLIKELY(status == CTF_MSG_ITER_STATUS_AGAIN)) {
-            BT_COMP_LOGD_STR("Medium returned CTF_MSG_ITER_STATUS_AGAIN.");
-            goto end;
-        } else if (G_UNLIKELY(status != CTF_MSG_ITER_STATUS_OK)) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot handle state: msg-it-addr=%p, state=%s",
-                                      msg_it, state_string(msg_it->state));
-            goto end;
-        }
-
-        switch (msg_it->state) {
-        case STATE_INIT:
-        case STATE_SWITCH_PACKET:
-        case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
-        case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
-        case STATE_AFTER_TRACE_PACKET_HEADER:
-        case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
-        case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
-        case STATE_AFTER_STREAM_PACKET_CONTEXT:
-        case STATE_EMIT_MSG_STREAM_BEGINNING:
-        case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
-        case STATE_EMIT_MSG_DISCARDED_EVENTS:
-        case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
-        case STATE_EMIT_MSG_DISCARDED_PACKETS:
-        case STATE_EMIT_MSG_PACKET_BEGINNING:
-        case STATE_DSCOPE_EVENT_HEADER_BEGIN:
-        case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
-        case STATE_AFTER_EVENT_HEADER:
-        case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
-        case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
-        case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
-        case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
-        case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
-        case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
-        case STATE_EMIT_MSG_EVENT:
-        case STATE_EMIT_QUEUED_MSG_EVENT:
-        case STATE_SKIP_PACKET_PADDING:
-        case STATE_EMIT_MSG_PACKET_END_MULTI:
-        case STATE_EMIT_MSG_PACKET_END_SINGLE:
-        case STATE_EMIT_QUEUED_MSG_PACKET_END:
-        case STATE_EMIT_MSG_STREAM_END:
-            break;
-        case STATE_DONE:
-            /* fall-through */
-        default:
-            /* We should never get to the STATE_DONE state. */
-            BT_COMP_LOGF("Unexpected state: msg-it-addr=%p, state=%s", msg_it,
-                         state_string(msg_it->state));
-            bt_common_abort();
-        }
-    } while (true);
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status read_packet_header_context_fields(struct ctf_msg_iter *msg_it)
-{
-    int ret;
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-
-    status = decode_until_state(msg_it, STATE_EMIT_MSG_PACKET_BEGINNING, (state) -1);
-    if (status != CTF_MSG_ITER_STATUS_OK) {
-        goto end;
-    }
-
-    ret = set_current_packet_content_sizes(msg_it);
-    if (ret) {
-        status = CTF_MSG_ITER_STATUS_ERROR;
-        goto end;
-    }
-
-end:
-    return status;
-}
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_seek(struct ctf_msg_iter *msg_it, off_t offset)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-    enum ctf_msg_iter_medium_status medium_status;
-
-    BT_ASSERT(msg_it);
-    BT_ASSERT(offset >= 0);
-    BT_ASSERT(msg_it->medium.medops.seek);
-
-    medium_status = msg_it->medium.medops.seek(offset, msg_it->medium.data);
-    if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-        if (medium_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
-            status = CTF_MSG_ITER_STATUS_EOF;
-        } else {
-            status = CTF_MSG_ITER_STATUS_ERROR;
-            goto end;
-        }
-    }
-
-    ctf_msg_iter_reset(msg_it);
-    msg_it->cur_packet_offset = offset;
-
-end:
-    return status;
-}
-
-static enum ctf_msg_iter_status clock_snapshot_at_msg_iter_state(struct ctf_msg_iter *msg_it,
-                                                                 enum state target_state_1,
-                                                                 enum state target_state_2,
-                                                                 uint64_t *clock_snapshot)
-{
-    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-
-    BT_ASSERT_DBG(msg_it);
-    BT_ASSERT_DBG(clock_snapshot);
-    status = decode_until_state(msg_it, target_state_1, target_state_2);
-    if (status != CTF_MSG_ITER_STATUS_OK) {
-        goto end;
-    }
-
-    *clock_snapshot = msg_it->default_clock_snapshot;
-end:
-    return status;
-}
-
-BT_HIDDEN
-enum ctf_msg_iter_status
-ctf_msg_iter_curr_packet_first_event_clock_snapshot(struct ctf_msg_iter *msg_it,
-                                                    uint64_t *first_clock_snapshot)
-{
-    return clock_snapshot_at_msg_iter_state(msg_it, STATE_AFTER_EVENT_HEADER, (state) -1,
-                                            first_clock_snapshot);
-}
-
-BT_HIDDEN
-enum ctf_msg_iter_status
-ctf_msg_iter_curr_packet_last_event_clock_snapshot(struct ctf_msg_iter *msg_it,
-                                                   uint64_t *last_clock_snapshot)
-{
-    return clock_snapshot_at_msg_iter_state(msg_it, STATE_EMIT_MSG_PACKET_END_SINGLE,
-                                            STATE_EMIT_MSG_PACKET_END_MULTI, last_clock_snapshot);
-}
-
-BT_HIDDEN
-enum ctf_msg_iter_status
-ctf_msg_iter_get_packet_properties(struct ctf_msg_iter *msg_it,
-                                   struct ctf_msg_iter_packet_properties *props)
-{
-    enum ctf_msg_iter_status status;
-
-    BT_ASSERT_DBG(msg_it);
-    BT_ASSERT_DBG(props);
-    status = read_packet_header_context_fields(msg_it);
-    if (status != CTF_MSG_ITER_STATUS_OK) {
-        goto end;
-    }
-
-    props->exp_packet_total_size = msg_it->cur_exp_packet_total_size;
-    props->exp_packet_content_size = msg_it->cur_exp_packet_content_size;
-    props->stream_class_id = (uint64_t) msg_it->cur_stream_class_id;
-    props->data_stream_id = msg_it->cur_data_stream_id;
-    props->snapshots.discarded_events = msg_it->snapshots.discarded_events;
-    props->snapshots.packets = msg_it->snapshots.packets;
-    props->snapshots.beginning_clock = msg_it->snapshots.beginning_clock;
-    props->snapshots.end_clock = msg_it->snapshots.end_clock;
-
-end:
-    return status;
-}
-
-BT_HIDDEN
-void ctf_msg_iter_set_dry_run(struct ctf_msg_iter *msg_it, bool val)
-{
-    msg_it->dry_run = val;
-}
diff --git a/src/plugins/ctf/common/msg-iter/msg-iter.hpp b/src/plugins/ctf/common/msg-iter/msg-iter.hpp
deleted file mode 100644 (file)
index 669cfe3..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
- * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
- *
- * Babeltrace - CTF message iterator
- */
-
-#ifndef CTF_MSG_ITER_H
-#define CTF_MSG_ITER_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-
-#include "../metadata/ctf-meta.hpp"
-
-/**
- * @file ctf-msg-iter.h
- *
- * CTF message iterator
- *
- * This is a common internal API used by CTF source plugins. It allows
- * one to get messages from a user-provided medium.
- */
-
-/**
- * Medium operations status codes.  These use the same values as
- * libbabeltrace2.
- */
-enum ctf_msg_iter_medium_status
-{
-    /**
-     * End of file.
-     *
-     * The medium function called by the message iterator
-     * function reached the end of the file.
-     */
-    CTF_MSG_ITER_MEDIUM_STATUS_EOF = 1,
-
-    /**
-     * There is no data available right now, try again later.
-     */
-    CTF_MSG_ITER_MEDIUM_STATUS_AGAIN = 11,
-
-    /** General error. */
-    CTF_MSG_ITER_MEDIUM_STATUS_ERROR = -1,
-
-    /** Memory error. */
-    CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR = -12,
-
-    /** Everything okay. */
-    CTF_MSG_ITER_MEDIUM_STATUS_OK = 0,
-};
-
-/**
- * CTF message iterator API status code.
- */
-enum ctf_msg_iter_status
-{
-    /**
-     * End of file.
-     *
-     * The medium function called by the message iterator
-     * function reached the end of the file.
-     */
-    CTF_MSG_ITER_STATUS_EOF = CTF_MSG_ITER_MEDIUM_STATUS_EOF,
-
-    /**
-     * There is no data available right now, try again later.
-     *
-     * Some condition resulted in the
-     * ctf_msg_iter_medium_ops::request_bytes() user function not
-     * having access to any data now. You should retry calling the
-     * last called message iterator function once the situation
-     * is resolved.
-     */
-    CTF_MSG_ITER_STATUS_AGAIN = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN,
-
-    /** General error. */
-    CTF_MSG_ITER_STATUS_ERROR = CTF_MSG_ITER_MEDIUM_STATUS_ERROR,
-
-    /** Memory error. */
-    CTF_MSG_ITER_STATUS_MEMORY_ERROR = CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR,
-
-    /** Everything okay. */
-    CTF_MSG_ITER_STATUS_OK = CTF_MSG_ITER_MEDIUM_STATUS_OK,
-};
-
-/**
- * Medium operations.
- *
- * Those user functions are called by the message iterator
- * functions to request medium actions.
- */
-struct ctf_msg_iter_medium_ops
-{
-    /**
-     * Returns the next byte buffer to be used by the binary file
-     * reader to deserialize binary data.
-     *
-     * This function \em must be defined.
-     *
-     * The purpose of this function is to return a buffer of bytes
-     * to the message iterator, of a maximum of \p request_sz
-     * bytes. If this function cannot return a buffer of at least
-     * \p request_sz bytes, it may return a smaller buffer. In
-     * either cases, \p buffer_sz must be set to the returned buffer
-     * size (in bytes).
-     *
-     * The returned buffer's ownership remains the medium, in that
-     * it won't be freed by the message iterator functions. The
-     * returned buffer won't be modified by the message
-     * iterator functions either.
-     *
-     * When this function is called for the first time for a given
-     * file, the offset within the file is considered to be 0. The
-     * next times this function is called, the returned buffer's
-     * byte offset within the complete file must be the previous
-     * offset plus the last returned value of \p buffer_sz by this
-     * medium.
-     *
-     * This function must return one of the following statuses:
-     *
-     *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_OK</b>: Everything
-     *     is okay, i.e. \p buffer_sz is set to a positive value
-     *     reflecting the number of available bytes in the buffer
-     *     starting at the address written in \p buffer_addr.
-     *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_AGAIN</b>: No data is
-     *     available right now. In this case, the message
-     *     iterator function called by the user returns
-     *     #CTF_MSG_ITER_STATUS_AGAIN, and it is the user's
-     *     responsibility to make sure enough data becomes available
-     *     before calling the \em same message iterator
-     *     function again to continue the decoding process.
-     *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_EOF</b>: The end of
-     *     the file was reached, and no more data will ever be
-     *     available for this file. In this case, the message
-     *     iterator function called by the user returns
-     *     #CTF_MSG_ITER_STATUS_EOF. This must \em not be
-     *     returned when returning at least one byte of data to the
-     *     caller, i.e. this must be returned when there's
-     *     absolutely nothing left; should the request size be
-     *     larger than what's left in the file, this function must
-     *     return what's left, setting \p buffer_sz to the number of
-     *     remaining bytes, and return
-     *     #CTF_MSG_ITER_MEDIUM_STATUS_EOF on the \em following
-     *     call.
-     *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_ERROR</b>: A fatal
-     *     error occurred during this operation. In this case, the
-     *     message iterator function called by the user returns
-     *     #CTF_MSG_ITER_STATUS_ERROR.
-     *
-     * If #CTF_MSG_ITER_MEDIUM_STATUS_OK is not returned, the
-     * values of \p buffer_sz and \p buffer_addr are \em ignored by
-     * the caller.
-     *
-     * @param request_sz       Requested buffer size (bytes)
-     * @param buffer_addr      Returned buffer address
-     * @param buffer_sz        Returned buffer's size (bytes)
-     * @param data             User data
-     * @returns                Status code (see description above)
-     */
-    enum ctf_msg_iter_medium_status (*request_bytes)(size_t request_sz, uint8_t **buffer_addr,
-                                                     size_t *buffer_sz, void *data);
-
-    /**
-     * Repositions the underlying stream's position.
-     *
-     * This *optional* method repositions the underlying stream
-     * to a given absolute position in the medium.
-     *
-     * @param offset   Offset to use for the given directive
-     * @param data             User data
-     * @returns                One of #ctf_msg_iter_medium_status values
-     */
-    enum ctf_msg_iter_medium_status (*seek)(off_t offset, void *data);
-
-    /**
-     * Called when the message iterator wishes to inform the medium that it
-     * is about to start a new packet.
-     *
-     * After the iterator has called switch_packet, the following call to
-     * request_bytes must return the content at the start of the next
-     * packet.  */
-    enum ctf_msg_iter_medium_status (*switch_packet)(void *data);
-
-    /**
-     * Returns a stream instance (weak reference) for the given
-     * stream class.
-     *
-     * This is called after a packet header is read, and the
-     * corresponding stream class is found by the message
-     * iterator.
-     *
-     * @param stream_class     Stream class of the stream to get
-     * @param stream_id        Stream (instance) ID of the stream
-     *                 to get (-1ULL if not available)
-     * @param data             User data
-     * @returns                Stream instance (weak reference) or
-     *                 \c NULL on error
-     */
-    bt_stream *(*borrow_stream)(bt_stream_class *stream_class, int64_t stream_id, void *data);
-};
-
-/** CTF message iterator. */
-struct ctf_msg_iter;
-
-/**
- * Creates a CTF message iterator.
- *
- * Upon successful completion, the reference count of \p trace is
- * incremented.
- *
- * @param trace                        Trace to read
- * @param max_request_sz       Maximum buffer size, in bytes, to
- *                             request to
- *                             ctf_msg_iter_medium_ops::request_bytes()
- *                             at a time
- * @param medops               Medium operations
- * @param medops_data          User data (passed to medium operations)
- * @returns                    New CTF message iterator on
- *                             success, or \c NULL on error
- */
-BT_HIDDEN
-struct ctf_msg_iter *ctf_msg_iter_create(struct ctf_trace_class *tc, size_t max_request_sz,
-                                         struct ctf_msg_iter_medium_ops medops, void *medops_data,
-                                         bt_logging_level log_level, bt_self_component *self_comp,
-                                         bt_self_message_iterator *self_msg_iter);
-
-/**
- * Destroys a CTF message iterator, freeing all internal resources.
- *
- * The registered trace's reference count is decremented.
- *
- * @param msg_iter             CTF message iterator
- */
-BT_HIDDEN
-void ctf_msg_iter_destroy(struct ctf_msg_iter *msg_iter);
-
-/**
- * Returns the next message from a CTF message iterator.
- *
- * Upon successful completion, #CTF_MSG_ITER_STATUS_OK is
- * returned, and the next message is written to \p msg.
- * In this case, the caller is responsible for calling
- * bt_message_put() on the returned message.
- *
- * If this function returns #CTF_MSG_ITER_STATUS_AGAIN, the caller
- * should make sure that data becomes available to its medium, and
- * call this function again, until another status is returned.
- *
- * @param msg_iter             CTF message iterator
- * @param message              Returned message if the function's
- *                             return value is #CTF_MSG_ITER_STATUS_OK
- * @returns                    One of #ctf_msg_iter_status values
- */
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_get_next_message(struct ctf_msg_iter *msg_it,
-                                                       const bt_message **message);
-
-struct ctf_msg_iter_packet_properties
-{
-    int64_t exp_packet_total_size;
-    int64_t exp_packet_content_size;
-    uint64_t stream_class_id;
-    int64_t data_stream_id;
-
-    struct
-    {
-        uint64_t discarded_events;
-        uint64_t packets;
-        uint64_t beginning_clock;
-        uint64_t end_clock;
-    } snapshots;
-};
-
-BT_HIDDEN
-enum ctf_msg_iter_status
-ctf_msg_iter_get_packet_properties(struct ctf_msg_iter *msg_it,
-                                   struct ctf_msg_iter_packet_properties *props);
-
-BT_HIDDEN
-enum ctf_msg_iter_status
-ctf_msg_iter_curr_packet_first_event_clock_snapshot(struct ctf_msg_iter *msg_it,
-                                                    uint64_t *first_event_cs);
-
-BT_HIDDEN
-enum ctf_msg_iter_status
-ctf_msg_iter_curr_packet_last_event_clock_snapshot(struct ctf_msg_iter *msg_it,
-                                                   uint64_t *last_event_cs);
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_seek(struct ctf_msg_iter *msg_it, off_t offset);
-
-/*
- * Resets the iterator so that the next requested medium bytes are
- * assumed to be the first bytes of a new stream. Depending on
- * ctf_msg_iter_set_emit_stream_beginning_message(), the first message
- * which this iterator emits after calling ctf_msg_iter_reset() is of
- * type `CTF_MESSAGE_TYPE_STREAM_BEGINNING`.
- */
-BT_HIDDEN
-void ctf_msg_iter_reset(struct ctf_msg_iter *msg_it);
-
-/*
- * Like ctf_msg_iter_reset(), but preserves stream-dependent state.
- */
-BT_HIDDEN
-void ctf_msg_iter_reset_for_next_stream_file(struct ctf_msg_iter *msg_it);
-
-BT_HIDDEN
-void ctf_msg_iter_set_dry_run(struct ctf_msg_iter *msg_it, bool val);
-
-static inline const char *ctf_msg_iter_medium_status_string(enum ctf_msg_iter_medium_status status)
-{
-    switch (status) {
-    case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
-        return "EOF";
-    case CTF_MSG_ITER_MEDIUM_STATUS_AGAIN:
-        return "AGAIN";
-    case CTF_MSG_ITER_MEDIUM_STATUS_ERROR:
-        return "ERROR";
-    case CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR:
-        return "MEMORY_ERROR";
-    case CTF_MSG_ITER_MEDIUM_STATUS_OK:
-        return "OK";
-    }
-
-    bt_common_abort();
-}
-
-static inline const char *ctf_msg_iter_status_string(enum ctf_msg_iter_status status)
-{
-    switch (status) {
-    case CTF_MSG_ITER_STATUS_EOF:
-        return "EOF";
-    case CTF_MSG_ITER_STATUS_AGAIN:
-        return "AGAIN";
-    case CTF_MSG_ITER_STATUS_ERROR:
-        return "ERROR";
-    case CTF_MSG_ITER_STATUS_MEMORY_ERROR:
-        return "MEMORY_ERROR";
-    case CTF_MSG_ITER_STATUS_OK:
-        return "OK";
-    }
-
-    bt_common_abort();
-}
-
-#endif /* CTF_MSG_ITER_H */
diff --git a/src/plugins/ctf/common/print.hpp b/src/plugins/ctf/common/print.hpp
deleted file mode 100644 (file)
index 90c8d40..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
- *
- * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file.
- */
-
-#ifndef CTF_BTR_PRINT_H
-#define CTF_BTR_PRINT_H
-
-#include <stdio.h>
-#include "common/macros.h"
-
-#define PERR(fmt, ...)                                                                             \
-    do {                                                                                           \
-        if (PRINT_ERR_STREAM) {                                                                    \
-            fprintf(PRINT_ERR_STREAM, "Error: " PRINT_PREFIX ": " fmt, ##__VA_ARGS__);             \
-        }                                                                                          \
-    } while (0)
-
-#define PWARN(fmt, ...)                                                                            \
-    do {                                                                                           \
-        if (PRINT_ERR_STREAM) {                                                                    \
-            fprintf(PRINT_ERR_STREAM, "Warning: " PRINT_PREFIX ": " fmt, ##__VA_ARGS__);           \
-        }                                                                                          \
-    } while (0)
-
-#define PDBG(fmt, ...)                                                                             \
-    do {                                                                                           \
-        if (babeltrace_debug) {                                                                    \
-            fprintf(stderr, "Debug: " PRINT_PREFIX ": " fmt, ##__VA_ARGS__);                       \
-        }                                                                                          \
-    } while (0)
-
-#endif /* CTF_BTR_PRINT_H */
diff --git a/src/plugins/ctf/common/src/bfcr/bfcr.cpp b/src/plugins/ctf/common/src/bfcr/bfcr.cpp
new file mode 100644 (file)
index 0000000..2b5cb79
--- /dev/null
@@ -0,0 +1,1271 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Babeltrace - CTF binary field class reader (BFCR)
+ */
+
+#include "common/align.h"
+#include "common/assert.h"
+#include "common/common.h"
+#include "compat/bitfield.h"
+#include "cpp-common/bt2c/logging.hpp"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "../metadata/tsdl/ctf-meta.hpp"
+#include "bfcr.hpp"
+
+#define DIV8(_x)                ((_x) >> 3)
+#define BYTES_TO_BITS(_x)       ((_x) *8)
+#define BITS_TO_BYTES_FLOOR(_x) DIV8(_x)
+#define BITS_TO_BYTES_CEIL(_x)  DIV8((_x) + 7)
+#define IN_BYTE_OFFSET(_at)     ((_at) &7)
+
+/* A visit stack entry */
+struct stack_entry
+{
+    /*
+     * Current class of base field, one of:
+     *
+     *   * Structure
+     *   * Array
+     *   * Sequence
+     *   * Variant
+     */
+    struct ctf_field_class *base_class;
+
+    /* Length of base field (always 1 for a variant class) */
+    int64_t base_len;
+
+    /* Index of next field to read */
+    int64_t index;
+};
+
+/* Visit stack */
+struct stack
+{
+    struct bt_bfcr *bfcr;
+
+    /* Entries (struct stack_entry) */
+    GArray *entries;
+
+    /* Number of active entries */
+    size_t size;
+};
+
+/* Reading states */
+enum bfcr_state
+{
+    BFCR_STATE_NEXT_FIELD,
+    BFCR_STATE_ALIGN_BASIC,
+    BFCR_STATE_ALIGN_COMPOUND,
+    BFCR_STATE_READ_BASIC_BEGIN,
+    BFCR_STATE_READ_BASIC_CONTINUE,
+    BFCR_STATE_DONE,
+};
+
+static const char *format_as(bfcr_state state) noexcept
+{
+    switch (state) {
+    case BFCR_STATE_NEXT_FIELD:
+        return "NEXT_FIELD";
+
+    case BFCR_STATE_ALIGN_BASIC:
+        return "ALIGN_BASIC";
+
+    case BFCR_STATE_ALIGN_COMPOUND:
+        return "ALIGN_COMPOUND";
+
+    case BFCR_STATE_READ_BASIC_BEGIN:
+        return "READ_BASIC_BEGIN";
+
+    case BFCR_STATE_READ_BASIC_CONTINUE:
+        return "READ_BASIC_CONTINUE";
+
+    case BFCR_STATE_DONE:
+        return "DONE";
+    }
+
+    bt_common_abort();
+}
+
+/* Binary class reader */
+struct bt_bfcr
+{
+    explicit bt_bfcr(const bt2c::Logger& parentLogger) : logger {parentLogger, "PLUGIN/CTF/BFCR"}
+    {
+    }
+
+    bt2c::Logger logger;
+
+    /* BFCR stack */
+    struct stack *stack = nullptr;
+
+    /* Current basic field class */
+    struct ctf_field_class *cur_basic_field_class = nullptr;
+
+    /* Current state */
+    enum bfcr_state state = static_cast<bfcr_state>(0);
+
+    /*
+     * Last basic field class's byte order.
+     *
+     * This is used to detect errors since two contiguous basic
+     * classes for which the common boundary is not the boundary of
+     * a byte cannot have different byte orders.
+     *
+     * This is set to CTF_BYTE_ORDER_UNKNOWN on reset and when the last
+     * basic field class was a string class.
+     */
+    enum ctf_byte_order last_bo = CTF_BYTE_ORDER_UNKNOWN;
+
+    /* Current byte order (copied to last_bo after a successful read) */
+    enum ctf_byte_order cur_bo = CTF_BYTE_ORDER_UNKNOWN;
+
+    /* Stitch buffer infos */
+    struct
+    {
+        /* Stitch buffer */
+        uint8_t buf[16] {};
+
+        /* Offset, within stitch buffer, of first bit */
+        size_t offset = 0;
+
+        /* Length (bits) of data in stitch buffer from offset */
+        size_t at = 0;
+    } stitch;
+
+    /* User buffer infos */
+    struct
+    {
+        /* Address */
+        const uint8_t *addr = nullptr;
+
+        /* Offset of data from address (bits) */
+        size_t offset = 0;
+
+        /* Current position from offset (bits) */
+        size_t at = 0;
+
+        /* Offset of offset within whole packet (bits) */
+        size_t packet_offset = 0;
+
+        /* Data size in buffer (bits) */
+        size_t sz = 0;
+
+        /* Buffer size (bytes) */
+        size_t buf_sz = 0;
+    } buf;
+
+    /* User stuff */
+    struct
+    {
+        /* Callback functions */
+        bt_bfcr_cbs cbs {};
+
+        /* Private data */
+        void *data = nullptr;
+    } user;
+};
+
+static struct stack *stack_new(struct bt_bfcr *bfcr)
+{
+    struct stack *stack = NULL;
+
+    stack = g_new0(struct stack, 1);
+    if (!stack) {
+        BT_CPPLOGE_STR_SPEC(bfcr->logger, "Failed to allocate one stack.");
+        goto error;
+    }
+
+    stack->bfcr = bfcr;
+    stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry));
+    if (!stack->entries) {
+        BT_CPPLOGE_STR_SPEC(bfcr->logger, "Failed to allocate a GArray.");
+        goto error;
+    }
+
+    BT_CPPLOGD_SPEC(bfcr->logger, "Created stack: addr={}", fmt::ptr(stack));
+    return stack;
+
+error:
+    g_free(stack);
+    return NULL;
+}
+
+static void stack_destroy(struct stack *stack)
+{
+    struct bt_bfcr *bfcr;
+
+    if (!stack) {
+        return;
+    }
+
+    bfcr = stack->bfcr;
+    BT_CPPLOGD_SPEC(bfcr->logger, "Destroying stack: addr={}", fmt::ptr(stack));
+
+    if (stack->entries) {
+        g_array_free(stack->entries, TRUE);
+    }
+
+    g_free(stack);
+}
+
+static int stack_push(struct stack *stack, struct ctf_field_class *base_class, size_t base_len)
+{
+    struct stack_entry *entry;
+    struct bt_bfcr *bfcr;
+
+    BT_ASSERT_DBG(stack);
+    BT_ASSERT_DBG(base_class);
+    bfcr = stack->bfcr;
+    BT_CPPLOGT_SPEC(bfcr->logger,
+                    "Pushing field class on stack: stack-addr={}, "
+                    "fc-addr={}, fc-type={}, base-length={}, "
+                    "stack-size-before={}, stack-size-after={}",
+                    fmt::ptr(stack), fmt::ptr(base_class), (int) base_class->type, base_len,
+                    stack->size, stack->size + 1);
+
+    if (stack->entries->len == stack->size) {
+        g_array_set_size(stack->entries, stack->size + 1);
+    }
+
+    entry = &bt_g_array_index(stack->entries, struct stack_entry, stack->size);
+    entry->base_class = base_class;
+    entry->base_len = base_len;
+    entry->index = 0;
+    stack->size++;
+    return 0;
+}
+
+static inline int64_t get_compound_field_class_length(struct bt_bfcr *bfcr,
+                                                      struct ctf_field_class *fc)
+{
+    int64_t length;
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+        length = (int64_t) struct_fc->members->len;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        /* Variant field classes always "contain" a single class */
+        length = 1;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    {
+        struct ctf_field_class_array *array_fc = ctf_field_class_as_array(fc);
+
+        length = (int64_t) array_fc->length;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+        length = bfcr->user.cbs.query.get_sequence_length(fc, bfcr->user.data);
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    return length;
+}
+
+static int stack_push_with_len(struct bt_bfcr *bfcr, struct ctf_field_class *base_class)
+{
+    int ret;
+    int64_t length = get_compound_field_class_length(bfcr, base_class);
+
+    if (length < 0) {
+        BT_CPPLOGW_SPEC(bfcr->logger,
+                        "Cannot get compound field class's field count: "
+                        "bfcr-addr={}, fc-addr={}, fc-type={}",
+                        fmt::ptr(bfcr), fmt::ptr(base_class), (int) base_class->type);
+        ret = BT_BFCR_STATUS_ERROR;
+        goto end;
+    }
+
+    ret = stack_push(bfcr->stack, base_class, (size_t) length);
+
+end:
+    return ret;
+}
+
+static inline unsigned int stack_size(struct stack *stack)
+{
+    BT_ASSERT_DBG(stack);
+    return stack->size;
+}
+
+static void stack_pop(struct stack *stack)
+{
+    struct bt_bfcr *bfcr;
+
+    BT_ASSERT_DBG(stack);
+    BT_ASSERT_DBG(stack_size(stack));
+    bfcr = stack->bfcr;
+    BT_CPPLOGT_SPEC(bfcr->logger,
+                    "Popping from stack: "
+                    "stack-addr={}, stack-size-before={}, stack-size-after={}",
+                    fmt::ptr(stack), stack->entries->len, stack->entries->len - 1);
+    stack->size--;
+}
+
+static inline bool stack_empty(struct stack *stack)
+{
+    return stack_size(stack) == 0;
+}
+
+static void stack_clear(struct stack *stack)
+{
+    BT_ASSERT_DBG(stack);
+    stack->size = 0;
+}
+
+static inline struct stack_entry *stack_top(struct stack *stack)
+{
+    BT_ASSERT_DBG(stack);
+    BT_ASSERT_DBG(stack_size(stack));
+    return &bt_g_array_index(stack->entries, struct stack_entry, stack->size - 1);
+}
+
+static inline size_t available_bits(struct bt_bfcr *bfcr)
+{
+    return bfcr->buf.sz - bfcr->buf.at;
+}
+
+static inline void consume_bits(struct bt_bfcr *bfcr, size_t incr)
+{
+    BT_CPPLOGT_SPEC(bfcr->logger, "Advancing cursor: bfcr-addr={}, cur-before={}, cur-after={}",
+                    fmt::ptr(bfcr), bfcr->buf.at, bfcr->buf.at + incr);
+    bfcr->buf.at += incr;
+}
+
+static inline bool has_enough_bits(struct bt_bfcr *bfcr, size_t sz)
+{
+    return available_bits(bfcr) >= sz;
+}
+
+static inline bool at_least_one_bit_left(struct bt_bfcr *bfcr)
+{
+    return has_enough_bits(bfcr, 1);
+}
+
+static inline size_t packet_at(struct bt_bfcr *bfcr)
+{
+    return bfcr->buf.packet_offset + bfcr->buf.at;
+}
+
+static inline size_t buf_at_from_addr(struct bt_bfcr *bfcr)
+{
+    /*
+     * Considering this:
+     *
+     *     ====== offset ===== (17)
+     *
+     *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
+     *     ^
+     *     addr (0)           ==== at ==== (12)
+     *
+     * We want this:
+     *
+     *     =============================== (29)
+     */
+    return bfcr->buf.offset + bfcr->buf.at;
+}
+
+static void stitch_reset(struct bt_bfcr *bfcr)
+{
+    bfcr->stitch.offset = 0;
+    bfcr->stitch.at = 0;
+}
+
+static inline size_t stitch_at_from_addr(struct bt_bfcr *bfcr)
+{
+    return bfcr->stitch.offset + bfcr->stitch.at;
+}
+
+static void stitch_append_from_buf(struct bt_bfcr *bfcr, size_t sz)
+{
+    size_t stitch_byte_at;
+    size_t buf_byte_at;
+    size_t nb_bytes;
+
+    if (sz == 0) {
+        return;
+    }
+
+    stitch_byte_at = BITS_TO_BYTES_FLOOR(stitch_at_from_addr(bfcr));
+    buf_byte_at = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr));
+    nb_bytes = BITS_TO_BYTES_CEIL(sz);
+    BT_ASSERT(nb_bytes > 0);
+    BT_ASSERT(bfcr->buf.addr);
+    memcpy(&bfcr->stitch.buf[stitch_byte_at], &bfcr->buf.addr[buf_byte_at], nb_bytes);
+    bfcr->stitch.at += sz;
+    consume_bits(bfcr, sz);
+}
+
+static void stitch_append_from_remaining_buf(struct bt_bfcr *bfcr)
+{
+    stitch_append_from_buf(bfcr, available_bits(bfcr));
+}
+
+static void stitch_set_from_remaining_buf(struct bt_bfcr *bfcr)
+{
+    stitch_reset(bfcr);
+    bfcr->stitch.offset = IN_BYTE_OFFSET(buf_at_from_addr(bfcr));
+    stitch_append_from_remaining_buf(bfcr);
+}
+
+static inline void read_unsigned_bitfield(struct bt_bfcr *bfcr, const uint8_t *buf, size_t at,
+                                          unsigned int field_size, enum ctf_byte_order bo,
+                                          uint64_t *v)
+{
+    switch (bo) {
+    case CTF_BYTE_ORDER_BIG:
+        bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
+        break;
+    case CTF_BYTE_ORDER_LITTLE:
+        bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    BT_CPPLOGT_SPEC(bfcr->logger, "Read unsigned bit array: cur={}, size={}, bo={}, val={}", at,
+                    field_size, (int) bo, *v);
+}
+
+static inline void read_signed_bitfield(struct bt_bfcr *bfcr, const uint8_t *buf, size_t at,
+                                        unsigned int field_size, enum ctf_byte_order bo, int64_t *v)
+{
+    switch (bo) {
+    case CTF_BYTE_ORDER_BIG:
+        bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
+        break;
+    case CTF_BYTE_ORDER_LITTLE:
+        bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    BT_CPPLOGT_SPEC(bfcr->logger, "Read signed bit array: cur={}, size={}, bo={}, val={}", at,
+                    field_size, (int) bo, *v);
+}
+
+typedef enum bt_bfcr_status (*read_basic_and_call_cb_t)(struct bt_bfcr *, const uint8_t *, size_t);
+
+static inline enum bt_bfcr_status validate_contiguous_bo(struct bt_bfcr *bfcr,
+                                                         enum ctf_byte_order next_bo)
+{
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+    /* Always valid when at a byte boundary */
+    if (packet_at(bfcr) % 8 == 0) {
+        goto end;
+    }
+
+    /* Always valid if last byte order is unknown */
+    if (bfcr->last_bo == CTF_BYTE_ORDER_UNKNOWN) {
+        goto end;
+    }
+
+    /* Always valid if next byte order is unknown */
+    if (next_bo == CTF_BYTE_ORDER_UNKNOWN) {
+        goto end;
+    }
+
+    /* Make sure last byte order is compatible with the next byte order */
+    switch (bfcr->last_bo) {
+    case CTF_BYTE_ORDER_BIG:
+        if (next_bo != CTF_BYTE_ORDER_BIG) {
+            status = BT_BFCR_STATUS_ERROR;
+        }
+        break;
+    case CTF_BYTE_ORDER_LITTLE:
+        if (next_bo != CTF_BYTE_ORDER_LITTLE) {
+            status = BT_BFCR_STATUS_ERROR;
+        }
+        break;
+    default:
+        status = BT_BFCR_STATUS_ERROR;
+    }
+
+end:
+    if (status < 0) {
+        BT_CPPLOGW_SPEC(bfcr->logger,
+                        "Cannot read bit array: two different byte orders not at a byte boundary: "
+                        "bfcr-addr={}, last-bo={}, next-bo={}",
+                        fmt::ptr(bfcr), (int) bfcr->last_bo, (int) next_bo);
+    }
+
+    return status;
+}
+
+static enum bt_bfcr_status read_basic_float_and_call_cb(struct bt_bfcr *bfcr, const uint8_t *buf,
+                                                        size_t at)
+{
+    double dblval;
+    unsigned int field_size;
+    enum ctf_byte_order bo;
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+    ctf_field_class_float *fc = ctf_field_class_as_float(bfcr->cur_basic_field_class);
+
+    BT_ASSERT_DBG(fc);
+    field_size = fc->base.size;
+    bo = fc->base.byte_order;
+    bfcr->cur_bo = bo;
+
+    switch (field_size) {
+    case 32:
+    {
+        uint64_t v;
+        union
+        {
+            uint32_t u;
+            float f;
+        } f32;
+
+        read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &v);
+        f32.u = (uint32_t) v;
+        dblval = (double) f32.f;
+        break;
+    }
+    case 64:
+    {
+        union
+        {
+            uint64_t u;
+            double d;
+        } f64;
+
+        read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &f64.u);
+        dblval = f64.d;
+        break;
+    }
+    default:
+        /* Only 32-bit and 64-bit fields are supported currently */
+        bt_common_abort();
+    }
+
+    BT_CPPLOGT_SPEC(bfcr->logger, "Read floating point number value: bfcr={}, cur={}, val={}",
+                    fmt::ptr(bfcr), at, dblval);
+
+    if (bfcr->user.cbs.classes.floating_point) {
+        BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (floating point number).");
+        status = bfcr->user.cbs.classes.floating_point(dblval, bfcr->cur_basic_field_class,
+                                                       bfcr->user.data);
+        BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", status);
+        if (status != BT_BFCR_STATUS_OK) {
+            BT_CPPLOGW_SPEC(bfcr->logger, "User function failed: bfcr-addr={}, status={}",
+                            fmt::ptr(bfcr), status);
+        }
+    }
+
+    return status;
+}
+
+static inline enum bt_bfcr_status read_basic_int_and_call_cb(struct bt_bfcr *bfcr,
+                                                             const uint8_t *buf, size_t at)
+{
+    unsigned int field_size;
+    enum ctf_byte_order bo;
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+    ctf_field_class_int *fc = ctf_field_class_as_int(bfcr->cur_basic_field_class);
+
+    field_size = fc->base.size;
+    bo = fc->base.byte_order;
+
+    /*
+     * Update current byte order now because we could be reading
+     * the integer value of an enumeration class, and thus we know
+     * here the actual supporting integer class's byte order.
+     */
+    bfcr->cur_bo = bo;
+
+    if (fc->is_signed) {
+        int64_t v;
+
+        read_signed_bitfield(bfcr, buf, at, field_size, bo, &v);
+
+        if (bfcr->user.cbs.classes.signed_int) {
+            BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (signed integer).");
+            status =
+                bfcr->user.cbs.classes.signed_int(v, bfcr->cur_basic_field_class, bfcr->user.data);
+            BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", status);
+            if (status != BT_BFCR_STATUS_OK) {
+                BT_CPPLOGW_SPEC(bfcr->logger,
+                                "User function failed: "
+                                "bfcr-addr={}, status={}",
+                                fmt::ptr(bfcr), status);
+            }
+        }
+    } else {
+        uint64_t v;
+
+        read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &v);
+
+        if (bfcr->user.cbs.classes.unsigned_int) {
+            BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (unsigned integer).");
+            status = bfcr->user.cbs.classes.unsigned_int(v, bfcr->cur_basic_field_class,
+                                                         bfcr->user.data);
+            BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", status);
+            if (status != BT_BFCR_STATUS_OK) {
+                BT_CPPLOGW_SPEC(bfcr->logger,
+                                "User function failed: "
+                                "bfcr-addr={}, status={}",
+                                fmt::ptr(bfcr), status);
+            }
+        }
+    }
+
+    return status;
+}
+
+static inline enum bt_bfcr_status
+read_bit_array_class_and_call_continue(struct bt_bfcr *bfcr,
+                                       read_basic_and_call_cb_t read_basic_and_call_cb)
+{
+    size_t available;
+    size_t needed_bits;
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+    ctf_field_class_bit_array *fc = ctf_field_class_as_bit_array(bfcr->cur_basic_field_class);
+
+    if (!at_least_one_bit_left(bfcr)) {
+        BT_CPPLOGT_SPEC(bfcr->logger, "Reached end of data: bfcr-addr={}", fmt::ptr(bfcr));
+        status = BT_BFCR_STATUS_EOF;
+        goto end;
+    }
+
+    available = available_bits(bfcr);
+    needed_bits = fc->size - bfcr->stitch.at;
+    BT_CPPLOGT_SPEC(bfcr->logger,
+                    "Continuing basic field decoding: "
+                    "bfcr-addr={}, field-size={}, needed-size={}, "
+                    "available-size={}",
+                    fmt::ptr(bfcr), fc->size, needed_bits, available);
+    if (needed_bits <= available) {
+        /* We have all the bits; append to stitch, then decode */
+        stitch_append_from_buf(bfcr, needed_bits);
+        status = read_basic_and_call_cb(bfcr, bfcr->stitch.buf, bfcr->stitch.offset);
+        if (status != BT_BFCR_STATUS_OK) {
+            BT_CPPLOGW_SPEC(bfcr->logger,
+                            "Cannot read basic field: "
+                            "bfcr-addr={}, fc-addr={}, status={}",
+                            fmt::ptr(bfcr), fmt::ptr(bfcr->cur_basic_field_class), status);
+            goto end;
+        }
+
+        if (stack_empty(bfcr->stack)) {
+            /* Root is a basic class */
+            bfcr->state = BFCR_STATE_DONE;
+        } else {
+            /* Go to next field */
+            stack_top(bfcr->stack)->index++;
+            bfcr->state = BFCR_STATE_NEXT_FIELD;
+            bfcr->last_bo = bfcr->cur_bo;
+        }
+        goto end;
+    }
+
+    /* We are here; it means we don't have enough data to decode this */
+    BT_CPPLOGT_STR_SPEC(
+        bfcr->logger, "Not enough data to read the next basic field: appending to stitch buffer.");
+    stitch_append_from_remaining_buf(bfcr);
+    status = BT_BFCR_STATUS_EOF;
+
+end:
+    return status;
+}
+
+static inline enum bt_bfcr_status
+read_bit_array_class_and_call_begin(struct bt_bfcr *bfcr,
+                                    read_basic_and_call_cb_t read_basic_and_call_cb)
+{
+    size_t available;
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+    ctf_field_class_bit_array *fc = ctf_field_class_as_bit_array(bfcr->cur_basic_field_class);
+
+    if (!at_least_one_bit_left(bfcr)) {
+        BT_CPPLOGT_SPEC(bfcr->logger, "Reached end of data: bfcr-addr={}", fmt::ptr(bfcr));
+        status = BT_BFCR_STATUS_EOF;
+        goto end;
+    }
+
+    status = validate_contiguous_bo(bfcr, fc->byte_order);
+    if (status != BT_BFCR_STATUS_OK) {
+        /* validate_contiguous_bo() logs errors */
+        goto end;
+    }
+
+    available = available_bits(bfcr);
+
+    if (fc->size <= available) {
+        /* We have all the bits; decode and set now */
+        BT_ASSERT_DBG(bfcr->buf.addr);
+        status = read_basic_and_call_cb(bfcr, bfcr->buf.addr, buf_at_from_addr(bfcr));
+        if (status != BT_BFCR_STATUS_OK) {
+            BT_CPPLOGW_SPEC(bfcr->logger,
+                            "Cannot read basic field: "
+                            "bfcr-addr={}, fc-addr={}, status={}",
+                            fmt::ptr(bfcr), fmt::ptr(bfcr->cur_basic_field_class), status);
+            goto end;
+        }
+
+        consume_bits(bfcr, fc->size);
+
+        if (stack_empty(bfcr->stack)) {
+            /* Root is a basic class */
+            bfcr->state = BFCR_STATE_DONE;
+        } else {
+            /* Go to next field */
+            stack_top(bfcr->stack)->index++;
+            bfcr->state = BFCR_STATE_NEXT_FIELD;
+            bfcr->last_bo = bfcr->cur_bo;
+        }
+
+        goto end;
+    }
+
+    /* We are here; it means we don't have enough data to decode this */
+    BT_CPPLOGT_STR_SPEC(bfcr->logger,
+                        "Not enough data to read the next basic field: setting stitch buffer.");
+    stitch_set_from_remaining_buf(bfcr);
+    bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE;
+    status = BT_BFCR_STATUS_EOF;
+
+end:
+    return status;
+}
+
+static inline enum bt_bfcr_status read_basic_int_class_and_call_begin(struct bt_bfcr *bfcr)
+{
+    return read_bit_array_class_and_call_begin(bfcr, read_basic_int_and_call_cb);
+}
+
+static inline enum bt_bfcr_status read_basic_int_class_and_call_continue(struct bt_bfcr *bfcr)
+{
+    return read_bit_array_class_and_call_continue(bfcr, read_basic_int_and_call_cb);
+}
+
+static inline enum bt_bfcr_status read_basic_float_class_and_call_begin(struct bt_bfcr *bfcr)
+{
+    return read_bit_array_class_and_call_begin(bfcr, read_basic_float_and_call_cb);
+}
+
+static inline enum bt_bfcr_status read_basic_float_class_and_call_continue(struct bt_bfcr *bfcr)
+{
+    return read_bit_array_class_and_call_continue(bfcr, read_basic_float_and_call_cb);
+}
+
+static inline enum bt_bfcr_status read_basic_string_class_and_call(struct bt_bfcr *bfcr, bool begin)
+{
+    size_t buf_at_bytes;
+    const uint8_t *result;
+    size_t available_bytes;
+    const uint8_t *first_chr;
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+    if (!at_least_one_bit_left(bfcr)) {
+        BT_CPPLOGT_SPEC(bfcr->logger, "Reached end of data: bfcr-addr={}", fmt::ptr(bfcr));
+        status = BT_BFCR_STATUS_EOF;
+        goto end;
+    }
+
+    BT_ASSERT_DBG(buf_at_from_addr(bfcr) % 8 == 0);
+    available_bytes = BITS_TO_BYTES_FLOOR(available_bits(bfcr));
+    buf_at_bytes = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr));
+    BT_ASSERT_DBG(bfcr->buf.addr);
+    first_chr = &bfcr->buf.addr[buf_at_bytes];
+    result = (const uint8_t *) memchr(first_chr, '\0', available_bytes);
+
+    if (begin && bfcr->user.cbs.classes.string_begin) {
+        BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (string, beginning).");
+        status = bfcr->user.cbs.classes.string_begin(bfcr->cur_basic_field_class, bfcr->user.data);
+        BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", status);
+        if (status != BT_BFCR_STATUS_OK) {
+            BT_CPPLOGW_SPEC(bfcr->logger, "User function failed: bfcr-addr={}, status={}",
+                            fmt::ptr(bfcr), status);
+            goto end;
+        }
+    }
+
+    if (!result) {
+        /* No null character yet */
+        if (bfcr->user.cbs.classes.string) {
+            BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (substring).");
+            status = bfcr->user.cbs.classes.string((const char *) first_chr, available_bytes,
+                                                   bfcr->cur_basic_field_class, bfcr->user.data);
+            BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", status);
+            if (status != BT_BFCR_STATUS_OK) {
+                BT_CPPLOGW_SPEC(bfcr->logger,
+                                "User function failed: "
+                                "bfcr-addr={}, status={}",
+                                fmt::ptr(bfcr), status);
+                goto end;
+            }
+        }
+
+        consume_bits(bfcr, BYTES_TO_BITS(available_bytes));
+        bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE;
+        status = BT_BFCR_STATUS_EOF;
+    } else {
+        /* Found the null character */
+        size_t result_len = (size_t) (result - first_chr);
+
+        if (bfcr->user.cbs.classes.string && result_len) {
+            BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (substring).");
+            status = bfcr->user.cbs.classes.string((const char *) first_chr, result_len,
+                                                   bfcr->cur_basic_field_class, bfcr->user.data);
+            BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", status);
+            if (status != BT_BFCR_STATUS_OK) {
+                BT_CPPLOGW_SPEC(bfcr->logger,
+                                "User function failed: "
+                                "bfcr-addr={}, status={}",
+                                fmt::ptr(bfcr), status);
+                goto end;
+            }
+        }
+
+        if (bfcr->user.cbs.classes.string_end) {
+            BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (string, end).");
+            status =
+                bfcr->user.cbs.classes.string_end(bfcr->cur_basic_field_class, bfcr->user.data);
+            BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", status);
+            if (status != BT_BFCR_STATUS_OK) {
+                BT_CPPLOGW_SPEC(bfcr->logger,
+                                "User function failed: "
+                                "bfcr-addr={}, status={}",
+                                fmt::ptr(bfcr), status);
+                goto end;
+            }
+        }
+
+        consume_bits(bfcr, BYTES_TO_BITS(result_len + 1));
+
+        if (stack_empty(bfcr->stack)) {
+            /* Root is a basic class */
+            bfcr->state = BFCR_STATE_DONE;
+        } else {
+            /* Go to next field */
+            stack_top(bfcr->stack)->index++;
+            bfcr->state = BFCR_STATE_NEXT_FIELD;
+            bfcr->last_bo = bfcr->cur_bo;
+        }
+    }
+
+end:
+    return status;
+}
+
+static inline enum bt_bfcr_status read_basic_begin_state(struct bt_bfcr *bfcr)
+{
+    enum bt_bfcr_status status;
+
+    BT_ASSERT_DBG(bfcr->cur_basic_field_class);
+
+    switch (bfcr->cur_basic_field_class->type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+        status = read_basic_int_class_and_call_begin(bfcr);
+        break;
+    case CTF_FIELD_CLASS_TYPE_FLOAT:
+        status = read_basic_float_class_and_call_begin(bfcr);
+        break;
+    case CTF_FIELD_CLASS_TYPE_STRING:
+        status = read_basic_string_class_and_call(bfcr, true);
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    return status;
+}
+
+static inline enum bt_bfcr_status read_basic_continue_state(struct bt_bfcr *bfcr)
+{
+    enum bt_bfcr_status status;
+
+    BT_ASSERT_DBG(bfcr->cur_basic_field_class);
+
+    switch (bfcr->cur_basic_field_class->type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+        status = read_basic_int_class_and_call_continue(bfcr);
+        break;
+    case CTF_FIELD_CLASS_TYPE_FLOAT:
+        status = read_basic_float_class_and_call_continue(bfcr);
+        break;
+    case CTF_FIELD_CLASS_TYPE_STRING:
+        status = read_basic_string_class_and_call(bfcr, false);
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    return status;
+}
+
+static inline size_t bits_to_skip_to_align_to(struct bt_bfcr *bfcr, size_t align)
+{
+    size_t aligned_packet_at;
+
+    aligned_packet_at = BT_ALIGN(packet_at(bfcr), align);
+    return aligned_packet_at - packet_at(bfcr);
+}
+
+static inline enum bt_bfcr_status align_class_state(struct bt_bfcr *bfcr,
+                                                    struct ctf_field_class *field_class,
+                                                    enum bfcr_state next_state)
+{
+    unsigned int field_alignment;
+    size_t skip_bits;
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+    /* Get field's alignment */
+    field_alignment = field_class->alignment;
+
+    /*
+     * 0 means "undefined" for variants; what we really want is 1
+     * (always aligned)
+     */
+    BT_ASSERT_DBG(field_alignment >= 1);
+
+    /* Compute how many bits we need to skip */
+    skip_bits = bits_to_skip_to_align_to(bfcr, (size_t) field_alignment);
+
+    /* Nothing to skip? aligned */
+    if (skip_bits == 0) {
+        bfcr->state = next_state;
+        goto end;
+    }
+
+    /* Make sure there's at least one bit left */
+    if (!at_least_one_bit_left(bfcr)) {
+        status = BT_BFCR_STATUS_EOF;
+        goto end;
+    }
+
+    /* Consume as many bits as possible in what's left */
+    consume_bits(bfcr, MIN(available_bits(bfcr), skip_bits));
+
+    /* Are we done now? */
+    skip_bits = bits_to_skip_to_align_to(bfcr, field_alignment);
+    if (skip_bits == 0) {
+        /* Yes: go to next state */
+        bfcr->state = next_state;
+        goto end;
+    } else {
+        /* No: need more data */
+        BT_CPPLOGT_SPEC(bfcr->logger, "Reached end of data when aligning: bfcr-addr={}",
+                        fmt::ptr(bfcr));
+        status = BT_BFCR_STATUS_EOF;
+    }
+
+end:
+    return status;
+}
+
+static inline enum bt_bfcr_status next_field_state(struct bt_bfcr *bfcr)
+{
+    int ret;
+    struct stack_entry *top;
+    struct ctf_field_class *next_field_class = NULL;
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+    if (stack_empty(bfcr->stack)) {
+        goto end;
+    }
+
+    top = stack_top(bfcr->stack);
+
+    /* Are we done with this base class? */
+    while (top->index == top->base_len) {
+        if (bfcr->user.cbs.classes.compound_end) {
+            BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (compound, end).");
+            status = bfcr->user.cbs.classes.compound_end(top->base_class, bfcr->user.data);
+            BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", status);
+            if (status != BT_BFCR_STATUS_OK) {
+                BT_CPPLOGW_SPEC(bfcr->logger, "User function failed: bfcr-addr={}, status={}",
+                                fmt::ptr(bfcr), status);
+                goto end;
+            }
+        }
+
+        stack_pop(bfcr->stack);
+
+        /* Are we done with the root class? */
+        if (stack_empty(bfcr->stack)) {
+            bfcr->state = BFCR_STATE_DONE;
+            goto end;
+        }
+
+        top = stack_top(bfcr->stack);
+        top->index++;
+    }
+
+    /* Get next field's class */
+    switch (top->base_class->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+        next_field_class = ctf_field_class_struct_borrow_member_by_index(
+                               ctf_field_class_as_struct(top->base_class), (uint64_t) top->index)
+                               ->fc;
+        break;
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(top->base_class);
+
+        next_field_class = array_fc->elem_fc;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+        /* Variant classes are dynamic: the user should know! */
+        next_field_class = bfcr->user.cbs.query.borrow_variant_selected_field_class(
+            top->base_class, bfcr->user.data);
+        break;
+    default:
+        break;
+    }
+
+    if (!next_field_class) {
+        BT_CPPLOGW_SPEC(bfcr->logger,
+                        "Cannot get the field class of the next field: "
+                        "bfcr-addr={}, base-fc-addr={}, base-fc-type={}, index={}",
+                        fmt::ptr(bfcr), fmt::ptr(top->base_class), (int) top->base_class->type,
+                        top->index);
+        status = BT_BFCR_STATUS_ERROR;
+        goto end;
+    }
+
+    if (next_field_class->is_compound) {
+        if (bfcr->user.cbs.classes.compound_begin) {
+            BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (compound, begin).");
+            status = bfcr->user.cbs.classes.compound_begin(next_field_class, bfcr->user.data);
+            BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", status);
+            if (status != BT_BFCR_STATUS_OK) {
+                BT_CPPLOGW_SPEC(bfcr->logger, "User function failed: bfcr-addr={}, status={}",
+                                fmt::ptr(bfcr), status);
+                goto end;
+            }
+        }
+
+        ret = stack_push_with_len(bfcr, next_field_class);
+        if (ret) {
+            /* stack_push_with_len() logs errors */
+            status = BT_BFCR_STATUS_ERROR;
+            goto end;
+        }
+
+        /* Next state: align a compound class */
+        bfcr->state = BFCR_STATE_ALIGN_COMPOUND;
+    } else {
+        /* Replace current basic field class */
+        BT_CPPLOGT_SPEC(bfcr->logger,
+                        "Replacing current basic field class: "
+                        "bfcr-addr={}, cur-basic-fc-addr={}, "
+                        "next-basic-fc-addr={}",
+                        fmt::ptr(bfcr), fmt::ptr(bfcr->cur_basic_field_class),
+                        fmt::ptr(next_field_class));
+        bfcr->cur_basic_field_class = next_field_class;
+
+        /* Next state: align a basic class */
+        bfcr->state = BFCR_STATE_ALIGN_BASIC;
+    }
+
+end:
+    return status;
+}
+
+static inline enum bt_bfcr_status handle_state(struct bt_bfcr *bfcr)
+{
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+    BT_CPPLOGT_SPEC(bfcr->logger, "Handling state: bfcr-addr={}, state={}", fmt::ptr(bfcr),
+                    bfcr->state);
+
+    switch (bfcr->state) {
+    case BFCR_STATE_NEXT_FIELD:
+        status = next_field_state(bfcr);
+        break;
+    case BFCR_STATE_ALIGN_BASIC:
+        status = align_class_state(bfcr, bfcr->cur_basic_field_class, BFCR_STATE_READ_BASIC_BEGIN);
+        break;
+    case BFCR_STATE_ALIGN_COMPOUND:
+        status = align_class_state(bfcr, stack_top(bfcr->stack)->base_class, BFCR_STATE_NEXT_FIELD);
+        break;
+    case BFCR_STATE_READ_BASIC_BEGIN:
+        status = read_basic_begin_state(bfcr);
+        break;
+    case BFCR_STATE_READ_BASIC_CONTINUE:
+        status = read_basic_continue_state(bfcr);
+        break;
+    case BFCR_STATE_DONE:
+        break;
+    }
+
+    BT_CPPLOGT_SPEC(bfcr->logger, "Handled state: bfcr-addr={}, status={}", fmt::ptr(bfcr), status);
+    return status;
+}
+
+struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data, const bt2c::Logger& logger)
+{
+    BT_CPPLOGD_STR_SPEC(logger, "Creating binary field class reader (BFCR).");
+
+    bt_bfcr *bfcr = new bt_bfcr {logger};
+    bfcr->stack = stack_new(bfcr);
+    if (!bfcr->stack) {
+        BT_CPPLOGE_STR_SPEC(bfcr->logger, "Cannot create BFCR's stack.");
+        bt_bfcr_destroy(bfcr);
+        bfcr = NULL;
+        goto end;
+    }
+
+    bfcr->state = BFCR_STATE_NEXT_FIELD;
+    bfcr->user.cbs = cbs;
+    bfcr->user.data = data;
+    BT_CPPLOGD_SPEC(bfcr->logger, "Created BFCR: addr={}", fmt::ptr(bfcr));
+
+end:
+    return bfcr;
+}
+
+void bt_bfcr_destroy(struct bt_bfcr *bfcr)
+{
+    if (bfcr->stack) {
+        stack_destroy(bfcr->stack);
+    }
+
+    BT_CPPLOGD_SPEC(bfcr->logger, "Destroying BFCR: addr={}", fmt::ptr(bfcr));
+    delete bfcr;
+}
+
+static void reset(struct bt_bfcr *bfcr)
+{
+    BT_CPPLOGD_SPEC(bfcr->logger, "Resetting BFCR: addr={}", fmt::ptr(bfcr));
+    stack_clear(bfcr->stack);
+    stitch_reset(bfcr);
+    bfcr->buf.addr = NULL;
+    bfcr->last_bo = CTF_BYTE_ORDER_UNKNOWN;
+}
+
+static void update_packet_offset(struct bt_bfcr *bfcr)
+{
+    BT_CPPLOGT_SPEC(bfcr->logger,
+                    "Updating packet offset for next call: "
+                    "bfcr-addr={}, cur-packet-offset={}, next-packet-offset={}",
+                    fmt::ptr(bfcr), bfcr->buf.packet_offset,
+                    bfcr->buf.packet_offset + bfcr->buf.at);
+    bfcr->buf.packet_offset += bfcr->buf.at;
+}
+
+size_t bt_bfcr_start(struct bt_bfcr *bfcr, struct ctf_field_class *cls, const uint8_t *buf,
+                     size_t offset, size_t packet_offset, size_t sz, enum bt_bfcr_status *status)
+{
+    BT_ASSERT_DBG(bfcr);
+    BT_ASSERT_DBG(BYTES_TO_BITS(sz) >= offset);
+    reset(bfcr);
+    bfcr->buf.addr = buf;
+    bfcr->buf.offset = offset;
+    bfcr->buf.at = 0;
+    bfcr->buf.packet_offset = packet_offset;
+    bfcr->buf.buf_sz = sz;
+    bfcr->buf.sz = BYTES_TO_BITS(sz) - offset;
+    *status = BT_BFCR_STATUS_OK;
+
+    BT_CPPLOGT_SPEC(bfcr->logger,
+                    "Starting decoding: bfcr-addr={}, fc-addr={}, "
+                    "buf-addr={}, buf-size={}, offset={}, "
+                    "packet-offset={}",
+                    fmt::ptr(bfcr), fmt::ptr(cls), fmt::ptr(buf), sz, offset, packet_offset);
+
+    /* Set root class */
+    if (cls->is_compound) {
+        /* Compound class: push on visit stack */
+        int stack_ret;
+
+        if (bfcr->user.cbs.classes.compound_begin) {
+            BT_CPPLOGT_SPEC(bfcr->logger, "Calling user function (compound, begin).");
+            *status = bfcr->user.cbs.classes.compound_begin(cls, bfcr->user.data);
+            BT_CPPLOGT_SPEC(bfcr->logger, "User function returned: status={}", *status);
+            if (*status != BT_BFCR_STATUS_OK) {
+                BT_CPPLOGW_SPEC(bfcr->logger, "User function failed: bfcr-addr={}, status={}",
+                                fmt::ptr(bfcr), *status);
+                goto end;
+            }
+        }
+
+        stack_ret = stack_push_with_len(bfcr, cls);
+        if (stack_ret) {
+            /* stack_push_with_len() logs errors */
+            *status = BT_BFCR_STATUS_ERROR;
+            goto end;
+        }
+
+        bfcr->state = BFCR_STATE_ALIGN_COMPOUND;
+    } else {
+        /* Basic class: set as current basic class */
+        bfcr->cur_basic_field_class = cls;
+        bfcr->state = BFCR_STATE_ALIGN_BASIC;
+    }
+
+    /* Run the machine! */
+    BT_CPPLOGT_STR_SPEC(bfcr->logger, "Running the state machine.");
+
+    while (true) {
+        *status = handle_state(bfcr);
+        if (*status != BT_BFCR_STATUS_OK || bfcr->state == BFCR_STATE_DONE) {
+            break;
+        }
+    }
+
+    /* Update packet offset for next time */
+    update_packet_offset(bfcr);
+
+end:
+    return bfcr->buf.at;
+}
+
+size_t bt_bfcr_continue(struct bt_bfcr *bfcr, const uint8_t *buf, size_t sz,
+                        enum bt_bfcr_status *status)
+{
+    BT_ASSERT_DBG(bfcr);
+    BT_ASSERT_DBG(buf);
+    BT_ASSERT_DBG(sz > 0);
+    bfcr->buf.addr = buf;
+    bfcr->buf.offset = 0;
+    bfcr->buf.at = 0;
+    bfcr->buf.buf_sz = sz;
+    bfcr->buf.sz = BYTES_TO_BITS(sz);
+    *status = BT_BFCR_STATUS_OK;
+
+    BT_CPPLOGT_SPEC(bfcr->logger, "Continuing decoding: bfcr-addr={}, buf-addr={}, buf-size={}",
+                    fmt::ptr(bfcr), fmt::ptr(buf), sz);
+
+    /* Continue running the machine */
+    BT_CPPLOGT_STR_SPEC(bfcr->logger, "Running the state machine.");
+
+    while (true) {
+        *status = handle_state(bfcr);
+        if (*status != BT_BFCR_STATUS_OK || bfcr->state == BFCR_STATE_DONE) {
+            break;
+        }
+    }
+
+    /* Update packet offset for next time */
+    update_packet_offset(bfcr);
+    return bfcr->buf.at;
+}
+
+void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr, bt_bfcr_unsigned_int_cb_func cb)
+{
+    BT_ASSERT_DBG(bfcr);
+    BT_ASSERT_DBG(cb);
+    bfcr->user.cbs.classes.unsigned_int = cb;
+}
diff --git a/src/plugins/ctf/common/src/bfcr/bfcr.hpp b/src/plugins/ctf/common/src/bfcr/bfcr.hpp
new file mode 100644 (file)
index 0000000..984b695
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Babeltrace - CTF binary field class reader (BFCR)
+ */
+
+#ifndef CTF_BFCR_H
+#define CTF_BFCR_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+
+#include "../metadata/tsdl/ctf-meta.hpp"
+
+namespace bt2c {
+
+class Logger;
+
+} /* namespace bt2c */
+
+/**
+ * @file bfcr.h
+ *
+ * Event-driven CTF binary field class reader (BFCR).
+ *
+ * This is a common, internal API used by CTF source plugins. It allows
+ * a binary CTF IR field class to be decoded from user-provided buffers.
+ * As the class is decoded (and, possibly, its nested classes),
+ * registered user callback functions are called.
+ *
+ * This API is only concerned with reading one CTF class at a time from
+ * one or more buffer of bytes. It does not know CTF dynamic scopes,
+ * events, or streams. Sequence lengths and selected variant classes are
+ * requested to the user when needed.
+ */
+
+/**
+ * Binary class reader API status codes.
+ */
+enum bt_bfcr_status
+{
+    /** Out of memory. */
+    BT_BFCR_STATUS_ENOMEM = -5,
+    /**
+     * The binary stream reader reached the end of the user-provided
+     * buffer, but data is still needed to finish decoding the
+     * requested class.
+     *
+     * The user needs to call bt_bfcr_continue() as long as
+     * #BT_BFCR_STATUS_EOF is returned to complete the decoding
+     * process of a given class.
+     */
+    BT_BFCR_STATUS_EOF = 1,
+
+    /** Invalid argument. */
+    BT_BFCR_STATUS_INVAL = -3,
+
+    /** General error. */
+    BT_BFCR_STATUS_ERROR = -1,
+
+    /** Everything okay. */
+    BT_BFCR_STATUS_OK = 0,
+};
+
+inline const char *format_as(bt_bfcr_status status) noexcept
+{
+    switch (status) {
+    case BT_BFCR_STATUS_ENOMEM:
+        return "BT_BFCR_STATUS_ENOMEM";
+
+    case BT_BFCR_STATUS_EOF:
+        return "BT_BFCR_STATUS_EOF";
+
+    case BT_BFCR_STATUS_INVAL:
+        return "BT_BFCR_STATUS_INVAL";
+
+    case BT_BFCR_STATUS_ERROR:
+        return "BT_BFCR_STATUS_ERROR";
+
+    case BT_BFCR_STATUS_OK:
+        return "BT_BFCR_STATUS_OK";
+        break;
+    }
+
+    bt_common_abort();
+}
+
+typedef enum bt_bfcr_status (*bt_bfcr_unsigned_int_cb_func)(uint64_t, struct ctf_field_class *,
+                                                            void *);
+
+/*
+ * Field class reader user callback functions.
+ */
+struct bt_bfcr_cbs
+{
+    /**
+     * Field class callback functions.
+     *
+     * This CTF binary class reader is event-driven. The following
+     * functions are called during the decoding process, either when
+     * a compound class begins/ends, or when a basic class is
+     * completely decoded (along with its value).
+     *
+     * Each function also receives the CTF field class associated
+     * with the call, and user data (registered to the class reader
+     * calling them).
+     *
+     * Actual trace IR fields are \em not created here; this would
+     * be the responsibility of a class reader's user (the provider
+     * of those callback functions).
+     *
+     * All the class callback functions return one of the following
+     * values:
+     *
+     *   - <b>#BT_BFCR_STATUS_OK</b>: Everything is okay;
+     *     continue the decoding process.
+     *   - <b>#BT_BFCR_STATUS_ERROR</b>: General error (reported
+     *     to class reader's user).
+     *
+     * Any member of this structure may be set to \c NULL, should
+     * a specific message be not needed.
+     */
+    struct
+    {
+        /**
+         * Called when a signed integer class is completely
+         * decoded. This could also be the supporting signed
+         * integer class of an enumeration class (\p class will
+         * indicate this).
+         *
+         * @param value                Signed integer value
+         * @param class                Integer or enumeration class
+         * @param data         User data
+         * @returns            #BT_BFCR_STATUS_OK or
+         *                     #BT_BFCR_STATUS_ERROR
+         */
+        enum bt_bfcr_status (*signed_int)(int64_t value, struct ctf_field_class *cls, void *data);
+
+        /**
+         * Called when an unsigned integer class is completely
+         * decoded. This could also be the supporting signed
+         * integer class of an enumeration class (\p class will
+         * indicate this).
+         *
+         * @param value                Unsigned integer value
+         * @param class                Integer or enumeration class
+         * @param data         User data
+         * @returns            #BT_BFCR_STATUS_OK or
+         *                     #BT_BFCR_STATUS_ERROR
+         */
+        bt_bfcr_unsigned_int_cb_func unsigned_int;
+
+        /**
+         * Called when a floating point number class is
+         * completely decoded.
+         *
+         * @param value                Floating point number value
+         * @param class                Floating point number class
+         * @param data         User data
+         * @returns            #BT_BFCR_STATUS_OK or
+         *                     #BT_BFCR_STATUS_ERROR
+         */
+        enum bt_bfcr_status (*floating_point)(double value, struct ctf_field_class *cls,
+                                              void *data);
+
+        /**
+         * Called when a string class begins.
+         *
+         * All the following user callback function calls will
+         * be made to bt_bfcr_cbs::classes::string(), each of
+         * them providing one substring of the complete string
+         * class's value.
+         *
+         * @param class                Beginning string class
+         * @param data         User data
+         * @returns            #BT_BFCR_STATUS_OK or
+         *                     #BT_BFCR_STATUS_ERROR
+         */
+        enum bt_bfcr_status (*string_begin)(struct ctf_field_class *cls, void *data);
+
+        /**
+         * Called when a string class's substring is decoded
+         * (between a call to bt_bfcr_cbs::classes::string_begin()
+         * and a call to bt_bfcr_cbs::classes::string_end()).
+         *
+         * @param value                String value (\em not null-terminated)
+         * @param len          String value length
+         * @param class                String class
+         * @param data         User data
+         * @returns            #BT_BFCR_STATUS_OK or
+         *                     #BT_BFCR_STATUS_ERROR
+         */
+        enum bt_bfcr_status (*string)(const char *value, size_t len, struct ctf_field_class *cls,
+                                      void *data);
+
+        /**
+         * Called when a string class ends.
+         *
+         * @param class                Ending string class
+         * @param data         User data
+         * @returns            #BT_BFCR_STATUS_OK or
+         *                     #BT_BFCR_STATUS_ERROR
+         */
+        enum bt_bfcr_status (*string_end)(struct ctf_field_class *cls, void *data);
+
+        /**
+         * Called when a compound class begins.
+         *
+         * All the following class callback function calls will
+         * signal sequential elements of this compound class,
+         * until the next corresponding
+         * bt_bfcr_cbs::classes::compound_end() is called.
+         *
+         * If \p class is a variant class, then only one class
+         * callback function call will follow before the call to
+         * bt_bfcr_cbs::classes::compound_end(). This single
+         * call indicates the selected class of this variant
+         * class.
+         *
+         * @param class                Beginning compound class
+         * @param data         User data
+         * @returns            #BT_BFCR_STATUS_OK or
+         *                     #BT_BFCR_STATUS_ERROR
+         */
+        enum bt_bfcr_status (*compound_begin)(struct ctf_field_class *cls, void *data);
+
+        /**
+         * Called when a compound class ends.
+         *
+         * @param class                Ending compound class
+         * @param data         User data
+         * @returns            #BT_BFCR_STATUS_OK or
+         *                     #BT_BFCR_STATUS_ERROR
+         */
+        enum bt_bfcr_status (*compound_end)(struct ctf_field_class *cls, void *data);
+    } classes;
+
+    /**
+     * Query callback functions are used when the class reader needs
+     * dynamic information, i.e. a sequence class's current length
+     * or a variant class's current selected class.
+     *
+     * Both functions need to be set unless it is known that no
+     * sequences or variants will have to be decoded.
+     */
+    struct
+    {
+        /**
+         * Called to query the current length of a given sequence
+         * class.
+         *
+         * @param class                Sequence class
+         * @param data         User data
+         * @returns            Sequence length or
+         *                     #BT_BFCR_STATUS_ERROR on error
+         */
+        int64_t (*get_sequence_length)(struct ctf_field_class *cls, void *data);
+
+        /**
+         * Called to query the current selected class of a given
+         * variant class.
+         *
+         * @param class                Variant class
+         * @param data         User data
+         * @returns            Current selected class (owned by
+         *                     this) or \c NULL on error
+         */
+        struct ctf_field_class *(*borrow_variant_selected_field_class)(struct ctf_field_class *cls,
+                                                                       void *data);
+    } query;
+};
+
+/**
+ * Creates a CTF binary class reader.
+ *
+ * @param cbs          User callback functions
+ * @param data         User data (passed to user callback functions)
+ * @returns            New binary class reader on success, or \c NULL on error
+ */
+struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data, const bt2c::Logger& logger);
+
+/**
+ * Destroys a CTF binary class reader, freeing all internal resources.
+ *
+ * @param bfcr Binary class reader
+ */
+void bt_bfcr_destroy(struct bt_bfcr *bfcr);
+
+/**
+ * Decodes a given CTF class from a buffer of bytes.
+ *
+ * The number of \em bits consumed by this function is returned.
+ *
+ * The \p status output parameter is where a status is written, amongst
+ * the following:
+ *
+ *   - <b>#BT_BFCR_STATUS_OK</b>: Decoding is done.
+ *   - <b>#BT_BFCR_STATUS_EOF</b>: The end of the buffer was reached,
+ *     but more data is needed to finish the decoding process of the
+ *     requested class. The user needs to call bt_bfcr_continue()
+ *     as long as #BT_BFCR_STATUS_EOF is returned to complete the
+ *     decoding process of the original class.
+ *   - <b>#BT_BFCR_STATUS_INVAL</b>: Invalid argument.
+ *   - <b>#BT_BFCR_STATUS_ERROR</b>: General error.
+ *
+ * Calling this function resets the class reader's internal state. If
+ * #BT_BFCR_STATUS_EOF is returned, bt_bfcr_continue() needs to
+ * be called next, \em not bt_bfcr_decode().
+ *
+ * @param bfcr                 Binary class reader
+ * @param class                        Field class to decode
+ * @param buf                  Buffer
+ * @param offset               Offset of first bit from \p buf (bits)
+ * @param packet_offset                Offset of \p offset within the CTF
+ *                             binary packet containing \p class (bits)
+ * @param sz                   Size of buffer in bytes (from \p buf)
+ * @param status               Returned status (see description above)
+ * @returns                    Number of consumed bits
+ */
+size_t bt_bfcr_start(struct bt_bfcr *bfcr, struct ctf_field_class *cls, const uint8_t *buf,
+                     size_t offset, size_t packet_offset, size_t sz, enum bt_bfcr_status *status);
+
+/**
+ * Continues the decoding process a given CTF class.
+ *
+ * The number of bits consumed by this function is returned.
+ *
+ * The \p status output parameter is where a status is placed, amongst
+ * the following:
+ *
+ *   - <b>#BT_BFCR_STATUS_OK</b>: decoding is done.
+ *   - <b>#BT_BFCR_STATUS_EOF</b>: the end of the buffer was reached,
+ *     but more data is needed to finish the decoding process of the
+ *     requested class. The user needs to call bt_bfcr_continue()
+ *     as long as #BT_BFCR_STATUS_EOF is returned to complete the
+ *     decoding process of the original class.
+ *   - <b>#BT_BFCR_STATUS_INVAL</b>: invalid argument.
+ *   - <b>#BT_BFCR_STATUS_ERROR</b>: general error.
+ *
+ * @param bfcr         Binary class reader
+ * @param buf          Buffer
+ * @param sz           Size of buffer in bytes (from \p offset)
+ * @param status       Returned status (see description above)
+ * @returns            Number of consumed bits
+ */
+size_t bt_bfcr_continue(struct bt_bfcr *bfcr, const uint8_t *buf, size_t sz,
+                        enum bt_bfcr_status *status);
+
+void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr, bt_bfcr_unsigned_int_cb_func cb);
+
+#endif /* CTF_BFCR_H */
diff --git a/src/plugins/ctf/common/src/clk-cls-cfg.hpp b/src/plugins/ctf/common/src/clk-cls-cfg.hpp
new file mode 100644 (file)
index 0000000..0c3b52d
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2022 EfficiOS Inc. and Linux Foundation
+ */
+
+#ifndef CTF_COMMON_SRC_CLK_CLS_CFG_HPP
+#define CTF_COMMON_SRC_CLK_CLS_CFG_HPP
+
+#include <cstdint>
+
+namespace ctf {
+namespace src {
+
+struct ClkClsCfg
+{
+    std::int64_t offsetSec = 0;
+    std::int64_t offsetNanoSec = 0;
+    bool forceOriginIsUnixEpoch = false;
+};
+
+} /* namespace src */
+} /* namespace ctf */
+
+#endif /* CTF_COMMON_SRC_CLK_CLS_CFG_HPP */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ast.hpp b/src/plugins/ctf/common/src/metadata/tsdl/ast.hpp
new file mode 100644 (file)
index 0000000..597cb3a
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _CTF_AST_H
+#define _CTF_AST_H
+
+#include <memory>
+
+#include <glib.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "common/list.h"
+#include "cpp-common/bt2/trace-ir.hpp"
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+
+#include "ctf-meta.hpp"
+#include "decoder.hpp"
+
+// the parameter name (of the reentrant 'yyparse' function)
+// data is a pointer to a 'SParserParam' structure
+//#define YYPARSE_PARAM        scanner
+
+struct ctf_node;
+struct ctf_parser;
+struct ctf_visitor_generate_ir;
+
+#define EINCOMPLETE 1000
+
+#define FOREACH_CTF_NODES(F)                                                                       \
+    F(NODE_UNKNOWN)                                                                                \
+    F(NODE_ROOT)                                                                                   \
+    F(NODE_ERROR)                                                                                  \
+    F(NODE_EVENT)                                                                                  \
+    F(NODE_STREAM)                                                                                 \
+    F(NODE_ENV)                                                                                    \
+    F(NODE_TRACE)                                                                                  \
+    F(NODE_CLOCK)                                                                                  \
+    F(NODE_CALLSITE)                                                                               \
+    F(NODE_CTF_EXPRESSION)                                                                         \
+    F(NODE_UNARY_EXPRESSION)                                                                       \
+    F(NODE_TYPEDEF)                                                                                \
+    F(NODE_TYPEALIAS_TARGET)                                                                       \
+    F(NODE_TYPEALIAS_ALIAS)                                                                        \
+    F(NODE_TYPEALIAS)                                                                              \
+    F(NODE_TYPE_SPECIFIER)                                                                         \
+    F(NODE_TYPE_SPECIFIER_LIST)                                                                    \
+    F(NODE_POINTER)                                                                                \
+    F(NODE_TYPE_DECLARATOR)                                                                        \
+    F(NODE_FLOATING_POINT)                                                                         \
+    F(NODE_INTEGER)                                                                                \
+    F(NODE_STRING)                                                                                 \
+    F(NODE_ENUMERATOR)                                                                             \
+    F(NODE_ENUM)                                                                                   \
+    F(NODE_STRUCT_OR_VARIANT_DECLARATION)                                                          \
+    F(NODE_VARIANT)                                                                                \
+    F(NODE_STRUCT)
+
+enum node_type
+{
+#define ENTRY(S) S,
+    FOREACH_CTF_NODES(ENTRY)
+#undef ENTRY
+};
+
+inline const char *format_as(enum node_type type) noexcept
+{
+    switch (type) {
+#define ENTRY(S)                                                                                   \
+case S:                                                                                            \
+    return G_STRINGIFY(S);
+
+        FOREACH_CTF_NODES(ENTRY)
+#undef ENTRY
+    }
+
+    bt_common_abort();
+}
+
+enum ctf_unary
+{
+    UNARY_UNKNOWN = 0,
+    UNARY_STRING,
+    UNARY_SIGNED_CONSTANT,
+    UNARY_UNSIGNED_CONSTANT,
+    UNARY_SBRAC,
+};
+
+inline const char *format_as(ctf_unary value) noexcept
+{
+    switch (value) {
+    case UNARY_UNKNOWN:
+        return "UNARY_UNKNOWN";
+
+    case UNARY_STRING:
+        return "UNARY_STRING";
+
+    case UNARY_SIGNED_CONSTANT:
+        return "UNARY_SIGNED_CONSTANT";
+
+    case UNARY_UNSIGNED_CONSTANT:
+        return "UNARY_UNSIGNED_CONSTANT";
+
+    case UNARY_SBRAC:
+        return "UNARY_SBRAC";
+    }
+
+    bt_common_abort();
+}
+
+enum ctf_unary_link
+{
+    UNARY_LINK_UNKNOWN = 0,
+    UNARY_DOTLINK,
+    UNARY_ARROWLINK,
+    UNARY_DOTDOTDOT,
+};
+
+enum ctf_typedec
+{
+    TYPEDEC_UNKNOWN = 0,
+    TYPEDEC_ID,     /* identifier */
+    TYPEDEC_NESTED, /* (), array or sequence */
+};
+
+inline const char *format_as(ctf_typedec value) noexcept
+{
+    switch (value) {
+    case TYPEDEC_UNKNOWN:
+        return "TYPEDEC_UNKNOWN";
+
+    case TYPEDEC_ID:
+        return "TYPEDEC_ID";
+
+    case TYPEDEC_NESTED:
+        return "TYPEDEC_NESTED";
+    }
+
+    bt_common_abort();
+}
+
+enum ctf_typespec
+{
+    TYPESPEC_UNKNOWN = 0,
+    TYPESPEC_VOID,
+    TYPESPEC_CHAR,
+    TYPESPEC_SHORT,
+    TYPESPEC_INT,
+    TYPESPEC_LONG,
+    TYPESPEC_FLOAT,
+    TYPESPEC_DOUBLE,
+    TYPESPEC_SIGNED,
+    TYPESPEC_UNSIGNED,
+    TYPESPEC_BOOL,
+    TYPESPEC_COMPLEX,
+    TYPESPEC_IMAGINARY,
+    TYPESPEC_CONST,
+    TYPESPEC_ID_TYPE,
+    TYPESPEC_FLOATING_POINT,
+    TYPESPEC_INTEGER,
+    TYPESPEC_STRING,
+    TYPESPEC_STRUCT,
+    TYPESPEC_VARIANT,
+    TYPESPEC_ENUM,
+};
+
+inline const char *format_as(ctf_typespec value) noexcept
+{
+    switch (value) {
+    case TYPESPEC_UNKNOWN:
+        return "TYPESPEC_UNKNOWN";
+
+    case TYPESPEC_VOID:
+        return "TYPESPEC_VOID";
+
+    case TYPESPEC_CHAR:
+        return "TYPESPEC_CHAR";
+
+    case TYPESPEC_SHORT:
+        return "TYPESPEC_SHORT";
+
+    case TYPESPEC_INT:
+        return "TYPESPEC_INT";
+
+    case TYPESPEC_LONG:
+        return "TYPESPEC_LONG";
+
+    case TYPESPEC_FLOAT:
+        return "TYPESPEC_FLOAT";
+
+    case TYPESPEC_DOUBLE:
+        return "TYPESPEC_DOUBLE";
+
+    case TYPESPEC_SIGNED:
+        return "TYPESPEC_SIGNED";
+
+    case TYPESPEC_UNSIGNED:
+        return "TYPESPEC_UNSIGNED";
+
+    case TYPESPEC_BOOL:
+        return "TYPESPEC_BOOL";
+
+    case TYPESPEC_COMPLEX:
+        return "TYPESPEC_COMPLEX";
+
+    case TYPESPEC_IMAGINARY:
+        return "TYPESPEC_IMAGINARY";
+
+    case TYPESPEC_CONST:
+        return "TYPESPEC_CONST";
+
+    case TYPESPEC_ID_TYPE:
+        return "TYPESPEC_ID_TYPE";
+
+    case TYPESPEC_FLOATING_POINT:
+        return "TYPESPEC_FLOATING_POINT";
+
+    case TYPESPEC_INTEGER:
+        return "TYPESPEC_INTEGER";
+
+    case TYPESPEC_STRING:
+        return "TYPESPEC_STRING";
+
+    case TYPESPEC_STRUCT:
+        return "TYPESPEC_STRUCT";
+
+    case TYPESPEC_VARIANT:
+        return "TYPESPEC_VARIANT";
+
+    case TYPESPEC_ENUM:
+        return "TYPESPEC_ENUM";
+    }
+
+    bt_common_abort();
+}
+
+struct ctf_node
+{
+    /*
+     * Parent node is only set on demand by specific visitor.
+     */
+    struct ctf_node *parent;
+    struct bt_list_head siblings;
+    struct bt_list_head tmp_head;
+    unsigned int lineno;
+    /*
+     * We mark nodes visited in the generate-ir phase (last
+     * phase). We only mark the 1-depth level nodes as visited
+     * (never the root node, and not their sub-nodes). This allows
+     * skipping already visited nodes when doing incremental
+     * metadata append.
+     */
+    int visited;
+
+    enum node_type type;
+    union
+    {
+        struct
+        {
+        } unknown;
+        struct
+        {
+            /*
+             * Children nodes are ctf_expression, field_class_def,
+             * field_class_alias and field_class_specifier_list.
+             */
+            struct bt_list_head declaration_list;
+            struct bt_list_head trace;
+            struct bt_list_head env;
+            struct bt_list_head stream;
+            struct bt_list_head event;
+            struct bt_list_head clock;
+            struct bt_list_head callsite;
+        } root;
+        struct
+        {
+            /*
+             * Children nodes are ctf_expression, field_class_def,
+             * field_class_alias and field_class_specifier_list.
+             */
+            struct bt_list_head declaration_list;
+        } event;
+        struct
+        {
+            /*
+             * Children nodes are ctf_expression, field_class_def,
+             * field_class_alias and field_class_specifier_list.
+             */
+            struct bt_list_head declaration_list;
+        } stream;
+        struct
+        {
+            /*
+             * Children nodes are ctf_expression, field_class_def,
+             * field_class_alias and field_class_specifier_list.
+             */
+            struct bt_list_head declaration_list;
+        } env;
+        struct
+        {
+            /*
+             * Children nodes are ctf_expression, field_class_def,
+             * field_class_alias and field_class_specifier_list.
+             */
+            struct bt_list_head declaration_list;
+        } trace;
+        struct
+        {
+            /*
+             * Children nodes are ctf_expression, field_class_def,
+             * field_class_alias and field_class_specifier_list.
+             */
+            struct bt_list_head declaration_list;
+        } clock;
+        struct
+        {
+            /*
+             * Children nodes are ctf_expression, field_class_def,
+             * field_class_alias and field_class_specifier_list.
+             */
+            struct bt_list_head declaration_list;
+        } callsite;
+        struct
+        {
+            struct bt_list_head left;  /* Should be string */
+            struct bt_list_head right; /* Unary exp. or type */
+        } ctf_expression;
+        struct
+        {
+            ctf_unary type;
+            union
+            {
+                /*
+                 * string for identifier, id_type, keywords,
+                 * string literals and character constants.
+                 */
+                char *string;
+                int64_t signed_constant;
+                uint64_t unsigned_constant;
+                struct ctf_node *sbrac_exp;
+            } u;
+            ctf_unary_link link;
+        } unary_expression;
+        struct
+        {
+            struct ctf_node *field_class_specifier_list;
+            struct bt_list_head field_class_declarators;
+        } field_class_def;
+        /* new type is "alias", existing type "target" */
+        struct
+        {
+            struct ctf_node *field_class_specifier_list;
+            struct bt_list_head field_class_declarators;
+        } field_class_alias_target;
+        struct
+        {
+            struct ctf_node *field_class_specifier_list;
+            struct bt_list_head field_class_declarators;
+        } field_class_alias_name;
+        struct
+        {
+            struct ctf_node *target;
+            struct ctf_node *alias;
+        } field_class_alias;
+        struct
+        {
+            ctf_typespec type;
+            /* For struct, variant and enum */
+            struct ctf_node *node;
+            const char *id_type;
+        } field_class_specifier;
+        struct
+        {
+            /* list of field_class_specifier */
+            struct bt_list_head head;
+        } field_class_specifier_list;
+        struct
+        {
+            unsigned int const_qualifier;
+        } pointer;
+        struct
+        {
+            struct bt_list_head pointers;
+            ctf_typedec type;
+            union
+            {
+                char *id;
+                struct
+                {
+                    /* typedec has no pointer list */
+                    struct ctf_node *field_class_declarator;
+                    /*
+                     * unary expression (value) or
+                     * field_class_specifier_list.
+                     */
+                    struct bt_list_head length;
+                    /* for abstract type declarator */
+                    unsigned int abstract_array;
+                } nested;
+            } u;
+            struct ctf_node *bitfield_len;
+        } field_class_declarator;
+        struct
+        {
+            /* Children nodes are ctf_expression. */
+            struct bt_list_head expressions;
+        } floating_point;
+        struct
+        {
+            /* Children nodes are ctf_expression. */
+            struct bt_list_head expressions;
+        } integer;
+        struct
+        {
+            /* Children nodes are ctf_expression. */
+            struct bt_list_head expressions;
+        } string;
+        struct
+        {
+            char *id;
+            /*
+             * Range list or single value node. Contains unary
+             * expressions.
+             */
+            struct bt_list_head values;
+        } enumerator;
+        struct
+        {
+            char *enum_id;
+            /*
+             * Either NULL, or points to unary expression or
+             * field_class_specifier_list.
+             */
+            struct ctf_node *container_field_class;
+            struct bt_list_head enumerator_list;
+            int has_body;
+        } _enum;
+        struct
+        {
+            struct ctf_node *field_class_specifier_list;
+            struct bt_list_head field_class_declarators;
+        } struct_or_variant_declaration;
+        struct
+        {
+            char *name;
+            char *choice;
+            /*
+             * list of field_class_def, field_class_alias and
+             * declarations
+             */
+            struct bt_list_head declaration_list;
+            int has_body;
+        } variant;
+        struct
+        {
+            char *name;
+            /*
+             * list of field_class_def, field_class_alias and
+             * declarations
+             */
+            struct bt_list_head declaration_list;
+            int has_body;
+            struct bt_list_head min_align; /* align() attribute */
+        } _struct;
+    } u;
+};
+
+struct ctf_ast
+{
+    struct ctf_node root;
+};
+
+const char *node_type(struct ctf_node *node);
+
+struct ctf_visitor_generate_ir
+{
+    using UP = std::unique_ptr<ctf_visitor_generate_ir>;
+
+    explicit ctf_visitor_generate_ir(ctf_metadata_decoder_config decoderConfig,
+                                     bt2c::Logger loggerParam) :
+        decoder_config {std::move(decoderConfig)},
+        logger {std::move(loggerParam)}
+    {
+    }
+
+    ~ctf_visitor_generate_ir();
+
+    /* Trace IR trace class being filled (owned by this) */
+    bt2::TraceClass::Shared trace_class;
+
+    /* CTF meta trace being filled (owned by this) */
+    struct ctf_trace_class *ctf_tc = nullptr;
+
+    /* Current declaration scope (top of the stack) (owned by this) */
+    struct ctx_decl_scope *current_scope = nullptr;
+
+    /* True if trace declaration is visited */
+    bool is_trace_visited = false;
+
+    /* True if this is an LTTng trace */
+    bool is_lttng = false;
+
+    /* Config passed by the user */
+    struct ctf_metadata_decoder_config decoder_config;
+
+    bt2c::Logger logger;
+};
+
+ctf_visitor_generate_ir::UP
+ctf_visitor_generate_ir_create(const struct ctf_metadata_decoder_config *config);
+
+bt2::TraceClass::Shared
+ctf_visitor_generate_ir_get_ir_trace_class(struct ctf_visitor_generate_ir *visitor);
+
+struct ctf_trace_class *
+ctf_visitor_generate_ir_borrow_ctf_trace_class(struct ctf_visitor_generate_ir *visitor);
+
+int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *visitor,
+                                       struct ctf_node *node);
+
+int ctf_visitor_semantic_check(int depth, struct ctf_node *node, const bt2c::Logger& logger);
+
+int ctf_visitor_parent_links(int depth, struct ctf_node *node, const bt2c::Logger& logger);
+
+static inline char *ctf_ast_concatenate_unary_strings(struct bt_list_head *head)
+{
+    int i = 0;
+    GString *str;
+    struct ctf_node *node;
+
+    str = g_string_new(NULL);
+    BT_ASSERT(str);
+
+    bt_list_for_each_entry (node, head, siblings) {
+        char *src_string;
+
+        if (node->type != NODE_UNARY_EXPRESSION || node->u.unary_expression.type != UNARY_STRING ||
+            !((node->u.unary_expression.link != UNARY_LINK_UNKNOWN) ^ (i == 0))) {
+            goto error;
+        }
+
+        switch (node->u.unary_expression.link) {
+        case UNARY_DOTLINK:
+            g_string_append(str, ".");
+            break;
+        case UNARY_ARROWLINK:
+            g_string_append(str, "->");
+            break;
+        case UNARY_DOTDOTDOT:
+            g_string_append(str, "...");
+            break;
+        default:
+            break;
+        }
+
+        src_string = node->u.unary_expression.u.string;
+        g_string_append(str, src_string);
+        i++;
+    }
+
+    /* Destroys the container, returns the underlying string */
+    return g_string_free(str, FALSE);
+
+error:
+    /* This always returns NULL */
+    return g_string_free(str, TRUE);
+}
+
+#ifndef BT_COMP_LOG_CUR_LVL
+#    define BT_AST_LOG_LEVEL_UNUSED_ATTR __attribute__((unused))
+#else
+#    define BT_AST_LOG_LEVEL_UNUSED_ATTR
+#endif
+
+static inline int ctf_ast_get_unary_uuid(struct bt_list_head *head, bt_uuid_t uuid,
+                                         const bt2c::Logger& logger)
+{
+    int i = 0;
+    int ret = 0;
+    struct ctf_node *node;
+
+    bt_list_for_each_entry (node, head, siblings) {
+        int uexpr_type = node->u.unary_expression.type;
+        int uexpr_link = node->u.unary_expression.link;
+        const char *src_string;
+
+        if (node->type != NODE_UNARY_EXPRESSION || uexpr_type != UNARY_STRING ||
+            uexpr_link != UNARY_LINK_UNKNOWN || i != 0) {
+            ret = -EINVAL;
+            goto end;
+        }
+
+        src_string = node->u.unary_expression.u.string;
+        ret = bt_uuid_from_str(src_string, uuid);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Cannot parse UUID: uuid=\"{}\"", src_string);
+            goto end;
+        }
+    }
+
+end:
+    return ret;
+}
+
+#endif /* _CTF_AST_H */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-configure-ir-trace.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-configure-ir-trace.cpp
new file mode 100644 (file)
index 0000000..dbcbbbd
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/uuid.hpp"
+
+#include "ctf-meta-configure-ir-trace.hpp"
+#include "plugins/ctf/common/src/metadata/tsdl/ctf-meta.hpp"
+
+void ctf_trace_class_configure_ir_trace(struct ctf_trace_class *tc, const bt2::Trace ir_trace)
+{
+    uint64_t i;
+
+    BT_ASSERT(tc);
+
+    if (tc->is_uuid_set) {
+        ir_trace.uuid(bt2c::Uuid {tc->uuid});
+    }
+
+    for (i = 0; i < tc->env_entries->len; i++) {
+        struct ctf_trace_class_env_entry *env_entry =
+            ctf_trace_class_borrow_env_entry_by_index(tc, i);
+
+        switch (env_entry->type) {
+        case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT:
+            ir_trace.environmentEntry(env_entry->name->str, env_entry->value.i);
+            break;
+        case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR:
+            ir_trace.environmentEntry(env_entry->name->str, env_entry->value.str->str);
+            break;
+        default:
+            bt_common_abort();
+        }
+    }
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-configure-ir-trace.hpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-configure-ir-trace.hpp
new file mode 100644 (file)
index 0000000..2adffb5
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef _CTF_META_CONFIGURE_IR_TRACE_H
+#define _CTF_META_CONFIGURE_IR_TRACE_H
+
+#include "cpp-common/bt2/trace-ir.hpp"
+
+void ctf_trace_class_configure_ir_trace(struct ctf_trace_class *tc, bt2::Trace ir_trace);
+
+#endif /* _CTF_META_CONFIGURE_IR_TRACE_H */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-resolve.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-resolve.cpp
new file mode 100644 (file)
index 0000000..0de4c67
--- /dev/null
@@ -0,0 +1,1250 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016-2018 Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "common/common.h"
+#include "cpp-common/bt2c/logging.hpp"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "ctf-meta-visitors.hpp"
+
+using field_class_stack_t = GPtrArray;
+
+/*
+ * A stack frame.
+ *
+ * `fc` contains a compound field class (structure, variant, array,
+ * or sequence) and `index` indicates the index of the field class in
+ * the upper frame (-1 for array and sequence field classes). `name`
+ * indicates the name of the field class in the upper frame (empty
+ * string for array and sequence field classes).
+ */
+struct field_class_stack_frame
+{
+    struct ctf_field_class *fc;
+    int64_t index;
+};
+
+/*
+ * The current context of the resolving engine.
+ */
+struct resolve_context
+{
+    explicit resolve_context(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/CTF/META/RESOLVE"}
+    {
+    }
+
+    bt2c::Logger logger;
+
+    struct ctf_trace_class *tc = nullptr;
+    struct ctf_stream_class *sc = nullptr;
+    struct ctf_event_class *ec = nullptr;
+
+    struct
+    {
+        struct ctf_field_class *packet_header = nullptr;
+        struct ctf_field_class *packet_context = nullptr;
+        struct ctf_field_class *event_header = nullptr;
+        struct ctf_field_class *event_common_context = nullptr;
+        struct ctf_field_class *event_spec_context = nullptr;
+        struct ctf_field_class *event_payload = nullptr;
+    } scopes;
+
+    /* Root scope being visited */
+    enum ctf_scope root_scope = CTF_SCOPE_PACKET_HEADER;
+    field_class_stack_t *field_class_stack = nullptr;
+    struct ctf_field_class *cur_fc = nullptr;
+};
+
+/* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */
+static const char * const absolute_path_prefixes[] = {
+    /* CTF_SCOPE_PACKET_HEADER */ "trace.packet.header.",
+    /* CTF_SCOPE_PACKET_CONTEXT */ "stream.packet.context.",
+    /* CTF_SCOPE_EVENT_HEADER */ "stream.event.header.",
+    /* CTF_SCOPE_EVENT_COMMON_CONTEXT */ "stream.event.context.",
+    /* CTF_SCOPE_EVENT_SPECIFIC_CONTEXT */ "event.context.",
+    /* CTF_SCOPE_EVENT_PAYLOAD */ "event.fields.",
+};
+
+/* Number of path tokens used for the absolute prefixes */
+static const uint64_t absolute_path_prefix_ptoken_counts[] = {
+    /* CTF_SCOPE_PACKET_HEADER */ 3,
+    /* CTF_SCOPE_PACKET_CONTEXT */ 3,
+    /* CTF_SCOPE_EVENT_HEADER */ 3,
+    /* CTF_SCOPE_EVENT_COMMON_CONTEXT */ 3,
+    /* CTF_SCOPE_EVENT_SPECIFIC_CONTEXT */ 2,
+    /* CTF_SCOPE_EVENT_PAYLOAD */ 2,
+};
+
+static void destroy_field_class_stack_frame(struct field_class_stack_frame *frame)
+{
+    if (!frame) {
+        return;
+    }
+
+    g_free(frame);
+}
+
+/*
+ * Creates a class stack.
+ */
+static field_class_stack_t *field_class_stack_create(void)
+{
+    return g_ptr_array_new_with_free_func((GDestroyNotify) destroy_field_class_stack_frame);
+}
+
+/*
+ * Destroys a class stack.
+ */
+static void field_class_stack_destroy(field_class_stack_t *stack)
+{
+    if (stack) {
+        g_ptr_array_free(stack, TRUE);
+    }
+}
+
+/*
+ * Pushes a field class onto a class stack.
+ */
+static int field_class_stack_push(field_class_stack_t *stack, struct ctf_field_class *fc,
+                                  struct resolve_context *ctx)
+{
+    int ret = 0;
+    struct field_class_stack_frame *frame = NULL;
+
+    if (!stack || !fc) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Invalid parameter: stack or field class is `NULL`.");
+        ret = -1;
+        goto end;
+    }
+
+    frame = g_new0(struct field_class_stack_frame, 1);
+    if (!frame) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Failed to allocate one field class stack frame.");
+        ret = -1;
+        goto end;
+    }
+
+    BT_CPPLOGD_SPEC(ctx->logger,
+                    "Pushing field class on context's stack: "
+                    "fc-addr={}, stack-size-before={}",
+                    fmt::ptr(fc), stack->len);
+    frame->fc = fc;
+    g_ptr_array_add(stack, frame);
+
+end:
+    return ret;
+}
+
+/*
+ * Checks whether or not `stack` is empty.
+ */
+static bool field_class_stack_empty(field_class_stack_t *stack)
+{
+    return stack->len == 0;
+}
+
+/*
+ * Returns the number of frames in `stack`.
+ */
+static size_t field_class_stack_size(field_class_stack_t *stack)
+{
+    return stack->len;
+}
+
+/*
+ * Returns the top frame of `stack`.
+ */
+static struct field_class_stack_frame *field_class_stack_peek(field_class_stack_t *stack)
+{
+    BT_ASSERT(stack);
+    BT_ASSERT(!field_class_stack_empty(stack));
+
+    return (field_class_stack_frame *) g_ptr_array_index(stack, stack->len - 1);
+}
+
+/*
+ * Returns the frame at index `index` in `stack`.
+ */
+static struct field_class_stack_frame *field_class_stack_at(field_class_stack_t *stack,
+                                                            size_t index)
+{
+    BT_ASSERT(stack);
+    BT_ASSERT(index < stack->len);
+
+    return (field_class_stack_frame *) g_ptr_array_index(stack, index);
+}
+
+/*
+ * Removes the top frame of `stack`.
+ */
+static void field_class_stack_pop(field_class_stack_t *stack, struct resolve_context *ctx)
+{
+    if (!field_class_stack_empty(stack)) {
+        /*
+         * This will call the frame's destructor and free it, as
+         * well as put its contained field class.
+         */
+        BT_CPPLOGD_SPEC(ctx->logger, "Popping context's stack: stack-size-before={}", stack->len);
+        g_ptr_array_set_size(stack, stack->len - 1);
+    }
+}
+
+/*
+ * Returns the scope field class of `scope` in the context `ctx`.
+ */
+static struct ctf_field_class *borrow_class_from_ctx(struct resolve_context *ctx,
+                                                     enum ctf_scope scope)
+{
+    switch (scope) {
+    case CTF_SCOPE_PACKET_HEADER:
+        return ctx->scopes.packet_header;
+    case CTF_SCOPE_PACKET_CONTEXT:
+        return ctx->scopes.packet_context;
+    case CTF_SCOPE_EVENT_HEADER:
+        return ctx->scopes.event_header;
+    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+        return ctx->scopes.event_common_context;
+    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+        return ctx->scopes.event_spec_context;
+    case CTF_SCOPE_EVENT_PAYLOAD:
+        return ctx->scopes.event_payload;
+    default:
+        bt_common_abort();
+    }
+
+    return NULL;
+}
+
+/*
+ * Returns the CTF scope from a path string. May return -1 if the path
+ * is found to be relative.
+ */
+static enum ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr,
+                                                           struct resolve_context *ctx)
+{
+    enum ctf_scope scope;
+    enum ctf_scope ret = CTF_SCOPE_PACKET_UNKNOWN;
+    const size_t prefixes_count = sizeof(absolute_path_prefixes) / sizeof(*absolute_path_prefixes);
+
+    for (scope = CTF_SCOPE_PACKET_HEADER; scope < CTF_SCOPE_PACKET_HEADER + prefixes_count;
+         scope = (ctf_scope) (scope + 1)) {
+        /*
+         * Check if path string starts with a known absolute
+         * path prefix.
+         *
+         * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES.
+         */
+        if (strncmp(pathstr, absolute_path_prefixes[scope],
+                    strlen(absolute_path_prefixes[scope]))) {
+            /* Prefix does not match: try the next one */
+            BT_CPPLOGD_SPEC(ctx->logger,
+                            "Prefix does not match: trying the next one: "
+                            "path=\"{}\", path-prefix=\"{}\", scope={}",
+                            pathstr, absolute_path_prefixes[scope], scope);
+            continue;
+        }
+
+        /* Found it! */
+        ret = scope;
+        BT_CPPLOGD_SPEC(ctx->logger,
+                        "Found root scope from absolute path: "
+                        "path=\"{}\", scope={}",
+                        pathstr, scope);
+        goto end;
+    }
+
+end:
+    return ret;
+}
+
+/*
+ * Destroys a path token.
+ */
+static void ptokens_destroy_func(gpointer ptoken, gpointer)
+{
+    g_string_free((GString *) ptoken, TRUE);
+}
+
+/*
+ * Destroys a path token list.
+ */
+static void ptokens_destroy(GList *ptokens)
+{
+    if (!ptokens) {
+        return;
+    }
+
+    g_list_foreach(ptokens, ptokens_destroy_func, NULL);
+    g_list_free(ptokens);
+}
+
+/*
+ * Returns the string contained in a path token.
+ */
+static const char *ptoken_get_string(GList *ptoken)
+{
+    GString *tokenstr = (GString *) ptoken->data;
+
+    return tokenstr->str;
+}
+
+/*
+ * Converts a path string to a path token list, that is, splits the
+ * individual words of a path string into a list of individual
+ * strings.
+ */
+static GList *pathstr_to_ptokens(const char *pathstr, struct resolve_context *ctx)
+{
+    const char *at = pathstr;
+    const char *last = at;
+    GList *ptokens = NULL;
+
+    for (;;) {
+        if (*at == '.' || *at == '\0') {
+            GString *tokenstr;
+
+            if (at == last) {
+                /* Error: empty token */
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Empty path token: path=\"{}\", pos={}",
+                                             pathstr, (unsigned int) (at - pathstr));
+                goto error;
+            }
+
+            tokenstr = g_string_new(NULL);
+            g_string_append_len(tokenstr, last, at - last);
+            ptokens = g_list_append(ptokens, tokenstr);
+            last = at + 1;
+        }
+
+        if (*at == '\0') {
+            break;
+        }
+
+        at++;
+    }
+
+    return ptokens;
+
+error:
+    ptokens_destroy(ptokens);
+    return NULL;
+}
+
+/*
+ * Converts a path token list to a field path object. The path token
+ * list is relative from `fc`. The index of the source looking for its
+ * target within `fc` is indicated by `src_index`. This can be
+ * `INT64_MAX` if the source is contained in `fc`.
+ *
+ * `field_path` is an output parameter owned by the caller that must be
+ * filled here.
+ */
+static int ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
+                                 struct ctf_field_class *fc, int64_t src_index,
+                                 struct resolve_context *ctx)
+{
+    int ret = 0;
+    GList *cur_ptoken = ptokens;
+    bool first_level_done = false;
+
+    /* Locate target */
+    while (cur_ptoken) {
+        int64_t child_index;
+        struct ctf_field_class *child_fc;
+        const char *ft_name = ptoken_get_string(cur_ptoken);
+
+        BT_CPPLOGD_SPEC(ctx->logger, "Current path token: token=\"{}\"", ft_name);
+
+        /* Find to which index corresponds the current path token */
+        if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+            child_index = -1;
+        } else {
+            child_index =
+                ctf_field_class_compound_get_field_class_index_from_orig_name(fc, ft_name);
+            if (child_index < 0) {
+                /*
+                 * Error: field name does not exist or
+                 * wrong current class.
+                 */
+                BT_CPPLOGD_SPEC(ctx->logger,
+                                "Cannot get index of field class: "
+                                "field-name=\"{}\", "
+                                "src-index={}, "
+                                "child-index={}, "
+                                "first-level-done={}",
+                                ft_name, src_index, child_index, first_level_done);
+                ret = -1;
+                goto end;
+            } else if (child_index > src_index && !first_level_done) {
+                BT_CPPLOGD_SPEC(ctx->logger,
+                                "Child field class is located after source field class: "
+                                "field-name=\"{}\", "
+                                "src-index={}, "
+                                "child-index={}, "
+                                "first-level-done={}",
+                                ft_name, src_index, child_index, first_level_done);
+                ret = -1;
+                goto end;
+            }
+
+            /* Next path token */
+            cur_ptoken = g_list_next(cur_ptoken);
+            first_level_done = true;
+        }
+
+        /* Create new field path entry */
+        ctf_field_path_append_index(field_path, child_index);
+
+        /* Get child field class */
+        child_fc = ctf_field_class_compound_borrow_field_class_by_index(fc, child_index);
+        BT_ASSERT(child_fc);
+
+        /* Move child class to current class */
+        fc = child_fc;
+    }
+
+end:
+    return ret;
+}
+
+/*
+ * Converts a known absolute path token list to a field path object
+ * within the resolving context `ctx`.
+ *
+ * `field_path` is an output parameter owned by the caller that must be
+ * filled here.
+ */
+static int absolute_ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
+                                          struct resolve_context *ctx)
+{
+    int ret = 0;
+    GList *cur_ptoken;
+    struct ctf_field_class *fc;
+
+    /*
+     * Make sure we're not referring to a scope within a translated
+     * object.
+     */
+    switch (field_path->root) {
+    case CTF_SCOPE_PACKET_HEADER:
+        if (ctx->tc->is_translated) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Trace class is already translated: "
+                                         "root-scope={}",
+                                         field_path->root);
+            ret = -1;
+            goto end;
+        }
+
+        break;
+    case CTF_SCOPE_PACKET_CONTEXT:
+    case CTF_SCOPE_EVENT_HEADER:
+    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+        if (!ctx->sc) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "No current stream class: "
+                                         "root-scope={}",
+                                         field_path->root);
+            ret = -1;
+            goto end;
+        }
+
+        if (ctx->sc->is_translated) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Stream class is already translated: "
+                                         "root-scope={}",
+                                         field_path->root);
+            ret = -1;
+            goto end;
+        }
+
+        break;
+    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+    case CTF_SCOPE_EVENT_PAYLOAD:
+        if (!ctx->ec) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "No current event class: "
+                                         "root-scope={}",
+                                         field_path->root);
+            ret = -1;
+            goto end;
+        }
+
+        if (ctx->ec->is_translated) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Event class is already translated: "
+                                         "root-scope={}",
+                                         field_path->root);
+            ret = -1;
+            goto end;
+        }
+
+        break;
+
+    default:
+        bt_common_abort();
+    }
+
+    /* Skip absolute path tokens */
+    cur_ptoken = g_list_nth(ptokens, absolute_path_prefix_ptoken_counts[field_path->root]);
+
+    /* Start with root class */
+    fc = borrow_class_from_ctx(ctx, field_path->root);
+    if (!fc) {
+        /* Error: root class is not available */
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Root field class is not available: "
+                                     "root-scope={}",
+                                     field_path->root);
+        ret = -1;
+        goto end;
+    }
+
+    /* Locate target */
+    ret = ptokens_to_field_path(cur_ptoken, field_path, fc, INT64_MAX, ctx);
+
+end:
+    return ret;
+}
+
+/*
+ * Converts a known relative path token list to a field path object
+ * within the resolving context `ctx`.
+ *
+ * `field_path` is an output parameter owned by the caller that must be
+ * filled here.
+ */
+static int relative_ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
+                                          struct resolve_context *ctx)
+{
+    int ret = 0;
+    int64_t parent_pos_in_stack;
+    struct ctf_field_path tail_field_path;
+
+    ctf_field_path_init(&tail_field_path);
+    parent_pos_in_stack = field_class_stack_size(ctx->field_class_stack) - 1;
+
+    while (parent_pos_in_stack >= 0) {
+        struct ctf_field_class *parent_class =
+            field_class_stack_at(ctx->field_class_stack, parent_pos_in_stack)->fc;
+        int64_t cur_index =
+            field_class_stack_at(ctx->field_class_stack, parent_pos_in_stack)->index;
+
+        BT_CPPLOGD_SPEC(ctx->logger,
+                        "Locating target field class from current parent field class: "
+                        "parent-pos={}, parent-fc-addr={}, "
+                        "cur-index={}",
+                        parent_pos_in_stack, fmt::ptr(parent_class), cur_index);
+
+        /* Locate target from current parent class */
+        ret = ptokens_to_field_path(ptokens, &tail_field_path, parent_class, cur_index, ctx);
+        if (ret) {
+            /* Not found... yet */
+            BT_CPPLOGD_STR_SPEC(ctx->logger, "Not found at this point.");
+            ctf_field_path_clear(&tail_field_path);
+        } else {
+            /* Found: stitch tail field path to head field path */
+            uint64_t i = 0;
+            size_t tail_field_path_len = tail_field_path.path->len;
+
+            while (BT_TRUE) {
+                struct ctf_field_class *cur_class =
+                    field_class_stack_at(ctx->field_class_stack, i)->fc;
+                int64_t index = field_class_stack_at(ctx->field_class_stack, i)->index;
+
+                if (cur_class == parent_class) {
+                    break;
+                }
+
+                ctf_field_path_append_index(field_path, index);
+                i++;
+            }
+
+            for (i = 0; i < tail_field_path_len; i++) {
+                int64_t index = ctf_field_path_borrow_index_by_index(&tail_field_path, i);
+
+                ctf_field_path_append_index(field_path, (int64_t) index);
+            }
+            break;
+        }
+
+        parent_pos_in_stack--;
+    }
+
+    if (parent_pos_in_stack < 0) {
+        /* Not found */
+        ret = -1;
+    }
+
+    ctf_field_path_fini(&tail_field_path);
+    return ret;
+}
+
+/*
+ * Converts a path string to a field path object within the resolving
+ * context `ctx`.
+ */
+static int pathstr_to_field_path(const char *pathstr, struct ctf_field_path *field_path,
+                                 struct resolve_context *ctx)
+{
+    int ret = 0;
+    enum ctf_scope root_scope;
+    GList *ptokens = NULL;
+
+    /* Convert path string to path tokens */
+    ptokens = pathstr_to_ptokens(pathstr, ctx);
+    if (!ptokens) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Cannot convert path string to path tokens: "
+                                     "path=\"{}\"",
+                                     pathstr);
+        ret = -1;
+        goto end;
+    }
+
+    /* Absolute or relative path? */
+    root_scope = get_root_scope_from_absolute_pathstr(pathstr, ctx);
+
+    if (root_scope == CTF_SCOPE_PACKET_UNKNOWN) {
+        /* Relative path: start with current root scope */
+        field_path->root = ctx->root_scope;
+        BT_CPPLOGD_SPEC(ctx->logger,
+                        "Detected relative path: starting with current root scope: "
+                        "scope={}",
+                        field_path->root);
+        ret = relative_ptokens_to_field_path(ptokens, field_path, ctx);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot get relative field path of path string: "
+                                         "path=\"{}\", start-scope={}, end-scope={}",
+                                         pathstr, ctx->root_scope, field_path->root);
+            goto end;
+        }
+    } else {
+        /* Absolute path: use found root scope */
+        field_path->root = root_scope;
+        BT_CPPLOGD_SPEC(ctx->logger,
+                        "Detected absolute path: using root scope: "
+                        "scope={}",
+                        field_path->root);
+        ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot get absolute field path of path string: "
+                                         "path=\"{}\", root-scope={}",
+                                         pathstr, root_scope);
+            goto end;
+        }
+    }
+
+    if (ret == 0) {
+        BT_CPPLOGD_SPEC(ctx->logger, "Found field path: path=\"{}\", field-path=\"{}\"", pathstr,
+                        *field_path);
+    }
+
+end:
+    ptokens_destroy(ptokens);
+    return ret;
+}
+
+/*
+ * Retrieves a field class by following the field path `field_path` in
+ * the resolving context `ctx`.
+ */
+static struct ctf_field_class *field_path_to_field_class(struct ctf_field_path *field_path,
+                                                         struct resolve_context *ctx)
+{
+    uint64_t i;
+    struct ctf_field_class *fc;
+
+    /* Start with root class */
+    fc = borrow_class_from_ctx(ctx, field_path->root);
+    if (!fc) {
+        /* Error: root class is not available */
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            ctx->logger, "Root field class is not available: root-scope={}", field_path->root);
+        goto end;
+    }
+
+    /* Locate target */
+    for (i = 0; i < field_path->path->len; i++) {
+        struct ctf_field_class *child_fc;
+        int64_t child_index = ctf_field_path_borrow_index_by_index(field_path, i);
+
+        /* Get child field class */
+        child_fc = ctf_field_class_compound_borrow_field_class_by_index(fc, child_index);
+        BT_ASSERT(child_fc);
+
+        /* Move child class to current class */
+        fc = child_fc;
+    }
+
+end:
+    return fc;
+}
+
+/*
+ * Fills the equivalent field path object of the context class stack.
+ */
+static void get_ctx_stack_field_path(struct resolve_context *ctx, struct ctf_field_path *field_path)
+{
+    uint64_t i;
+
+    BT_ASSERT(field_path);
+    field_path->root = ctx->root_scope;
+    ctf_field_path_clear(field_path);
+
+    for (i = 0; i < field_class_stack_size(ctx->field_class_stack); i++) {
+        struct field_class_stack_frame *frame = field_class_stack_at(ctx->field_class_stack, i);
+
+        ctf_field_path_append_index(field_path, frame->index);
+    }
+}
+
+/*
+ * Returns the index of the lowest common ancestor of two field path
+ * objects having the same root scope.
+ */
+static int64_t get_field_paths_lca_index(struct ctf_field_path *field_path1,
+                                         struct ctf_field_path *field_path2,
+                                         struct resolve_context *ctx)
+{
+    int64_t lca_index = 0;
+    uint64_t field_path1_len, field_path2_len;
+
+    BT_CPPLOGD_SPEC(ctx->logger,
+                    "Finding lowest common ancestor (LCA) between two field paths: "
+                    "field-path-1=\"{}\", field-path-2=\"{}\"",
+                    *field_path1, *field_path2);
+
+    /*
+     * Start from both roots and find the first mismatch.
+     */
+    BT_ASSERT(field_path1->root == field_path2->root);
+    field_path1_len = field_path1->path->len;
+    field_path2_len = field_path2->path->len;
+
+    while (true) {
+        int64_t target_index, ctx_index;
+
+        if (lca_index == (int64_t) field_path2_len || lca_index == (int64_t) field_path1_len) {
+            /*
+             * This means that both field paths never split.
+             * This is invalid because the target cannot be
+             * an ancestor of the source.
+             */
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger,
+                "Source field class is an ancestor of target field class or vice versa: "
+                "lca-index={}, "
+                "field-path-1-len={}, "
+                "field-path-2-len={}",
+                lca_index, field_path1_len, field_path2_len);
+            lca_index = -1;
+            break;
+        }
+
+        target_index = ctf_field_path_borrow_index_by_index(field_path1, lca_index);
+        ctx_index = ctf_field_path_borrow_index_by_index(field_path2, lca_index);
+
+        if (target_index != ctx_index) {
+            /* LCA index is the previous */
+            break;
+        }
+
+        lca_index++;
+    }
+
+    BT_CPPLOGD_SPEC(ctx->logger, "Found LCA: lca-index={}", lca_index);
+    return lca_index;
+}
+
+/*
+ * Validates a target field path.
+ */
+static int validate_target_field_path(struct ctf_field_path *target_field_path,
+                                      struct ctf_field_class *target_fc,
+                                      struct resolve_context *ctx)
+{
+    int ret = 0;
+    struct ctf_field_path ctx_field_path;
+    uint64_t target_field_path_len = target_field_path->path->len;
+    int64_t lca_index;
+
+    /* Get context field path */
+    ctf_field_path_init(&ctx_field_path);
+    get_ctx_stack_field_path(ctx, &ctx_field_path);
+
+    /*
+     * Make sure the target is not a root.
+     */
+    if (target_field_path_len == 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Target field path's length is 0 (targeting the root).");
+        ret = -1;
+        goto end;
+    }
+
+    /*
+     * Make sure the root of the target field path is not located
+     * after the context field path's root.
+     */
+    if (target_field_path->root > ctx_field_path.root) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Target field class is located after source field class: "
+                                     "target-root={}, source-root={}",
+                                     target_field_path->root, ctx_field_path.root);
+        ret = -1;
+        goto end;
+    }
+
+    if (target_field_path->root == ctx_field_path.root) {
+        int64_t target_index, ctx_index;
+
+        /*
+         * Find the index of the lowest common ancestor of both field
+         * paths.
+         */
+        lca_index = get_field_paths_lca_index(target_field_path, &ctx_field_path, ctx);
+        if (lca_index < 0) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot get least common ancestor.");
+            ret = -1;
+            goto end;
+        }
+
+        /*
+         * Make sure the target field path is located before the
+         * context field path.
+         */
+        target_index =
+            ctf_field_path_borrow_index_by_index(target_field_path, (uint64_t) lca_index);
+        ctx_index = ctf_field_path_borrow_index_by_index(&ctx_field_path, (uint64_t) lca_index);
+
+        if (target_index >= ctx_index) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger,
+                "Target field class's index is greater than or equal to source field class's index in LCA: "
+                "lca-index={}, "
+                "target-index={}, "
+                "source-index={}",
+                lca_index, target_index, ctx_index);
+            ret = -1;
+            goto end;
+        }
+    }
+
+    /*
+     * Make sure the target class has the right class and properties.
+     */
+    switch (ctx->cur_fc->type) {
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+        if (target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger,
+                "Variant field class's tag field class is not an enumeration field class: "
+                "tag-fc-addr={}, tag-fc-id={}",
+                fmt::ptr(target_fc), (int) target_fc->type);
+            ret = -1;
+            goto end;
+        }
+        break;
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        if (target_fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+            target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger,
+                "Sequence field class's length field class is not an unsigned integer field class: "
+                "length-fc-addr={}, length-fc-id={}",
+                fmt::ptr(target_fc), (int) target_fc->type);
+            ret = -1;
+            goto end;
+        }
+
+        ctf_field_class_int *int_fc = ctf_field_class_as_int(target_fc);
+
+        if (int_fc->is_signed) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger,
+                "Sequence field class's length field class is not an unsigned integer field class: "
+                "length-fc-addr={}, length-fc-id={}",
+                fmt::ptr(target_fc), (int) target_fc->type);
+            ret = -1;
+            goto end;
+        }
+        break;
+    }
+    default:
+        bt_common_abort();
+    }
+
+end:
+    ctf_field_path_fini(&ctx_field_path);
+    return ret;
+}
+
+/*
+ * Resolves a variant or sequence field class `fc`.
+ */
+static int resolve_sequence_or_variant_field_class(struct ctf_field_class *fc,
+                                                   struct resolve_context *ctx)
+{
+    int ret = 0;
+    const char *pathstr;
+    struct ctf_field_path target_field_path;
+    struct ctf_field_class *target_fc = NULL;
+
+    ctf_field_path_init(&target_field_path);
+
+    /* Get path string */
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+        pathstr = seq_fc->length_ref->str;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+        pathstr = var_fc->tag_ref->str;
+        break;
+    }
+    default:
+        bt_common_abort();
+    }
+
+    if (!pathstr) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot get path string.");
+        ret = -1;
+        goto end;
+    }
+
+    /* Get target field path out of path string */
+    ret = pathstr_to_field_path(pathstr, &target_field_path, ctx);
+    if (ret) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Cannot get target field path for path string: "
+                                     "path=\"{}\"",
+                                     pathstr);
+        goto end;
+    }
+
+    /* Get target field class */
+    target_fc = field_path_to_field_class(&target_field_path, ctx);
+    if (!target_fc) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Cannot get target field class for path string: "
+                                     "path=\"{}\", target-field-path=\"{}\"",
+                                     pathstr, target_field_path);
+        ret = -1;
+        goto end;
+    }
+
+    ret = validate_target_field_path(&target_field_path, target_fc, ctx);
+    if (ret) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Invalid target field path for path string: "
+                                     "path=\"{}\", target-field-path=\"{}\"",
+                                     pathstr, target_field_path);
+        goto end;
+    }
+
+    /* Set target field path and target field class */
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+
+        ctf_field_path_copy_content(&seq_fc->length_path, &target_field_path);
+        seq_fc->length_fc = ctf_field_class_as_int(target_fc);
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        ctf_field_path_copy_content(&var_fc->tag_path, &target_field_path);
+        ctf_field_class_variant_set_tag_field_class(var_fc, ctf_field_class_as_enum(target_fc));
+        break;
+    }
+    default:
+        bt_common_abort();
+    }
+
+end:
+    ctf_field_path_fini(&target_field_path);
+    return ret;
+}
+
+/*
+ * Resolves a field class `fc`.
+ */
+static int resolve_field_class(struct ctf_field_class *fc, struct resolve_context *ctx)
+{
+    int ret = 0;
+
+    if (!fc) {
+        /* Field class is not available; still valid */
+        goto end;
+    }
+
+    ctx->cur_fc = fc;
+
+    /* Resolve sequence/variant field class */
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+        ret = resolve_sequence_or_variant_field_class(fc, ctx);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger,
+                "Cannot resolve sequence field class's length or variant field class's tag: "
+                "ret={}, fc-addr={}",
+                ret, fmt::ptr(fc));
+            goto end;
+        }
+
+        break;
+    default:
+        break;
+    }
+
+    /* Recurse into compound classes */
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    {
+        uint64_t i;
+        uint64_t field_count = ctf_field_class_compound_get_field_class_count(fc);
+
+        ret = field_class_stack_push(ctx->field_class_stack, fc, ctx);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot push field class on context's stack: "
+                                         "fc-addr={}",
+                                         fmt::ptr(fc));
+            goto end;
+        }
+
+        for (i = 0; i < field_count; i++) {
+            struct ctf_field_class *child_fc =
+                ctf_field_class_compound_borrow_field_class_by_index(fc, i);
+
+            BT_ASSERT(child_fc);
+
+            if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
+                fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+                field_class_stack_peek(ctx->field_class_stack)->index = -1;
+            } else {
+                field_class_stack_peek(ctx->field_class_stack)->index = (int64_t) i;
+            }
+
+            BT_CPPLOGD_SPEC(ctx->logger,
+                            "Resolving field class's child field class: "
+                            "parent-fc-addr={}, child-fc-addr={}, "
+                            "index={}, count={}",
+                            fmt::ptr(fc), fmt::ptr(child_fc), i, field_count);
+            ret = resolve_field_class(child_fc, ctx);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        field_class_stack_pop(ctx->field_class_stack, ctx);
+        break;
+    }
+    default:
+        break;
+    }
+
+end:
+    return ret;
+}
+
+/*
+ * Resolves the root field class corresponding to the scope `root_scope`.
+ */
+static int resolve_root_class(enum ctf_scope root_scope, struct resolve_context *ctx)
+{
+    int ret;
+
+    BT_ASSERT(field_class_stack_size(ctx->field_class_stack) == 0);
+    ctx->root_scope = root_scope;
+    ret = resolve_field_class(borrow_class_from_ctx(ctx, root_scope), ctx);
+    ctx->root_scope = CTF_SCOPE_PACKET_UNKNOWN;
+    return ret;
+}
+
+static int resolve_event_class_field_classes(struct resolve_context *ctx,
+                                             struct ctf_event_class *ec)
+{
+    int ret = 0;
+
+    BT_ASSERT(!ctx->scopes.event_spec_context);
+    BT_ASSERT(!ctx->scopes.event_payload);
+
+    if (ec->is_translated) {
+        goto end;
+    }
+
+    ctx->ec = ec;
+    ctx->scopes.event_spec_context = ec->spec_context_fc;
+    ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
+    if (ret) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Cannot resolve event specific context field class: "
+                                     "ret={}",
+                                     ret);
+        goto end;
+    }
+
+    ctx->scopes.event_payload = ec->payload_fc;
+    ret = resolve_root_class(CTF_SCOPE_EVENT_PAYLOAD, ctx);
+    if (ret) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Cannot resolve event payload field class: "
+                                     "ret={}",
+                                     ret);
+        goto end;
+    }
+
+end:
+    ctx->scopes.event_spec_context = NULL;
+    ctx->scopes.event_payload = NULL;
+    ctx->ec = NULL;
+    return ret;
+}
+
+static int resolve_stream_class_field_classes(struct resolve_context *ctx,
+                                              struct ctf_stream_class *sc)
+{
+    int ret = 0;
+    uint64_t i;
+
+    BT_ASSERT(!ctx->scopes.packet_context);
+    BT_ASSERT(!ctx->scopes.event_header);
+    BT_ASSERT(!ctx->scopes.event_common_context);
+    ctx->sc = sc;
+
+    if (!sc->is_translated) {
+        ctx->scopes.packet_context = sc->packet_context_fc;
+        ret = resolve_root_class(CTF_SCOPE_PACKET_CONTEXT, ctx);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot resolve packet context field class: "
+                                         "ret={}",
+                                         ret);
+            goto end;
+        }
+
+        ctx->scopes.event_header = sc->event_header_fc;
+        ret = resolve_root_class(CTF_SCOPE_EVENT_HEADER, ctx);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot resolve event header field class: "
+                                         "ret={}",
+                                         ret);
+            goto end;
+        }
+
+        ctx->scopes.event_common_context = sc->event_common_context_fc;
+        ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot resolve event common context field class: "
+                                         "ret={}",
+                                         ret);
+            goto end;
+        }
+    }
+
+    ctx->scopes.packet_context = sc->packet_context_fc;
+    ctx->scopes.event_header = sc->event_header_fc;
+    ctx->scopes.event_common_context = sc->event_common_context_fc;
+
+    for (i = 0; i < sc->event_classes->len; i++) {
+        ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[i];
+
+        ret = resolve_event_class_field_classes(ctx, ec);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot resolve event class's field classes: "
+                                         "ec-id={}, ec-name=\"{}\"",
+                                         ec->id, ec->name->str);
+            goto end;
+        }
+    }
+
+end:
+    ctx->scopes.packet_context = NULL;
+    ctx->scopes.event_header = NULL;
+    ctx->scopes.event_common_context = NULL;
+    ctx->sc = NULL;
+    return ret;
+}
+
+int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc,
+                                          const bt2c::Logger& parentLogger)
+{
+    int ret = 0;
+    uint64_t i;
+
+    resolve_context local_ctx(parentLogger);
+    local_ctx.tc = tc;
+    local_ctx.scopes.packet_header = tc->packet_header_fc;
+    local_ctx.root_scope = CTF_SCOPE_PACKET_HEADER;
+
+    struct resolve_context *ctx = &local_ctx;
+
+    /* Initialize class stack */
+    ctx->field_class_stack = field_class_stack_create();
+    if (!ctx->field_class_stack) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot create field class stack.");
+        ret = -1;
+        goto end;
+    }
+
+    if (!tc->is_translated) {
+        ctx->scopes.packet_header = tc->packet_header_fc;
+        ret = resolve_root_class(CTF_SCOPE_PACKET_HEADER, ctx);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot resolve packet header field class: "
+                                         "ret={}",
+                                         ret);
+            goto end;
+        }
+    }
+
+    ctx->scopes.packet_header = tc->packet_header_fc;
+
+    for (i = 0; i < tc->stream_classes->len; i++) {
+        ctf_stream_class *sc = (ctf_stream_class *) tc->stream_classes->pdata[i];
+
+        ret = resolve_stream_class_field_classes(ctx, sc);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot resolve stream class's field classes: "
+                                         "sc-id={}",
+                                         sc->id);
+            goto end;
+        }
+    }
+
+end:
+    field_class_stack_destroy(ctx->field_class_stack);
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-translate.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-translate.cpp
new file mode 100644 (file)
index 0000000..b62dabf
--- /dev/null
@@ -0,0 +1,656 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+
+#include "ctf-meta-visitors.hpp"
+
+namespace ctf {
+
+struct MetaTranslateCtx
+{
+    bt_self_component *self_comp;
+    bt_trace_class *ir_tc;
+    bt_stream_class *ir_sc;
+    struct ctf_trace_class *tc;
+    struct ctf_stream_class *sc;
+    struct ctf_event_class *ec;
+    enum ctf_scope scope;
+};
+
+} /* namespace ctf */
+
+static inline bt_field_class *ctf_field_class_to_ir(ctf::MetaTranslateCtx *ctx,
+                                                    struct ctf_field_class *fc);
+
+static inline void ctf_field_class_int_set_props(struct ctf_field_class_int *fc,
+                                                 bt_field_class *ir_fc)
+{
+    bt_field_class_integer_set_field_value_range(ir_fc, fc->base.size);
+    bt_field_class_integer_set_preferred_display_base(ir_fc, fc->disp_base);
+}
+
+static inline bt_field_class *ctf_field_class_int_to_ir(ctf::MetaTranslateCtx *ctx,
+                                                        struct ctf_field_class_int *fc)
+{
+    bt_field_class *ir_fc;
+
+    if (fc->is_signed) {
+        ir_fc = bt_field_class_integer_signed_create(ctx->ir_tc);
+    } else {
+        ir_fc = bt_field_class_integer_unsigned_create(ctx->ir_tc);
+    }
+
+    BT_ASSERT(ir_fc);
+    ctf_field_class_int_set_props(fc, ir_fc);
+    return ir_fc;
+}
+
+static inline bt_field_class *ctf_field_class_enum_to_ir(ctf::MetaTranslateCtx *ctx,
+                                                         struct ctf_field_class_enum *fc)
+{
+    int ret;
+    bt_field_class *ir_fc;
+    uint64_t i;
+
+    if (fc->base.is_signed) {
+        ir_fc = bt_field_class_enumeration_signed_create(ctx->ir_tc);
+    } else {
+        ir_fc = bt_field_class_enumeration_unsigned_create(ctx->ir_tc);
+    }
+
+    BT_ASSERT(ir_fc);
+    ctf_field_class_int_set_props(&fc->base, ir_fc);
+
+    for (i = 0; i < fc->mappings->len; i++) {
+        struct ctf_field_class_enum_mapping *mapping =
+            ctf_field_class_enum_borrow_mapping_by_index(fc, i);
+        bt_integer_range_set_signed *range_set_signed = NULL;
+        bt_integer_range_set_unsigned *range_set_unsigned = NULL;
+        uint64_t range_i;
+
+        if (fc->base.is_signed) {
+            range_set_signed = bt_integer_range_set_signed_create();
+            BT_ASSERT(range_set_signed);
+        } else {
+            range_set_unsigned = bt_integer_range_set_unsigned_create();
+            BT_ASSERT(range_set_unsigned);
+        }
+
+        for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
+            struct ctf_range *range =
+                ctf_field_class_enum_mapping_borrow_range_by_index(mapping, range_i);
+
+            if (fc->base.is_signed) {
+                ret = bt_integer_range_set_signed_add_range(range_set_signed, range->lower.i,
+                                                            range->upper.i);
+            } else {
+                ret = bt_integer_range_set_unsigned_add_range(range_set_unsigned, range->lower.u,
+                                                              range->upper.u);
+            }
+
+            BT_ASSERT(ret == 0);
+        }
+
+        if (fc->base.is_signed) {
+            ret = bt_field_class_enumeration_signed_add_mapping(ir_fc, mapping->label->str,
+                                                                range_set_signed);
+            BT_INTEGER_RANGE_SET_SIGNED_PUT_REF_AND_RESET(range_set_signed);
+        } else {
+            ret = bt_field_class_enumeration_unsigned_add_mapping(ir_fc, mapping->label->str,
+                                                                  range_set_unsigned);
+            BT_INTEGER_RANGE_SET_UNSIGNED_PUT_REF_AND_RESET(range_set_unsigned);
+        }
+
+        BT_ASSERT(ret == 0);
+    }
+
+    return ir_fc;
+}
+
+static inline bt_field_class *ctf_field_class_float_to_ir(ctf::MetaTranslateCtx *ctx,
+                                                          struct ctf_field_class_float *fc)
+{
+    bt_field_class *ir_fc;
+
+    if (fc->base.size == 32) {
+        ir_fc = bt_field_class_real_single_precision_create(ctx->ir_tc);
+    } else {
+        ir_fc = bt_field_class_real_double_precision_create(ctx->ir_tc);
+    }
+    BT_ASSERT(ir_fc);
+
+    return ir_fc;
+}
+
+static inline bt_field_class *ctf_field_class_string_to_ir(ctf::MetaTranslateCtx *ctx)
+{
+    bt_field_class *ir_fc = bt_field_class_string_create(ctx->ir_tc);
+
+    BT_ASSERT(ir_fc);
+    return ir_fc;
+}
+
+static inline void translate_struct_field_class_members(ctf::MetaTranslateCtx *ctx,
+                                                        struct ctf_field_class_struct *fc,
+                                                        bt_field_class *ir_fc, bool,
+                                                        struct ctf_field_class_struct *)
+{
+    uint64_t i;
+    int ret;
+
+    for (i = 0; i < fc->members->len; i++) {
+        struct ctf_named_field_class *named_fc =
+            ctf_field_class_struct_borrow_member_by_index(fc, i);
+        bt_field_class *member_ir_fc;
+        const char *name = named_fc->name->str;
+
+        if (!named_fc->fc->in_ir) {
+            continue;
+        }
+
+        member_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc);
+        BT_ASSERT(member_ir_fc);
+        ret = bt_field_class_structure_append_member(ir_fc, name, member_ir_fc);
+        BT_ASSERT(ret == 0);
+        bt_field_class_put_ref(member_ir_fc);
+    }
+}
+
+static inline bt_field_class *ctf_field_class_struct_to_ir(ctf::MetaTranslateCtx *ctx,
+                                                           struct ctf_field_class_struct *fc)
+{
+    bt_field_class *ir_fc = bt_field_class_structure_create(ctx->ir_tc);
+
+    BT_ASSERT(ir_fc);
+    translate_struct_field_class_members(ctx, fc, ir_fc, false, NULL);
+    return ir_fc;
+}
+
+static inline bt_field_class *borrow_ir_fc_from_field_path(ctf::MetaTranslateCtx *ctx,
+                                                           struct ctf_field_path *field_path)
+{
+    bt_field_class *ir_fc = NULL;
+    struct ctf_field_class *fc =
+        ctf_field_path_borrow_field_class(field_path, ctx->tc, ctx->sc, ctx->ec);
+
+    BT_ASSERT(fc);
+
+    if (fc->in_ir) {
+        ir_fc = fc->ir_fc;
+    }
+
+    return ir_fc;
+}
+
+static inline const bt_field_class_enumeration_mapping *
+find_ir_enum_field_class_mapping_by_label(const bt_field_class *fc, const char *label,
+                                          bool is_signed)
+{
+    const bt_field_class_enumeration_mapping *mapping = NULL;
+    uint64_t i;
+
+    for (i = 0; i < bt_field_class_enumeration_get_mapping_count(fc); i++) {
+        const bt_field_class_enumeration_mapping *this_mapping;
+        const bt_field_class_enumeration_signed_mapping *signed_this_mapping = NULL;
+        const bt_field_class_enumeration_unsigned_mapping *unsigned_this_mapping = NULL;
+
+        if (is_signed) {
+            signed_this_mapping =
+                bt_field_class_enumeration_signed_borrow_mapping_by_index_const(fc, i);
+            BT_ASSERT(signed_this_mapping);
+            this_mapping =
+                bt_field_class_enumeration_signed_mapping_as_mapping_const(signed_this_mapping);
+        } else {
+            unsigned_this_mapping =
+                bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(fc, i);
+            BT_ASSERT(unsigned_this_mapping);
+            this_mapping =
+                bt_field_class_enumeration_unsigned_mapping_as_mapping_const(unsigned_this_mapping);
+        }
+
+        BT_ASSERT(this_mapping);
+
+        if (strcmp(bt_field_class_enumeration_mapping_get_label(this_mapping), label) == 0) {
+            mapping = this_mapping;
+            goto end;
+        }
+    }
+
+end:
+    return mapping;
+}
+
+static inline bt_field_class *ctf_field_class_variant_to_ir(ctf::MetaTranslateCtx *ctx,
+                                                            struct ctf_field_class_variant *fc)
+{
+    int ret;
+    bt_field_class *ir_fc;
+    uint64_t i;
+    bt_field_class *ir_tag_fc = NULL;
+
+    if (fc->tag_path.root != CTF_SCOPE_PACKET_HEADER &&
+        fc->tag_path.root != CTF_SCOPE_EVENT_HEADER) {
+        ir_tag_fc = borrow_ir_fc_from_field_path(ctx, &fc->tag_path);
+        BT_ASSERT(ir_tag_fc);
+    }
+
+    ir_fc = bt_field_class_variant_create(ctx->ir_tc, ir_tag_fc);
+    BT_ASSERT(ir_fc);
+
+    for (i = 0; i < fc->options->len; i++) {
+        struct ctf_named_field_class *named_fc =
+            ctf_field_class_variant_borrow_option_by_index(fc, i);
+        bt_field_class *option_ir_fc;
+
+        BT_ASSERT(named_fc->fc->in_ir);
+        option_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc);
+        BT_ASSERT(option_ir_fc);
+
+        if (ir_tag_fc) {
+            /*
+             * At this point the trace IR selector
+             * (enumeration) field class already exists if
+             * the variant is tagged (`ir_tag_fc`). This one
+             * already contains range sets for its mappings,
+             * so we just reuse the same, finding them by
+             * matching a variant field class's option's
+             * _original_ name (with a leading underscore,
+             * possibly) with a selector field class's
+             * mapping name.
+             */
+            if (fc->tag_fc->base.is_signed) {
+                const bt_field_class_enumeration_signed_mapping *mapping =
+                    (bt_field_class_enumeration_signed_mapping *)
+                        find_ir_enum_field_class_mapping_by_label(ir_tag_fc,
+                                                                  named_fc->orig_name->str, true);
+                const bt_integer_range_set_signed *range_set;
+
+                BT_ASSERT(mapping);
+                range_set = bt_field_class_enumeration_signed_mapping_borrow_ranges_const(mapping);
+                BT_ASSERT(range_set);
+                ret = bt_field_class_variant_with_selector_field_integer_signed_append_option(
+                    ir_fc, named_fc->name->str, option_ir_fc, range_set);
+            } else {
+                const bt_field_class_enumeration_unsigned_mapping *mapping =
+                    (bt_field_class_enumeration_unsigned_mapping *)
+                        find_ir_enum_field_class_mapping_by_label(ir_tag_fc,
+                                                                  named_fc->orig_name->str, false);
+                const bt_integer_range_set_unsigned *range_set;
+
+                BT_ASSERT(mapping);
+                range_set =
+                    bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(mapping);
+                BT_ASSERT(range_set);
+                ret = bt_field_class_variant_with_selector_field_integer_unsigned_append_option(
+                    ir_fc, named_fc->name->str, option_ir_fc, range_set);
+            }
+        } else {
+            ret = bt_field_class_variant_without_selector_append_option(ir_fc, named_fc->name->str,
+                                                                        option_ir_fc);
+        }
+
+        BT_ASSERT(ret == 0);
+        bt_field_class_put_ref(option_ir_fc);
+    }
+
+    return ir_fc;
+}
+
+static inline bt_field_class *ctf_field_class_array_to_ir(ctf::MetaTranslateCtx *ctx,
+                                                          struct ctf_field_class_array *fc)
+{
+    bt_field_class *ir_fc;
+    bt_field_class *elem_ir_fc;
+
+    if (fc->base.is_text) {
+        ir_fc = bt_field_class_string_create(ctx->ir_tc);
+        BT_ASSERT(ir_fc);
+        goto end;
+    }
+
+    elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc);
+    BT_ASSERT(elem_ir_fc);
+    ir_fc = bt_field_class_array_static_create(ctx->ir_tc, elem_ir_fc, fc->length);
+    BT_ASSERT(ir_fc);
+    bt_field_class_put_ref(elem_ir_fc);
+
+end:
+    return ir_fc;
+}
+
+static inline bt_field_class *ctf_field_class_sequence_to_ir(ctf::MetaTranslateCtx *ctx,
+                                                             struct ctf_field_class_sequence *fc)
+{
+    bt_field_class *ir_fc;
+    bt_field_class *elem_ir_fc;
+    bt_field_class *length_fc = NULL;
+
+    if (fc->base.is_text) {
+        ir_fc = bt_field_class_string_create(ctx->ir_tc);
+        BT_ASSERT(ir_fc);
+        goto end;
+    }
+
+    elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc);
+    BT_ASSERT(elem_ir_fc);
+
+    if (fc->length_path.root != CTF_SCOPE_PACKET_HEADER &&
+        fc->length_path.root != CTF_SCOPE_EVENT_HEADER) {
+        length_fc = borrow_ir_fc_from_field_path(ctx, &fc->length_path);
+        BT_ASSERT(length_fc);
+    }
+
+    ir_fc = bt_field_class_array_dynamic_create(ctx->ir_tc, elem_ir_fc, length_fc);
+    BT_ASSERT(ir_fc);
+    bt_field_class_put_ref(elem_ir_fc);
+    BT_ASSERT(ir_fc);
+
+end:
+    return ir_fc;
+}
+
+static inline bt_field_class *ctf_field_class_to_ir(ctf::MetaTranslateCtx *ctx,
+                                                    struct ctf_field_class *fc)
+{
+    bt_field_class *ir_fc = NULL;
+
+    BT_ASSERT(fc);
+    BT_ASSERT(fc->in_ir);
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+        ir_fc = ctf_field_class_int_to_ir(ctx, ctf_field_class_as_int(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+        ir_fc = ctf_field_class_enum_to_ir(ctx, ctf_field_class_as_enum(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_FLOAT:
+        ir_fc = ctf_field_class_float_to_ir(ctx, ctf_field_class_as_float(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_STRING:
+        ir_fc = ctf_field_class_string_to_ir(ctx);
+        break;
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+        ir_fc = ctf_field_class_struct_to_ir(ctx, ctf_field_class_as_struct(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+        ir_fc = ctf_field_class_array_to_ir(ctx, ctf_field_class_as_array(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+        ir_fc = ctf_field_class_sequence_to_ir(ctx, ctf_field_class_as_sequence(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+        ir_fc = ctf_field_class_variant_to_ir(ctx, ctf_field_class_as_variant(fc));
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    fc->ir_fc = ir_fc;
+    return ir_fc;
+}
+
+static inline bool
+ctf_field_class_struct_has_immediate_member_in_ir(struct ctf_field_class_struct *fc)
+{
+    uint64_t i;
+    bool has_immediate_member_in_ir = false;
+
+    /*
+     * If the structure field class has no members at all, then it
+     * was an empty structure in the beginning, so leave it existing
+     * and empty.
+     */
+    if (fc->members->len == 0) {
+        has_immediate_member_in_ir = true;
+        goto end;
+    }
+
+    for (i = 0; i < fc->members->len; i++) {
+        struct ctf_named_field_class *named_fc =
+            ctf_field_class_struct_borrow_member_by_index(fc, i);
+
+        if (named_fc->fc->in_ir) {
+            has_immediate_member_in_ir = true;
+            goto end;
+        }
+    }
+
+end:
+    return has_immediate_member_in_ir;
+}
+
+static inline bt_field_class *scope_ctf_field_class_to_ir(ctf::MetaTranslateCtx *ctx)
+{
+    bt_field_class *ir_fc = NULL;
+    struct ctf_field_class *fc = NULL;
+
+    switch (ctx->scope) {
+    case CTF_SCOPE_PACKET_CONTEXT:
+        fc = ctx->sc->packet_context_fc;
+        break;
+    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+        fc = ctx->sc->event_common_context_fc;
+        break;
+    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+        fc = ctx->ec->spec_context_fc;
+        break;
+    case CTF_SCOPE_EVENT_PAYLOAD:
+        fc = ctx->ec->payload_fc;
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    if (fc && ctf_field_class_struct_has_immediate_member_in_ir(ctf_field_class_as_struct(fc))) {
+        ir_fc = ctf_field_class_to_ir(ctx, fc);
+    }
+
+    return ir_fc;
+}
+
+static inline void ctf_event_class_to_ir(ctf::MetaTranslateCtx *ctx)
+{
+    int ret;
+    bt_event_class *ir_ec = NULL;
+    bt_field_class *ir_fc;
+
+    BT_ASSERT(ctx->ec);
+
+    if (ctx->ec->is_translated) {
+        ir_ec = bt_stream_class_borrow_event_class_by_id(ctx->ir_sc, ctx->ec->id);
+        BT_ASSERT(ir_ec);
+        goto end;
+    }
+
+    ir_ec = bt_event_class_create_with_id(ctx->ir_sc, ctx->ec->id);
+    BT_ASSERT(ir_ec);
+    bt_event_class_put_ref(ir_ec);
+    ctx->scope = CTF_SCOPE_EVENT_SPECIFIC_CONTEXT;
+    ir_fc = scope_ctf_field_class_to_ir(ctx);
+    if (ir_fc) {
+        ret = bt_event_class_set_specific_context_field_class(ir_ec, ir_fc);
+        BT_ASSERT(ret == 0);
+        bt_field_class_put_ref(ir_fc);
+    }
+
+    ctx->scope = CTF_SCOPE_EVENT_PAYLOAD;
+    ir_fc = scope_ctf_field_class_to_ir(ctx);
+    if (ir_fc) {
+        ret = bt_event_class_set_payload_field_class(ir_ec, ir_fc);
+        BT_ASSERT(ret == 0);
+        bt_field_class_put_ref(ir_fc);
+    }
+
+    if (ctx->ec->name->len > 0) {
+        ret = bt_event_class_set_name(ir_ec, ctx->ec->name->str);
+        BT_ASSERT(ret == 0);
+    }
+
+    if (ctx->ec->emf_uri->len > 0) {
+        ret = bt_event_class_set_emf_uri(ir_ec, ctx->ec->emf_uri->str);
+        BT_ASSERT(ret == 0);
+    }
+
+    if (ctx->ec->is_log_level_set) {
+        bt_event_class_set_log_level(ir_ec, ctx->ec->log_level);
+    }
+
+    ctx->ec->is_translated = true;
+    ctx->ec->ir_ec = ir_ec;
+
+end:
+    return;
+}
+
+static inline void ctf_stream_class_to_ir(ctf::MetaTranslateCtx *ctx)
+{
+    int ret;
+    bt_field_class *ir_fc;
+
+    BT_ASSERT(ctx->sc);
+
+    if (ctx->sc->is_translated) {
+        ctx->ir_sc = bt_trace_class_borrow_stream_class_by_id(ctx->ir_tc, ctx->sc->id);
+        BT_ASSERT(ctx->ir_sc);
+        goto end;
+    }
+
+    ctx->ir_sc = bt_stream_class_create_with_id(ctx->ir_tc, ctx->sc->id);
+    BT_ASSERT(ctx->ir_sc);
+    bt_stream_class_put_ref(ctx->ir_sc);
+
+    if (ctx->sc->default_clock_class) {
+        BT_ASSERT(ctx->sc->default_clock_class->ir_cc);
+        ret = bt_stream_class_set_default_clock_class(ctx->ir_sc,
+                                                      ctx->sc->default_clock_class->ir_cc);
+        BT_ASSERT(ret == 0);
+    }
+
+    bt_stream_class_set_supports_packets(ctx->ir_sc, BT_TRUE, ctx->sc->packets_have_ts_begin,
+                                         ctx->sc->packets_have_ts_end);
+    bt_stream_class_set_supports_discarded_events(ctx->ir_sc, ctx->sc->has_discarded_events,
+                                                  ctx->sc->discarded_events_have_default_cs);
+    bt_stream_class_set_supports_discarded_packets(ctx->ir_sc, ctx->sc->has_discarded_packets,
+                                                   ctx->sc->discarded_packets_have_default_cs);
+    ctx->scope = CTF_SCOPE_PACKET_CONTEXT;
+    ir_fc = scope_ctf_field_class_to_ir(ctx);
+    if (ir_fc) {
+        ret = bt_stream_class_set_packet_context_field_class(ctx->ir_sc, ir_fc);
+        BT_ASSERT(ret == 0);
+        bt_field_class_put_ref(ir_fc);
+    }
+
+    ctx->scope = CTF_SCOPE_EVENT_COMMON_CONTEXT;
+    ir_fc = scope_ctf_field_class_to_ir(ctx);
+    if (ir_fc) {
+        ret = bt_stream_class_set_event_common_context_field_class(ctx->ir_sc, ir_fc);
+        BT_ASSERT(ret == 0);
+        bt_field_class_put_ref(ir_fc);
+    }
+
+    bt_stream_class_set_assigns_automatic_event_class_id(ctx->ir_sc, BT_FALSE);
+    bt_stream_class_set_assigns_automatic_stream_id(ctx->ir_sc, BT_FALSE);
+
+    ctx->sc->is_translated = true;
+    ctx->sc->ir_sc = ctx->ir_sc;
+
+end:
+    return;
+}
+
+static inline void ctf_clock_class_to_ir(bt_clock_class *ir_cc, struct ctf_clock_class *cc)
+{
+    int ret;
+
+    if (strlen(cc->name->str) > 0) {
+        ret = bt_clock_class_set_name(ir_cc, cc->name->str);
+        BT_ASSERT(ret == 0);
+    }
+
+    if (strlen(cc->description->str) > 0) {
+        ret = bt_clock_class_set_description(ir_cc, cc->description->str);
+        BT_ASSERT(ret == 0);
+    }
+
+    bt_clock_class_set_frequency(ir_cc, cc->frequency);
+    bt_clock_class_set_precision(ir_cc, cc->precision);
+    bt_clock_class_set_offset(ir_cc, cc->offset_seconds, cc->offset_cycles);
+
+    if (cc->has_uuid) {
+        bt_clock_class_set_uuid(ir_cc, cc->uuid);
+    }
+
+    bt_clock_class_set_origin_is_unix_epoch(ir_cc, cc->is_absolute);
+}
+
+static inline int ctf_trace_class_to_ir(ctf::MetaTranslateCtx *ctx)
+{
+    int ret = 0;
+    uint64_t i;
+
+    BT_ASSERT(ctx->tc);
+    BT_ASSERT(ctx->ir_tc);
+
+    if (ctx->tc->is_translated) {
+        goto end;
+    }
+
+    for (i = 0; i < ctx->tc->clock_classes->len; i++) {
+        ctf_clock_class *cc = (ctf_clock_class *) ctx->tc->clock_classes->pdata[i];
+
+        cc->ir_cc = bt_clock_class_create(ctx->self_comp);
+        ctf_clock_class_to_ir(cc->ir_cc, cc);
+    }
+
+    bt_trace_class_set_assigns_automatic_stream_class_id(ctx->ir_tc, BT_FALSE);
+    ctx->tc->is_translated = true;
+    ctx->tc->ir_tc = ctx->ir_tc;
+
+end:
+    return ret;
+}
+
+int ctf_trace_class_translate(bt_self_component *self_comp, bt_trace_class *ir_tc,
+                              struct ctf_trace_class *tc)
+{
+    int ret = 0;
+    uint64_t i;
+    ctf::MetaTranslateCtx ctx = {};
+
+    ctx.self_comp = self_comp;
+    ctx.tc = tc;
+    ctx.ir_tc = ir_tc;
+    ret = ctf_trace_class_to_ir(&ctx);
+    if (ret) {
+        goto end;
+    }
+
+    for (i = 0; i < tc->stream_classes->len; i++) {
+        uint64_t j;
+        ctx.sc = (ctf_stream_class *) tc->stream_classes->pdata[i];
+
+        ctf_stream_class_to_ir(&ctx);
+
+        for (j = 0; j < ctx.sc->event_classes->len; j++) {
+            ctx.ec = (ctf_event_class *) ctx.sc->event_classes->pdata[j];
+
+            ctf_event_class_to_ir(&ctx);
+            ctx.ec = NULL;
+        }
+
+        ctx.sc = NULL;
+    }
+
+end:
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-alignments.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-alignments.cpp
new file mode 100644 (file)
index 0000000..bea62bf
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2020 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <stdint.h>
+
+#include "ctf-meta-visitors.hpp"
+
+static inline int set_alignments(struct ctf_field_class *fc)
+{
+    int ret = 0;
+    uint64_t i;
+
+    if (!fc) {
+        goto end;
+    }
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+        for (i = 0; i < struct_fc->members->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+
+            ret = set_alignments(named_fc->fc);
+            if (ret) {
+                goto end;
+            }
+
+            if (named_fc->fc->alignment > fc->alignment) {
+                fc->alignment = named_fc->fc->alignment;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        for (i = 0; i < var_fc->options->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+
+            ret = set_alignments(named_fc->fc);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        ret = set_alignments(array_fc->elem_fc);
+        if (ret) {
+            goto end;
+        }
+
+        /*
+         * Use the alignment of the array/sequence field class's
+         * element FC as its own alignment.
+         *
+         * This is especially important when the array/sequence
+         * field's effective length is zero: as per CTF 1.8, the
+         * stream data decoding process still needs to align the
+         * cursor using the element's alignment [1]:
+         *
+         * > Arrays are always aligned on their element
+         * > alignment requirement.
+         *
+         * For example:
+         *
+         *     struct {
+         *         integer { size = 8; } a;
+         *         integer { size = 8; align = 16; } b[0];
+         *         integer { size = 8; } c;
+         *     };
+         *
+         * When using this to decode the bytes 1, 2, and 3, then
+         * the decoded values are:
+         *
+         * `a`: 1
+         * `b`: []
+         * `c`: 3
+         *
+         * [1]: https://diamon.org/ctf/#spec4.2.3
+         */
+        array_fc->base.alignment = array_fc->elem_fc->alignment;
+        break;
+    }
+    default:
+        break;
+    }
+
+end:
+    return ret;
+}
+
+int ctf_trace_class_update_alignments(struct ctf_trace_class *ctf_tc)
+{
+    int ret = 0;
+    uint64_t i;
+
+    if (!ctf_tc->is_translated) {
+        ret = set_alignments(ctf_tc->packet_header_fc);
+        if (ret) {
+            goto end;
+        }
+    }
+
+    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+        uint64_t j;
+
+        if (!sc->is_translated) {
+            ret = set_alignments(sc->packet_context_fc);
+            if (ret) {
+                goto end;
+            }
+
+            ret = set_alignments(sc->event_header_fc);
+            if (ret) {
+                goto end;
+            }
+
+            ret = set_alignments(sc->event_common_context_fc);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        for (j = 0; j < sc->event_classes->len; j++) {
+            struct ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[j];
+
+            if (ec->is_translated) {
+                continue;
+            }
+
+            ret = set_alignments(ec->spec_context_fc);
+            if (ret) {
+                goto end;
+            }
+
+            ret = set_alignments(ec->payload_fc);
+            if (ret) {
+                goto end;
+            }
+        }
+    }
+
+end:
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-default-clock-classes.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-default-clock-classes.cpp
new file mode 100644 (file)
index 0000000..cd9acdf
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/logging.hpp"
+
+#include "ctf-meta-visitors.hpp"
+
+static inline int find_mapped_clock_class(struct ctf_field_class *fc,
+                                          struct ctf_clock_class **clock_class,
+                                          const bt2c::Logger& logger)
+{
+    int ret = 0;
+    uint64_t i;
+
+    if (!fc) {
+        goto end;
+    }
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+    {
+        struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+        if (int_fc->mapped_clock_class) {
+            if (*clock_class && *clock_class != int_fc->mapped_clock_class) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                             "Stream class contains more than one "
+                                             "clock class: expected-cc-name=\"{}\", "
+                                             "other-cc-name=\"{}\"",
+                                             (*clock_class)->name->str,
+                                             int_fc->mapped_clock_class->name->str);
+                ret = -1;
+                goto end;
+            }
+
+            *clock_class = int_fc->mapped_clock_class;
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+        for (i = 0; i < struct_fc->members->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+
+            ret = find_mapped_clock_class(named_fc->fc, clock_class, logger);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        for (i = 0; i < var_fc->options->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+
+            ret = find_mapped_clock_class(named_fc->fc, clock_class, logger);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        ret = find_mapped_clock_class(array_fc->elem_fc, clock_class, logger);
+        if (ret) {
+            goto end;
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+end:
+    return ret;
+}
+
+static inline int update_stream_class_default_clock_class(struct ctf_stream_class *stream_class,
+                                                          const bt2c::Logger& logger)
+{
+    int ret = 0;
+    struct ctf_clock_class *clock_class = stream_class->default_clock_class;
+    uint64_t i;
+
+    ret = find_mapped_clock_class(stream_class->packet_context_fc, &clock_class, logger);
+    if (ret) {
+        goto end;
+    }
+
+    ret = find_mapped_clock_class(stream_class->event_header_fc, &clock_class, logger);
+    if (ret) {
+        goto end;
+    }
+
+    ret = find_mapped_clock_class(stream_class->event_common_context_fc, &clock_class, logger);
+    if (ret) {
+        goto end;
+    }
+
+    for (i = 0; i < stream_class->event_classes->len; i++) {
+        struct ctf_event_class *event_class =
+            (ctf_event_class *) stream_class->event_classes->pdata[i];
+
+        ret = find_mapped_clock_class(event_class->spec_context_fc, &clock_class, logger);
+        if (ret) {
+            goto end;
+        }
+
+        ret = find_mapped_clock_class(event_class->payload_fc, &clock_class, logger);
+        if (ret) {
+            goto end;
+        }
+    }
+
+    if (!stream_class->default_clock_class) {
+        stream_class->default_clock_class = clock_class;
+    }
+
+end:
+    return ret;
+}
+
+int ctf_trace_class_update_default_clock_classes(struct ctf_trace_class *ctf_tc,
+                                                 const bt2c::Logger& parentLogger)
+{
+    uint64_t i;
+    int ret = 0;
+    struct ctf_clock_class *clock_class = NULL;
+    bt2c::Logger logger {parentLogger, "PLUGIN/CTF/META/UPDATE-DEF-CC"};
+
+    ret = find_mapped_clock_class(ctf_tc->packet_header_fc, &clock_class, logger);
+    if (ret) {
+        goto end;
+    }
+
+    if (clock_class) {
+        ret = -1;
+        goto end;
+    }
+
+    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+        struct ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+        ret = update_stream_class_default_clock_class(
+            (ctf_stream_class *) ctf_tc->stream_classes->pdata[i], logger);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "Stream class contains more than one "
+                                         "clock class: stream-class-id={}",
+                                         sc->id);
+            goto end;
+        }
+    }
+
+end:
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-in-ir.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-in-ir.cpp
new file mode 100644 (file)
index 0000000..c790238
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <assert.h>
+#include <glib.h>
+#include <stdint.h>
+
+#include "common/assert.h"
+#include "compat/glib.h"
+
+#include "ctf-meta-visitors.hpp"
+
+static void force_update_field_class_in_ir(struct ctf_field_class *fc, bool in_ir)
+{
+    uint64_t i;
+
+    if (!fc) {
+        goto end;
+    }
+
+    fc->in_ir = in_ir;
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+        for (i = 0; i < struct_fc->members->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+
+            force_update_field_class_in_ir(named_fc->fc, in_ir);
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_named_field_class *named_fc;
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        for (i = 0; i < var_fc->options->len; i++) {
+            named_fc = ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+
+            force_update_field_class_in_ir(named_fc->fc, in_ir);
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        force_update_field_class_in_ir(array_fc->elem_fc, in_ir);
+        break;
+    }
+    default:
+        break;
+    }
+
+end:
+    return;
+}
+
+static void update_field_class_in_ir(struct ctf_field_class *fc, GHashTable *ft_dependents)
+{
+    int64_t i;
+
+    if (!fc) {
+        goto end;
+    }
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+    {
+        struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+        /*
+         * Conditions to be in trace IR; one of:
+         *
+         * 1. Does NOT have a mapped clock class AND does not
+         *    have a special meaning.
+         * 2. Another field class depends on it.
+         */
+        if ((!int_fc->mapped_clock_class && int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE) ||
+            bt_g_hash_table_contains(ft_dependents, fc)) {
+            fc->in_ir = true;
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+        /*
+         * Make it part of IR if it's empty because it was
+         * originally empty.
+         */
+        if (struct_fc->members->len == 0) {
+            fc->in_ir = true;
+        }
+
+        /* Reverse order */
+        for (i = (int64_t) struct_fc->members->len - 1; i >= 0; i--) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+
+            update_field_class_in_ir(named_fc->fc, ft_dependents);
+
+            if (named_fc->fc->in_ir) {
+                /* At least one member is part of IR */
+                fc->in_ir = true;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_named_field_class *named_fc;
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        /*
+         * Reverse order, although it is not important for this
+         * loop because a field class within a variant field
+         * type's option cannot depend on a field class in
+         * another option of the same variant field class.
+         */
+        for (i = (int64_t) var_fc->options->len - 1; i >= 0; i--) {
+            named_fc = ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+
+            update_field_class_in_ir(named_fc->fc, ft_dependents);
+
+            if (named_fc->fc->in_ir) {
+                /* At least one option is part of IR */
+                fc->in_ir = true;
+            }
+        }
+
+        if (fc->in_ir) {
+            /*
+             * At least one option will make it to IR. In
+             * this case, make all options part of IR
+             * because the variant's tag could still select
+             * (dynamically) a removed option. This can mean
+             * having an empty structure as an option, for
+             * example, but at least all the options are
+             * selectable.
+             */
+            for (i = 0; i < var_fc->options->len; i++) {
+                ctf_field_class_variant_borrow_option_by_index(var_fc, i)->fc->in_ir = true;
+            }
+
+            /*
+             * This variant field class is part of IR and
+             * depends on a tag field class (which must also
+             * be part of IR).
+             */
+            g_hash_table_insert(ft_dependents, var_fc->tag_fc, var_fc->tag_fc);
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        update_field_class_in_ir(array_fc->elem_fc, ft_dependents);
+        fc->in_ir = array_fc->elem_fc->in_ir;
+
+        if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) {
+            struct ctf_field_class_array *arr_fc = ctf_field_class_as_array(fc);
+
+            assert(arr_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE ||
+                   arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID);
+
+            /*
+             * UUID field class: nothing depends on this, so
+             * it's not part of IR.
+             */
+            if (arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID) {
+                fc->in_ir = false;
+                array_fc->elem_fc->in_ir = false;
+            }
+        } else if (fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+            if (fc->in_ir) {
+                struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+
+                /*
+                 * This sequence field class is part of
+                 * IR and depends on a length field class
+                 * (which must also be part of IR).
+                 */
+                g_hash_table_insert(ft_dependents, seq_fc->length_fc, seq_fc->length_fc);
+            }
+        }
+
+        break;
+    }
+    default:
+        fc->in_ir = true;
+        break;
+    }
+
+end:
+    return;
+}
+
+/*
+ * Scopes and field classes are processed in reverse order because we need
+ * to know if a given integer field class has dependents (sequence or
+ * variant field classes) when we reach it. Dependents can only be located
+ * after the length/tag field class in the metadata tree.
+ */
+int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc)
+{
+    int ret = 0;
+    uint64_t i;
+
+    GHashTable *ft_dependents = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+    BT_ASSERT(ft_dependents);
+
+    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+        uint64_t j;
+
+        for (j = 0; j < sc->event_classes->len; j++) {
+            ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[j];
+
+            if (ec->is_translated) {
+                continue;
+            }
+
+            update_field_class_in_ir(ec->payload_fc, ft_dependents);
+            update_field_class_in_ir(ec->spec_context_fc, ft_dependents);
+        }
+
+        if (!sc->is_translated) {
+            update_field_class_in_ir(sc->event_common_context_fc, ft_dependents);
+            force_update_field_class_in_ir(sc->event_header_fc, false);
+            update_field_class_in_ir(sc->packet_context_fc, ft_dependents);
+        }
+    }
+
+    if (!ctf_tc->is_translated) {
+        force_update_field_class_in_ir(ctf_tc->packet_header_fc, false);
+    }
+
+    g_hash_table_destroy(ft_dependents);
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-meanings.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-meanings.cpp
new file mode 100644 (file)
index 0000000..23c6574
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "ctf-meta-visitors.hpp"
+
+static int set_int_field_class_meaning_by_name(struct ctf_field_class *fc, const char *field_name,
+                                               const char *id_name,
+                                               enum ctf_field_class_meaning meaning)
+{
+    int ret = 0;
+    uint64_t i;
+
+    if (!fc) {
+        goto end;
+    }
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+    {
+        struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+        if (field_name && strcmp(field_name, id_name) == 0) {
+            int_fc->meaning = meaning;
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+        for (i = 0; i < struct_fc->members->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+
+            ret = set_int_field_class_meaning_by_name(named_fc->fc, named_fc->name->str, id_name,
+                                                      meaning);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        for (i = 0; i < var_fc->options->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+
+            ret = set_int_field_class_meaning_by_name(named_fc->fc, NULL, id_name, meaning);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        ret = set_int_field_class_meaning_by_name(array_fc->elem_fc, NULL, id_name, meaning);
+        if (ret) {
+            goto end;
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+end:
+    return ret;
+}
+
+static int update_stream_class_meanings(struct ctf_stream_class *sc)
+{
+    int ret = 0;
+    struct ctf_field_class_int *int_fc;
+    uint64_t i;
+
+    if (!sc->is_translated) {
+        if (sc->packet_context_fc) {
+            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_begin");
+            if (int_fc) {
+                int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME;
+            }
+
+            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_end");
+            if (int_fc) {
+                int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_END_TIME;
+
+                /*
+                 * Remove mapped clock class to avoid updating
+                 * the clock immediately when decoding.
+                 */
+                int_fc->mapped_clock_class = NULL;
+            }
+
+            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                ctf_field_class_as_struct(sc->packet_context_fc), "events_discarded");
+            if (int_fc) {
+                int_fc->meaning = CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT;
+            }
+
+            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                ctf_field_class_as_struct(sc->packet_context_fc), "packet_seq_num");
+            if (int_fc) {
+                int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT;
+            }
+
+            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                ctf_field_class_as_struct(sc->packet_context_fc), "packet_size");
+            if (int_fc) {
+                int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE;
+            }
+
+            int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                ctf_field_class_as_struct(sc->packet_context_fc), "content_size");
+            if (int_fc) {
+                int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE;
+            }
+        }
+
+        if (sc->event_header_fc) {
+            ret = set_int_field_class_meaning_by_name(sc->event_header_fc, NULL, "id",
+                                                      CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID);
+            if (ret) {
+                goto end;
+            }
+        }
+    }
+
+    for (i = 0; i < sc->event_classes->len; i++) {
+        struct ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[i];
+
+        if (ec->is_translated) {
+            continue;
+        }
+    }
+
+end:
+    return ret;
+}
+
+int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc)
+{
+    int ret = 0;
+    struct ctf_field_class_int *int_fc;
+    struct ctf_named_field_class *named_fc;
+    uint64_t i;
+
+    if (!ctf_tc->is_translated && ctf_tc->packet_header_fc) {
+        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "magic");
+        if (int_fc) {
+            int_fc->meaning = CTF_FIELD_CLASS_MEANING_MAGIC;
+        }
+
+        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_id");
+        if (int_fc) {
+            int_fc->meaning = CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID;
+        }
+
+        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_instance_id");
+        if (int_fc) {
+            int_fc->meaning = CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID;
+        }
+
+        named_fc = ctf_field_class_struct_borrow_member_by_name(
+            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "uuid");
+        if (named_fc && named_fc->fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) {
+            struct ctf_field_class_array *array_fc = ctf_field_class_as_array(named_fc->fc);
+
+            array_fc->meaning = CTF_FIELD_CLASS_MEANING_UUID;
+        }
+    }
+
+    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+        ret = update_stream_class_meanings((ctf_stream_class *) ctf_tc->stream_classes->pdata[i]);
+        if (ret) {
+            goto end;
+        }
+    }
+
+end:
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-stream-class-config.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-stream-class-config.cpp
new file mode 100644 (file)
index 0000000..a8f5add
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <stdint.h>
+
+#include "ctf-meta-visitors.hpp"
+
+int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc)
+{
+    struct ctf_field_class_int *int_fc;
+    uint64_t i;
+
+    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+        struct ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+        if (sc->is_translated) {
+            continue;
+        }
+
+        if (!sc->packet_context_fc) {
+            continue;
+        }
+
+        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+            ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_begin");
+        if (int_fc && int_fc->meaning == CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME) {
+            sc->packets_have_ts_begin = true;
+        }
+
+        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+            ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_end");
+        if (int_fc && int_fc->meaning == CTF_FIELD_CLASS_MEANING_PACKET_END_TIME) {
+            sc->packets_have_ts_end = true;
+        }
+
+        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+            ctf_field_class_as_struct(sc->packet_context_fc), "events_discarded");
+        if (int_fc && int_fc->meaning == CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT) {
+            sc->has_discarded_events = true;
+        }
+
+        sc->discarded_events_have_default_cs =
+            sc->has_discarded_events && sc->packets_have_ts_begin && sc->packets_have_ts_end;
+        int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+            ctf_field_class_as_struct(sc->packet_context_fc), "packet_seq_num");
+        if (int_fc && int_fc->meaning == CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT) {
+            sc->has_discarded_packets = true;
+        }
+
+        sc->discarded_packets_have_default_cs =
+            sc->has_discarded_packets && sc->packets_have_ts_begin && sc->packets_have_ts_end;
+    }
+
+    return 0;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-text-array-sequence.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-text-array-sequence.cpp
new file mode 100644 (file)
index 0000000..6c3bfc9
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <stdint.h>
+
+#include "ctf-meta-visitors.hpp"
+
+static inline int set_text_array_sequence_field_class(struct ctf_field_class *fc)
+{
+    int ret = 0;
+    uint64_t i;
+
+    if (!fc) {
+        goto end;
+    }
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+        for (i = 0; i < struct_fc->members->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+
+            ret = set_text_array_sequence_field_class(named_fc->fc);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        for (i = 0; i < var_fc->options->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+
+            ret = set_text_array_sequence_field_class(named_fc->fc);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        if (array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_INT ||
+            array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_ENUM) {
+            struct ctf_field_class_int *int_fc = ctf_field_class_as_int(array_fc->elem_fc);
+
+            if (int_fc->base.base.alignment == 8 && int_fc->base.size == 8 &&
+                int_fc->encoding == CTF_ENCODING_UTF8) {
+                array_fc->is_text = true;
+
+                /*
+                 * Force integer element to be unsigned;
+                 * this makes the decoder enter a single
+                 * path when reading a text
+                 * array/sequence and we can safely
+                 * decode bytes as characters anyway.
+                 */
+                int_fc->is_signed = false;
+            }
+        }
+
+        ret = set_text_array_sequence_field_class(array_fc->elem_fc);
+        if (ret) {
+            goto end;
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+end:
+    return ret;
+}
+
+int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc)
+{
+    int ret = 0;
+    uint64_t i;
+
+    if (!ctf_tc->is_translated) {
+        ret = set_text_array_sequence_field_class(ctf_tc->packet_header_fc);
+        if (ret) {
+            goto end;
+        }
+    }
+
+    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+        uint64_t j;
+
+        if (!sc->is_translated) {
+            ret = set_text_array_sequence_field_class(sc->packet_context_fc);
+            if (ret) {
+                goto end;
+            }
+
+            ret = set_text_array_sequence_field_class(sc->event_header_fc);
+            if (ret) {
+                goto end;
+            }
+
+            ret = set_text_array_sequence_field_class(sc->event_common_context_fc);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        for (j = 0; j < sc->event_classes->len; j++) {
+            struct ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[j];
+
+            if (ec->is_translated) {
+                continue;
+            }
+
+            ret = set_text_array_sequence_field_class(ec->spec_context_fc);
+            if (ret) {
+                goto end;
+            }
+
+            ret = set_text_array_sequence_field_class(ec->payload_fc);
+            if (ret) {
+                goto end;
+            }
+        }
+    }
+
+end:
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-value-storing-indexes.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-update-value-storing-indexes.cpp
new file mode 100644 (file)
index 0000000..65e4c9f
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "common/assert.h"
+
+#include "ctf-meta-visitors.hpp"
+
+static int update_field_class_stored_value_index(struct ctf_field_class *fc,
+                                                 struct ctf_trace_class *tc,
+                                                 struct ctf_stream_class *sc,
+                                                 struct ctf_event_class *ec)
+{
+    int ret = 0;
+    uint64_t i;
+    struct ctf_field_path *field_path = NULL;
+    struct ctf_field_class_int *tgt_fc = NULL;
+    uint64_t *stored_value_index = NULL;
+
+    if (!fc) {
+        goto end;
+    }
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        field_path = &var_fc->tag_path;
+        stored_value_index = &var_fc->stored_tag_index;
+        tgt_fc = &var_fc->tag_fc->base;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+
+        field_path = &seq_fc->length_path;
+        stored_value_index = &seq_fc->stored_length_index;
+        tgt_fc = seq_fc->length_fc;
+        break;
+    }
+    default:
+        break;
+    }
+
+    if (field_path) {
+        BT_ASSERT(tgt_fc);
+        BT_ASSERT(tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_INT ||
+                  tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_ENUM);
+        if (tgt_fc->storing_index >= 0) {
+            /* Already storing its value */
+            *stored_value_index = (uint64_t) tgt_fc->storing_index;
+        } else {
+            /* Not storing its value: allocate new index */
+            tgt_fc->storing_index = tc->stored_value_count;
+            *stored_value_index = (uint64_t) tgt_fc->storing_index;
+            tc->stored_value_count++;
+        }
+    }
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+        for (i = 0; i < struct_fc->members->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+
+            ret = update_field_class_stored_value_index(named_fc->fc, tc, sc, ec);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        for (i = 0; i < var_fc->options->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+
+            ret = update_field_class_stored_value_index(named_fc->fc, tc, sc, ec);
+            if (ret) {
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        ret = update_field_class_stored_value_index(array_fc->elem_fc, tc, sc, ec);
+        if (ret) {
+            goto end;
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+end:
+    return ret;
+}
+
+int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc)
+{
+    uint64_t i;
+
+    if (!ctf_tc->is_translated) {
+        update_field_class_stored_value_index(ctf_tc->packet_header_fc, ctf_tc, NULL, NULL);
+    }
+
+    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+        uint64_t j;
+        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+        if (!sc->is_translated) {
+            update_field_class_stored_value_index(sc->packet_context_fc, ctf_tc, sc, NULL);
+            update_field_class_stored_value_index(sc->event_header_fc, ctf_tc, sc, NULL);
+            update_field_class_stored_value_index(sc->event_common_context_fc, ctf_tc, sc, NULL);
+        }
+
+        for (j = 0; j < sc->event_classes->len; j++) {
+            struct ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[j];
+
+            if (!ec->is_translated) {
+                update_field_class_stored_value_index(ec->spec_context_fc, ctf_tc, sc, ec);
+                update_field_class_stored_value_index(ec->payload_fc, ctf_tc, sc, ec);
+            }
+        }
+    }
+
+    return 0;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-validate.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-validate.cpp
new file mode 100644 (file)
index 0000000..cb4e2b8
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/logging.hpp"
+
+#include "ctf-meta-visitors.hpp"
+
+static int validate_stream_class(struct ctf_stream_class *sc, const bt2c::Logger& logger)
+{
+    int ret = 0;
+    struct ctf_field_class_int *int_fc;
+    struct ctf_field_class *fc;
+
+    if (sc->is_translated) {
+        goto end;
+    }
+
+    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+        ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_begin");
+    if (fc) {
+        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "Invalid packet context field class: "
+                                         "`timestamp_begin` member is not an integer field class.");
+            goto invalid;
+        }
+
+        int_fc = ctf_field_class_as_int(fc);
+
+        if (int_fc->is_signed) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet context field class: "
+                                                 "`timestamp_begin` member is signed.");
+            goto invalid;
+        }
+    }
+
+    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+        ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_end");
+    if (fc) {
+        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "Invalid packet context field class: "
+                                         "`timestamp_end` member is not an integer field class.");
+            goto invalid;
+        }
+
+        int_fc = ctf_field_class_as_int(fc);
+
+        if (int_fc->is_signed) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet context field class: "
+                                                 "`timestamp_end` member is signed.");
+            goto invalid;
+        }
+    }
+
+    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+        ctf_field_class_as_struct(sc->packet_context_fc), "events_discarded");
+    if (fc) {
+        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                logger, "Invalid packet context field class: "
+                        "`events_discarded` member is not an integer field class.");
+            goto invalid;
+        }
+
+        int_fc = ctf_field_class_as_int(fc);
+
+        if (int_fc->is_signed) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet context field class: "
+                                                 "`events_discarded` member is signed.");
+            goto invalid;
+        }
+    }
+
+    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+        ctf_field_class_as_struct(sc->packet_context_fc), "packet_seq_num");
+    if (fc) {
+        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "Invalid packet context field class: "
+                                         "`packet_seq_num` member is not an integer field class.");
+            goto invalid;
+        }
+
+        int_fc = ctf_field_class_as_int(fc);
+
+        if (int_fc->is_signed) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet context field class: "
+                                                 "`packet_seq_num` member is signed.");
+            goto invalid;
+        }
+    }
+
+    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+        ctf_field_class_as_struct(sc->packet_context_fc), "packet_size");
+    if (fc) {
+        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "Invalid packet context field class: "
+                                         "`packet_size` member is not an integer field class.");
+            goto invalid;
+        }
+
+        int_fc = ctf_field_class_as_int(fc);
+
+        if (int_fc->is_signed) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet context field class: "
+                                                 "`packet_size` member is signed.");
+            goto invalid;
+        }
+    }
+
+    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+        ctf_field_class_as_struct(sc->packet_context_fc), "content_size");
+    if (fc) {
+        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "Invalid packet context field class: "
+                                         "`content_size` member is not an integer field class.");
+            goto invalid;
+        }
+
+        int_fc = ctf_field_class_as_int(fc);
+
+        if (int_fc->is_signed) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet context field class: "
+                                                 "`content_size` member is signed.");
+            goto invalid;
+        }
+    }
+
+    fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+        ctf_field_class_as_struct(sc->event_header_fc), "id");
+    if (fc) {
+        if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid event header field class: "
+                                                 "`id` member is not an integer field class.");
+            goto invalid;
+        }
+
+        int_fc = ctf_field_class_as_int(fc);
+
+        if (int_fc->is_signed) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid event header field class: "
+                                                 "`id` member is signed.");
+            goto invalid;
+        }
+    } else {
+        if (sc->event_classes->len > 1) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid event header field class: "
+                                                 "missing `id` member as there's "
+                                                 "more than one event class.");
+            goto invalid;
+        }
+    }
+
+    goto end;
+
+invalid:
+    ret = -1;
+
+end:
+    return ret;
+}
+
+int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc, const bt2c::Logger& parentLogger)
+{
+    int ret = 0;
+    struct ctf_field_class_int *int_fc;
+    uint64_t i;
+
+    bt2c::Logger logger {parentLogger, "PLUGIN/CTF/META/VALIDATE"};
+
+    if (!ctf_tc->is_translated) {
+        struct ctf_field_class *fc;
+
+        fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "magic");
+        if (fc) {
+            struct ctf_named_field_class *named_fc = ctf_field_class_struct_borrow_member_by_index(
+                ctf_field_class_as_struct(ctf_tc->packet_header_fc), 0);
+
+            if (named_fc->fc != fc) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`magic` member is not the first member.");
+                goto invalid;
+            }
+
+            if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                             "Invalid packet header field class: "
+                                             "`magic` member is not an integer field class.");
+                goto invalid;
+            }
+
+            int_fc = ctf_field_class_as_int(fc);
+
+            if (int_fc->is_signed) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`magic` member is signed.");
+                goto invalid;
+            }
+
+            if (int_fc->base.size != 32) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`magic` member is not 32-bit.");
+                goto invalid;
+            }
+        }
+
+        fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_id");
+        if (fc) {
+            if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                             "Invalid packet header field class: "
+                                             "`stream_id` member is not an integer field class.");
+                goto invalid;
+            }
+
+            int_fc = ctf_field_class_as_int(fc);
+
+            if (int_fc->is_signed) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`stream_id` member is signed.");
+                goto invalid;
+            }
+        } else {
+            if (ctf_tc->stream_classes->len > 1) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "missing `stream_id` member as there's "
+                                                     "more than one stream class.");
+                goto invalid;
+            }
+        }
+
+        fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_instance_id");
+        if (fc) {
+            if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    logger, "Invalid packet header field class: "
+                            "`stream_instance_id` member is not an integer field class.");
+                goto invalid;
+            }
+
+            int_fc = ctf_field_class_as_int(fc);
+
+            if (int_fc->is_signed) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`stream_instance_id` member is signed.");
+                goto invalid;
+            }
+        }
+
+        fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+            ctf_field_class_as_struct(ctf_tc->packet_header_fc), "uuid");
+        if (fc) {
+            if (fc->type != CTF_FIELD_CLASS_TYPE_ARRAY) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`uuid` member is not an array field class.");
+                goto invalid;
+            }
+
+            ctf_field_class_array *array_fc = ctf_field_class_as_array(fc);
+
+            if (array_fc->length != 16) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    logger, "Invalid packet header field class: "
+                            "`uuid` member is not a 16-element array field class.");
+                goto invalid;
+            }
+
+            if (array_fc->base.elem_fc->type != CTF_FIELD_CLASS_TYPE_INT) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`uuid` member's element field class is not "
+                                                     "an integer field class.");
+                goto invalid;
+            }
+
+            int_fc = ctf_field_class_as_int(array_fc->base.elem_fc);
+
+            if (int_fc->is_signed) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`uuid` member's element field class "
+                                                     "is a signed integer field class.");
+                goto invalid;
+            }
+
+            if (int_fc->base.size != 8) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`uuid` member's element field class "
+                                                     "is not an 8-bit integer field class.");
+                goto invalid;
+            }
+
+            if (int_fc->base.base.alignment != 8) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid packet header field class: "
+                                                     "`uuid` member's element field class's "
+                                                     "alignment is not 8.");
+                goto invalid;
+            }
+        }
+    }
+
+    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+        struct ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+        ret = validate_stream_class(sc, logger);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Invalid stream class: sc-id={}", sc->id);
+            goto invalid;
+        }
+    }
+
+    goto end;
+
+invalid:
+    ret = -1;
+
+end:
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-visitors.hpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-visitors.hpp
new file mode 100644 (file)
index 0000000..1a6f708
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef _CTF_META_VISITORS_H
+#define _CTF_META_VISITORS_H
+
+#include <babeltrace2/babeltrace.h>
+
+#include "ctf-meta.hpp"
+
+namespace bt2c {
+
+class Logger;
+
+} /* namespace bt2c */
+
+int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc,
+                                          const bt2c::Logger& parentLogger);
+
+int ctf_trace_class_translate(bt_self_component *self_comp, bt_trace_class *ir_tc,
+                              struct ctf_trace_class *tc);
+
+int ctf_trace_class_update_default_clock_classes(struct ctf_trace_class *ctf_tc,
+                                                 const bt2c::Logger& parentLogger);
+
+int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc);
+
+int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc);
+
+int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc);
+
+int ctf_trace_class_update_alignments(struct ctf_trace_class *ctf_tc);
+
+int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc);
+
+int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc);
+
+int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc, const bt2c::Logger& parentLogger);
+
+void ctf_trace_class_warn_meaningless_header_fields(struct ctf_trace_class *ctf_tc,
+                                                    const bt2c::Logger& parentLogger);
+
+#endif /* _CTF_META_VISITORS_H */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-warn-meaningless-header-fields.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta-warn-meaningless-header-fields.cpp
new file mode 100644 (file)
index 0000000..f09a6b3
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "cpp-common/bt2c/logging.hpp"
+
+#include "ctf-meta-visitors.hpp"
+
+static inline void warn_meaningless_field(const char *name, const char *scope_name,
+                                          const bt2c::Logger& logger)
+{
+    BT_ASSERT(name);
+    BT_CPPLOGW_SPEC(logger, "User field found in {}: ignoring: name=\"{}\"", scope_name, name);
+}
+
+static inline void warn_meaningless_fields(struct ctf_field_class *fc, const char *name,
+                                           const char *scope_name, const bt2c::Logger& logger)
+{
+    uint64_t i;
+
+    if (!fc) {
+        goto end;
+    }
+
+    /*
+     * 'name' is guaranteed to be non-NULL whenever the field class is not a
+     * structure. In the case of a structure field class, its members' names
+     * are used.
+     */
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_FLOAT:
+    case CTF_FIELD_CLASS_TYPE_STRING:
+        warn_meaningless_field(name, scope_name, logger);
+        break;
+    case CTF_FIELD_CLASS_TYPE_INT:
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+    {
+        struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+        if (int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE && !int_fc->mapped_clock_class) {
+            warn_meaningless_field(name, scope_name, logger);
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+        for (i = 0; i < struct_fc->members->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+
+            warn_meaningless_fields(named_fc->fc, named_fc->name->str, scope_name, logger);
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+        for (i = 0; i < var_fc->options->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+
+            warn_meaningless_fields(named_fc->fc, named_fc->name->str, scope_name, logger);
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    {
+        struct ctf_field_class_array *array_fc = ctf_field_class_as_array(fc);
+
+        if (array_fc->meaning != CTF_FIELD_CLASS_MEANING_NONE) {
+            goto end;
+        }
+    }
+    /* fall-through */
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        warn_meaningless_fields(array_fc->elem_fc, name, scope_name, logger);
+        break;
+    }
+    default:
+        bt_common_abort();
+    }
+
+end:
+    return;
+}
+
+void ctf_trace_class_warn_meaningless_header_fields(struct ctf_trace_class *ctf_tc,
+                                                    const bt2c::Logger& parentLogger)
+{
+    uint64_t i;
+    bt2c::Logger logger {parentLogger, "PLUGIN/CTF/META/WARN-MEANINGLESS-HEADER-FIELDS"};
+
+    if (!ctf_tc->is_translated) {
+        warn_meaningless_fields(ctf_tc->packet_header_fc, NULL, "packet header", logger);
+    }
+
+    for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+        ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+        if (!sc->is_translated) {
+            warn_meaningless_fields(sc->event_header_fc, NULL, "event header", logger);
+        }
+    }
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta.hpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-meta.hpp
new file mode 100644 (file)
index 0000000..5efa710
--- /dev/null
@@ -0,0 +1,1776 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef _CTF_META_H
+#define _CTF_META_H
+
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "common/common.h"
+#include "common/uuid.h"
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+
+enum ctf_field_class_type
+{
+    CTF_FIELD_CLASS_TYPE_INT,
+    CTF_FIELD_CLASS_TYPE_ENUM,
+    CTF_FIELD_CLASS_TYPE_FLOAT,
+    CTF_FIELD_CLASS_TYPE_STRING,
+    CTF_FIELD_CLASS_TYPE_STRUCT,
+    CTF_FIELD_CLASS_TYPE_ARRAY,
+    CTF_FIELD_CLASS_TYPE_SEQUENCE,
+    CTF_FIELD_CLASS_TYPE_VARIANT,
+};
+
+inline const char *format_as(ctf_field_class_type type) noexcept
+{
+    switch (type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+        return "CTF_FIELD_CLASS_TYPE_INT";
+
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+        return "CTF_FIELD_CLASS_TYPE_ENUM";
+
+    case CTF_FIELD_CLASS_TYPE_FLOAT:
+        return "CTF_FIELD_CLASS_TYPE_FLOAT";
+
+    case CTF_FIELD_CLASS_TYPE_STRING:
+        return "CTF_FIELD_CLASS_TYPE_STRING";
+
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+        return "CTF_FIELD_CLASS_TYPE_STRUCT";
+
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+        return "CTF_FIELD_CLASS_TYPE_ARRAY";
+
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+        return "CTF_FIELD_CLASS_TYPE_SEQUENCE";
+
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+        return "CTF_FIELD_CLASS_TYPE_VARIANT";
+    }
+
+    bt_common_abort();
+}
+
+enum ctf_field_class_meaning
+{
+    CTF_FIELD_CLASS_MEANING_NONE,
+    CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME,
+    CTF_FIELD_CLASS_MEANING_PACKET_END_TIME,
+    CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID,
+    CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID,
+    CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID,
+    CTF_FIELD_CLASS_MEANING_MAGIC,
+    CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT,
+    CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT,
+    CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE,
+    CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE,
+    CTF_FIELD_CLASS_MEANING_UUID,
+};
+
+enum ctf_byte_order
+{
+    CTF_BYTE_ORDER_UNKNOWN,
+    CTF_BYTE_ORDER_DEFAULT,
+    CTF_BYTE_ORDER_LITTLE,
+    CTF_BYTE_ORDER_BIG,
+};
+
+enum ctf_encoding
+{
+    CTF_ENCODING_NONE,
+    CTF_ENCODING_UTF8,
+};
+
+enum ctf_scope
+{
+    CTF_SCOPE_PACKET_UNKNOWN = -1,
+    CTF_SCOPE_PACKET_HEADER = 0,
+    CTF_SCOPE_PACKET_CONTEXT,
+    CTF_SCOPE_EVENT_HEADER,
+    CTF_SCOPE_EVENT_COMMON_CONTEXT,
+    CTF_SCOPE_EVENT_SPECIFIC_CONTEXT,
+    CTF_SCOPE_EVENT_PAYLOAD,
+};
+
+inline const char *format_as(const ctf_scope scope) noexcept
+{
+    switch (scope) {
+    case CTF_SCOPE_PACKET_UNKNOWN:
+        return "PACKET_UNKNOWN";
+
+    case CTF_SCOPE_PACKET_HEADER:
+        return "PACKET_HEADER";
+
+    case CTF_SCOPE_PACKET_CONTEXT:
+        return "PACKET_CONTEXT";
+
+    case CTF_SCOPE_EVENT_HEADER:
+        return "EVENT_HEADER";
+
+    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+        return "EVENT_COMMON_CONTEXT";
+
+    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+        return "EVENT_SPECIFIC_CONTEXT";
+
+    case CTF_SCOPE_EVENT_PAYLOAD:
+        return "EVENT_PAYLOAD";
+    }
+
+    bt_common_abort();
+}
+
+struct ctf_clock_class
+{
+    GString *name;
+    GString *description;
+    uint64_t frequency;
+    uint64_t precision;
+    int64_t offset_seconds;
+    uint64_t offset_cycles;
+    bt_uuid_t uuid;
+    bool has_uuid;
+    bool is_absolute;
+
+    /* Weak, set during translation */
+    bt_clock_class *ir_cc;
+};
+
+struct ctf_field_class
+{
+    enum ctf_field_class_type type;
+    unsigned int alignment;
+    bool is_compound;
+    bool in_ir;
+
+    /* Weak, set during translation. NULL if `in_ir` is false below. */
+    bt_field_class *ir_fc;
+};
+
+struct ctf_field_class_bit_array
+{
+    struct ctf_field_class base;
+    enum ctf_byte_order byte_order;
+    unsigned int size;
+};
+
+struct ctf_field_class_int
+{
+    struct ctf_field_class_bit_array base;
+    enum ctf_field_class_meaning meaning;
+    bool is_signed;
+    bt_field_class_integer_preferred_display_base disp_base;
+    enum ctf_encoding encoding;
+    int64_t storing_index;
+
+    /* Weak */
+    struct ctf_clock_class *mapped_clock_class;
+};
+
+struct ctf_range
+{
+    union
+    {
+        uint64_t u;
+        int64_t i;
+    } lower;
+
+    union
+    {
+        uint64_t u;
+        int64_t i;
+    } upper;
+};
+
+struct ctf_field_class_enum_mapping
+{
+    GString *label;
+
+    /* Array of `struct ctf_range` */
+    GArray *ranges;
+};
+
+struct ctf_field_class_enum
+{
+    struct ctf_field_class_int base;
+
+    /* Array of `struct ctf_field_class_enum_mapping` */
+    GArray *mappings;
+};
+
+struct ctf_field_class_float
+{
+    struct ctf_field_class_bit_array base;
+};
+
+struct ctf_field_class_string
+{
+    struct ctf_field_class base;
+    enum ctf_encoding encoding;
+};
+
+struct ctf_named_field_class
+{
+    /* Original name which can include a leading `_` */
+    GString *orig_name;
+
+    /* Name as translated to trace IR (leading `_` removed) */
+    GString *name;
+
+    /* Owned by this */
+    struct ctf_field_class *fc;
+};
+
+struct ctf_field_class_struct
+{
+    struct ctf_field_class base;
+
+    /* Array of `struct ctf_named_field_class` */
+    GArray *members;
+};
+
+struct ctf_field_path
+{
+    enum ctf_scope root;
+
+    /* Array of `int64_t` */
+    GArray *path;
+};
+
+struct ctf_field_class_variant_range
+{
+    struct ctf_range range;
+    uint64_t option_index;
+};
+
+struct ctf_field_class_variant
+{
+    struct ctf_field_class base;
+    GString *tag_ref;
+    struct ctf_field_path tag_path;
+    uint64_t stored_tag_index;
+
+    /* Array of `struct ctf_named_field_class` */
+    GArray *options;
+
+    /* Array of `struct ctf_field_class_variant_range` */
+    GArray *ranges;
+
+    /* Weak */
+    struct ctf_field_class_enum *tag_fc;
+};
+
+struct ctf_field_class_array_base
+{
+    struct ctf_field_class base;
+    struct ctf_field_class *elem_fc;
+    bool is_text;
+};
+
+struct ctf_field_class_array
+{
+    struct ctf_field_class_array_base base;
+    enum ctf_field_class_meaning meaning;
+    uint64_t length;
+};
+
+struct ctf_field_class_sequence
+{
+    struct ctf_field_class_array_base base;
+    GString *length_ref;
+    struct ctf_field_path length_path;
+    uint64_t stored_length_index;
+
+    /* Weak */
+    struct ctf_field_class_int *length_fc;
+};
+
+struct ctf_event_class
+{
+    GString *name;
+    uint64_t id;
+    GString *emf_uri;
+    bt_event_class_log_level log_level;
+    bool is_translated;
+    bool is_log_level_set;
+
+    /* Owned by this */
+    struct ctf_field_class *spec_context_fc;
+
+    /* Owned by this */
+    struct ctf_field_class *payload_fc;
+
+    /* Weak, set during translation */
+    bt_event_class *ir_ec;
+};
+
+struct ctf_stream_class
+{
+    uint64_t id;
+    bool is_translated;
+    bool packets_have_ts_begin;
+    bool packets_have_ts_end;
+    bool has_discarded_events;
+    bool has_discarded_packets;
+    bool discarded_events_have_default_cs;
+    bool discarded_packets_have_default_cs;
+
+    /* Owned by this */
+    struct ctf_field_class *packet_context_fc;
+
+    /* Owned by this */
+    struct ctf_field_class *event_header_fc;
+
+    /* Owned by this */
+    struct ctf_field_class *event_common_context_fc;
+
+    /* Array of `struct ctf_event_class *`, owned by this */
+    GPtrArray *event_classes;
+
+    /*
+     * Hash table mapping event class IDs to `struct ctf_event_class *`,
+     * weak.
+     */
+    GHashTable *event_classes_by_id;
+
+    /* Weak */
+    struct ctf_clock_class *default_clock_class;
+
+    /* Weak, set during translation */
+    bt_stream_class *ir_sc;
+};
+
+enum ctf_trace_class_env_entry_type
+{
+    CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT,
+    CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR,
+};
+
+struct ctf_trace_class_env_entry
+{
+    enum ctf_trace_class_env_entry_type type;
+    GString *name;
+
+    struct
+    {
+        int64_t i;
+        GString *str;
+    } value;
+};
+
+struct ctf_trace_class
+{
+    unsigned int major;
+    unsigned int minor;
+    bt_uuid_t uuid;
+    bool is_uuid_set;
+    enum ctf_byte_order default_byte_order;
+
+    /* Owned by this */
+    struct ctf_field_class *packet_header_fc;
+
+    uint64_t stored_value_count;
+
+    /* Array of `struct ctf_clock_class *` (owned by this) */
+    GPtrArray *clock_classes;
+
+    /* Array of `struct ctf_stream_class *` */
+    GPtrArray *stream_classes;
+
+    /* Array of `struct ctf_trace_class_env_entry` */
+    GArray *env_entries;
+
+    bool is_translated;
+
+    /* Weak, set during translation */
+    bt_trace_class *ir_tc;
+
+    struct
+    {
+        bool lttng_crash;
+        bool lttng_event_after_packet;
+        bool barectf_event_before_packet;
+    } quirks;
+};
+
+static inline ctf_field_class_bit_array *ctf_field_class_as_bit_array(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc ||
+                  (fc->type == CTF_FIELD_CLASS_TYPE_INT || fc->type == CTF_FIELD_CLASS_TYPE_ENUM ||
+                   fc->type == CTF_FIELD_CLASS_TYPE_FLOAT));
+    return (ctf_field_class_bit_array *) fc;
+}
+
+static inline ctf_field_class_int *ctf_field_class_as_int(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc ||
+                  (fc->type == CTF_FIELD_CLASS_TYPE_INT || fc->type == CTF_FIELD_CLASS_TYPE_ENUM));
+    return (ctf_field_class_int *) fc;
+}
+
+static inline ctf_field_class_enum *ctf_field_class_as_enum(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_ENUM);
+    return (ctf_field_class_enum *) fc;
+}
+
+static inline ctf_field_class_float *ctf_field_class_as_float(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_FLOAT);
+    return (ctf_field_class_float *) fc;
+}
+
+static inline ctf_field_class_string *ctf_field_class_as_string(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_STRING);
+    return (ctf_field_class_string *) fc;
+}
+
+static inline ctf_field_class_struct *ctf_field_class_as_struct(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_STRUCT);
+    return (ctf_field_class_struct *) fc;
+}
+
+static inline ctf_field_class_array_base *ctf_field_class_as_array_base(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc || (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
+                          fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE));
+    return (ctf_field_class_array_base *) fc;
+}
+
+static inline ctf_field_class_array *ctf_field_class_as_array(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_ARRAY);
+    return (ctf_field_class_array *) fc;
+}
+
+static inline ctf_field_class_sequence *ctf_field_class_as_sequence(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE);
+    return (ctf_field_class_sequence *) fc;
+}
+
+static inline ctf_field_class_variant *ctf_field_class_as_variant(ctf_field_class *fc)
+{
+    BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_VARIANT);
+    return (ctf_field_class_variant *) fc;
+}
+
+static inline void ctf_field_class_destroy(struct ctf_field_class *fc);
+
+static inline void _ctf_field_class_init(struct ctf_field_class *fc, enum ctf_field_class_type type,
+                                         unsigned int alignment)
+{
+    BT_ASSERT(fc);
+    fc->type = type;
+    fc->alignment = alignment;
+    fc->in_ir = false;
+}
+
+static inline void _ctf_field_class_bit_array_init(struct ctf_field_class_bit_array *fc,
+                                                   enum ctf_field_class_type type)
+{
+    _ctf_field_class_init(&fc->base, type, 1);
+}
+
+static inline void _ctf_field_class_int_init(struct ctf_field_class_int *fc,
+                                             enum ctf_field_class_type type)
+{
+    _ctf_field_class_bit_array_init(&fc->base, type);
+    fc->meaning = CTF_FIELD_CLASS_MEANING_NONE;
+    fc->storing_index = -1;
+}
+
+static inline void ctf_field_path_init(struct ctf_field_path *field_path)
+{
+    BT_ASSERT(field_path);
+    field_path->path = g_array_new(FALSE, TRUE, sizeof(int64_t));
+    BT_ASSERT(field_path->path);
+}
+
+static inline void ctf_field_path_fini(struct ctf_field_path *field_path)
+{
+    BT_ASSERT(field_path);
+
+    if (field_path->path) {
+        g_array_free(field_path->path, TRUE);
+    }
+}
+
+static inline void _ctf_named_field_class_init(struct ctf_named_field_class *named_fc)
+{
+    BT_ASSERT(named_fc);
+    named_fc->name = g_string_new(NULL);
+    BT_ASSERT(named_fc->name);
+    named_fc->orig_name = g_string_new(NULL);
+    BT_ASSERT(named_fc->orig_name);
+}
+
+static inline void _ctf_named_field_class_fini(struct ctf_named_field_class *named_fc)
+{
+    BT_ASSERT(named_fc);
+
+    if (named_fc->name) {
+        g_string_free(named_fc->name, TRUE);
+    }
+
+    if (named_fc->orig_name) {
+        g_string_free(named_fc->orig_name, TRUE);
+    }
+
+    ctf_field_class_destroy(named_fc->fc);
+}
+
+static inline void _ctf_field_class_enum_mapping_init(struct ctf_field_class_enum_mapping *mapping)
+{
+    BT_ASSERT(mapping);
+    mapping->label = g_string_new(NULL);
+    BT_ASSERT(mapping->label);
+    mapping->ranges = g_array_new(FALSE, TRUE, sizeof(struct ctf_range));
+    BT_ASSERT(mapping->ranges);
+}
+
+static inline void _ctf_field_class_enum_mapping_fini(struct ctf_field_class_enum_mapping *mapping)
+{
+    BT_ASSERT(mapping);
+
+    if (mapping->label) {
+        g_string_free(mapping->label, TRUE);
+    }
+
+    if (mapping->ranges) {
+        g_array_free(mapping->ranges, TRUE);
+    }
+}
+
+static inline struct ctf_field_class_int *ctf_field_class_int_create(void)
+{
+    struct ctf_field_class_int *fc = g_new0(struct ctf_field_class_int, 1);
+
+    BT_ASSERT(fc);
+    _ctf_field_class_int_init(fc, CTF_FIELD_CLASS_TYPE_INT);
+    return fc;
+}
+
+static inline struct ctf_field_class_float *ctf_field_class_float_create(void)
+{
+    struct ctf_field_class_float *fc = g_new0(struct ctf_field_class_float, 1);
+
+    BT_ASSERT(fc);
+    _ctf_field_class_bit_array_init(&fc->base, CTF_FIELD_CLASS_TYPE_FLOAT);
+    return fc;
+}
+
+static inline struct ctf_field_class_string *ctf_field_class_string_create(void)
+{
+    struct ctf_field_class_string *fc = g_new0(struct ctf_field_class_string, 1);
+
+    BT_ASSERT(fc);
+    _ctf_field_class_init(&fc->base, CTF_FIELD_CLASS_TYPE_STRING, 8);
+    return fc;
+}
+
+static inline struct ctf_field_class_enum *ctf_field_class_enum_create(void)
+{
+    struct ctf_field_class_enum *fc = g_new0(struct ctf_field_class_enum, 1);
+
+    BT_ASSERT(fc);
+    _ctf_field_class_int_init(&fc->base, CTF_FIELD_CLASS_TYPE_ENUM);
+    fc->mappings = g_array_new(FALSE, TRUE, sizeof(struct ctf_field_class_enum_mapping));
+    BT_ASSERT(fc->mappings);
+    return fc;
+}
+
+static inline struct ctf_field_class_struct *ctf_field_class_struct_create(void)
+{
+    struct ctf_field_class_struct *fc = g_new0(struct ctf_field_class_struct, 1);
+
+    BT_ASSERT(fc);
+    _ctf_field_class_init(&fc->base, CTF_FIELD_CLASS_TYPE_STRUCT, 1);
+    fc->members = g_array_new(FALSE, TRUE, sizeof(struct ctf_named_field_class));
+    BT_ASSERT(fc->members);
+    fc->base.is_compound = true;
+    return fc;
+}
+
+static inline struct ctf_field_class_variant *ctf_field_class_variant_create(void)
+{
+    struct ctf_field_class_variant *fc = g_new0(struct ctf_field_class_variant, 1);
+
+    BT_ASSERT(fc);
+    _ctf_field_class_init(&fc->base, CTF_FIELD_CLASS_TYPE_VARIANT, 1);
+    fc->options = g_array_new(FALSE, TRUE, sizeof(struct ctf_named_field_class));
+    BT_ASSERT(fc->options);
+    fc->ranges = g_array_new(FALSE, TRUE, sizeof(struct ctf_field_class_variant_range));
+    BT_ASSERT(fc->ranges);
+    fc->tag_ref = g_string_new(NULL);
+    BT_ASSERT(fc->tag_ref);
+    ctf_field_path_init(&fc->tag_path);
+    fc->base.is_compound = true;
+    return fc;
+}
+
+static inline struct ctf_field_class_array *ctf_field_class_array_create(void)
+{
+    struct ctf_field_class_array *fc = g_new0(struct ctf_field_class_array, 1);
+
+    BT_ASSERT(fc);
+    _ctf_field_class_init(&fc->base.base, CTF_FIELD_CLASS_TYPE_ARRAY, 1);
+    fc->base.base.is_compound = true;
+    return fc;
+}
+
+static inline struct ctf_field_class_sequence *ctf_field_class_sequence_create(void)
+{
+    struct ctf_field_class_sequence *fc = g_new0(struct ctf_field_class_sequence, 1);
+
+    BT_ASSERT(fc);
+    _ctf_field_class_init(&fc->base.base, CTF_FIELD_CLASS_TYPE_SEQUENCE, 1);
+    fc->length_ref = g_string_new(NULL);
+    BT_ASSERT(fc->length_ref);
+    ctf_field_path_init(&fc->length_path);
+    fc->base.base.is_compound = true;
+    return fc;
+}
+
+static inline void _ctf_field_class_int_destroy(struct ctf_field_class_int *fc)
+{
+    BT_ASSERT(fc);
+    g_free(fc);
+}
+
+static inline void _ctf_field_class_enum_destroy(struct ctf_field_class_enum *fc)
+{
+    BT_ASSERT(fc);
+
+    if (fc->mappings) {
+        uint64_t i;
+
+        for (i = 0; i < fc->mappings->len; i++) {
+            struct ctf_field_class_enum_mapping *mapping =
+                &bt_g_array_index(fc->mappings, struct ctf_field_class_enum_mapping, i);
+
+            _ctf_field_class_enum_mapping_fini(mapping);
+        }
+
+        g_array_free(fc->mappings, TRUE);
+    }
+
+    g_free(fc);
+}
+
+static inline void _ctf_field_class_float_destroy(struct ctf_field_class_float *fc)
+{
+    BT_ASSERT(fc);
+    g_free(fc);
+}
+
+static inline void _ctf_field_class_string_destroy(struct ctf_field_class_string *fc)
+{
+    BT_ASSERT(fc);
+    g_free(fc);
+}
+
+static inline void _ctf_field_class_struct_destroy(struct ctf_field_class_struct *fc)
+{
+    BT_ASSERT(fc);
+
+    if (fc->members) {
+        uint64_t i;
+
+        for (i = 0; i < fc->members->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                &bt_g_array_index(fc->members, struct ctf_named_field_class, i);
+
+            _ctf_named_field_class_fini(named_fc);
+        }
+
+        g_array_free(fc->members, TRUE);
+    }
+
+    g_free(fc);
+}
+
+static inline void _ctf_field_class_array_base_fini(struct ctf_field_class_array_base *fc)
+{
+    BT_ASSERT(fc);
+    ctf_field_class_destroy(fc->elem_fc);
+}
+
+static inline void _ctf_field_class_array_destroy(struct ctf_field_class_array *fc)
+{
+    BT_ASSERT(fc);
+    _ctf_field_class_array_base_fini(&fc->base);
+    g_free(fc);
+}
+
+static inline void _ctf_field_class_sequence_destroy(struct ctf_field_class_sequence *fc)
+{
+    BT_ASSERT(fc);
+    _ctf_field_class_array_base_fini(&fc->base);
+
+    if (fc->length_ref) {
+        g_string_free(fc->length_ref, TRUE);
+    }
+
+    ctf_field_path_fini(&fc->length_path);
+    g_free(fc);
+}
+
+static inline void _ctf_field_class_variant_destroy(struct ctf_field_class_variant *fc)
+{
+    BT_ASSERT(fc);
+
+    if (fc->options) {
+        uint64_t i;
+
+        for (i = 0; i < fc->options->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                &bt_g_array_index(fc->options, struct ctf_named_field_class, i);
+
+            _ctf_named_field_class_fini(named_fc);
+        }
+
+        g_array_free(fc->options, TRUE);
+    }
+
+    if (fc->ranges) {
+        g_array_free(fc->ranges, TRUE);
+    }
+
+    if (fc->tag_ref) {
+        g_string_free(fc->tag_ref, TRUE);
+    }
+
+    ctf_field_path_fini(&fc->tag_path);
+    g_free(fc);
+}
+
+static inline void ctf_field_class_destroy(struct ctf_field_class *fc)
+{
+    if (!fc) {
+        return;
+    }
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+        _ctf_field_class_int_destroy(ctf_field_class_as_int(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+        _ctf_field_class_enum_destroy(ctf_field_class_as_enum(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_FLOAT:
+        _ctf_field_class_float_destroy(ctf_field_class_as_float(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_STRING:
+        _ctf_field_class_string_destroy(ctf_field_class_as_string(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+        _ctf_field_class_struct_destroy(ctf_field_class_as_struct(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+        _ctf_field_class_array_destroy(ctf_field_class_as_array(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+        _ctf_field_class_sequence_destroy(ctf_field_class_as_sequence(fc));
+        break;
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+        _ctf_field_class_variant_destroy(ctf_field_class_as_variant(fc));
+        break;
+    default:
+        bt_common_abort();
+    }
+}
+
+static inline struct ctf_range *
+ctf_field_class_enum_mapping_borrow_range_by_index(struct ctf_field_class_enum_mapping *mapping,
+                                                   uint64_t index)
+{
+    BT_ASSERT_DBG(mapping);
+    BT_ASSERT_DBG(index < mapping->ranges->len);
+    return &bt_g_array_index(mapping->ranges, struct ctf_range, index);
+}
+
+static inline struct ctf_field_class_enum_mapping *
+ctf_field_class_enum_borrow_mapping_by_index(struct ctf_field_class_enum *fc, uint64_t index)
+{
+    BT_ASSERT_DBG(fc);
+    BT_ASSERT_DBG(index < fc->mappings->len);
+    return &bt_g_array_index(fc->mappings, struct ctf_field_class_enum_mapping, index);
+}
+
+static inline struct ctf_field_class_enum_mapping *
+ctf_field_class_enum_borrow_mapping_by_label(struct ctf_field_class_enum *fc, const char *label)
+{
+    struct ctf_field_class_enum_mapping *ret_mapping = NULL;
+    uint64_t i;
+
+    BT_ASSERT_DBG(fc);
+    BT_ASSERT_DBG(label);
+
+    for (i = 0; i < fc->mappings->len; i++) {
+        struct ctf_field_class_enum_mapping *mapping =
+            ctf_field_class_enum_borrow_mapping_by_index(fc, i);
+
+        if (strcmp(mapping->label->str, label) == 0) {
+            ret_mapping = mapping;
+            goto end;
+        }
+    }
+
+end:
+    return ret_mapping;
+}
+
+static inline void ctf_field_class_enum_map_range(struct ctf_field_class_enum *fc,
+                                                  const char *label, uint64_t u_lower,
+                                                  uint64_t u_upper)
+{
+    struct ctf_field_class_enum_mapping *mapping = NULL;
+    struct ctf_range range = {
+        .lower =
+            {
+                .u = u_lower,
+            },
+        .upper =
+            {
+                .u = u_upper,
+            },
+    };
+    uint64_t i;
+
+    BT_ASSERT(fc);
+    BT_ASSERT(label);
+
+    for (i = 0; i < fc->mappings->len; i++) {
+        mapping = ctf_field_class_enum_borrow_mapping_by_index(fc, i);
+
+        if (strcmp(mapping->label->str, label) == 0) {
+            break;
+        }
+    }
+
+    if (i == fc->mappings->len) {
+        mapping = NULL;
+    }
+
+    if (!mapping) {
+        g_array_set_size(fc->mappings, fc->mappings->len + 1);
+        mapping = ctf_field_class_enum_borrow_mapping_by_index(fc, fc->mappings->len - 1);
+        _ctf_field_class_enum_mapping_init(mapping);
+        g_string_assign(mapping->label, label);
+    }
+
+    g_array_append_val(mapping->ranges, range);
+}
+
+static inline struct ctf_named_field_class *
+ctf_field_class_struct_borrow_member_by_index(struct ctf_field_class_struct *fc, uint64_t index)
+{
+    BT_ASSERT_DBG(fc);
+    BT_ASSERT_DBG(index < fc->members->len);
+    return &bt_g_array_index(fc->members, struct ctf_named_field_class, index);
+}
+
+static inline struct ctf_named_field_class *
+ctf_field_class_struct_borrow_member_by_name(struct ctf_field_class_struct *fc, const char *name)
+{
+    uint64_t i;
+    struct ctf_named_field_class *ret_named_fc = NULL;
+
+    BT_ASSERT_DBG(fc);
+    BT_ASSERT_DBG(name);
+
+    for (i = 0; i < fc->members->len; i++) {
+        struct ctf_named_field_class *named_fc =
+            ctf_field_class_struct_borrow_member_by_index(fc, i);
+
+        if (strcmp(name, named_fc->name->str) == 0) {
+            ret_named_fc = named_fc;
+            goto end;
+        }
+    }
+
+end:
+    return ret_named_fc;
+}
+
+static inline struct ctf_field_class *
+ctf_field_class_struct_borrow_member_field_class_by_name(struct ctf_field_class_struct *struct_fc,
+                                                         const char *name)
+{
+    struct ctf_named_field_class *named_fc = NULL;
+    struct ctf_field_class *fc = NULL;
+
+    if (!struct_fc) {
+        goto end;
+    }
+
+    named_fc = ctf_field_class_struct_borrow_member_by_name(struct_fc, name);
+    if (!named_fc) {
+        goto end;
+    }
+
+    fc = named_fc->fc;
+
+end:
+    return fc;
+}
+
+static inline struct ctf_field_class_int *
+ctf_field_class_struct_borrow_member_int_field_class_by_name(
+    struct ctf_field_class_struct *struct_fc, const char *name)
+{
+    ctf_field_class *member_fc =
+        ctf_field_class_struct_borrow_member_field_class_by_name(struct_fc, name);
+
+    if (!member_fc) {
+        return nullptr;
+    }
+
+    if (member_fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+        member_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+        return nullptr;
+    }
+
+    return ctf_field_class_as_int(member_fc);
+}
+
+static inline void _ctf_named_field_class_unescape_orig_name(struct ctf_named_field_class *named_fc)
+{
+    const char *name = named_fc->orig_name->str;
+
+    if (name[0] == '_') {
+        name++;
+    }
+
+    g_string_assign(named_fc->name, name);
+}
+
+static inline void ctf_field_class_struct_append_member(struct ctf_field_class_struct *fc,
+                                                        const char *orig_name,
+                                                        struct ctf_field_class *member_fc)
+{
+    struct ctf_named_field_class *named_fc;
+
+    BT_ASSERT(fc);
+    BT_ASSERT(orig_name);
+    g_array_set_size(fc->members, fc->members->len + 1);
+
+    named_fc = &bt_g_array_index(fc->members, struct ctf_named_field_class, fc->members->len - 1);
+    _ctf_named_field_class_init(named_fc);
+    g_string_assign(named_fc->orig_name, orig_name);
+    _ctf_named_field_class_unescape_orig_name(named_fc);
+    named_fc->fc = member_fc;
+
+    if (member_fc->alignment > fc->base.alignment) {
+        fc->base.alignment = member_fc->alignment;
+    }
+}
+
+static inline struct ctf_named_field_class *
+ctf_field_class_variant_borrow_option_by_index(struct ctf_field_class_variant *fc, uint64_t index)
+{
+    BT_ASSERT_DBG(fc);
+    BT_ASSERT_DBG(index < fc->options->len);
+    return &bt_g_array_index(fc->options, struct ctf_named_field_class, index);
+}
+
+static inline struct ctf_named_field_class *
+ctf_field_class_variant_borrow_option_by_name(struct ctf_field_class_variant *fc, const char *name)
+{
+    uint64_t i;
+    struct ctf_named_field_class *ret_named_fc = NULL;
+
+    BT_ASSERT_DBG(fc);
+    BT_ASSERT_DBG(name);
+
+    for (i = 0; i < fc->options->len; i++) {
+        struct ctf_named_field_class *named_fc =
+            ctf_field_class_variant_borrow_option_by_index(fc, i);
+
+        if (strcmp(name, named_fc->name->str) == 0) {
+            ret_named_fc = named_fc;
+            goto end;
+        }
+    }
+
+end:
+    return ret_named_fc;
+}
+
+static inline struct ctf_field_class_variant_range *
+ctf_field_class_variant_borrow_range_by_index(struct ctf_field_class_variant *fc, uint64_t index)
+{
+    BT_ASSERT_DBG(fc);
+    BT_ASSERT_DBG(index < fc->ranges->len);
+    return &bt_g_array_index(fc->ranges, struct ctf_field_class_variant_range, index);
+}
+
+static inline void ctf_field_class_variant_append_option(struct ctf_field_class_variant *fc,
+                                                         const char *orig_name,
+                                                         struct ctf_field_class *option_fc)
+{
+    struct ctf_named_field_class *named_fc;
+
+    BT_ASSERT(fc);
+    BT_ASSERT(orig_name);
+    g_array_set_size(fc->options, fc->options->len + 1);
+
+    named_fc = &bt_g_array_index(fc->options, struct ctf_named_field_class, fc->options->len - 1);
+    _ctf_named_field_class_init(named_fc);
+    g_string_assign(named_fc->orig_name, orig_name);
+    _ctf_named_field_class_unescape_orig_name(named_fc);
+    named_fc->fc = option_fc;
+}
+
+static inline void ctf_field_class_variant_set_tag_field_class(struct ctf_field_class_variant *fc,
+                                                               struct ctf_field_class_enum *tag_fc)
+{
+    uint64_t option_i;
+
+    BT_ASSERT(fc);
+    BT_ASSERT(tag_fc);
+    fc->tag_fc = tag_fc;
+
+    for (option_i = 0; option_i < fc->options->len; option_i++) {
+        uint64_t range_i;
+        struct ctf_named_field_class *named_fc =
+            ctf_field_class_variant_borrow_option_by_index(fc, option_i);
+        struct ctf_field_class_enum_mapping *mapping;
+
+        mapping = ctf_field_class_enum_borrow_mapping_by_label(tag_fc, named_fc->orig_name->str);
+        if (!mapping) {
+            continue;
+        }
+
+        for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
+            struct ctf_range *range =
+                ctf_field_class_enum_mapping_borrow_range_by_index(mapping, range_i);
+            struct ctf_field_class_variant_range var_range;
+
+            var_range.range = *range;
+            var_range.option_index = option_i;
+            g_array_append_val(fc->ranges, var_range);
+        }
+    }
+}
+
+static inline struct ctf_field_class *
+ctf_field_class_compound_borrow_field_class_by_index(struct ctf_field_class *comp_fc,
+                                                     uint64_t index)
+{
+    struct ctf_field_class *fc = NULL;
+
+    switch (comp_fc->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_named_field_class *named_fc = ctf_field_class_struct_borrow_member_by_index(
+            (struct ctf_field_class_struct *) comp_fc, index);
+
+        BT_ASSERT_DBG(named_fc);
+        fc = named_fc->fc;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_named_field_class *named_fc = ctf_field_class_variant_borrow_option_by_index(
+            (struct ctf_field_class_variant *) comp_fc, index);
+
+        BT_ASSERT_DBG(named_fc);
+        fc = named_fc->fc;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+    {
+        struct ctf_field_class_array_base *array_fc = (struct ctf_field_class_array_base *) comp_fc;
+
+        fc = array_fc->elem_fc;
+        break;
+    }
+    default:
+        break;
+    }
+
+    return fc;
+}
+
+static inline uint64_t ctf_field_class_compound_get_field_class_count(struct ctf_field_class *fc)
+{
+    uint64_t field_count;
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = (struct ctf_field_class_struct *) fc;
+
+        field_count = struct_fc->members->len;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_field_class_variant *var_fc = (struct ctf_field_class_variant *) fc;
+
+        field_count = var_fc->options->len;
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+        /*
+         * Array and sequence types always contain a single
+         * member (the element type).
+         */
+        field_count = 1;
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    return field_count;
+}
+
+static inline int64_t
+ctf_field_class_compound_get_field_class_index_from_orig_name(struct ctf_field_class *fc,
+                                                              const char *orig_name)
+{
+    int64_t ret_index = -1;
+    uint64_t i;
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+    {
+        struct ctf_field_class_struct *struct_fc = (struct ctf_field_class_struct *) fc;
+
+        for (i = 0; i < struct_fc->members->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+
+            if (strcmp(orig_name, named_fc->orig_name->str) == 0) {
+                ret_index = (int64_t) i;
+                goto end;
+            }
+        }
+
+        break;
+    }
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+    {
+        struct ctf_field_class_variant *var_fc = (struct ctf_field_class_variant *) fc;
+
+        for (i = 0; i < var_fc->options->len; i++) {
+            struct ctf_named_field_class *named_fc =
+                ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+
+            if (strcmp(orig_name, named_fc->orig_name->str) == 0) {
+                ret_index = (int64_t) i;
+                goto end;
+            }
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+end:
+    return ret_index;
+}
+
+static inline void ctf_field_path_append_index(struct ctf_field_path *fp, int64_t index)
+{
+    BT_ASSERT(fp);
+    g_array_append_val(fp->path, index);
+}
+
+static inline int64_t ctf_field_path_borrow_index_by_index(const ctf_field_path *fp, uint64_t index)
+{
+    BT_ASSERT_DBG(fp);
+    BT_ASSERT_DBG(index < fp->path->len);
+    return bt_g_array_index(fp->path, int64_t, index);
+}
+
+static inline void ctf_field_path_clear(struct ctf_field_path *fp)
+{
+    BT_ASSERT(fp);
+    g_array_set_size(fp->path, 0);
+}
+
+inline std::string format_as(const ctf_field_path& path)
+{
+    std::string str = fmt::format("[{}", path.root);
+
+    for (guint i = 0; i < path.path->len; i++) {
+        str += fmt::format(", {}", ctf_field_path_borrow_index_by_index(&path, i));
+    }
+
+    str += ']';
+    return str;
+}
+
+static inline struct ctf_field_class *
+ctf_field_path_borrow_field_class(struct ctf_field_path *field_path, struct ctf_trace_class *tc,
+                                  struct ctf_stream_class *sc, struct ctf_event_class *ec)
+{
+    uint64_t i;
+    struct ctf_field_class *fc;
+
+    switch (field_path->root) {
+    case CTF_SCOPE_PACKET_HEADER:
+        fc = tc->packet_header_fc;
+        break;
+    case CTF_SCOPE_PACKET_CONTEXT:
+        fc = sc->packet_context_fc;
+        break;
+    case CTF_SCOPE_EVENT_HEADER:
+        fc = sc->event_header_fc;
+        break;
+    case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+        fc = sc->event_common_context_fc;
+        break;
+    case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+        fc = ec->spec_context_fc;
+        break;
+    case CTF_SCOPE_EVENT_PAYLOAD:
+        fc = ec->payload_fc;
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    BT_ASSERT_DBG(fc);
+
+    for (i = 0; i < field_path->path->len; i++) {
+        int64_t child_index = ctf_field_path_borrow_index_by_index(field_path, i);
+        struct ctf_field_class *child_fc =
+            ctf_field_class_compound_borrow_field_class_by_index(fc, child_index);
+        BT_ASSERT_DBG(child_fc);
+        fc = child_fc;
+    }
+
+    BT_ASSERT_DBG(fc);
+    return fc;
+}
+
+static inline struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc);
+
+static inline void ctf_field_class_bit_array_copy_content(struct ctf_field_class_bit_array *dst_fc,
+                                                          struct ctf_field_class_bit_array *src_fc)
+{
+    BT_ASSERT(dst_fc);
+    BT_ASSERT(src_fc);
+    dst_fc->byte_order = src_fc->byte_order;
+    dst_fc->size = src_fc->size;
+}
+
+static inline void ctf_field_class_int_copy_content(struct ctf_field_class_int *dst_fc,
+                                                    struct ctf_field_class_int *src_fc)
+{
+    ctf_field_class_bit_array_copy_content(&dst_fc->base, &src_fc->base);
+    dst_fc->meaning = src_fc->meaning;
+    dst_fc->is_signed = src_fc->is_signed;
+    dst_fc->disp_base = src_fc->disp_base;
+    dst_fc->encoding = src_fc->encoding;
+    dst_fc->mapped_clock_class = src_fc->mapped_clock_class;
+    dst_fc->storing_index = src_fc->storing_index;
+}
+
+static inline struct ctf_field_class_int *_ctf_field_class_int_copy(struct ctf_field_class_int *fc)
+{
+    struct ctf_field_class_int *copy_fc = ctf_field_class_int_create();
+
+    BT_ASSERT(copy_fc);
+    ctf_field_class_int_copy_content(copy_fc, fc);
+    return copy_fc;
+}
+
+static inline struct ctf_field_class_enum *
+_ctf_field_class_enum_copy(struct ctf_field_class_enum *fc)
+{
+    struct ctf_field_class_enum *copy_fc = ctf_field_class_enum_create();
+    uint64_t i;
+
+    BT_ASSERT(copy_fc);
+    ctf_field_class_int_copy_content(&copy_fc->base, &fc->base);
+
+    for (i = 0; i < fc->mappings->len; i++) {
+        uint64_t range_i;
+
+        struct ctf_field_class_enum_mapping *mapping =
+            &bt_g_array_index(fc->mappings, struct ctf_field_class_enum_mapping, i);
+
+        for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
+            struct ctf_range *range = &bt_g_array_index(mapping->ranges, struct ctf_range, range_i);
+
+            ctf_field_class_enum_map_range(copy_fc, mapping->label->str, range->lower.u,
+                                           range->upper.u);
+        }
+    }
+
+    return copy_fc;
+}
+
+static inline struct ctf_field_class_float *
+_ctf_field_class_float_copy(struct ctf_field_class_float *fc)
+{
+    struct ctf_field_class_float *copy_fc = ctf_field_class_float_create();
+
+    BT_ASSERT(copy_fc);
+    ctf_field_class_bit_array_copy_content(&copy_fc->base, &fc->base);
+    return copy_fc;
+}
+
+static inline struct ctf_field_class_string *
+_ctf_field_class_string_copy(struct ctf_field_class_string *)
+{
+    struct ctf_field_class_string *copy_fc = ctf_field_class_string_create();
+
+    BT_ASSERT(copy_fc);
+    return copy_fc;
+}
+
+static inline struct ctf_field_class_struct *
+_ctf_field_class_struct_copy(struct ctf_field_class_struct *fc)
+{
+    struct ctf_field_class_struct *copy_fc = ctf_field_class_struct_create();
+    uint64_t i;
+
+    BT_ASSERT(copy_fc);
+
+    for (i = 0; i < fc->members->len; i++) {
+        struct ctf_named_field_class *named_fc =
+            &bt_g_array_index(fc->members, struct ctf_named_field_class, i);
+
+        ctf_field_class_struct_append_member(copy_fc, named_fc->name->str,
+                                             ctf_field_class_copy(named_fc->fc));
+    }
+
+    return copy_fc;
+}
+
+static inline void ctf_field_path_copy_content(struct ctf_field_path *dst_fp,
+                                               struct ctf_field_path *src_fp)
+{
+    uint64_t i;
+
+    BT_ASSERT(dst_fp);
+    BT_ASSERT(src_fp);
+    dst_fp->root = src_fp->root;
+    ctf_field_path_clear(dst_fp);
+
+    for (i = 0; i < src_fp->path->len; i++) {
+        int64_t index = ctf_field_path_borrow_index_by_index(src_fp, i);
+
+        ctf_field_path_append_index(dst_fp, index);
+    }
+}
+
+static inline struct ctf_field_class_variant *
+_ctf_field_class_variant_copy(struct ctf_field_class_variant *fc)
+{
+    struct ctf_field_class_variant *copy_fc = ctf_field_class_variant_create();
+    uint64_t i;
+
+    BT_ASSERT(copy_fc);
+
+    for (i = 0; i < fc->options->len; i++) {
+        struct ctf_named_field_class *named_fc =
+            &bt_g_array_index(fc->options, struct ctf_named_field_class, i);
+
+        ctf_field_class_variant_append_option(copy_fc, named_fc->name->str,
+                                              ctf_field_class_copy(named_fc->fc));
+    }
+
+    for (i = 0; i < fc->ranges->len; i++) {
+        struct ctf_field_class_variant_range *range =
+            &bt_g_array_index(fc->ranges, struct ctf_field_class_variant_range, i);
+
+        g_array_append_val(copy_fc->ranges, *range);
+    }
+
+    ctf_field_path_copy_content(&copy_fc->tag_path, &fc->tag_path);
+    g_string_assign(copy_fc->tag_ref, fc->tag_ref->str);
+    copy_fc->stored_tag_index = fc->stored_tag_index;
+    return copy_fc;
+}
+
+static inline void
+ctf_field_class_array_base_copy_content(struct ctf_field_class_array_base *dst_fc,
+                                        struct ctf_field_class_array_base *src_fc)
+{
+    BT_ASSERT(dst_fc);
+    BT_ASSERT(src_fc);
+    dst_fc->elem_fc = ctf_field_class_copy(src_fc->elem_fc);
+    dst_fc->is_text = src_fc->is_text;
+}
+
+static inline struct ctf_field_class_array *
+_ctf_field_class_array_copy(struct ctf_field_class_array *fc)
+{
+    struct ctf_field_class_array *copy_fc = ctf_field_class_array_create();
+
+    BT_ASSERT(copy_fc);
+    ctf_field_class_array_base_copy_content(&copy_fc->base, &fc->base);
+    copy_fc->length = fc->length;
+    return copy_fc;
+}
+
+static inline struct ctf_field_class_sequence *
+_ctf_field_class_sequence_copy(struct ctf_field_class_sequence *fc)
+{
+    struct ctf_field_class_sequence *copy_fc = ctf_field_class_sequence_create();
+
+    BT_ASSERT(copy_fc);
+    ctf_field_class_array_base_copy_content(&copy_fc->base, &fc->base);
+    ctf_field_path_copy_content(&copy_fc->length_path, &fc->length_path);
+    g_string_assign(copy_fc->length_ref, fc->length_ref->str);
+    copy_fc->stored_length_index = fc->stored_length_index;
+    return copy_fc;
+}
+
+static inline struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc)
+{
+    struct ctf_field_class *copy_fc = NULL;
+
+    if (!fc) {
+        goto end;
+    }
+
+    /*
+     * Translation should not have happened yet.
+     */
+    BT_ASSERT(!fc->ir_fc);
+
+    switch (fc->type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+        copy_fc = &_ctf_field_class_int_copy(ctf_field_class_as_int(fc))->base.base;
+        break;
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+        copy_fc = &_ctf_field_class_enum_copy(ctf_field_class_as_enum(fc))->base.base.base;
+        break;
+    case CTF_FIELD_CLASS_TYPE_FLOAT:
+        copy_fc = &_ctf_field_class_float_copy(ctf_field_class_as_float(fc))->base.base;
+        break;
+    case CTF_FIELD_CLASS_TYPE_STRING:
+        copy_fc = &_ctf_field_class_string_copy(ctf_field_class_as_string(fc))->base;
+        break;
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+        copy_fc = &_ctf_field_class_struct_copy(ctf_field_class_as_struct(fc))->base;
+        break;
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+        copy_fc = &_ctf_field_class_array_copy(ctf_field_class_as_array(fc))->base.base;
+        break;
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+        copy_fc = &_ctf_field_class_sequence_copy(ctf_field_class_as_sequence(fc))->base.base;
+        break;
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+        copy_fc = &_ctf_field_class_variant_copy(ctf_field_class_as_variant(fc))->base;
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    copy_fc->type = fc->type;
+    copy_fc->alignment = fc->alignment;
+    copy_fc->in_ir = fc->in_ir;
+
+end:
+    return copy_fc;
+}
+
+static inline struct ctf_event_class *ctf_event_class_create(void)
+{
+    struct ctf_event_class *ec = g_new0(struct ctf_event_class, 1);
+
+    BT_ASSERT(ec);
+    ec->name = g_string_new(NULL);
+    BT_ASSERT(ec->name);
+    ec->emf_uri = g_string_new(NULL);
+    BT_ASSERT(ec->emf_uri);
+    ec->is_log_level_set = false;
+    return ec;
+}
+
+static inline void ctf_event_class_set_log_level(struct ctf_event_class *ec,
+                                                 enum bt_event_class_log_level log_level)
+{
+    BT_ASSERT(ec);
+    ec->log_level = log_level;
+    ec->is_log_level_set = true;
+}
+
+static inline void ctf_event_class_destroy(struct ctf_event_class *ec)
+{
+    if (!ec) {
+        return;
+    }
+
+    if (ec->name) {
+        g_string_free(ec->name, TRUE);
+    }
+
+    if (ec->emf_uri) {
+        g_string_free(ec->emf_uri, TRUE);
+    }
+
+    ctf_field_class_destroy(ec->spec_context_fc);
+    ctf_field_class_destroy(ec->payload_fc);
+    g_free(ec);
+}
+
+static inline struct ctf_stream_class *ctf_stream_class_create(void)
+{
+    struct ctf_stream_class *sc = g_new0(struct ctf_stream_class, 1);
+
+    BT_ASSERT(sc);
+    sc->event_classes = g_ptr_array_new_with_free_func((GDestroyNotify) ctf_event_class_destroy);
+    BT_ASSERT(sc->event_classes);
+    sc->event_classes_by_id = g_hash_table_new(g_direct_hash, g_direct_equal);
+    BT_ASSERT(sc->event_classes_by_id);
+    return sc;
+}
+
+static inline void ctf_stream_class_destroy(struct ctf_stream_class *sc)
+{
+    if (!sc) {
+        return;
+    }
+
+    if (sc->event_classes) {
+        g_ptr_array_free(sc->event_classes, TRUE);
+    }
+
+    if (sc->event_classes_by_id) {
+        g_hash_table_destroy(sc->event_classes_by_id);
+    }
+
+    ctf_field_class_destroy(sc->packet_context_fc);
+    ctf_field_class_destroy(sc->event_header_fc);
+    ctf_field_class_destroy(sc->event_common_context_fc);
+    g_free(sc);
+}
+
+static inline void ctf_stream_class_append_event_class(struct ctf_stream_class *sc,
+                                                       struct ctf_event_class *ec)
+{
+    g_ptr_array_add(sc->event_classes, ec);
+    g_hash_table_insert(sc->event_classes_by_id, GUINT_TO_POINTER((guint) ec->id), ec);
+}
+
+static inline struct ctf_event_class *
+ctf_stream_class_borrow_event_class_by_id(struct ctf_stream_class *sc, uint64_t type)
+{
+    BT_ASSERT_DBG(sc);
+    return (struct ctf_event_class *) g_hash_table_lookup(sc->event_classes_by_id,
+                                                          GUINT_TO_POINTER((guint) type));
+}
+
+static inline void _ctf_trace_class_env_entry_init(struct ctf_trace_class_env_entry *entry)
+{
+    BT_ASSERT(entry);
+    entry->name = g_string_new(NULL);
+    BT_ASSERT(entry->name);
+    entry->value.str = g_string_new(NULL);
+    BT_ASSERT(entry->value.str);
+}
+
+static inline void _ctf_trace_class_env_entry_fini(struct ctf_trace_class_env_entry *entry)
+{
+    BT_ASSERT(entry);
+
+    if (entry->name) {
+        g_string_free(entry->name, TRUE);
+    }
+
+    if (entry->value.str) {
+        g_string_free(entry->value.str, TRUE);
+    }
+}
+
+static inline struct ctf_clock_class *ctf_clock_class_create(void)
+{
+    struct ctf_clock_class *cc = g_new0(struct ctf_clock_class, 1);
+
+    BT_ASSERT(cc);
+    cc->name = g_string_new(NULL);
+    BT_ASSERT(cc->name);
+    cc->description = g_string_new(NULL);
+    BT_ASSERT(cc->description);
+    return cc;
+}
+
+static inline void ctf_clock_class_destroy(struct ctf_clock_class *cc)
+{
+    if (!cc) {
+        return;
+    }
+
+    if (cc->name) {
+        g_string_free(cc->name, TRUE);
+    }
+
+    if (cc->description) {
+        g_string_free(cc->description, TRUE);
+    }
+
+    bt_clock_class_put_ref(cc->ir_cc);
+    g_free(cc);
+}
+
+static inline struct ctf_trace_class *ctf_trace_class_create(void)
+{
+    struct ctf_trace_class *tc = g_new0(struct ctf_trace_class, 1);
+
+    BT_ASSERT(tc);
+    tc->default_byte_order = CTF_BYTE_ORDER_UNKNOWN;
+    tc->clock_classes = g_ptr_array_new_with_free_func((GDestroyNotify) ctf_clock_class_destroy);
+    BT_ASSERT(tc->clock_classes);
+    tc->stream_classes = g_ptr_array_new_with_free_func((GDestroyNotify) ctf_stream_class_destroy);
+    BT_ASSERT(tc->stream_classes);
+    tc->env_entries = g_array_new(FALSE, TRUE, sizeof(struct ctf_trace_class_env_entry));
+    return tc;
+}
+
+static inline void ctf_trace_class_destroy(struct ctf_trace_class *tc)
+{
+    if (!tc) {
+        return;
+    }
+
+    ctf_field_class_destroy(tc->packet_header_fc);
+
+    if (tc->clock_classes) {
+        g_ptr_array_free(tc->clock_classes, TRUE);
+    }
+
+    if (tc->stream_classes) {
+        g_ptr_array_free(tc->stream_classes, TRUE);
+    }
+
+    if (tc->env_entries) {
+        uint64_t i;
+
+        for (i = 0; i < tc->env_entries->len; i++) {
+            struct ctf_trace_class_env_entry *entry =
+                &bt_g_array_index(tc->env_entries, struct ctf_trace_class_env_entry, i);
+
+            _ctf_trace_class_env_entry_fini(entry);
+        }
+
+        g_array_free(tc->env_entries, TRUE);
+    }
+
+    g_free(tc);
+}
+
+static inline void ctf_trace_class_append_env_entry(struct ctf_trace_class *tc, const char *name,
+                                                    enum ctf_trace_class_env_entry_type type,
+                                                    const char *str_value, int64_t i_value)
+{
+    struct ctf_trace_class_env_entry *entry;
+
+    BT_ASSERT(tc);
+    BT_ASSERT(name);
+    g_array_set_size(tc->env_entries, tc->env_entries->len + 1);
+
+    entry = &bt_g_array_index(tc->env_entries, struct ctf_trace_class_env_entry,
+                              tc->env_entries->len - 1);
+    entry->type = type;
+    _ctf_trace_class_env_entry_init(entry);
+    g_string_assign(entry->name, name);
+
+    if (str_value) {
+        g_string_assign(entry->value.str, str_value);
+    }
+
+    entry->value.i = i_value;
+}
+
+static inline struct ctf_stream_class *
+ctf_trace_class_borrow_stream_class_by_id(struct ctf_trace_class *tc, uint64_t id)
+{
+    uint64_t i;
+    struct ctf_stream_class *ret_sc = NULL;
+
+    BT_ASSERT_DBG(tc);
+
+    for (i = 0; i < tc->stream_classes->len; i++) {
+        struct ctf_stream_class *sc = (struct ctf_stream_class *) tc->stream_classes->pdata[i];
+
+        if (sc->id == id) {
+            ret_sc = sc;
+            goto end;
+        }
+    }
+
+end:
+    return ret_sc;
+}
+
+static inline struct ctf_clock_class *
+ctf_trace_class_borrow_clock_class_by_name(struct ctf_trace_class *tc, const char *name)
+{
+    uint64_t i;
+    struct ctf_clock_class *ret_cc = NULL;
+
+    BT_ASSERT_DBG(tc);
+    BT_ASSERT_DBG(name);
+
+    for (i = 0; i < tc->clock_classes->len; i++) {
+        struct ctf_clock_class *cc = (struct ctf_clock_class *) tc->clock_classes->pdata[i];
+
+        BT_ASSERT_DBG(cc->name);
+        if (strcmp(cc->name->str, name) == 0) {
+            ret_cc = cc;
+            goto end;
+        }
+    }
+
+end:
+    return ret_cc;
+}
+
+static inline struct ctf_trace_class_env_entry *
+ctf_trace_class_borrow_env_entry_by_index(struct ctf_trace_class *tc, uint64_t index)
+{
+    BT_ASSERT_DBG(tc);
+    BT_ASSERT_DBG(index < tc->env_entries->len);
+    return &bt_g_array_index(tc->env_entries, struct ctf_trace_class_env_entry, index);
+}
+
+static inline struct ctf_trace_class_env_entry *
+ctf_trace_class_borrow_env_entry_by_name(struct ctf_trace_class *tc, const char *name)
+{
+    struct ctf_trace_class_env_entry *ret_entry = NULL;
+    uint64_t i;
+
+    BT_ASSERT_DBG(tc);
+    BT_ASSERT_DBG(name);
+
+    for (i = 0; i < tc->env_entries->len; i++) {
+        struct ctf_trace_class_env_entry *env_entry =
+            ctf_trace_class_borrow_env_entry_by_index(tc, i);
+
+        if (strcmp(env_entry->name->str, name) == 0) {
+            ret_entry = env_entry;
+            goto end;
+        }
+    }
+
+end:
+    return ret_entry;
+}
+
+#endif /* _CTF_META_H */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/decoder-packetized-file-stream-to-buf.cpp b/src/plugins/ctf/common/src/metadata/tsdl/decoder-packetized-file-stream-to-buf.cpp
new file mode 100644 (file)
index 0000000..846df31
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "common/uuid.h"
+#include "compat/memstream.h"
+
+#include "decoder-packetized-file-stream-to-buf.hpp"
+#include "decoder.hpp"
+
+#define TSDL_MAGIC 0x75d11d57
+
+struct packet_header
+{
+    uint32_t magic;
+    bt_uuid_t uuid;
+    uint32_t checksum;
+    uint32_t content_size;
+    uint32_t packet_size;
+    uint8_t compression_scheme;
+    uint8_t encryption_scheme;
+    uint8_t checksum_scheme;
+    uint8_t major;
+    uint8_t minor;
+} __attribute__((__packed__));
+
+static int decode_packet(FILE *in_fp, FILE *out_fp, int byte_order, bool *is_uuid_set,
+                         uint8_t *uuid, const bt2c::Logger& logger)
+{
+    struct packet_header header;
+    size_t readlen, writelen, toread;
+    uint8_t buf[512 + 1]; /* + 1 for debug-mode \0 */
+    int ret = 0;
+    const long offset = ftell(in_fp);
+
+    if (offset < 0) {
+        BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(logger, "Failed to get current metadata file position",
+                                           ".");
+        goto error;
+    }
+    BT_CPPLOGD_SPEC(logger, "Decoding metadata packet: offset={}", offset);
+    readlen = fread(&header, sizeof(header), 1, in_fp);
+    if (feof(in_fp) != 0) {
+        BT_CPPLOGI_SPEC(logger, "Reached end of file: offset={}", ftell(in_fp));
+        goto end;
+    }
+    if (readlen < 1) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Cannot decode metadata packet: offset={}", offset);
+        goto error;
+    }
+
+    if (byte_order != BYTE_ORDER) {
+        header.magic = GUINT32_SWAP_LE_BE(header.magic);
+        header.checksum = GUINT32_SWAP_LE_BE(header.checksum);
+        header.content_size = GUINT32_SWAP_LE_BE(header.content_size);
+        header.packet_size = GUINT32_SWAP_LE_BE(header.packet_size);
+    }
+
+    if (header.compression_scheme) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            logger,
+            "Metadata packet compression is not supported as of this version: "
+            "compression-scheme={}, offset={}",
+            (unsigned int) header.compression_scheme, offset);
+        goto error;
+    }
+
+    if (header.encryption_scheme) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            logger,
+            "Metadata packet encryption is not supported as of this version: "
+            "encryption-scheme={}, offset={}",
+            (unsigned int) header.encryption_scheme, offset);
+        goto error;
+    }
+
+    if (header.checksum || header.checksum_scheme) {
+        auto checksum = header.checksum;
+
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            logger,
+            "Metadata packet checksum verification is not supported as of this version: "
+            "checksum-scheme={}, checksum={}, offset={}",
+            (unsigned int) header.checksum_scheme, checksum, offset);
+        goto error;
+    }
+
+    if (!ctf_metadata_decoder_is_packet_version_valid(header.major, header.minor)) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                     "Invalid metadata packet version: "
+                                     "version={}.{}, offset={}",
+                                     header.major, header.minor, offset);
+        goto error;
+    }
+
+    /* Set expected trace UUID if not set; otherwise validate it */
+    if (is_uuid_set) {
+        if (!*is_uuid_set) {
+            bt_uuid_copy(uuid, header.uuid);
+            *is_uuid_set = true;
+        } else if (bt_uuid_compare(header.uuid, uuid)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                logger,
+                "Metadata UUID mismatch between packets of the same stream: "
+                "packet-uuid=\"" BT_UUID_FMT "\", "
+                "expected-uuid=\"" BT_UUID_FMT "\", "
+                "offset={}",
+                BT_UUID_FMT_VALUES(header.uuid), BT_UUID_FMT_VALUES(uuid), offset);
+            goto error;
+        }
+    }
+
+    if ((header.content_size / CHAR_BIT) < sizeof(header)) {
+        auto content_size = header.content_size;
+
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                     "Bad metadata packet content size: content-size={}, "
+                                     "offset={}",
+                                     content_size, offset);
+        goto error;
+    }
+
+    toread = header.content_size / CHAR_BIT - sizeof(header);
+
+    for (;;) {
+        size_t loop_read;
+
+        loop_read = MIN(sizeof(buf) - 1, toread);
+        readlen = fread(buf, sizeof(uint8_t), loop_read, in_fp);
+        if (ferror(in_fp)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "Cannot read metadata packet buffer: "
+                                         "offset={}, read-size={}",
+                                         ftell(in_fp), loop_read);
+            goto error;
+        }
+        if (readlen > loop_read) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "fread returned more byte than expected: "
+                                         "read-size-asked={}, read-size-returned={}",
+                                         loop_read, readlen);
+            goto error;
+        }
+
+        writelen = fwrite(buf, sizeof(uint8_t), readlen, out_fp);
+        if (writelen < readlen || ferror(out_fp)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "Cannot write decoded metadata text to buffer: "
+                                         "read-offset={}, write-size={}",
+                                         ftell(in_fp), readlen);
+            goto error;
+        }
+
+        toread -= readlen;
+        if (toread == 0) {
+            int fseek_ret;
+
+            /* Read leftover padding */
+            toread = (header.packet_size - header.content_size) / CHAR_BIT;
+            fseek_ret = fseek(in_fp, toread, SEEK_CUR);
+            if (fseek_ret < 0) {
+                BT_CPPLOGW_STR_SPEC(logger, "Missing padding at the end of the metadata stream.");
+            }
+            break;
+        }
+    }
+
+    goto end;
+
+error:
+    ret = -1;
+
+end:
+    return ret;
+}
+
+int ctf_metadata_decoder_packetized_file_stream_to_buf(FILE *fp, char **buf, int byte_order,
+                                                       bool *is_uuid_set, uint8_t *uuid,
+                                                       const bt2c::Logger& parentLogger)
+{
+    FILE *out_fp;
+    size_t size;
+    int ret = 0;
+    int tret;
+    size_t packet_index = 0;
+    bt2c::Logger logger {parentLogger, "PLUGIN/CTF/META/DECODER-DECODE-PACKET"};
+
+    out_fp = bt_open_memstream(buf, &size);
+    if (!out_fp) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Cannot open memory stream: {}.", strerror(errno));
+        goto error;
+    }
+
+    for (;;) {
+        if (feof(fp) != 0) {
+            break;
+        }
+
+        tret = decode_packet(fp, out_fp, byte_order, is_uuid_set, uuid, logger);
+        if (tret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Cannot decode packet: index={}", packet_index);
+            goto error;
+        }
+
+        packet_index++;
+    }
+
+    /* Make sure the whole string ends with a null character */
+    tret = fputc('\0', out_fp);
+    if (tret == EOF) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Cannot append '\\0' to the decoded metadata buffer.");
+        goto error;
+    }
+
+    /* Close stream, which also flushes the buffer */
+    ret = bt_close_memstream(buf, &size, out_fp);
+    /*
+     * See fclose(3). Further access to out_fp after both success
+     * and error, even through another bt_close_memstream(), results
+     * in undefined behavior. Nullify out_fp to ensure we don't
+     * fclose it twice on error.
+     */
+    out_fp = NULL;
+    if (ret < 0) {
+        BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(logger, "Cannot close memory stream", ".");
+        goto error;
+    }
+
+    goto end;
+
+error:
+    ret = -1;
+
+    if (out_fp) {
+        if (bt_close_memstream(buf, &size, out_fp)) {
+            BT_CPPLOGE_ERRNO_SPEC(logger, "Cannot close memory stream", ".");
+        }
+    }
+
+    if (*buf) {
+        free(*buf);
+        *buf = NULL;
+    }
+
+end:
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/decoder-packetized-file-stream-to-buf.hpp b/src/plugins/ctf/common/src/metadata/tsdl/decoder-packetized-file-stream-to-buf.hpp
new file mode 100644 (file)
index 0000000..6190b06
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Efficios Inc.
+ */
+
+#ifndef SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF
+#define SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF
+
+#include <cstdio>
+
+#include <stdint.h>
+
+#include <babeltrace2/babeltrace.h>
+
+namespace bt2c {
+
+class Logger;
+
+} /* namespace bt2c */
+
+int ctf_metadata_decoder_packetized_file_stream_to_buf(FILE *fp, char **buf, int byte_order,
+                                                       bool *is_uuid_set, uint8_t *uuid,
+                                                       const bt2c::Logger& parentLogger);
+
+#endif /* SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/decoder.cpp b/src/plugins/ctf/common/src/metadata/tsdl/decoder.cpp
new file mode 100644 (file)
index 0000000..7e5e8bb
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common/assert.h"
+#include "common/uuid.h"
+#include "compat/memstream.h"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "ast.hpp"
+#include "decoder-packetized-file-stream-to-buf.hpp"
+#include "decoder.hpp"
+#include "parser-wrap.hpp"
+#include "scanner.hpp"
+
+#define TSDL_MAGIC 0x75d11d57
+
+struct ctf_metadata_decoder
+{
+    explicit ctf_metadata_decoder(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/CTF/META/DECODER"}, config {logger}
+    {
+    }
+
+    bt2c::Logger logger;
+    struct ctf_scanner *scanner = nullptr;
+    GString *text = nullptr;
+    ctf_visitor_generate_ir::UP visitor;
+    bt_uuid_t uuid {};
+    bool is_uuid_set = false;
+    int bo = 0;
+    struct ctf_metadata_decoder_config config;
+    bool has_checked_plaintext_signature = false;
+};
+
+struct packet_header
+{
+    uint32_t magic;
+    bt_uuid_t uuid;
+    uint32_t checksum;
+    uint32_t content_size;
+    uint32_t packet_size;
+    uint8_t compression_scheme;
+    uint8_t encryption_scheme;
+    uint8_t checksum_scheme;
+    uint8_t major;
+    uint8_t minor;
+} __attribute__((__packed__));
+
+int ctf_metadata_decoder_is_packetized(FILE *fp, bool *is_packetized, int *byte_order,
+                                       const bt2c::Logger& logger)
+{
+    uint32_t magic;
+    size_t len;
+    int ret = 0;
+
+    *is_packetized = false;
+    len = fread(&magic, sizeof(magic), 1, fp);
+    if (len != 1) {
+        BT_CPPLOGI_SPEC(
+            logger,
+            "Cannot read first metadata packet header: assuming the stream is not packetized.");
+        ret = -1;
+        goto end;
+    }
+
+    if (byte_order) {
+        if (magic == TSDL_MAGIC) {
+            *is_packetized = true;
+            *byte_order = BYTE_ORDER;
+        } else if (magic == GUINT32_SWAP_LE_BE(TSDL_MAGIC)) {
+            *is_packetized = true;
+            *byte_order = BYTE_ORDER == BIG_ENDIAN ? LITTLE_ENDIAN : BIG_ENDIAN;
+        }
+    }
+
+end:
+    rewind(fp);
+
+    return ret;
+}
+
+ctf_metadata_decoder_up
+ctf_metadata_decoder_create(const struct ctf_metadata_decoder_config *config)
+{
+    BT_ASSERT(config);
+    BT_CPPLOGD_SPEC(config->logger,
+                    "Creating CTF metadata decoder: "
+                    "clock-class-offset-s={}, "
+                    "clock-class-offset-ns={}",
+                    config->clkClsCfg.offsetSec, config->clkClsCfg.offsetNanoSec);
+
+    ctf_metadata_decoder *mdec = new ctf_metadata_decoder {config->logger};
+    mdec->scanner = ctf_scanner_alloc(mdec->logger);
+    if (!mdec->scanner) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger,
+                                     "Cannot allocate a metadata lexical scanner: "
+                                     "mdec-addr={}",
+                                     fmt::ptr(mdec));
+        goto error;
+    }
+
+    mdec->text = g_string_new(NULL);
+    if (!mdec->text) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger,
+                                     "Failed to allocate one GString: "
+                                     "mdec-addr={}",
+                                     fmt::ptr(mdec));
+        goto error;
+    }
+
+    mdec->bo = -1;
+    mdec->config = *config;
+    mdec->visitor = ctf_visitor_generate_ir_create(config);
+    if (!mdec->visitor) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger,
+                                     "Failed to create a CTF IR metadata AST visitor: "
+                                     "mdec-addr={}",
+                                     fmt::ptr(mdec));
+        goto error;
+    }
+
+    BT_CPPLOGD_SPEC(mdec->logger,
+                    "Creating CTF metadata decoder: "
+                    "clock-class-offset-s={}, "
+                    "clock-class-offset-ns={}, addr={}",
+                    config->clkClsCfg.offsetSec, config->clkClsCfg.offsetNanoSec, fmt::ptr(mdec));
+    goto end;
+
+error:
+    ctf_metadata_decoder_destroy(mdec);
+    mdec = NULL;
+
+end:
+    return ctf_metadata_decoder_up {mdec};
+}
+
+void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder *mdec)
+{
+    if (!mdec) {
+        return;
+    }
+
+    if (mdec->scanner) {
+        ctf_scanner_free(mdec->scanner);
+    }
+
+    if (mdec->text) {
+        g_string_free(mdec->text, TRUE);
+    }
+
+    BT_CPPLOGD_SPEC(mdec->logger, "Destroying CTF metadata decoder: addr={}", fmt::ptr(mdec));
+
+    delete mdec;
+}
+
+void ctf_metadata_decoder_deleter::operator()(ctf_metadata_decoder *decoder)
+{
+    ctf_metadata_decoder_destroy(decoder);
+}
+
+enum ctf_metadata_decoder_status
+ctf_metadata_decoder_append_content(struct ctf_metadata_decoder *mdec, FILE *fp)
+{
+    enum ctf_metadata_decoder_status status = CTF_METADATA_DECODER_STATUS_OK;
+    int ret;
+    char *buf = NULL;
+    bool close_fp = false;
+    long start_pos = -1;
+    bool is_packetized;
+
+    BT_ASSERT(mdec);
+    ret = ctf_metadata_decoder_is_packetized(fp, &is_packetized, &mdec->bo, mdec->logger);
+    if (ret) {
+        status = CTF_METADATA_DECODER_STATUS_ERROR;
+        goto end;
+    }
+
+    if (is_packetized) {
+        BT_CPPLOGI_SPEC(mdec->logger, "Metadata stream is packetized: mdec-addr={}",
+                        fmt::ptr(mdec));
+        ret = ctf_metadata_decoder_packetized_file_stream_to_buf(
+            fp, &buf, mdec->bo, &mdec->is_uuid_set, mdec->uuid, mdec->logger);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                mdec->logger,
+                "Cannot decode packetized metadata packets to metadata text: "
+                "mdec-addr={}, ret={}",
+                fmt::ptr(mdec), ret);
+            status = CTF_METADATA_DECODER_STATUS_ERROR;
+            goto end;
+        }
+
+        if (strlen(buf) == 0) {
+            /* An empty metadata packet is OK. */
+            goto end;
+        }
+
+        /* Convert the real file pointer to a memory file pointer */
+        fp = bt_fmemopen(buf, strlen(buf), "rb");
+        close_fp = true;
+        if (!fp) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger,
+                                         "Cannot memory-open metadata buffer: {}: "
+                                         "mdec-addr={}",
+                                         strerror(errno), fmt::ptr(mdec));
+            status = CTF_METADATA_DECODER_STATUS_ERROR;
+            goto end;
+        }
+    } else if (!mdec->has_checked_plaintext_signature) {
+        unsigned int major, minor;
+        ssize_t nr_items;
+        const long init_pos = ftell(fp);
+
+        BT_CPPLOGI_SPEC(mdec->logger, "Metadata stream is plain text: mdec-addr={}",
+                        fmt::ptr(mdec));
+
+        if (init_pos < 0) {
+            BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(mdec->logger, "Failed to get current file position",
+                                               ".");
+            status = CTF_METADATA_DECODER_STATUS_ERROR;
+            goto end;
+        }
+
+        /* Check text-only metadata header and version */
+        nr_items = fscanf(fp, "/* CTF %10u.%10u", &major, &minor);
+        if (nr_items < 2) {
+            BT_CPPLOGW_SPEC(
+                mdec->logger,
+                "Missing \"/* CTF major.minor\" signature in plain text metadata file stream: "
+                "mdec-addr={}",
+                fmt::ptr(mdec));
+        }
+
+        BT_CPPLOGI_SPEC(mdec->logger, "Found metadata stream version in signature: version={}.{}",
+                        major, minor);
+
+        if (!ctf_metadata_decoder_is_packet_version_valid(major, minor)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger,
+                                         "Invalid metadata version found in plain text signature: "
+                                         "version={}.{}, mdec-addr={}",
+                                         major, minor, fmt::ptr(mdec));
+            status = CTF_METADATA_DECODER_STATUS_INVAL_VERSION;
+            goto end;
+        }
+
+        if (fseek(fp, init_pos, SEEK_SET)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                mdec->logger,
+                "Cannot seek metadata file stream to initial position: {}: "
+                "mdec-addr={}",
+                strerror(errno), fmt::ptr(mdec));
+            status = CTF_METADATA_DECODER_STATUS_ERROR;
+            goto end;
+        }
+
+        mdec->has_checked_plaintext_signature = true;
+    }
+
+#if YYDEBUG
+    if (mdec->logger.wouldLogT()) {
+        yydebug = 1;
+    }
+#endif
+
+    /* Save the file's position: we'll seek back to append the plain text */
+    BT_ASSERT(fp);
+
+    if (mdec->config.keep_plain_text) {
+        start_pos = ftell(fp);
+    }
+
+    /* Append the metadata text content */
+    ret = ctf_scanner_append_ast(mdec->scanner, fp);
+    if (ret) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger,
+                                     "Cannot create the metadata AST out of the metadata text: "
+                                     "mdec-addr={}",
+                                     fmt::ptr(mdec));
+        status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+        goto end;
+    }
+
+    /* We know it's complete: append plain text */
+    if (mdec->config.keep_plain_text) {
+        BT_ASSERT(start_pos != -1);
+        ret = fseek(fp, start_pos, SEEK_SET);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger, "Failed to seek file: ret={}, mdec-addr={}",
+                                         ret, fmt::ptr(mdec));
+            status = CTF_METADATA_DECODER_STATUS_ERROR;
+            goto end;
+        }
+
+        ret = bt_common_append_file_content_to_g_string(mdec->text, fp);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger,
+                                         "Failed to append to current plain text: "
+                                         "ret={}, mdec-addr={}",
+                                         ret, fmt::ptr(mdec));
+            status = CTF_METADATA_DECODER_STATUS_ERROR;
+            goto end;
+        }
+    }
+
+    ret = ctf_visitor_semantic_check(0, &mdec->scanner->ast->root, mdec->logger);
+    if (ret) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger,
+                                     "Validation of the metadata semantics failed: "
+                                     "mdec-addr={}",
+                                     fmt::ptr(mdec));
+        status = CTF_METADATA_DECODER_STATUS_ERROR;
+        goto end;
+    }
+
+    if (mdec->config.create_trace_class) {
+        ret = ctf_visitor_generate_ir_visit_node(mdec->visitor.get(), &mdec->scanner->ast->root);
+        switch (ret) {
+        case 0:
+            /* Success */
+            break;
+        case -EINCOMPLETE:
+            BT_CPPLOGD_SPEC(mdec->logger,
+                            "While visiting metadata AST: incomplete data: "
+                            "mdec-addr={}",
+                            fmt::ptr(mdec));
+            status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+            goto end;
+        default:
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger,
+                                         "Failed to visit AST node to create CTF IR objects: "
+                                         "mdec-addr={}, ret={}",
+                                         fmt::ptr(mdec), ret);
+            status = CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR;
+            goto end;
+        }
+    }
+
+end:
+#if YYDEBUG
+    yydebug = 0;
+#endif
+
+    if (fp && close_fp) {
+        if (fclose(fp)) {
+            BT_CPPLOGE_SPEC(mdec->logger,
+                            "Cannot close metadata file stream: "
+                            "mdec-addr={}",
+                            fmt::ptr(mdec));
+        }
+    }
+
+    free(buf);
+
+    return status;
+}
+
+bt2::TraceClass::Shared ctf_metadata_decoder_get_ir_trace_class(struct ctf_metadata_decoder *mdec)
+{
+    BT_ASSERT_DBG(mdec);
+    BT_ASSERT_DBG(mdec->config.create_trace_class);
+    return ctf_visitor_generate_ir_get_ir_trace_class(mdec->visitor.get());
+}
+
+struct ctf_trace_class *
+ctf_metadata_decoder_borrow_ctf_trace_class(struct ctf_metadata_decoder *mdec)
+{
+    BT_ASSERT_DBG(mdec);
+    BT_ASSERT_DBG(mdec->config.create_trace_class);
+    return ctf_visitor_generate_ir_borrow_ctf_trace_class(mdec->visitor.get());
+}
+
+const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder *mdec)
+{
+    BT_ASSERT_DBG(mdec);
+    BT_ASSERT_DBG(mdec->config.keep_plain_text);
+    return mdec->text->str;
+}
+
+int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder *mdec)
+{
+    BT_ASSERT_DBG(mdec);
+    return mdec->bo;
+}
+
+int ctf_metadata_decoder_get_uuid(struct ctf_metadata_decoder *mdec, bt_uuid_t uuid)
+{
+    int ret = 0;
+
+    BT_ASSERT_DBG(mdec);
+
+    if (!mdec->is_uuid_set) {
+        ret = -1;
+        goto end;
+    }
+
+    bt_uuid_copy(uuid, mdec->uuid);
+
+end:
+    return ret;
+}
+
+static enum ctf_metadata_decoder_status find_uuid_in_trace_decl(struct ctf_metadata_decoder *mdec,
+                                                                struct ctf_node *trace_node,
+                                                                bt_uuid_t uuid)
+{
+    enum ctf_metadata_decoder_status status = CTF_METADATA_DECODER_STATUS_OK;
+    struct ctf_node *entry_node;
+    struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
+    char *left = NULL;
+
+    bt_list_for_each_entry (entry_node, decl_list, siblings) {
+        if (entry_node->type == NODE_CTF_EXPRESSION) {
+            int ret;
+
+            left = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.left);
+            if (!left) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger, "Cannot concatenate unary strings.");
+                status = CTF_METADATA_DECODER_STATUS_ERROR;
+                goto end;
+            }
+
+            if (strcmp(left, "uuid") == 0) {
+                ret =
+                    ctf_ast_get_unary_uuid(&entry_node->u.ctf_expression.right, uuid, mdec->logger);
+                if (ret) {
+                    BT_CPPLOGE_APPEND_CAUSE_SPEC(mdec->logger, "Invalid trace's `uuid` attribute.");
+                    status = CTF_METADATA_DECODER_STATUS_ERROR;
+                    goto end;
+                }
+
+                goto end;
+            }
+
+            g_free(left);
+            left = NULL;
+        }
+    }
+
+    status = CTF_METADATA_DECODER_STATUS_NONE;
+
+end:
+    g_free(left);
+    return status;
+}
+
+enum ctf_metadata_decoder_status
+ctf_metadata_decoder_get_trace_class_uuid(struct ctf_metadata_decoder *mdec, bt_uuid_t uuid)
+{
+    enum ctf_metadata_decoder_status status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+    struct ctf_node *root_node = &mdec->scanner->ast->root;
+    struct ctf_node *trace_node;
+
+    if (!root_node) {
+        status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+        goto end;
+    }
+
+    trace_node = bt_list_entry(root_node->u.root.trace.next, struct ctf_node, siblings);
+    if (!trace_node) {
+        status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+        goto end;
+    }
+
+    status = find_uuid_in_trace_decl(mdec, trace_node, uuid);
+
+end:
+    return status;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/decoder.hpp b/src/plugins/ctf/common/src/metadata/tsdl/decoder.hpp
new file mode 100644 (file)
index 0000000..b4dbce8
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef _METADATA_DECODER_H
+#define _METADATA_DECODER_H
+
+#include <stdio.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/uuid.h"
+#include "cpp-common/bt2/trace-ir.hpp"
+#include "cpp-common/bt2c/logging.hpp"
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+
+#include "../../../src/clk-cls-cfg.hpp"
+
+/* A CTF metadata decoder object */
+struct ctf_metadata_decoder;
+
+/* CTF metadata decoder status */
+enum ctf_metadata_decoder_status
+{
+    CTF_METADATA_DECODER_STATUS_OK = 0,
+    CTF_METADATA_DECODER_STATUS_NONE = 1,
+    CTF_METADATA_DECODER_STATUS_ERROR = -1,
+    CTF_METADATA_DECODER_STATUS_INCOMPLETE = -2,
+    CTF_METADATA_DECODER_STATUS_INVAL_VERSION = -3,
+    CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR = -4,
+};
+
+inline const char *format_as(ctf_metadata_decoder_status status) noexcept
+{
+    switch (status) {
+    case CTF_METADATA_DECODER_STATUS_OK:
+        return "CTF_METADATA_DECODER_STATUS_OK";
+
+    case CTF_METADATA_DECODER_STATUS_NONE:
+        return "CTF_METADATA_DECODER_STATUS_NONE";
+
+    case CTF_METADATA_DECODER_STATUS_ERROR:
+        return "CTF_METADATA_DECODER_STATUS_ERROR";
+
+    case CTF_METADATA_DECODER_STATUS_INCOMPLETE:
+        return "CTF_METADATA_DECODER_STATUS_INCOMPLETE";
+
+    case CTF_METADATA_DECODER_STATUS_INVAL_VERSION:
+        return "CTF_METADATA_DECODER_STATUS_INVAL_VERSION";
+
+    case CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR:
+        return "CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR";
+    }
+
+    bt_common_abort();
+}
+
+/* Decoding configuration */
+struct ctf_metadata_decoder_config
+{
+    explicit ctf_metadata_decoder_config(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/CTF/META/DECODER-CONFIG"}
+    {
+    }
+
+    bt2c::Logger logger;
+
+    /* Weak, used to create a bt_trace_class, if not nullptr. */
+    bt_self_component *self_comp = nullptr;
+
+    ctf::src::ClkClsCfg clkClsCfg;
+
+    /* True to create trace class objects */
+    bool create_trace_class = false;
+
+    /*
+     * True to keep the plain text when content is appended with
+     * ctf_metadata_decoder_append_content().
+     */
+    bool keep_plain_text = false;
+};
+
+struct ctf_metadata_decoder_deleter
+{
+    void operator()(struct ctf_metadata_decoder *decoder);
+};
+
+using ctf_metadata_decoder_up = std::unique_ptr<ctf_metadata_decoder, ctf_metadata_decoder_deleter>;
+
+/*
+ * Creates a CTF metadata decoder.
+ *
+ * Returns `NULL` on error.
+ */
+ctf_metadata_decoder_up
+ctf_metadata_decoder_create(const struct ctf_metadata_decoder_config *config);
+
+/*
+ * Destroys a CTF metadata decoder that you created with
+ * ctf_metadata_decoder_create().
+ */
+void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder *metadata_decoder);
+
+/*
+ * Appends content to the metadata decoder.
+ *
+ * This function reads the metadata from the current position of `fp`
+ * until the end of this file stream.
+ *
+ * The metadata can be packetized or not.
+ *
+ * The metadata chunk needs to be complete and lexically scannable, that
+ * is, zero or more complete top-level blocks. If it's incomplete, this
+ * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`. If this
+ * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`, then you
+ * need to call it again with the _same_ metadata and more to make it
+ * complete. For example:
+ *
+ *     First call:  event { name = hell
+ *     Second call: event { name = hello_world; ... };
+ *
+ * If everything goes as expected, this function returns
+ * `CTF_METADATA_DECODER_STATUS_OK`.
+ */
+enum ctf_metadata_decoder_status
+ctf_metadata_decoder_append_content(struct ctf_metadata_decoder *metadata_decoder, FILE *fp);
+
+/*
+ * Returns the trace IR trace class of this metadata decoder (new
+ * reference).
+ *
+ * Returns `NULL` if there's none yet or if the metadata decoder is not
+ * configured to create trace classes.
+ */
+bt2::TraceClass::Shared ctf_metadata_decoder_get_ir_trace_class(struct ctf_metadata_decoder *mdec);
+
+/*
+ * Returns the CTF IR trace class of this metadata decoder.
+ *
+ * Returns `NULL` if there's none yet or if the metadata decoder is not
+ * configured to create trace classes.
+ */
+struct ctf_trace_class *
+ctf_metadata_decoder_borrow_ctf_trace_class(struct ctf_metadata_decoder *mdec);
+
+/*
+ * Checks whether or not a given metadata file stream `fp` is
+ * packetized, setting `is_packetized` accordingly on success. On
+ * success, also sets `*byte_order` to the byte order of the first
+ * packet.
+ */
+int ctf_metadata_decoder_is_packetized(FILE *fp, bool *is_packetized, int *byte_order,
+                                       const bt2c::Logger& logger);
+
+/*
+ * Returns the byte order of the decoder's metadata stream as set by the
+ * last call to ctf_metadata_decoder_append_content().
+ *
+ * Returns -1 if unknown (plain text content).
+ */
+int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder *mdec);
+
+/*
+ * Returns the UUID of the decoder's metadata stream as set by the last
+ * call to ctf_metadata_decoder_append_content().
+ */
+int ctf_metadata_decoder_get_uuid(struct ctf_metadata_decoder *mdec, bt_uuid_t uuid);
+
+/*
+ * Returns the UUID of the decoder's trace class, if available.
+ *
+ * Returns:
+ *
+ * * `CTF_METADATA_DECODER_STATUS_OK`: success.
+ * * `CTF_METADATA_DECODER_STATUS_NONE`: no UUID.
+ * * `CTF_METADATA_DECODER_STATUS_INCOMPLETE`: missing metadata content.
+ */
+enum ctf_metadata_decoder_status
+ctf_metadata_decoder_get_trace_class_uuid(struct ctf_metadata_decoder *mdec, bt_uuid_t uuid);
+
+/*
+ * Returns the metadata decoder's current metadata text.
+ */
+const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder *mdec);
+
+static inline bool ctf_metadata_decoder_is_packet_version_valid(unsigned int major,
+                                                                unsigned int minor)
+{
+    return major == 1 && minor == 8;
+}
+
+#endif /* _METADATA_DECODER_H */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/lexer.lpp b/src/plugins/ctf/common/src/metadata/tsdl/lexer.lpp
new file mode 100644 (file)
index 0000000..826cca1
--- /dev/null
@@ -0,0 +1,119 @@
+%{
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Formal Lexer
+ */
+
+#include "plugins/ctf/common/src/metadata/tsdl/ast.hpp"
+#include "plugins/ctf/common/src/metadata/tsdl/logging.hpp"
+#include "plugins/ctf/common/src/metadata/tsdl/parser-wrap.hpp"
+#include "plugins/ctf/common/src/metadata/tsdl/scanner.hpp"
+
+#define YY_FATAL_ERROR(_msg)   BT_CPPLOGF_STR_SPEC(currentCtfScanner->logger, _msg)
+
+#define PARSE_INTEGER_LITERAL(base)                                    \
+       do {                                                            \
+               errno = 0;                                              \
+               yylval->ull = strtoull(yytext, NULL, base);             \
+               if (errno) {                                            \
+                       _BT_CPPLOGE_APPEND_CAUSE_LINENO(                \
+                               currentCtfScanner->logger,              \
+                               yylineno,                               \
+                               "Cannot parser constant integer: "      \
+                               "base={}, text=\"{}\"", base, yytext);  \
+                       return CTF_ERROR;                               \
+               }                                                       \
+       } while (0)
+
+extern thread_local const ctf_scanner *const currentCtfScanner;
+%}
+
+%x comment_ml comment_sl string_lit char_const
+%option reentrant yylineno noyywrap bison-bridge
+%option extra-type="struct ctf_scanner *"
+       /* bison-locations */
+INTEGER_SUFFIX                 (U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu)
+DIGIT                          [0-9]
+NONDIGIT                       [a-zA-Z_]
+HEXDIGIT                       [0-9A-Fa-f]
+OCTALDIGIT                     [0-7]
+UCHARLOWERCASE                 \\u{HEXDIGIT}{4}
+UCHARUPPERCASE                 \\U{HEXDIGIT}{8}
+ID_NONDIGIT                    {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}
+IDENTIFIER                     {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})*
+%%
+
+                               /*
+                                * Using start conditions to deal with comments
+                                * and strings.
+                                */
+
+"/*"                           BEGIN(comment_ml);
+<comment_ml>[^*\n]*            /* eat anything that's not a '*' */
+<comment_ml>"*"+[^*/\n]*       /* eat up '*'s not followed by '/'s */
+<comment_ml>\n
+<comment_ml>"*"+"/"            BEGIN(INITIAL);
+
+"//"[^\n]*\n                   /* skip comment */
+
+L?\"(\\.|[^\\"])*\"            { if (import_string(yyextra, yylval, yytext, '\"') < 0) return CTF_ERROR; else return CTF_STRING_LITERAL; }
+L?\'(\\.|[^\\'])*\'            { if (import_string(yyextra, yylval, yytext, '\'') < 0) return CTF_ERROR; else return CTF_CHARACTER_LITERAL; }
+
+"["                            return CTF_LSBRAC;
+"]"                            return CTF_RSBRAC;
+"("                            return CTF_LPAREN;
+")"                            return CTF_RPAREN;
+"{"                            return CTF_LBRAC;
+"}"                            return CTF_RBRAC;
+"->"                           return CTF_RARROW;
+"*"                            return CTF_STAR;
+"+"                            return CTF_PLUS;
+"-"                            return CTF_MINUS;
+"<"                            return CTF_LT;
+">"                            return CTF_GT;
+:=                             return CTF_TYPEASSIGN;
+:                              return CTF_COLON;
+;                              return CTF_SEMICOLON;
+"..."                          return CTF_DOTDOTDOT;
+"."                            return CTF_DOT;
+=                              return CTF_EQUAL;
+","                            return CTF_COMMA;
+align                          setstring(yyextra, yylval, yytext); return CTF_TOK_ALIGN;
+const                          setstring(yyextra, yylval, yytext); return CTF_CONST;
+char                           setstring(yyextra, yylval, yytext); return CTF_CHAR;
+clock                          setstring(yyextra, yylval, yytext); return CTF_CLOCK;
+double                         setstring(yyextra, yylval, yytext); return CTF_DOUBLE;
+enum                           setstring(yyextra, yylval, yytext); return CTF_ENUM;
+env                            setstring(yyextra, yylval, yytext); return CTF_ENV;
+event                          setstring(yyextra, yylval, yytext); return CTF_EVENT;
+floating_point                 setstring(yyextra, yylval, yytext); return CTF_FLOATING_POINT;
+float                          setstring(yyextra, yylval, yytext); return CTF_FLOAT;
+integer                                setstring(yyextra, yylval, yytext); return CTF_INTEGER;
+int                            setstring(yyextra, yylval, yytext); return CTF_INT;
+long                           setstring(yyextra, yylval, yytext); return CTF_LONG;
+short                          setstring(yyextra, yylval, yytext); return CTF_SHORT;
+signed                         setstring(yyextra, yylval, yytext); return CTF_SIGNED;
+stream                         setstring(yyextra, yylval, yytext); return CTF_STREAM;
+string                         setstring(yyextra, yylval, yytext); return CTF_STRING;
+struct                         setstring(yyextra, yylval, yytext); return CTF_STRUCT;
+trace                          setstring(yyextra, yylval, yytext); return CTF_TRACE;
+callsite                       setstring(yyextra, yylval, yytext); return CTF_CALLSITE;
+typealias                      setstring(yyextra, yylval, yytext); return CTF_TYPEALIAS;
+typedef                                setstring(yyextra, yylval, yytext); return CTF_TYPEDEF;
+unsigned                       setstring(yyextra, yylval, yytext); return CTF_UNSIGNED;
+variant                                setstring(yyextra, yylval, yytext); return CTF_VARIANT;
+void                           setstring(yyextra, yylval, yytext); return CTF_VOID;
+_Bool                          setstring(yyextra, yylval, yytext); return CTF_BOOL;
+_Complex                       setstring(yyextra, yylval, yytext); return CTF_COMPLEX;
+_Imaginary                     setstring(yyextra, yylval, yytext); return CTF_IMAGINARY;
+[1-9]{DIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(10); return CTF_INTEGER_LITERAL;
+0{OCTALDIGIT}*{INTEGER_SUFFIX}?        PARSE_INTEGER_LITERAL(8); return CTF_INTEGER_LITERAL;
+0[xX]{HEXDIGIT}+{INTEGER_SUFFIX}?      PARSE_INTEGER_LITERAL(16); return CTF_INTEGER_LITERAL;
+
+{IDENTIFIER}                   BT_CPPLOGT_SPEC(currentCtfScanner->logger, "Got identifier: id=\"{}\"", yytext); setstring(yyextra, yylval, yytext); if (is_type(yyextra, yytext)) return ID_TYPE; else return IDENTIFIER;
+[ \t\r\n]                      ; /* ignore */
+.                              _BT_CPPLOGE_APPEND_CAUSE_LINENO(currentCtfScanner->logger, yylineno, "Invalid character: char=\"{}\", val={:#02x}", isprint((unsigned char) yytext[0]) ? yytext[0] : '\0', yytext[0]); return CTF_ERROR;
+%%
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/logging.hpp b/src/plugins/ctf/common/src/metadata/tsdl/logging.hpp
new file mode 100644 (file)
index 0000000..ee019a3
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ */
+
+#ifndef CTF_METADATA_LOGGING_H
+#define CTF_METADATA_LOGGING_H
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/logging.hpp"
+
+#define _BT_CPPLOGT_LINENO(logger, _lineno, _msg, args...)                                         \
+    BT_CPPLOGT_SPEC((logger), "At line {} in metadata stream: " _msg, _lineno, ##args)
+
+#define _BT_CPPLOGW_LINENO(logger, _lineno, _msg, args...)                                         \
+    BT_CPPLOGW_SPEC((logger), "At line {} in metadata stream: " _msg, _lineno, ##args)
+
+#define _BT_CPPLOGE_LINENO(logger, _lineno, _msg, args...)                                         \
+    BT_CPPLOGE_SPEC((logger), "At line {} in metadata stream: " _msg, _lineno, ##args)
+
+#define _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, _lineno, _msg, args...)                            \
+    BT_CPPLOGE_APPEND_CAUSE_SPEC((logger), "At line {} in metadata stream: " _msg, _lineno, ##args)
+
+#endif /* CTF_METADATA_LOGGING_H */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/objstack.cpp b/src/plugins/ctf/common/src/metadata/tsdl/objstack.cpp
new file mode 100644 (file)
index 0000000..cb13f63
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Object Stack.
+ */
+
+#include "common/align.h"
+#include "common/list.h"
+#include "cpp-common/bt2c/logging.hpp"
+
+#include "objstack.hpp"
+
+#define OBJSTACK_ALIGN    8 /* Object stack alignment */
+#define OBJSTACK_INIT_LEN 128
+#define OBJSTACK_POISON   0xcc
+
+struct objstack
+{
+    explicit objstack(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/CTF/META/OBJSTACK"}
+    {
+    }
+
+    /* list of struct objstack_node */
+    bt_list_head head {};
+
+    bt2c::Logger logger;
+};
+
+struct objstack_node
+{
+    struct bt_list_head node;
+    size_t len;
+    size_t used_len;
+    char __attribute__((aligned(OBJSTACK_ALIGN))) data[];
+};
+
+objstack *objstack_create(const bt2c::Logger& parentLogger)
+{
+    struct objstack *objstack;
+    struct objstack_node *node;
+
+    objstack = new ::objstack {parentLogger};
+    node = (objstack_node *) calloc(sizeof(struct objstack_node) + OBJSTACK_INIT_LEN, sizeof(char));
+    if (!node) {
+        BT_CPPLOGE_STR_SPEC(objstack->logger, "Failed to allocate one object stack node.");
+        delete objstack;
+        return NULL;
+    }
+    BT_INIT_LIST_HEAD(&objstack->head);
+    bt_list_add_tail(&node->node, &objstack->head);
+    node->len = OBJSTACK_INIT_LEN;
+    return objstack;
+}
+
+static void objstack_node_free(struct objstack_node *node)
+{
+    size_t offset, len;
+    char *p;
+
+    if (!node)
+        return;
+    p = (char *) node;
+    len = sizeof(*node) + node->len;
+    for (offset = 0; offset < len; offset++)
+        p[offset] = OBJSTACK_POISON;
+    free(node);
+}
+
+void objstack_destroy(struct objstack *objstack)
+{
+    struct objstack_node *node, *p;
+
+    if (!objstack)
+        return;
+    bt_list_for_each_entry_safe (node, p, &objstack->head, node) {
+        bt_list_del(&node->node);
+        objstack_node_free(node);
+    }
+
+    delete objstack;
+}
+
+static struct objstack_node *objstack_append_node(struct objstack *objstack)
+{
+    struct objstack_node *last_node, *new_node;
+
+    /* Get last node */
+    last_node = bt_list_entry(objstack->head.prev, struct objstack_node, node);
+
+    /* Allocate new node with double of size of last node */
+    new_node = (objstack_node *) calloc(sizeof(struct objstack_node) + (last_node->len << 1),
+                                        sizeof(char));
+    if (!new_node) {
+        BT_CPPLOGE_STR_SPEC(objstack->logger, "Failed to allocate one object stack node.");
+        return NULL;
+    }
+    bt_list_add_tail(&new_node->node, &objstack->head);
+    new_node->len = last_node->len << 1;
+    return new_node;
+}
+
+void *objstack_alloc(struct objstack *objstack, size_t len)
+{
+    struct objstack_node *last_node;
+    void *p;
+
+    len = BT_ALIGN(len, OBJSTACK_ALIGN);
+
+    /* Get last node */
+    last_node = bt_list_entry(objstack->head.prev, struct objstack_node, node);
+    while (last_node->len - last_node->used_len < len) {
+        last_node = objstack_append_node(objstack);
+        if (!last_node) {
+            return NULL;
+        }
+    }
+    p = &last_node->data[last_node->used_len];
+    last_node->used_len += len;
+    return p;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/objstack.hpp b/src/plugins/ctf/common/src/metadata/tsdl/objstack.hpp
new file mode 100644 (file)
index 0000000..cc17d43
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Object Stack.
+ */
+
+#ifndef _OBJSTACK_H
+#define _OBJSTACK_H
+
+#include <cstddef>
+
+namespace bt2c {
+
+class Logger;
+
+} /* namespace bt2c */
+
+struct objstack *objstack_create(const bt2c::Logger& parentLogger);
+void objstack_destroy(struct objstack *objstack);
+
+/*
+ * Allocate len bytes of zeroed memory.
+ * Return NULL on error.
+ */
+void *objstack_alloc(struct objstack *objstack, size_t len);
+
+#endif /* _OBJSTACK_H */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/parser-wrap.hpp b/src/plugins/ctf/common/src/metadata/tsdl/parser-wrap.hpp
new file mode 100644 (file)
index 0000000..bfc0e90
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 EfficiOS Inc.
+ */
+
+#ifndef BABELTRACE_PLUGINS_CTF_COMMON_METADATA_PARSER_WRAP_H
+#define BABELTRACE_PLUGINS_CTF_COMMON_METADATA_PARSER_WRAP_H
+
+/*
+ * Small wrapper around the bison-generated parser.h to conditionally define
+ * YYDEBUG (and therefore the yydebug declaration).
+ */
+
+#include "logging/log-api.h"
+
+#if BT_LOG_ENABLED_TRACE
+#    define YYDEBUG 1
+#else
+#    define YYDEBUG 0
+#endif
+
+#define ALLOW_INCLUDE_PARSER_H
+#include "plugins/ctf/common/src/metadata/tsdl/parser.hpp"
+#undef ALLOW_INCLUDE_PARSER_H
+
+#endif
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/parser.ypp b/src/plugins/ctf/common/src/metadata/tsdl/parser.ypp
new file mode 100644 (file)
index 0000000..7ae25b6
--- /dev/null
@@ -0,0 +1,2617 @@
+%{
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Metadata Grammar.
+ */
+
+#include "plugins/ctf/common/src/metadata/tsdl/logging.hpp"
+
+#include "common/list.h"
+#include "common/assert.h"
+
+#include "plugins/ctf/common/src/metadata/tsdl/scanner.hpp"
+#include "plugins/ctf/common/src/metadata/tsdl/ast.hpp"
+#include "plugins/ctf/common/src/metadata/tsdl/objstack.hpp"
+#include "plugins/ctf/common/src/metadata/tsdl/parser-wrap.hpp"
+
+/*
+ * Avoid warning about "yynerrs" being unused, seen with bison 3.5.1 + clang 15
+ * on Ubuntu 20.04.
+ */
+BT_DIAG_IGNORE_UNUSED_BUT_SET_VARIABLE
+
+thread_local const ctf_scanner *currentCtfScanner;
+
+#define YYFPRINTF(_stream, _fmt, args...)                                                          \
+    do {                                                                                           \
+        int size = snprintf(NULL, 0, (_fmt), ##args);                                              \
+        std::string str(size, '\0');                                                               \
+        int written = snprintf(&str[0], size + 1, (_fmt), ##args);                                 \
+        BT_ASSERT(size == written);                                                                \
+        BT_CPPLOGT_STR_SPEC(currentCtfScanner->logger, str.c_str());                               \
+    } while (0)
+
+/* Join two lists, put "add" at the end of "head".  */
+static inline void
+_bt_list_splice_tail (struct bt_list_head *add, struct bt_list_head *head)
+{
+       /* Do nothing if the list which gets added is empty.  */
+       if (add != add->next) {
+               add->next->prev = head->prev;
+               add->prev->next = head;
+               head->prev->next = add->next;
+               head->prev = add->prev;
+       }
+}
+
+int yylex(union YYSTYPE *yyval, yyscan_t yyscanner);
+int yylex_init_extra(struct ctf_scanner *scanner, yyscan_t * ptr_yy_globals);
+int yylex_destroy(yyscan_t yyscanner);
+void yyrestart(FILE * in_str, yyscan_t yyscanner);
+int yyget_lineno(yyscan_t yyscanner);
+char *yyget_text(yyscan_t yyscanner);
+
+/*
+ * Static node for out of memory errors. Only "type" is used. lineno is
+ * always left at 0. The rest of the node content can be overwritten,
+ * but is never used.
+ */
+static struct ctf_node error_node = {
+       .parent = nullptr,
+       .siblings = {},
+       .tmp_head = {},
+       .lineno = 0,
+       .visited = 0,
+       .type = NODE_ERROR,
+};
+
+const char *node_type(struct ctf_node *node)
+{
+       switch (node->type) {
+#define ENTRY(S) case S: return #S;
+       FOREACH_CTF_NODES(ENTRY)
+#undef ENTRY
+       };
+
+       bt_common_abort();
+}
+
+void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src)
+{
+       lvalp->s = (char *) objstack_alloc(scanner->objstack, strlen(src) + 1);
+       strcpy(lvalp->s, src);
+}
+
+static
+int str_check(size_t str_len, size_t offset, size_t len)
+{
+       /* check overflow */
+       if (offset + len < offset)
+               return -1;
+       if (offset + len > str_len)
+               return -1;
+       return 0;
+}
+
+static
+int bt_isodigit(int c)
+{
+       switch (c) {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static
+int parse_base_sequence(const char *src, size_t len, size_t pos,
+               char *buffer, size_t *buf_len, int base)
+{
+       const size_t max_char = 3;
+       int nr_char = 0;
+
+       while (!str_check(len, pos, 1) && nr_char < max_char) {
+               char c = src[pos++];
+
+               if (base == 8) {
+                       if (bt_isodigit(c))
+                               buffer[nr_char++] = c;
+                       else
+                               break;
+               } else if (base == 16) {
+                       if (isxdigit(c))
+                               buffer[nr_char++] = c;
+                       else
+                               break;
+
+               } else {
+                       /* Unsupported base */
+                       return -1;
+               }
+       }
+       BT_ASSERT_DBG(nr_char > 0);
+       buffer[nr_char] = '\0';
+       *buf_len = nr_char;
+       return 0;
+}
+
+static
+int import_basic_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
+               size_t len, const char *src, char delim)
+{
+       size_t pos = 0, dpos = 0;
+
+       if (str_check(len, pos, 1))
+               return -1;
+       if (src[pos++] != delim)
+               return -1;
+
+       while (src[pos] != delim) {
+               char c;
+
+               if (str_check(len, pos, 1))
+                       return -1;
+               c = src[pos++];
+               if (c == '\\') {
+                       if (str_check(len, pos, 1))
+                               return -1;
+                       c = src[pos++];
+
+                       switch (c) {
+                       case 'a':
+                               c = '\a';
+                               break;
+                       case 'b':
+                               c = '\b';
+                               break;
+                       case 'f':
+                               c = '\f';
+                               break;
+                       case 'n':
+                               c = '\n';
+                               break;
+                       case 'r':
+                               c = '\r';
+                               break;
+                       case 't':
+                               c = '\t';
+                               break;
+                       case 'v':
+                               c = '\v';
+                               break;
+                       case '\\':
+                               c = '\\';
+                               break;
+                       case '\'':
+                               c = '\'';
+                               break;
+                       case '\"':
+                               c = '\"';
+                               break;
+                       case '?':
+                               c = '?';
+                               break;
+                       case '0':
+                       case '1':
+                       case '2':
+                       case '3':
+                       case '4':
+                       case '5':
+                       case '6':
+                       case '7':
+                       {
+                               char oct_buffer[4];
+                               size_t oct_len;
+
+                               if (parse_base_sequence(src, len, pos - 1,
+                                               oct_buffer, &oct_len, 8))
+                                       return -1;
+                               c = strtoul(&oct_buffer[0], NULL, 8);
+                               pos += oct_len - 1;
+                               break;
+                       }
+                       case 'x':
+                       {
+                               char hex_buffer[4];
+                               size_t hex_len;
+
+                               if (parse_base_sequence(src, len, pos,
+                                               hex_buffer, &hex_len, 16))
+                                       return -1;
+                               c = strtoul(&hex_buffer[0], NULL, 16);
+                               pos += hex_len;
+                               break;
+                       }
+                       default:
+                               return -1;
+                       }
+               }
+               if (str_check(len, dpos, 1))
+                       return -1;
+               lvalp->s[dpos++] = c;
+       }
+
+       if (str_check(len, dpos, 1))
+               return -1;
+       lvalp->s[dpos++] = '\0';
+
+       if (str_check(len, pos, 1))
+               return -1;
+       if (src[pos++] != delim)
+               return -1;
+
+       if (str_check(len, pos, 1))
+               return -1;
+       if (src[pos] != '\0')
+               return -1;
+       return 0;
+}
+
+int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
+               const char *src, char delim)
+{
+       size_t len;
+
+       len = strlen(src) + 1;
+       lvalp->s = (char *) objstack_alloc(scanner->objstack, len);
+       if (src[0] == 'L') {
+               // TODO: import wide string
+               _BT_CPPLOGE_APPEND_CAUSE_LINENO(currentCtfScanner->logger,
+                       yyget_lineno(scanner),
+                       "wide characters are not supported as of this version: "
+                       "scanner-addr={}", fmt::ptr(scanner));
+               return -1;
+       } else {
+               return import_basic_string(scanner, lvalp, len, src, delim);
+       }
+}
+
+static void init_scope(struct ctf_scanner_scope *scope,
+                      struct ctf_scanner_scope *parent)
+{
+       scope->parent = parent;
+       scope->classes = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                            NULL, NULL);
+}
+
+static void finalize_scope(struct ctf_scanner_scope *scope)
+{
+       g_hash_table_destroy(scope->classes);
+}
+
+static void push_scope(struct ctf_scanner *scanner)
+{
+       struct ctf_scanner_scope *ns;
+
+       BT_CPPLOGT_SPEC(currentCtfScanner->logger,
+               "Pushing scope: scanner-addr={}", fmt::ptr(scanner));
+       ns = (ctf_scanner_scope *) malloc(sizeof(struct ctf_scanner_scope));
+       init_scope(ns, scanner->cs);
+       scanner->cs = ns;
+}
+
+static void pop_scope(struct ctf_scanner *scanner)
+{
+       struct ctf_scanner_scope *os;
+
+       BT_CPPLOGT_SPEC(currentCtfScanner->logger,
+               "Popping scope: scanner-addr={}", fmt::ptr(scanner));
+       os = scanner->cs;
+       scanner->cs = os->parent;
+       finalize_scope(os);
+       free(os);
+}
+
+static int lookup_type(struct ctf_scanner_scope *s, const char *id)
+{
+       int ret;
+
+       ret = GPOINTER_TO_INT(g_hash_table_lookup(s->classes, id));
+       BT_CPPLOGT_SPEC(currentCtfScanner->logger,
+               "Looked up type: scanner-addr={}, id=\"{}\", ret={}",
+               fmt::ptr(s), id, ret);
+       return ret;
+}
+
+int is_type(struct ctf_scanner *scanner, const char *id)
+{
+       struct ctf_scanner_scope *it;
+       int ret = 0;
+
+       for (it = scanner->cs; it; it = it->parent) {
+               if (lookup_type(it, id)) {
+                       ret = 1;
+                       break;
+               }
+       }
+       BT_CPPLOGT_SPEC(currentCtfScanner->logger,
+               "Found if ID is type: scanner-addr={}, id=\"{}\", ret={}",
+               fmt::ptr(scanner), id, ret);
+       return ret;
+}
+
+static void add_type(struct ctf_scanner *scanner, char *id)
+{
+       BT_CPPLOGT_SPEC(currentCtfScanner->logger,
+               "Adding type: scanner-addr={}, id=\"{}\"", fmt::ptr(scanner),
+               id);
+       if (lookup_type(scanner->cs, id))
+               return;
+       g_hash_table_insert(scanner->cs->classes, id, id);
+}
+
+static struct ctf_node *make_node(struct ctf_scanner *scanner,
+                                 enum node_type type)
+{
+       struct ctf_node *node;
+
+       node = (ctf_node *) objstack_alloc(scanner->objstack, sizeof(*node));
+       if (!node) {
+               _BT_CPPLOGE_APPEND_CAUSE_LINENO(currentCtfScanner->logger,
+                       yyget_lineno(scanner->scanner),
+                       "failed to allocate one stack entry: "
+                       "scanner-addr={}", fmt::ptr(scanner));
+               return &error_node;
+       }
+       node->type = type;
+       node->lineno = yyget_lineno(scanner->scanner);
+       BT_INIT_LIST_HEAD(&node->tmp_head);
+       bt_list_add(&node->siblings, &node->tmp_head);
+
+       switch (type) {
+       case NODE_ROOT:
+               node->type = NODE_ERROR;
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "Trying to create root node: scanner-addr={}",
+                       fmt::ptr(scanner));
+               break;
+       case NODE_EVENT:
+               BT_INIT_LIST_HEAD(&node->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               BT_INIT_LIST_HEAD(&node->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               BT_INIT_LIST_HEAD(&node->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               BT_INIT_LIST_HEAD(&node->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               BT_INIT_LIST_HEAD(&node->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               BT_INIT_LIST_HEAD(&node->u.callsite.declaration_list);
+               break;
+       case NODE_CTF_EXPRESSION:
+               BT_INIT_LIST_HEAD(&node->u.ctf_expression.left);
+               BT_INIT_LIST_HEAD(&node->u.ctf_expression.right);
+               break;
+       case NODE_UNARY_EXPRESSION:
+               break;
+       case NODE_TYPEDEF:
+               BT_INIT_LIST_HEAD(&node->u.field_class_def.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               BT_INIT_LIST_HEAD(&node->u.field_class_alias_target.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               BT_INIT_LIST_HEAD(&node->u.field_class_alias_name.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS:
+               break;
+       case NODE_TYPE_SPECIFIER:
+               break;
+       case NODE_TYPE_SPECIFIER_LIST:
+               BT_INIT_LIST_HEAD(&node->u.field_class_specifier_list.head);
+               break;
+       case NODE_POINTER:
+               break;
+       case NODE_TYPE_DECLARATOR:
+               BT_INIT_LIST_HEAD(&node->u.field_class_declarator.pointers);
+               break;
+       case NODE_FLOATING_POINT:
+               BT_INIT_LIST_HEAD(&node->u.floating_point.expressions);
+               break;
+       case NODE_INTEGER:
+               BT_INIT_LIST_HEAD(&node->u.integer.expressions);
+               break;
+       case NODE_STRING:
+               BT_INIT_LIST_HEAD(&node->u.string.expressions);
+               break;
+       case NODE_ENUMERATOR:
+               BT_INIT_LIST_HEAD(&node->u.enumerator.values);
+               break;
+       case NODE_ENUM:
+               BT_INIT_LIST_HEAD(&node->u._enum.enumerator_list);
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               BT_INIT_LIST_HEAD(&node->u.struct_or_variant_declaration.field_class_declarators);
+               break;
+       case NODE_VARIANT:
+               BT_INIT_LIST_HEAD(&node->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               BT_INIT_LIST_HEAD(&node->u._struct.declaration_list);
+               BT_INIT_LIST_HEAD(&node->u._struct.min_align);
+               break;
+       case NODE_UNKNOWN:
+       default:
+               node->type = NODE_ERROR;
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "Unknown node type: scanner-addr={}, node-type={}",
+                       fmt::ptr(scanner), type);
+               break;
+       }
+
+       return node;
+}
+
+static int reparent_ctf_expression(struct ctf_node *node,
+                                  struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_EVENT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_FLOATING_POINT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.floating_point.expressions);
+               break;
+       case NODE_INTEGER:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.integer.expressions);
+               break;
+       case NODE_STRING:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.string.expressions);
+               break;
+
+       case NODE_ROOT:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "Unknown node type: node-type={}", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_typedef(struct ctf_node *node, struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_ROOT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
+               break;
+       case NODE_EVENT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_VARIANT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
+               break;
+
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "Unknown node type: node-type={}", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_field_class_alias(struct ctf_node *node, struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_ROOT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
+               break;
+       case NODE_EVENT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_VARIANT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
+               break;
+
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "Unknown node type: node-type={}", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_field_class_specifier(struct ctf_node *node,
+                                  struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_TYPE_SPECIFIER_LIST:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_specifier_list.head);
+               break;
+
+       case NODE_TYPE_SPECIFIER:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_TYPEALIAS:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_POINTER:
+       case NODE_ENUMERATOR:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "Unknown node type: node-type={}", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_field_class_specifier_list(struct ctf_node *node,
+                                       struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_ROOT:
+               bt_list_add_tail(&node->siblings, &parent->u.root.declaration_list);
+               break;
+       case NODE_EVENT:
+               bt_list_add_tail(&node->siblings, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               bt_list_add_tail(&node->siblings, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               bt_list_add_tail(&node->siblings, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               bt_list_add_tail(&node->siblings, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               bt_list_add_tail(&node->siblings, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               bt_list_add_tail(&node->siblings, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_VARIANT:
+               bt_list_add_tail(&node->siblings, &parent->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               bt_list_add_tail(&node->siblings, &parent->u._struct.declaration_list);
+               break;
+       case NODE_TYPEDEF:
+               parent->u.field_class_def.field_class_specifier_list = node;
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               parent->u.field_class_alias_target.field_class_specifier_list = node;
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               parent->u.field_class_alias_name.field_class_specifier_list = node;
+               break;
+       case NODE_ENUM:
+               parent->u._enum.container_field_class = node;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               parent->u.struct_or_variant_declaration.field_class_specifier_list = node;
+               break;
+       case NODE_TYPE_DECLARATOR:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPEALIAS:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_POINTER:
+       case NODE_ENUMERATOR:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "Unknown node type: node-type={}", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_field_class_declarator(struct ctf_node *node,
+                                   struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_TYPE_DECLARATOR:
+               parent->u.field_class_declarator.type = TYPEDEC_NESTED;
+               parent->u.field_class_declarator.u.nested.field_class_declarator = node;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.struct_or_variant_declaration.field_class_declarators);
+               break;
+       case NODE_TYPEDEF:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_def.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_target.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_name.field_class_declarators);
+               break;
+
+       case NODE_ROOT:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       case NODE_TYPEALIAS:
+       case NODE_ENUM:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_ENUMERATOR:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "Unknown node type: node-type={}", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*
+ * set_parent_node
+ *
+ * Link node to parent. Returns 0 on success, -EPERM if it is not permitted to
+ * create the link declared by the input, -ENOENT if node or parent is NULL,
+ * -EINVAL if there is an internal structure problem.
+ */
+static int set_parent_node(struct ctf_node *node,
+                        struct ctf_node *parent)
+{
+       if (!node || !parent)
+               return -ENOENT;
+
+       /* Note: Linking to parent will be done only by an external visitor */
+
+       switch (node->type) {
+       case NODE_ROOT:
+               BT_CPPLOGE_STR_SPEC(currentCtfScanner->logger,
+                       "Trying to reparent root node.");
+               return -EINVAL;
+
+       case NODE_EVENT:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.event);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_STREAM:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.stream);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_ENV:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.env);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_TRACE:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.trace);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_CLOCK:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.clock);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_CALLSITE:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.callsite);
+               } else {
+                       return -EPERM;
+               }
+               break;
+
+       case NODE_CTF_EXPRESSION:
+               return reparent_ctf_expression(node, parent);
+       case NODE_UNARY_EXPRESSION:
+               if (parent->type == NODE_TYPE_DECLARATOR)
+                       parent->u.field_class_declarator.bitfield_len = node;
+               else
+                       return -EPERM;
+               break;
+
+       case NODE_TYPEDEF:
+               return reparent_typedef(node, parent);
+       case NODE_TYPEALIAS_TARGET:
+               if (parent->type == NODE_TYPEALIAS)
+                       parent->u.field_class_alias.target = node;
+               else
+                       return -EINVAL;
+               /* fall-through */
+       case NODE_TYPEALIAS_ALIAS:
+               if (parent->type == NODE_TYPEALIAS)
+                       parent->u.field_class_alias.alias = node;
+               else
+                       return -EINVAL;
+               /* fall-through */
+       case NODE_TYPEALIAS:
+               return reparent_field_class_alias(node, parent);
+
+       case NODE_POINTER:
+               if (parent->type == NODE_TYPE_DECLARATOR) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_declarator.pointers);
+               } else
+                       return -EPERM;
+               break;
+       case NODE_TYPE_DECLARATOR:
+               return reparent_field_class_declarator(node, parent);
+
+       case NODE_TYPE_SPECIFIER_LIST:
+               return reparent_field_class_specifier_list(node, parent);
+
+       case NODE_TYPE_SPECIFIER:
+               return reparent_field_class_specifier(node, parent);
+
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUM:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+               return -EINVAL; /* Dealt with internally within grammar */
+
+       case NODE_ENUMERATOR:
+               if (parent->type == NODE_ENUM) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u._enum.enumerator_list);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               switch (parent->type) {
+               case NODE_STRUCT:
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
+                       break;
+               case NODE_VARIANT:
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "Unknown node type: node-type={}", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static
+void yyerror(struct ctf_scanner *scanner, yyscan_t yyscanner, const char *str)
+{
+       _BT_CPPLOGE_APPEND_CAUSE_LINENO(currentCtfScanner->logger,
+               yyget_lineno(scanner->scanner),
+               "{}: token=\"{}\"", str, yyget_text(scanner->scanner));
+}
+
+#define reparent_error(scanner, str)                           \
+do {                                                           \
+       yyerror(scanner, scanner->scanner, YY_("reparent_error: " str)); \
+       YYERROR;                                                \
+} while (0)
+
+static struct ctf_ast *ctf_ast_alloc(struct ctf_scanner *scanner)
+{
+       struct ctf_ast *ast;
+
+       ast = (ctf_ast *) objstack_alloc(scanner->objstack, sizeof(*ast));
+       if (!ast)
+               return NULL;
+       ast->root.type = NODE_ROOT;
+       BT_INIT_LIST_HEAD(&ast->root.tmp_head);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.declaration_list);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.trace);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.env);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.stream);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.event);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.clock);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.callsite);
+       return ast;
+}
+
+int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input)
+{
+       /* Start processing new stream */
+       struct ClearCurrentCtfScanner {
+               ~ClearCurrentCtfScanner() {
+                       currentCtfScanner = nullptr;
+               }
+       } clearMoiLa;
+
+       currentCtfScanner = scanner;
+       yyrestart(input, scanner->scanner);
+       return yyparse(scanner, scanner->scanner);
+}
+
+struct ctf_scanner *ctf_scanner_alloc(const bt2c::Logger &parentLogger)
+{
+       ctf_scanner *scanner = new ctf_scanner {parentLogger};
+       int ret = yylex_init_extra(scanner, &scanner->scanner);
+       if (ret) {
+               BT_CPPLOGE_SPEC(scanner->logger, "yylex_init_extra() failed: ret={}", ret);
+               goto cleanup_scanner;
+       }
+       scanner->objstack = objstack_create(scanner->logger);
+       if (!scanner->objstack)
+               goto cleanup_lexer;
+       scanner->ast = ctf_ast_alloc(scanner);
+       if (!scanner->ast)
+               goto cleanup_objstack;
+       init_scope(&scanner->root_scope, NULL);
+       scanner->cs = &scanner->root_scope;
+
+       return scanner;
+
+cleanup_objstack:
+       objstack_destroy(scanner->objstack);
+cleanup_lexer:
+       ret = yylex_destroy(scanner->scanner);
+       if (!ret)
+               BT_CPPLOGE_SPEC(scanner->logger, "yylex_destroy() failed: scanner-addr={}, ret={}",
+                       fmt::ptr(scanner), ret);
+cleanup_scanner:
+       delete scanner;
+       return NULL;
+}
+
+void ctf_scanner_free(struct ctf_scanner *scanner)
+{
+       int ret;
+
+       if (!scanner)
+               return;
+
+       struct ctf_scanner_scope *scope = scanner->cs;
+
+       do {
+               struct ctf_scanner_scope *parent = scope->parent;
+               finalize_scope(scope);
+
+               /*
+                * The root scope is allocated within the ctf_scanner structure,
+                * do doesn't need freeing.  All others are allocated on their
+                * own.
+                */
+               if (scope != &scanner->root_scope)
+                       free(scope);
+
+               scope = parent;
+       } while (scope);
+
+       objstack_destroy(scanner->objstack);
+       ret = yylex_destroy(scanner->scanner);
+       if (ret)
+               BT_CPPLOGE_SPEC(currentCtfScanner->logger, "yylex_destroy() failed: scanner-addr={}, ret={}",
+                       fmt::ptr(scanner), ret);
+       delete scanner;
+}
+
+/*
+ * The bison-provided version of strlen (yystrlen) generates a benign
+ * -Wnull-dereference warning.  That version is used when building on cygwin,
+ * for example, but you can also enable it by hand (to test) by removing the
+ * preprocessor conditional around it.
+ *
+ * Define yystrlen such that it will always use strlen.  As far as we know,
+ * strlen provided by all the platforms we use is reliable.
+ */
+#define yystrlen strlen
+
+%}
+
+/*
+ * This ends up in parser.h and makes sure those who want to include it pass
+ * through parser-wrap.h.
+ */
+%code requires {
+#ifndef ALLOW_INCLUDE_PARSER_H
+# error "Don't include parser.h directly, include parser-wrap.h instead."
+#endif
+
+#include "plugins/ctf/common/src/metadata/tsdl/scanner.hpp"
+}
+
+%code provides {
+       void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src);
+
+       int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src, char delim);
+}
+
+%define api.pure
+       /* %locations */
+%error-verbose
+%parse-param {struct ctf_scanner *scanner}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+/*
+ * Expect two shift-reduce conflicts. Caused by enum name-opt : type {}
+ * vs struct { int :value; } (unnamed bit-field). The default is to
+ * shift, so whenever we encounter an enumeration, we are doing the
+ * proper thing (shift). It is illegal to declare an enumeration
+ * "bit-field", so it is OK if this situation ends up in a parsing
+ * error.
+ */
+%expect 2
+%start file
+%token CTF_INTEGER_LITERAL CTF_STRING_LITERAL CTF_CHARACTER_LITERAL CTF_LSBRAC CTF_RSBRAC CTF_LPAREN CTF_RPAREN CTF_LBRAC CTF_RBRAC CTF_RARROW CTF_STAR CTF_PLUS CTF_MINUS CTF_LT CTF_GT CTF_TYPEASSIGN CTF_COLON CTF_SEMICOLON CTF_DOTDOTDOT CTF_DOT CTF_EQUAL CTF_COMMA CTF_CONST CTF_CHAR CTF_DOUBLE CTF_ENUM CTF_ENV CTF_EVENT CTF_FLOATING_POINT CTF_FLOAT CTF_INTEGER CTF_INT CTF_LONG CTF_SHORT CTF_SIGNED CTF_STREAM CTF_STRING CTF_STRUCT CTF_TRACE CTF_CALLSITE CTF_CLOCK CTF_TYPEALIAS CTF_TYPEDEF CTF_UNSIGNED CTF_VARIANT CTF_VOID CTF_BOOL CTF_COMPLEX CTF_IMAGINARY CTF_TOK_ALIGN
+%token <s> IDENTIFIER ID_TYPE
+%token CTF_ERROR
+%union
+{
+       long long ll;
+       unsigned long long ull;
+       char c;
+       char *s;
+       struct ctf_node *n;
+}
+
+%type <s> CTF_STRING_LITERAL CTF_CHARACTER_LITERAL
+
+%type <s> keywords
+
+%type <ull> CTF_INTEGER_LITERAL
+%type <n> postfix_expression unary_expression unary_expression_or_range
+
+%type <n> declaration
+%type <n> event_declaration
+%type <n> stream_declaration
+%type <n> env_declaration
+%type <n> trace_declaration
+%type <n> clock_declaration
+%type <n> callsite_declaration
+%type <n> integer_declaration_specifiers
+%type <n> declaration_specifiers
+%type <n> alias_declaration_specifiers
+
+%type <n> field_class_declarator_list
+%type <n> integer_field_class_specifier
+%type <n> field_class_specifier
+%type <n> struct_class_specifier
+%type <n> variant_field_class_specifier
+%type <n> enum_field_class_specifier
+%type <n> struct_or_variant_declaration_list
+%type <n> struct_or_variant_declaration
+%type <n> struct_or_variant_declarator_list
+%type <n> struct_or_variant_declarator
+%type <n> enumerator_list
+%type <n> enumerator
+%type <n> abstract_declarator_list
+%type <n> abstract_declarator
+%type <n> direct_abstract_declarator
+%type <n> alias_abstract_declarator_list
+%type <n> alias_abstract_declarator
+%type <n> direct_alias_abstract_declarator
+%type <n> declarator
+%type <n> direct_declarator
+%type <n> field_class_declarator
+%type <n> direct_field_class_declarator
+%type <n> pointer
+%type <n> ctf_assignment_expression_list
+%type <n> ctf_assignment_expression
+
+%%
+
+file:
+               declaration
+               {
+                       if (set_parent_node($1, &ctf_scanner_get_ast(scanner)->root))
+                               reparent_error(scanner, "error reparenting to root");
+               }
+       |       file declaration
+               {
+                       if (set_parent_node($2, &ctf_scanner_get_ast(scanner)->root))
+                               reparent_error(scanner, "error reparenting to root");
+               }
+       ;
+
+keywords:
+               CTF_VOID
+               {       $$ = yylval.s;          }
+       |       CTF_CHAR
+               {       $$ = yylval.s;          }
+       |       CTF_SHORT
+               {       $$ = yylval.s;          }
+       |       CTF_INT
+               {       $$ = yylval.s;          }
+       |       CTF_LONG
+               {       $$ = yylval.s;          }
+       |       CTF_FLOAT
+               {       $$ = yylval.s;          }
+       |       CTF_DOUBLE
+               {       $$ = yylval.s;          }
+       |       CTF_SIGNED
+               {       $$ = yylval.s;          }
+       |       CTF_UNSIGNED
+               {       $$ = yylval.s;          }
+       |       CTF_BOOL
+               {       $$ = yylval.s;          }
+       |       CTF_COMPLEX
+               {       $$ = yylval.s;          }
+       |       CTF_IMAGINARY
+               {       $$ = yylval.s;          }
+       |       CTF_FLOATING_POINT
+               {       $$ = yylval.s;          }
+       |       CTF_INTEGER
+               {       $$ = yylval.s;          }
+       |       CTF_STRING
+               {       $$ = yylval.s;          }
+       |       CTF_ENUM
+               {       $$ = yylval.s;          }
+       |       CTF_VARIANT
+               {       $$ = yylval.s;          }
+       |       CTF_STRUCT
+               {       $$ = yylval.s;          }
+       |       CTF_CONST
+               {       $$ = yylval.s;          }
+       |       CTF_TYPEDEF
+               {       $$ = yylval.s;          }
+       |       CTF_EVENT
+               {       $$ = yylval.s;          }
+       |       CTF_STREAM
+               {       $$ = yylval.s;          }
+       |       CTF_ENV
+               {       $$ = yylval.s;          }
+       |       CTF_TRACE
+               {       $$ = yylval.s;          }
+       |       CTF_CLOCK
+               {       $$ = yylval.s;          }
+       |       CTF_CALLSITE
+               {       $$ = yylval.s;          }
+       |       CTF_TOK_ALIGN
+               {       $$ = yylval.s;          }
+       ;
+
+
+/* 2: Phrase structure grammar */
+
+postfix_expression:
+               IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+               }
+       |       keywords
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+               }
+       |       CTF_INTEGER_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_UNSIGNED_CONSTANT;
+                       $$->u.unary_expression.u.unsigned_constant = $1;
+               }
+       |       CTF_STRING_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = $1;
+               }
+       |       CTF_CHARACTER_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = $1;
+               }
+       |       CTF_LPAREN unary_expression CTF_RPAREN
+               {
+                       $$ = $2;
+               }
+       |       postfix_expression CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_SBRAC;
+                       $$->u.unary_expression.u.sbrac_exp = $3;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_DOT IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_DOTLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_DOT ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_DOTLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_DOT keywords
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_DOTLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_RARROW IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_ARROWLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_RARROW ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_ARROWLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+unary_expression:
+               postfix_expression
+               {       $$ = $1;                                }
+       |       CTF_PLUS postfix_expression
+               {
+                       $$ = $2;
+                       if ($$->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT
+                               && $$->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
+                               reparent_error(scanner, "expecting numeric constant");
+                       }
+               }
+       |       CTF_MINUS postfix_expression
+               {
+                       $$ = $2;
+                       if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) {
+                               $$->u.unary_expression.type = UNARY_SIGNED_CONSTANT;
+                               $$->u.unary_expression.u.signed_constant =
+                                       -($$->u.unary_expression.u.unsigned_constant);
+                       } else if ($$->u.unary_expression.type == UNARY_SIGNED_CONSTANT) {
+                               $$->u.unary_expression.u.signed_constant =
+                                       -($$->u.unary_expression.u.signed_constant);
+                       } else {
+                               reparent_error(scanner, "expecting numeric constant");
+                       }
+               }
+       ;
+
+unary_expression_or_range:
+               unary_expression CTF_DOTDOTDOT unary_expression
+               {
+                       $$ = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->tmp_head);
+                       $3->u.unary_expression.link = UNARY_DOTDOTDOT;
+               }
+       |       unary_expression
+               {       $$ = $1;                }
+       ;
+
+/* 2.2: Declarations */
+
+declaration:
+               declaration_specifiers CTF_SEMICOLON
+               {       $$ = $1;        }
+       |       event_declaration
+               {       $$ = $1;        }
+       |       stream_declaration
+               {       $$ = $1;        }
+       |       env_declaration
+               {       $$ = $1;        }
+       |       trace_declaration
+               {       $$ = $1;        }
+       |       clock_declaration
+               {       $$ = $1;        }
+       |       callsite_declaration
+               {       $$ = $1;        }
+       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEALIAS);
+                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
+                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
+               }
+       ;
+
+event_declaration:
+               event_declaration_begin event_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_EVENT);
+               }
+       |       event_declaration_begin ctf_assignment_expression_list event_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_EVENT);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "event_declaration");
+               }
+       ;
+
+event_declaration_begin:
+               CTF_EVENT CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+event_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+
+stream_declaration:
+               stream_declaration_begin stream_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STREAM);
+               }
+       |       stream_declaration_begin ctf_assignment_expression_list stream_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STREAM);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "stream_declaration");
+               }
+       ;
+
+stream_declaration_begin:
+               CTF_STREAM CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+stream_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+env_declaration:
+               env_declaration_begin env_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_ENV);
+               }
+       |       env_declaration_begin ctf_assignment_expression_list env_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_ENV);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "env declaration");
+               }
+       ;
+
+env_declaration_begin:
+               CTF_ENV CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+env_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+trace_declaration:
+               trace_declaration_begin trace_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_TRACE);
+               }
+       |       trace_declaration_begin ctf_assignment_expression_list trace_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_TRACE);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "trace_declaration");
+               }
+       ;
+
+trace_declaration_begin:
+               CTF_TRACE CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+trace_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+clock_declaration:
+               CTF_CLOCK clock_declaration_begin clock_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CLOCK);
+               }
+       |       CTF_CLOCK clock_declaration_begin ctf_assignment_expression_list clock_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CLOCK);
+                       if (set_parent_node($3, $$))
+                               reparent_error(scanner, "trace_declaration");
+               }
+       ;
+
+clock_declaration_begin:
+               CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+clock_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+callsite_declaration:
+               CTF_CALLSITE callsite_declaration_begin callsite_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CALLSITE);
+               }
+       |       CTF_CALLSITE callsite_declaration_begin ctf_assignment_expression_list callsite_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CALLSITE);
+                       if (set_parent_node($3, $$))
+                               reparent_error(scanner, "trace_declaration");
+               }
+       ;
+
+callsite_declaration_begin:
+               CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+callsite_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+integer_declaration_specifiers:
+               CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       integer_field_class_specifier
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = $1;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       integer_declaration_specifiers CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       integer_declaration_specifiers integer_field_class_specifier
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       ;
+
+declaration_specifiers:
+               CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       field_class_specifier
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = $1;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       declaration_specifiers CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       declaration_specifiers field_class_specifier
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       ;
+
+field_class_declarator_list:
+               field_class_declarator
+               {       $$ = $1;        }
+       |       field_class_declarator_list CTF_COMMA field_class_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+integer_field_class_specifier:
+               CTF_CHAR
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_CHAR;
+               }
+       |       CTF_SHORT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_SHORT;
+               }
+       |       CTF_INT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INT;
+               }
+       |       CTF_LONG
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_LONG;
+               }
+       |       CTF_SIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_SIGNED;
+               }
+       |       CTF_UNSIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED;
+               }
+       |       CTF_BOOL
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_BOOL;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
+                       $$->u.field_class_specifier.id_type = yylval.s;
+               }
+       |       CTF_INTEGER CTF_LBRAC CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
+               }
+       |       CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
+                       if (set_parent_node($3, $$->u.field_class_specifier.node))
+                               reparent_error(scanner, "integer reparent error");
+               }
+       ;
+
+field_class_specifier:
+               CTF_VOID
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_VOID;
+               }
+       |       CTF_CHAR
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_CHAR;
+               }
+       |       CTF_SHORT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_SHORT;
+               }
+       |       CTF_INT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INT;
+               }
+       |       CTF_LONG
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_LONG;
+               }
+       |       CTF_FLOAT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_FLOAT;
+               }
+       |       CTF_DOUBLE
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_DOUBLE;
+               }
+       |       CTF_SIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_SIGNED;
+               }
+       |       CTF_UNSIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED;
+               }
+       |       CTF_BOOL
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_BOOL;
+               }
+       |       CTF_COMPLEX
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_COMPLEX;
+               }
+       |       CTF_IMAGINARY
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_IMAGINARY;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
+                       $$->u.field_class_specifier.id_type = yylval.s;
+               }
+       |       CTF_FLOATING_POINT CTF_LBRAC CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
+               }
+       |       CTF_FLOATING_POINT CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
+                       if (set_parent_node($3, $$->u.field_class_specifier.node))
+                               reparent_error(scanner, "floating point reparent error");
+               }
+       |       CTF_INTEGER CTF_LBRAC CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
+               }
+       |       CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
+                       if (set_parent_node($3, $$->u.field_class_specifier.node))
+                               reparent_error(scanner, "integer reparent error");
+               }
+       |       CTF_STRING
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
+               }
+       |       CTF_STRING CTF_LBRAC CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
+               }
+       |       CTF_STRING CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
+                       if (set_parent_node($3, $$->u.field_class_specifier.node))
+                               reparent_error(scanner, "string reparent error");
+               }
+       |       CTF_ENUM enum_field_class_specifier
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_ENUM;
+                       $$->u.field_class_specifier.node = $2;
+               }
+       |       CTF_VARIANT variant_field_class_specifier
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_VARIANT;
+                       $$->u.field_class_specifier.node = $2;
+               }
+       |       CTF_STRUCT struct_class_specifier
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_STRUCT;
+                       $$->u.field_class_specifier.node = $2;
+               }
+       ;
+
+struct_class_specifier:
+               struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       if ($2 && set_parent_node($2, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 0;
+                       $$->u._struct.name = $1;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 0;
+                       $$->u._struct.name = $1;
+               }
+       |       struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       bt_list_add_tail(&($6)->siblings, &$$->u._struct.min_align);
+                       if ($2 && set_parent_node($2, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       ;
+
+struct_declaration_begin:
+               CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+struct_declaration_end:
+               CTF_RBRAC
+               {       pop_scope(scanner);     }
+       ;
+
+variant_field_class_specifier:
+               variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       if ($2 && set_parent_node($2, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.choice = $2;
+                       if ($5 && set_parent_node($5, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.choice = $2;
+                       if ($5 && set_parent_node($5, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER CTF_LT IDENTIFIER CTF_GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       |       IDENTIFIER CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER CTF_LT ID_TYPE CTF_GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       |       ID_TYPE variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       ID_TYPE CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       ID_TYPE CTF_LT IDENTIFIER CTF_GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       |       ID_TYPE CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       ID_TYPE CTF_LT ID_TYPE CTF_GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       ;
+
+variant_declaration_begin:
+               CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+variant_declaration_end:
+               CTF_RBRAC
+               {       pop_scope(scanner);     }
+       ;
+
+enum_field_class_specifier:
+               CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       ($$)->u._enum.container_field_class = $2;
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_field_class = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_field_class = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       ($$)->u._enum.container_field_class = $2;
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_field_class = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 0;
+                       $$->u._enum.enum_id = $1;
+               }
+       |       ID_TYPE CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_field_class = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 0;
+                       $$->u._enum.enum_id = $1;
+               }
+       ;
+
+struct_or_variant_declaration_list:
+               /* empty */
+               {       $$ = NULL;      }
+       |       struct_or_variant_declaration_list struct_or_variant_declaration
+               {
+                       if ($1) {
+                               $$ = $1;
+                               bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
+                       } else {
+                               $$ = $2;
+                               bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+                       }
+               }
+       ;
+
+struct_or_variant_declaration:
+               declaration_specifiers struct_or_variant_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       $$ = make_node(scanner, NODE_STRUCT_OR_VARIANT_DECLARATION);
+                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u.struct_or_variant_declaration.field_class_declarators);
+               }
+       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEALIAS);
+                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
+                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
+               }
+       ;
+
+alias_declaration_specifiers:
+               CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       field_class_specifier
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = $1;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       IDENTIFIER
+               {
+                       struct ctf_node *node;
+
+                       add_type(scanner, $1);
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
+                       node->u.field_class_specifier.id_type = yylval.s;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       alias_declaration_specifiers CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       alias_declaration_specifiers field_class_specifier
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       alias_declaration_specifiers IDENTIFIER
+               {
+                       struct ctf_node *node;
+
+                       add_type(scanner, $2);
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
+                       node->u.field_class_specifier.id_type = yylval.s;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       ;
+
+struct_or_variant_declarator_list:
+               struct_or_variant_declarator
+               {       $$ = $1;        }
+       |       struct_or_variant_declarator_list CTF_COMMA struct_or_variant_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+struct_or_variant_declarator:
+               declarator
+               {       $$ = $1;        }
+       |       CTF_COLON unary_expression
+               {       $$ = $2;        }
+       |       declarator CTF_COLON unary_expression
+               {
+                       $$ = $1;
+                       if (set_parent_node($3, $1))
+                               reparent_error(scanner, "struct_or_variant_declarator");
+               }
+       ;
+
+enumerator_list:
+               enumerator
+               {       $$ = $1;        }
+       |       enumerator_list CTF_COMMA enumerator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+enumerator:
+               IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       keywords
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       CTF_STRING_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       IDENTIFIER CTF_EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       |       ID_TYPE CTF_EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       |       keywords CTF_EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       |       CTF_STRING_LITERAL CTF_EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       ;
+
+abstract_declarator_list:
+               abstract_declarator
+               {       $$ = $1;        }
+       |       abstract_declarator_list CTF_COMMA abstract_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+abstract_declarator:
+               direct_abstract_declarator
+               {       $$ = $1;        }
+       |       pointer direct_abstract_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
+               }
+       ;
+
+direct_abstract_declarator:
+               /* empty */
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                        $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       /* id is NULL */
+               }
+       |       IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       $$->u.field_class_declarator.u.id = $1;
+               }
+       |       CTF_LPAREN abstract_declarator CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
+               }
+       |       direct_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
+               }
+       |       direct_abstract_declarator CTF_LSBRAC CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       $$->u.field_class_declarator.u.nested.abstract_array = 1;
+               }
+       ;
+
+alias_abstract_declarator_list:
+               alias_abstract_declarator
+               {       $$ = $1;        }
+       |       alias_abstract_declarator_list CTF_COMMA alias_abstract_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+alias_abstract_declarator:
+               direct_alias_abstract_declarator
+               {       $$ = $1;        }
+       |       pointer direct_alias_abstract_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
+               }
+       ;
+
+direct_alias_abstract_declarator:
+               /* empty */
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                        $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       /* id is NULL */
+               }
+       |       CTF_LPAREN alias_abstract_declarator CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
+               }
+       |       direct_alias_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
+               }
+       |       direct_alias_abstract_declarator CTF_LSBRAC CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       $$->u.field_class_declarator.u.nested.abstract_array = 1;
+               }
+       ;
+
+declarator:
+               direct_declarator
+               {       $$ = $1;        }
+       |       pointer direct_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
+               }
+       ;
+
+direct_declarator:
+               IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       $$->u.field_class_declarator.u.id = $1;
+               }
+       |       CTF_LPAREN declarator CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
+               }
+       |       direct_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
+               }
+       ;
+
+field_class_declarator:
+               direct_field_class_declarator
+               {       $$ = $1;        }
+       |       pointer direct_field_class_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
+               }
+       ;
+
+direct_field_class_declarator:
+               IDENTIFIER
+               {
+                       add_type(scanner, $1);
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       $$->u.field_class_declarator.u.id = $1;
+               }
+       |       CTF_LPAREN field_class_declarator CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
+               }
+       |       direct_field_class_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
+               }
+       ;
+
+pointer:
+               CTF_STAR
+               {
+                       $$ = make_node(scanner, NODE_POINTER);
+               }
+       |       CTF_STAR pointer
+               {
+                       $$ = make_node(scanner, NODE_POINTER);
+                       bt_list_splice(&($2)->tmp_head, &($$)->tmp_head);
+               }
+       |       CTF_STAR type_qualifier_list pointer
+               {
+                       $$ = make_node(scanner, NODE_POINTER);
+                       $$->u.pointer.const_qualifier = 1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->tmp_head);
+               }
+       ;
+
+type_qualifier_list:
+               /* pointer assumes only const type qualifier */
+               CTF_CONST
+       |       type_qualifier_list CTF_CONST
+       ;
+
+/* 2.3: CTF-specific declarations */
+
+ctf_assignment_expression_list:
+               ctf_assignment_expression CTF_SEMICOLON
+               {       $$ = $1;        }
+       |       ctf_assignment_expression_list ctf_assignment_expression CTF_SEMICOLON
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+ctf_assignment_expression:
+               unary_expression CTF_EQUAL unary_expression
+               {
+                       /*
+                        * Because we have left and right, cannot use
+                        * set_parent_node.
+                        */
+                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
+                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
+                       if ($1->u.unary_expression.type != UNARY_STRING)
+                               reparent_error(scanner, "ctf_assignment_expression left expects string");
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.ctf_expression.right);
+               }
+       |       unary_expression CTF_TYPEASSIGN declaration_specifiers  /* Only allow struct */
+               {
+                       /*
+                        * Because we have left and right, cannot use
+                        * set_parent_node.
+                        */
+                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
+                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
+                       if ($1->u.unary_expression.type != UNARY_STRING)
+                               reparent_error(scanner, "ctf_assignment_expression left expects string");
+                       bt_list_add_tail(&($3)->siblings, &($$)->u.ctf_expression.right);
+               }
+       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEALIAS);
+                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
+                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
+               }
+       ;
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/scanner-symbols.hpp b/src/plugins/ctf/common/src/metadata/tsdl/scanner-symbols.hpp
new file mode 100644 (file)
index 0000000..1f6f144
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _CTF_SCANNER_SYMBOLS
+#define _CTF_SCANNER_SYMBOLS
+
+#define yy_create_buffer    bt_yy_create_buffer
+#define yy_delete_buffer    bt_yy_delete_buffer
+#define yy_flush_buffer     bt_yy_flush_buffer
+#define yy_scan_buffer      bt_yy_scan_buffer
+#define yy_scan_bytes       bt_yy_scan_bytes
+#define yy_scan_string      bt_yy_scan_string
+#define yy_switch_to_buffer bt_yy_switch_to_buffer
+#define yyalloc             bt_yyalloc
+#define yyfree              bt_yyfree
+#define yyget_column        bt_yyget_column
+#define yyget_debug         bt_yyget_debug
+#define yyget_extra         bt_yyget_extra
+#define yyget_in            bt_yyget_in
+#define yyget_leng          bt_yyget_leng
+#define yyget_lineno        bt_yyget_lineno
+#define yyget_lval          bt_yyget_lval
+#define yyget_out           bt_yyget_out
+#define yyget_text          bt_yyget_text
+#define yylex_init          bt_yylex_init
+#define yypop_buffer_state  bt_yypop_buffer_state
+#define yypush_buffer_state bt_yypush_buffer_state
+#define yyrealloc           bt_yyrealloc
+#define yyset_column        bt_yyset_column
+#define yyset_debug         bt_yyset_debug
+#define yyset_extra         bt_yyset_extra
+#define yyset_in            bt_yyset_in
+#define yyset_lineno        bt_yyset_lineno
+#define yyset_lval          bt_yyset_lval
+#define yyset_out           bt_yyset_out
+
+#endif /* _CTF_SCANNER_SYMBOLS */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/scanner.hpp b/src/plugins/ctf/common/src/metadata/tsdl/scanner.hpp
new file mode 100644 (file)
index 0000000..4f2a1f8
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _CTF_SCANNER_H
+#define _CTF_SCANNER_H
+
+#include <stdio.h>
+
+#include "ast.hpp"
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#    define YY_TYPEDEF_YY_SCANNER_T
+typedef void *yyscan_t;
+#endif
+
+struct ctf_scanner_scope;
+struct ctf_scanner_scope
+{
+    struct ctf_scanner_scope *parent;
+    GHashTable *classes;
+};
+
+struct ctf_scanner
+{
+    explicit ctf_scanner(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/CTF/META/PARSER"}
+    {
+    }
+
+    bt2c::Logger logger;
+    yyscan_t scanner {};
+    ctf_ast *ast = nullptr;
+    ctf_scanner_scope root_scope {};
+    ctf_scanner_scope *cs = nullptr;
+    struct objstack *objstack = nullptr;
+};
+
+struct ctf_scanner *ctf_scanner_alloc(const bt2c::Logger& parentLogger);
+
+void ctf_scanner_free(struct ctf_scanner *scanner);
+
+int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input);
+
+static inline struct ctf_ast *ctf_scanner_get_ast(struct ctf_scanner *scanner)
+{
+    return scanner->ast;
+}
+
+int is_type(struct ctf_scanner *scanner, const char *id);
+
+#endif /* _CTF_SCANNER_H */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/visitor-generate-ir.cpp b/src/plugins/ctf/common/src/metadata/tsdl/visitor-generate-ir.cpp
new file mode 100644 (file)
index 0000000..7900662
--- /dev/null
@@ -0,0 +1,4667 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2015-2018 Philippe Proulx <philippe.proulx@efficios.com>
+ *
+ * Common Trace Format metadata visitor (generates CTF IR objects).
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "logging.hpp"
+
+#include "common/assert.h"
+#include "common/common.h"
+#include "common/uuid.h"
+#include "compat/endian.h" /* IWYU pragma: keep  */
+#include "cpp-common/bt2c/logging.hpp"
+
+#include "ast.hpp"
+#include "ctf-meta-visitors.hpp"
+
+/* Bit value (left shift) */
+#define _BV(_val) (1 << (_val))
+
+/* Bit is set in a set of bits */
+#define _IS_SET(_set, _mask) (*(_set) & (_mask))
+
+/* Set bit in a set of bits */
+#define _SET(_set, _mask) (*(_set) |= (_mask))
+
+/* Try to push scope, or go to the `error` label */
+#define _TRY_PUSH_SCOPE_OR_GOTO_ERROR()                                                            \
+    do {                                                                                           \
+        ret = ctx_push_scope(ctx);                                                                 \
+        if (ret) {                                                                                 \
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot push scope.");                       \
+            goto error;                                                                            \
+        }                                                                                          \
+    } while (0)
+
+/* Bits for verifying existing attributes in various declarations */
+enum
+{
+    _CLOCK_NAME_SET = _BV(0),
+    _CLOCK_UUID_SET = _BV(1),
+    _CLOCK_FREQ_SET = _BV(2),
+    _CLOCK_PRECISION_SET = _BV(3),
+    _CLOCK_OFFSET_S_SET = _BV(4),
+    _CLOCK_OFFSET_SET = _BV(5),
+    _CLOCK_ABSOLUTE_SET = _BV(6),
+    _CLOCK_DESCRIPTION_SET = _BV(7),
+};
+
+enum
+{
+    _INTEGER_ALIGN_SET = _BV(0),
+    _INTEGER_SIZE_SET = _BV(1),
+    _INTEGER_BASE_SET = _BV(2),
+    _INTEGER_ENCODING_SET = _BV(3),
+    _INTEGER_BYTE_ORDER_SET = _BV(4),
+    _INTEGER_SIGNED_SET = _BV(5),
+    _INTEGER_MAP_SET = _BV(6),
+};
+
+enum
+{
+    _FLOAT_ALIGN_SET = _BV(0),
+    _FLOAT_MANT_DIG_SET = _BV(1),
+    _FLOAT_EXP_DIG_SET = _BV(2),
+    _FLOAT_BYTE_ORDER_SET = _BV(3),
+};
+
+enum
+{
+    _STRING_ENCODING_SET = _BV(0),
+};
+
+enum
+{
+    _TRACE_MINOR_SET = _BV(0),
+    _TRACE_MAJOR_SET = _BV(1),
+    _TRACE_BYTE_ORDER_SET = _BV(2),
+    _TRACE_UUID_SET = _BV(3),
+    _TRACE_PACKET_HEADER_SET = _BV(4),
+};
+
+enum
+{
+    _STREAM_ID_SET = _BV(0),
+    _STREAM_PACKET_CONTEXT_SET = _BV(1),
+    _STREAM_EVENT_HEADER_SET = _BV(2),
+    _STREAM_EVENT_CONTEXT_SET = _BV(3),
+};
+
+enum
+{
+    _EVENT_NAME_SET = _BV(0),
+    _EVENT_ID_SET = _BV(1),
+    _EVENT_MODEL_EMF_URI_SET = _BV(2),
+    _EVENT_STREAM_ID_SET = _BV(3),
+    _EVENT_LOG_LEVEL_SET = _BV(4),
+    _EVENT_CONTEXT_SET = _BV(5),
+    _EVENT_FIELDS_SET = _BV(6),
+};
+
+enum loglevel
+{
+    LOG_LEVEL_EMERG = 0,
+    LOG_LEVEL_ALERT = 1,
+    LOG_LEVEL_CRIT = 2,
+    LOG_LEVEL_ERR = 3,
+    LOG_LEVEL_WARNING = 4,
+    LOG_LEVEL_NOTICE = 5,
+    LOG_LEVEL_INFO = 6,
+    LOG_LEVEL_DEBUG_SYSTEM = 7,
+    LOG_LEVEL_DEBUG_PROGRAM = 8,
+    LOG_LEVEL_DEBUG_PROCESS = 9,
+    LOG_LEVEL_DEBUG_MODULE = 10,
+    LOG_LEVEL_DEBUG_UNIT = 11,
+    LOG_LEVEL_DEBUG_FUNCTION = 12,
+    LOG_LEVEL_DEBUG_LINE = 13,
+    LOG_LEVEL_DEBUG = 14,
+    _NR_LOGLEVELS = 15,
+};
+
+/* Prefixes of class aliases */
+#define _PREFIX_ALIAS   'a'
+#define _PREFIX_ENUM    'e'
+#define _PREFIX_STRUCT  's'
+#define _PREFIX_VARIANT 'v'
+
+/* First entry in a BT list */
+#define _BT_LIST_FIRST_ENTRY(_ptr, _class, _member) bt_list_entry((_ptr)->next, _class, _member)
+
+#define _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(_node, _attr, _entity)                                   \
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(ctx->logger, (_node)->lineno,                                  \
+                                    "Duplicate attribute in {}: attr-name=\"{}\"", _entity, _attr)
+
+#define _BT_CPPLOGE_NODE(_node, _msg, args...)                                                     \
+    _BT_CPPLOGE_LINENO(ctx->logger, (_node)->lineno, _msg, ##args)
+
+#define _BT_CPPLOGE_APPEND_CAUSE_NODE(_node, _msg, args...)                                        \
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(ctx->logger, (_node)->lineno, _msg, ##args)
+
+#define _BT_CPPLOGW_NODE(_node, _msg, args...)                                                     \
+    _BT_CPPLOGW_LINENO(ctx->logger, (_node)->lineno, _msg, ##args)
+
+#define _BT_CPPLOGT_NODE(_node, _msg, args...)                                                     \
+    _BT_CPPLOGT_LINENO(ctx->logger, (_node)->lineno, _msg, ##args)
+
+/*
+ * Declaration scope of a visitor context. This represents a TSDL
+ * lexical scope, so that aliases and named structures, variants,
+ * and enumerations may be registered and looked up hierarchically.
+ */
+struct ctx_decl_scope
+{
+    /*
+     * Alias name to field class.
+     *
+     * GQuark -> struct ctf_field_class * (owned by this)
+     */
+    GHashTable *decl_map;
+
+    /* Parent scope; NULL if this is the root declaration scope */
+    struct ctx_decl_scope *parent_scope;
+};
+
+/**
+ * Creates a new declaration scope.
+ *
+ * @param par_scope    Parent scope (NULL if creating a root scope)
+ * @returns            New declaration scope, or NULL on error
+ */
+static struct ctx_decl_scope *ctx_decl_scope_create(struct ctf_visitor_generate_ir *ctx,
+                                                    struct ctx_decl_scope *par_scope)
+{
+    struct ctx_decl_scope *scope;
+
+    scope = g_new(struct ctx_decl_scope, 1);
+    if (!scope) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Failed to allocate one declaration scope.");
+        goto end;
+    }
+
+    scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+                                            (GDestroyNotify) ctf_field_class_destroy);
+    scope->parent_scope = par_scope;
+
+end:
+    return scope;
+}
+
+/**
+ * Destroys a declaration scope.
+ *
+ * This function does not destroy the parent scope.
+ *
+ * @param scope        Scope to destroy
+ */
+static void ctx_decl_scope_destroy(struct ctx_decl_scope *scope)
+{
+    if (!scope) {
+        goto end;
+    }
+
+    g_hash_table_destroy(scope->decl_map);
+    g_free(scope);
+
+end:
+    return;
+}
+
+/**
+ * Returns the GQuark of a prefixed alias.
+ *
+ * @param prefix       Prefix character
+ * @param name         Name
+ * @returns            Associated GQuark, or 0 on error
+ */
+static GQuark get_prefixed_named_quark(char prefix, const char *name)
+{
+    BT_ASSERT(name);
+    std::string prname = std::string {prefix} + name;
+    return g_quark_from_string(prname.c_str());
+}
+
+/**
+ * Looks up a prefixed class alias within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param prefix       Prefix character
+ * @param name         Alias name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static struct ctf_field_class *ctx_decl_scope_lookup_prefix_alias(struct ctx_decl_scope *scope,
+                                                                  char prefix, const char *name,
+                                                                  int levels, bool copy)
+{
+    GQuark qname = 0;
+    int cur_levels = 0;
+    struct ctf_field_class *decl = NULL;
+    struct ctx_decl_scope *cur_scope = scope;
+
+    BT_ASSERT(scope);
+    BT_ASSERT(name);
+    qname = get_prefixed_named_quark(prefix, name);
+    if (!qname) {
+        goto end;
+    }
+
+    if (levels < 0) {
+        levels = INT_MAX;
+    }
+
+    while (cur_scope && cur_levels < levels) {
+        decl = (ctf_field_class *) g_hash_table_lookup(cur_scope->decl_map,
+                                                       (gconstpointer) GUINT_TO_POINTER(qname));
+        if (decl) {
+            /* Caller's reference */
+            if (copy) {
+                decl = ctf_field_class_copy(decl);
+                BT_ASSERT(decl);
+            }
+
+            goto end;
+        }
+
+        cur_scope = cur_scope->parent_scope;
+        cur_levels++;
+    }
+
+end:
+    return decl;
+}
+
+/**
+ * Looks up a class alias within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Alias name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static struct ctf_field_class *ctx_decl_scope_lookup_alias(struct ctx_decl_scope *scope,
+                                                           const char *name, int levels, bool copy)
+{
+    return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ALIAS, name, levels, copy);
+}
+
+/**
+ * Looks up an enumeration within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Enumeration name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static struct ctf_field_class_enum *
+ctx_decl_scope_lookup_enum(struct ctx_decl_scope *scope, const char *name, int levels, bool copy)
+{
+    return ctf_field_class_as_enum(
+        ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ENUM, name, levels, copy));
+}
+
+/**
+ * Looks up a structure within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Structure name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static struct ctf_field_class_struct *
+ctx_decl_scope_lookup_struct(struct ctx_decl_scope *scope, const char *name, int levels, bool copy)
+{
+    return ctf_field_class_as_struct(
+        ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_STRUCT, name, levels, copy));
+}
+
+/**
+ * Looks up a variant within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Variant name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static struct ctf_field_class_variant *
+ctx_decl_scope_lookup_variant(struct ctx_decl_scope *scope, const char *name, int levels, bool copy)
+{
+    return ctf_field_class_as_variant(
+        ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_VARIANT, name, levels, copy));
+}
+
+/**
+ * Registers a prefixed class alias within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param prefix       Prefix character
+ * @param name         Alias name (non-NULL)
+ * @param decl         Field class to register (copied)
+ * @returns            0 if registration went okay, negative value otherwise
+ */
+static int ctx_decl_scope_register_prefix_alias(struct ctx_decl_scope *scope, char prefix,
+                                                const char *name, struct ctf_field_class *decl)
+{
+    int ret = 0;
+    GQuark qname = 0;
+
+    BT_ASSERT(scope);
+    BT_ASSERT(name);
+    BT_ASSERT(decl);
+    qname = get_prefixed_named_quark(prefix, name);
+    if (!qname) {
+        ret = -ENOMEM;
+        goto end;
+    }
+
+    /* Make sure alias does not exist in local scope */
+    if (ctx_decl_scope_lookup_prefix_alias(scope, prefix, name, 1, false)) {
+        ret = -EEXIST;
+        goto end;
+    }
+
+    decl = ctf_field_class_copy(decl);
+    BT_ASSERT(decl);
+    g_hash_table_insert(scope->decl_map, GUINT_TO_POINTER(qname), decl);
+
+end:
+    return ret;
+}
+
+/**
+ * Registers a class alias within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Alias name (non-NULL)
+ * @param decl Field class to register (copied)
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static int ctx_decl_scope_register_alias(struct ctx_decl_scope *scope, const char *name,
+                                         struct ctf_field_class *decl)
+{
+    return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ALIAS, name, decl);
+}
+
+/**
+ * Registers an enumeration declaration within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Enumeration name (non-NULL)
+ * @param decl Enumeration field class to register (copied)
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static int ctx_decl_scope_register_enum(struct ctx_decl_scope *scope, const char *name,
+                                        struct ctf_field_class_enum *decl)
+{
+    return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ENUM, name, &decl->base.base.base);
+}
+
+/**
+ * Registers a structure declaration within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Structure name (non-NULL)
+ * @param decl Structure field class to register (copied)
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static int ctx_decl_scope_register_struct(struct ctx_decl_scope *scope, const char *name,
+                                          struct ctf_field_class_struct *decl)
+{
+    return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_STRUCT, name, &decl->base);
+}
+
+/**
+ * Registers a variant declaration within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Variant name (non-NULL)
+ * @param decl Variant field class to register
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static int ctx_decl_scope_register_variant(struct ctx_decl_scope *scope, const char *name,
+                                           struct ctf_field_class_variant *decl)
+{
+    return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_VARIANT, name, &decl->base);
+}
+
+ctf_visitor_generate_ir::~ctf_visitor_generate_ir()
+{
+    struct ctx_decl_scope *scope = this->current_scope;
+
+    /*
+     * Destroy all scopes, from current one to the root scope.
+     */
+    while (scope) {
+        struct ctx_decl_scope *parent_scope = scope->parent_scope;
+
+        ctx_decl_scope_destroy(scope);
+        scope = parent_scope;
+    }
+
+    if (this->ctf_tc) {
+        ctf_trace_class_destroy(this->ctf_tc);
+    }
+}
+
+/**
+ * Creates a new visitor context.
+ *
+ * @param trace        Associated trace
+ * @returns    New visitor context, or NULL on error
+ */
+static ctf_visitor_generate_ir::UP
+ctx_create(const struct ctf_metadata_decoder_config *decoder_config, const bt2c::Logger& logger)
+{
+    BT_ASSERT(decoder_config);
+
+    ctf_visitor_generate_ir::UP ctx {new ctf_visitor_generate_ir {*decoder_config, logger}};
+
+    if (decoder_config->self_comp) {
+        bt_trace_class *trace_class = bt_trace_class_create(decoder_config->self_comp);
+        if (!trace_class) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot create empty trace class.");
+            goto error;
+        }
+
+        ctx->trace_class = bt2::TraceClass::Shared::createWithoutRef(trace_class);
+    }
+
+    ctx->ctf_tc = ctf_trace_class_create();
+    if (!ctx->ctf_tc) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot create CTF trace class.");
+        goto error;
+    }
+
+    /* Root declaration scope */
+    ctx->current_scope = ctx_decl_scope_create(ctx.get(), NULL);
+    if (!ctx->current_scope) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot create declaration scope.");
+        goto error;
+    }
+
+    goto end;
+
+error:
+    ctx.reset();
+
+end:
+    return ctx;
+}
+
+/**
+ * Pushes a new declaration scope on top of a visitor context's
+ * declaration scope stack.
+ *
+ * @param ctx  Visitor context
+ * @returns    0 on success, or a negative value on error
+ */
+static int ctx_push_scope(struct ctf_visitor_generate_ir *ctx)
+{
+    int ret = 0;
+    struct ctx_decl_scope *new_scope;
+
+    BT_ASSERT(ctx);
+    new_scope = ctx_decl_scope_create(ctx, ctx->current_scope);
+    if (!new_scope) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot create declaration scope.");
+        ret = -ENOMEM;
+        goto end;
+    }
+
+    ctx->current_scope = new_scope;
+
+end:
+    return ret;
+}
+
+static void ctx_pop_scope(struct ctf_visitor_generate_ir *ctx)
+{
+    struct ctx_decl_scope *parent_scope = NULL;
+
+    BT_ASSERT(ctx);
+
+    if (!ctx->current_scope) {
+        goto end;
+    }
+
+    parent_scope = ctx->current_scope->parent_scope;
+    ctx_decl_scope_destroy(ctx->current_scope);
+    ctx->current_scope = parent_scope;
+
+end:
+    return;
+}
+
+static int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
+                                            struct ctf_node *ts_list,
+                                            struct ctf_field_class **decl);
+
+static int is_unary_string(struct bt_list_head *head)
+{
+    int ret = TRUE;
+    struct ctf_node *node;
+
+    bt_list_for_each_entry (node, head, siblings) {
+        if (node->type != NODE_UNARY_EXPRESSION) {
+            ret = FALSE;
+        }
+
+        if (node->u.unary_expression.type != UNARY_STRING) {
+            ret = FALSE;
+        }
+    }
+
+    return ret;
+}
+
+static const char *get_map_clock_name_value(struct bt_list_head *head)
+{
+    int i = 0;
+    struct ctf_node *node;
+    const char *name = NULL;
+
+    bt_list_for_each_entry (node, head, siblings) {
+        char *src_string;
+        int uexpr_type = node->u.unary_expression.type;
+        int uexpr_link = node->u.unary_expression.link;
+        int cond = node->type != NODE_UNARY_EXPRESSION || uexpr_type != UNARY_STRING ||
+                   !((uexpr_link != UNARY_LINK_UNKNOWN) ^ (i == 0));
+        if (cond) {
+            goto error;
+        }
+
+        /* Needs to be chained with . */
+        switch (node->u.unary_expression.link) {
+        case UNARY_DOTLINK:
+            break;
+        case UNARY_ARROWLINK:
+        case UNARY_DOTDOTDOT:
+            goto error;
+        default:
+            break;
+        }
+
+        src_string = node->u.unary_expression.u.string;
+
+        switch (i) {
+        case 0:
+            if (strcmp("clock", src_string)) {
+                goto error;
+            }
+            break;
+        case 1:
+            name = src_string;
+            break;
+        case 2:
+            if (strcmp("value", src_string)) {
+                goto error;
+            }
+            break;
+        default:
+            /* Extra identifier, unknown */
+            goto error;
+        }
+
+        i++;
+    }
+
+    return name;
+
+error:
+    return NULL;
+}
+
+static int is_unary_unsigned(struct bt_list_head *head)
+{
+    int ret = TRUE;
+    struct ctf_node *node;
+
+    bt_list_for_each_entry (node, head, siblings) {
+        if (node->type != NODE_UNARY_EXPRESSION) {
+            ret = FALSE;
+        }
+
+        if (node->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
+            ret = FALSE;
+        }
+    }
+
+    return ret;
+}
+
+static int get_unary_unsigned(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
+                              uint64_t *value)
+{
+    int i = 0;
+    int ret = 0;
+    struct ctf_node *node;
+
+    *value = 0;
+
+    if (bt_list_empty(head)) {
+        ret = -1;
+        goto end;
+    }
+
+    bt_list_for_each_entry (node, head, siblings) {
+        int uexpr_type = node->u.unary_expression.type;
+        int uexpr_link = node->u.unary_expression.link;
+        int cond = node->type != NODE_UNARY_EXPRESSION || uexpr_type != UNARY_UNSIGNED_CONSTANT ||
+                   uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
+        if (cond) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Invalid constant unsigned integer.");
+            ret = -EINVAL;
+            goto end;
+        }
+
+        *value = node->u.unary_expression.u.unsigned_constant;
+        i++;
+    }
+
+end:
+    return ret;
+}
+
+static int is_unary_signed(struct bt_list_head *head)
+{
+    int ret = TRUE;
+    struct ctf_node *node;
+
+    bt_list_for_each_entry (node, head, siblings) {
+        if (node->type != NODE_UNARY_EXPRESSION) {
+            ret = FALSE;
+        }
+
+        if (node->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
+            ret = FALSE;
+        }
+    }
+
+    return ret;
+}
+
+static int get_unary_signed(struct bt_list_head *head, int64_t *value)
+{
+    int i = 0;
+    int ret = 0;
+    struct ctf_node *node;
+
+    bt_list_for_each_entry (node, head, siblings) {
+        int uexpr_type = node->u.unary_expression.type;
+        int uexpr_link = node->u.unary_expression.link;
+        int cond = node->type != NODE_UNARY_EXPRESSION ||
+                   (uexpr_type != UNARY_UNSIGNED_CONSTANT && uexpr_type != UNARY_SIGNED_CONSTANT) ||
+                   uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
+        if (cond) {
+            ret = -EINVAL;
+            goto end;
+        }
+
+        switch (uexpr_type) {
+        case UNARY_UNSIGNED_CONSTANT:
+            *value = (int64_t) node->u.unary_expression.u.unsigned_constant;
+            break;
+        case UNARY_SIGNED_CONSTANT:
+            *value = node->u.unary_expression.u.signed_constant;
+            break;
+        default:
+            ret = -EINVAL;
+            goto end;
+        }
+
+        i++;
+    }
+
+end:
+    return ret;
+}
+
+static int get_unary_uuid(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
+                          bt_uuid_t uuid)
+{
+    return ctf_ast_get_unary_uuid(head, uuid, ctx->logger);
+}
+
+static int get_boolean(struct ctf_visitor_generate_ir *ctx, struct ctf_node *unary_expr)
+{
+    int ret = 0;
+
+    if (unary_expr->type != NODE_UNARY_EXPRESSION) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(unary_expr, "Expecting unary expression: node-type={}",
+                                      unary_expr->type);
+        ret = -EINVAL;
+        goto end;
+    }
+
+    switch (unary_expr->u.unary_expression.type) {
+    case UNARY_UNSIGNED_CONSTANT:
+        ret = (unary_expr->u.unary_expression.u.unsigned_constant != 0);
+        break;
+    case UNARY_SIGNED_CONSTANT:
+        ret = (unary_expr->u.unary_expression.u.signed_constant != 0);
+        break;
+    case UNARY_STRING:
+    {
+        const char *str = unary_expr->u.unary_expression.u.string;
+
+        if (strcmp(str, "true") == 0 || strcmp(str, "TRUE") == 0) {
+            ret = TRUE;
+        } else if (strcmp(str, "false") == 0 || strcmp(str, "FALSE") == 0) {
+            ret = FALSE;
+        } else {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(unary_expr, "Unexpected boolean value: value=\"{}\"",
+                                          str);
+            ret = -EINVAL;
+            goto end;
+        }
+        break;
+    }
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(unary_expr, "Unexpected unary expression type: node-type={}",
+                                      unary_expr->u.unary_expression.type);
+        ret = -EINVAL;
+        goto end;
+    }
+
+end:
+    return ret;
+}
+
+static enum ctf_byte_order byte_order_from_unary_expr(struct ctf_visitor_generate_ir *ctx,
+                                                      struct ctf_node *unary_expr)
+{
+    const char *str;
+    enum ctf_byte_order bo = CTF_BYTE_ORDER_UNKNOWN;
+
+    if (unary_expr->u.unary_expression.type != UNARY_STRING) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(
+            unary_expr, "\"byte_order\" attribute: expecting `be`, `le`, `network`, or `native`.");
+        goto end;
+    }
+
+    str = unary_expr->u.unary_expression.u.string;
+
+    if (strcmp(str, "be") == 0 || strcmp(str, "network") == 0) {
+        bo = CTF_BYTE_ORDER_BIG;
+    } else if (strcmp(str, "le") == 0) {
+        bo = CTF_BYTE_ORDER_LITTLE;
+    } else if (strcmp(str, "native") == 0) {
+        bo = CTF_BYTE_ORDER_DEFAULT;
+    } else {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(unary_expr,
+                                      "Unexpected \"byte_order\" attribute value: "
+                                      "expecting `be`, `le`, `network`, or `native`: value=\"{}\"",
+                                      str);
+        goto end;
+    }
+
+end:
+    return bo;
+}
+
+static enum ctf_byte_order get_real_byte_order(struct ctf_visitor_generate_ir *ctx,
+                                               struct ctf_node *uexpr)
+{
+    enum ctf_byte_order bo = byte_order_from_unary_expr(ctx, uexpr);
+
+    if (bo == CTF_BYTE_ORDER_DEFAULT) {
+        bo = ctx->ctf_tc->default_byte_order;
+    }
+
+    return bo;
+}
+
+static int is_align_valid(uint64_t align)
+{
+    return (align != 0) && !(align & (align - UINT64_C(1)));
+}
+
+static int get_class_specifier_name(struct ctf_visitor_generate_ir *ctx,
+                                    struct ctf_node *cls_specifier, GString *str)
+{
+    int ret = 0;
+
+    if (cls_specifier->type != NODE_TYPE_SPECIFIER) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(cls_specifier, "Unexpected node type: node-type={}",
+                                      cls_specifier->type);
+        ret = -EINVAL;
+        goto end;
+    }
+
+    switch (cls_specifier->u.field_class_specifier.type) {
+    case TYPESPEC_VOID:
+        g_string_append(str, "void");
+        break;
+    case TYPESPEC_CHAR:
+        g_string_append(str, "char");
+        break;
+    case TYPESPEC_SHORT:
+        g_string_append(str, "short");
+        break;
+    case TYPESPEC_INT:
+        g_string_append(str, "int");
+        break;
+    case TYPESPEC_LONG:
+        g_string_append(str, "long");
+        break;
+    case TYPESPEC_FLOAT:
+        g_string_append(str, "float");
+        break;
+    case TYPESPEC_DOUBLE:
+        g_string_append(str, "double");
+        break;
+    case TYPESPEC_SIGNED:
+        g_string_append(str, "signed");
+        break;
+    case TYPESPEC_UNSIGNED:
+        g_string_append(str, "unsigned");
+        break;
+    case TYPESPEC_BOOL:
+        g_string_append(str, "bool");
+        break;
+    case TYPESPEC_COMPLEX:
+        g_string_append(str, "_Complex");
+        break;
+    case TYPESPEC_IMAGINARY:
+        g_string_append(str, "_Imaginary");
+        break;
+    case TYPESPEC_CONST:
+        g_string_append(str, "const");
+        break;
+    case TYPESPEC_ID_TYPE:
+        if (cls_specifier->u.field_class_specifier.id_type) {
+            g_string_append(str, cls_specifier->u.field_class_specifier.id_type);
+        }
+        break;
+    case TYPESPEC_STRUCT:
+    {
+        struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
+
+        if (!node->u._struct.name) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Unexpected empty structure field class name.");
+            ret = -EINVAL;
+            goto end;
+        }
+
+        g_string_append(str, "struct ");
+        g_string_append(str, node->u._struct.name);
+        break;
+    }
+    case TYPESPEC_VARIANT:
+    {
+        struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
+
+        if (!node->u.variant.name) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Unexpected empty variant field class name.");
+            ret = -EINVAL;
+            goto end;
+        }
+
+        g_string_append(str, "variant ");
+        g_string_append(str, node->u.variant.name);
+        break;
+    }
+    case TYPESPEC_ENUM:
+    {
+        struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
+
+        if (!node->u._enum.enum_id) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                node, "Unexpected empty enumeration field class (`enum`) name.");
+            ret = -EINVAL;
+            goto end;
+        }
+
+        g_string_append(str, "enum ");
+        g_string_append(str, node->u._enum.enum_id);
+        break;
+    }
+    case TYPESPEC_FLOATING_POINT:
+    case TYPESPEC_INTEGER:
+    case TYPESPEC_STRING:
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(cls_specifier->u.field_class_specifier.node,
+                                      "Unexpected field class specifier type: {}",
+                                      cls_specifier->u.field_class_specifier.type);
+        ret = -EINVAL;
+        goto end;
+    }
+
+end:
+    return ret;
+}
+
+static int get_class_specifier_list_name(struct ctf_visitor_generate_ir *ctx,
+                                         struct ctf_node *cls_specifier_list, GString *str)
+{
+    int ret = 0;
+    struct ctf_node *iter;
+    int alias_item_nr = 0;
+    struct bt_list_head *head = &cls_specifier_list->u.field_class_specifier_list.head;
+
+    bt_list_for_each_entry (iter, head, siblings) {
+        if (alias_item_nr != 0) {
+            g_string_append(str, " ");
+        }
+
+        alias_item_nr++;
+        ret = get_class_specifier_name(ctx, iter, str);
+        if (ret) {
+            goto end;
+        }
+    }
+
+end:
+    return ret;
+}
+
+static GQuark create_class_alias_identifier(struct ctf_visitor_generate_ir *ctx,
+                                            struct ctf_node *cls_specifier_list,
+                                            struct ctf_node *node_field_class_declarator)
+{
+    int ret;
+    char *str_c;
+    GString *str;
+    GQuark qalias = 0;
+    struct ctf_node *iter;
+    struct bt_list_head *pointers = &node_field_class_declarator->u.field_class_declarator.pointers;
+
+    str = g_string_new("");
+    ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
+    if (ret) {
+        g_string_free(str, TRUE);
+        goto end;
+    }
+
+    bt_list_for_each_entry (iter, pointers, siblings) {
+        g_string_append(str, " *");
+
+        if (iter->u.pointer.const_qualifier) {
+            g_string_append(str, " const");
+        }
+    }
+
+    str_c = g_string_free(str, FALSE);
+    qalias = g_quark_from_string(str_c);
+    g_free(str_c);
+
+end:
+    return qalias;
+}
+
+static int visit_field_class_declarator(struct ctf_visitor_generate_ir *ctx,
+                                        struct ctf_node *cls_specifier_list, GQuark *field_name,
+                                        struct ctf_node *node_field_class_declarator,
+                                        struct ctf_field_class **field_decl,
+                                        struct ctf_field_class *nested_decl)
+{
+    /*
+     * During this whole function, nested_decl is always OURS,
+     * whereas field_decl is an output which we create, but
+     * belongs to the caller (it is moved).
+     */
+    int ret = 0;
+    *field_decl = NULL;
+
+    /* Validate field class declarator node */
+    if (node_field_class_declarator) {
+        if (node_field_class_declarator->u.field_class_declarator.type == TYPEDEC_UNKNOWN) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                node_field_class_declarator, "Unexpected field class declarator type: type={}",
+                node_field_class_declarator->u.field_class_declarator.type);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        /* TODO: GCC bitfields not supported yet */
+        if (node_field_class_declarator->u.field_class_declarator.bitfield_len != NULL) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                          "GCC bitfields are not supported as of this version.");
+            ret = -EPERM;
+            goto error;
+        }
+    }
+
+    /* Find the right nested declaration if not provided */
+    if (!nested_decl) {
+        if (node_field_class_declarator &&
+            !bt_list_empty(&node_field_class_declarator->u.field_class_declarator.pointers)) {
+            GQuark qalias;
+
+            /*
+             * If we have a pointer declarator, it HAS to
+             * be present in the field class aliases (else
+             * fail).
+             */
+            qalias =
+                create_class_alias_identifier(ctx, cls_specifier_list, node_field_class_declarator);
+            nested_decl = ctx_decl_scope_lookup_alias(ctx->current_scope, g_quark_to_string(qalias),
+                                                      -1, true);
+            if (!nested_decl) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                              "Cannot find class alias: name=\"{}\"",
+                                              g_quark_to_string(qalias));
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (nested_decl->type == CTF_FIELD_CLASS_TYPE_INT) {
+                /* Pointer: force integer's base to 16 */
+                struct ctf_field_class_int *int_fc = ctf_field_class_as_int(nested_decl);
+
+                int_fc->disp_base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
+            }
+        } else {
+            ret = visit_field_class_specifier_list(ctx, cls_specifier_list, &nested_decl);
+            if (ret) {
+                BT_ASSERT(!nested_decl);
+                goto error;
+            }
+        }
+    }
+
+    BT_ASSERT(nested_decl);
+
+    if (!node_field_class_declarator) {
+        *field_decl = nested_decl;
+        nested_decl = NULL;
+        goto end;
+    }
+
+    if (node_field_class_declarator->u.field_class_declarator.type == TYPEDEC_ID) {
+        if (node_field_class_declarator->u.field_class_declarator.u.id) {
+            const char *id = node_field_class_declarator->u.field_class_declarator.u.id;
+
+            *field_name = g_quark_from_string(id);
+        } else {
+            *field_name = 0;
+        }
+
+        *field_decl = nested_decl;
+        nested_decl = NULL;
+        goto end;
+    } else {
+        struct ctf_node *first;
+        struct ctf_field_class *decl = NULL;
+        struct ctf_field_class *outer_field_decl = NULL;
+        struct bt_list_head *length =
+            &node_field_class_declarator->u.field_class_declarator.u.nested.length;
+
+        /* Create array/sequence, pass nested_decl as child */
+        if (bt_list_empty(length)) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                          "Expecting length field reference or value.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        first = _BT_LIST_FIRST_ENTRY(length, struct ctf_node, siblings);
+        if (first->type != NODE_UNARY_EXPRESSION) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(first, "Unexpected node type: node-type={}", first->type);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        switch (first->u.unary_expression.type) {
+        case UNARY_UNSIGNED_CONSTANT:
+        {
+            struct ctf_field_class_array *array_decl = NULL;
+
+            array_decl = ctf_field_class_array_create();
+            BT_ASSERT(array_decl);
+            array_decl->length = first->u.unary_expression.u.unsigned_constant;
+            array_decl->base.elem_fc = nested_decl;
+            nested_decl = NULL;
+            decl = &array_decl->base.base;
+            break;
+        }
+        case UNARY_STRING:
+        {
+            /* Lookup unsigned integer definition, create seq. */
+            struct ctf_field_class_sequence *seq_decl = NULL;
+            char *length_name = ctf_ast_concatenate_unary_strings(length);
+
+            if (!length_name) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                              "Cannot concatenate unary strings.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (strncmp(length_name, "env.", 4) == 0) {
+                /* This is, in fact, an array */
+                const char *env_entry_name = &length_name[4];
+                struct ctf_trace_class_env_entry *env_entry =
+                    ctf_trace_class_borrow_env_entry_by_name(ctx->ctf_tc, env_entry_name);
+                struct ctf_field_class_array *array_decl;
+
+                if (!env_entry) {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                                  "Cannot find environment entry: "
+                                                  "name=\"{}\"",
+                                                  env_entry_name);
+                    ret = -EINVAL;
+                    goto error;
+                }
+
+                if (env_entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                                  "Wrong environment entry type "
+                                                  "(expecting integer): "
+                                                  "name=\"{}\"",
+                                                  env_entry_name);
+                    ret = -EINVAL;
+                    goto error;
+                }
+
+                if (env_entry->value.i < 0) {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                                  "Invalid, negative array length: "
+                                                  "env-entry-name=\"{}\", "
+                                                  "value={}",
+                                                  env_entry_name, env_entry->value.i);
+                    ret = -EINVAL;
+                    goto error;
+                }
+
+                array_decl = ctf_field_class_array_create();
+                BT_ASSERT(array_decl);
+                array_decl->length = (uint64_t) env_entry->value.i;
+                array_decl->base.elem_fc = nested_decl;
+                nested_decl = NULL;
+                decl = &array_decl->base.base;
+            } else {
+                seq_decl = ctf_field_class_sequence_create();
+                BT_ASSERT(seq_decl);
+                seq_decl->base.elem_fc = nested_decl;
+                nested_decl = NULL;
+                g_string_assign(seq_decl->length_ref, length_name);
+                decl = &seq_decl->base.base;
+            }
+
+            g_free(length_name);
+            break;
+        }
+        default:
+            ret = -EINVAL;
+            goto error;
+        }
+
+        BT_ASSERT(!nested_decl);
+        BT_ASSERT(decl);
+        BT_ASSERT(!*field_decl);
+
+        /*
+         * At this point, we found the next nested declaration.
+         * We currently own this (and lost the ownership of
+         * nested_decl in the meantime). Pass this next
+         * nested declaration as the content of the outer
+         * container, MOVING its ownership.
+         */
+        ret = visit_field_class_declarator(
+            ctx, cls_specifier_list, field_name,
+            node_field_class_declarator->u.field_class_declarator.u.nested.field_class_declarator,
+            &outer_field_decl, decl);
+        decl = NULL;
+        if (ret) {
+            BT_ASSERT(!outer_field_decl);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        BT_ASSERT(outer_field_decl);
+        *field_decl = outer_field_decl;
+        outer_field_decl = NULL;
+    }
+
+    BT_ASSERT(*field_decl);
+    goto end;
+
+error:
+    ctf_field_class_destroy(*field_decl);
+    *field_decl = NULL;
+
+    if (ret >= 0) {
+        ret = -1;
+    }
+
+end:
+    ctf_field_class_destroy(nested_decl);
+    nested_decl = NULL;
+    return ret;
+}
+
+static int visit_struct_decl_field(struct ctf_visitor_generate_ir *ctx,
+                                   struct ctf_field_class_struct *struct_decl,
+                                   struct ctf_node *cls_specifier_list,
+                                   struct bt_list_head *field_class_declarators)
+{
+    int ret = 0;
+    struct ctf_node *iter;
+    struct ctf_field_class *field_decl = NULL;
+
+    bt_list_for_each_entry (iter, field_class_declarators, siblings) {
+        field_decl = NULL;
+        GQuark qfield_name;
+        const char *field_name;
+
+        ret = visit_field_class_declarator(ctx, cls_specifier_list, &qfield_name, iter, &field_decl,
+                                           NULL);
+        if (ret) {
+            BT_ASSERT(!field_decl);
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                                          "Cannot visit field class declarator: ret={}", ret);
+            goto error;
+        }
+
+        BT_ASSERT(field_decl);
+        field_name = g_quark_to_string(qfield_name);
+
+        /* Check if field with same name already exists */
+        if (ctf_field_class_struct_borrow_member_by_name(struct_decl, field_name)) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                                          "Duplicate field in structure field class: "
+                                          "field-name=\"{}\"",
+                                          field_name);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        /* Add field to structure */
+        ctf_field_class_struct_append_member(struct_decl, field_name, field_decl);
+        field_decl = NULL;
+    }
+
+    return 0;
+
+error:
+    ctf_field_class_destroy(field_decl);
+    field_decl = NULL;
+    return ret;
+}
+
+static int visit_variant_decl_field(struct ctf_visitor_generate_ir *ctx,
+                                    struct ctf_field_class_variant *variant_decl,
+                                    struct ctf_node *cls_specifier_list,
+                                    struct bt_list_head *field_class_declarators)
+{
+    int ret = 0;
+    struct ctf_node *iter;
+    struct ctf_field_class *field_decl = NULL;
+
+    bt_list_for_each_entry (iter, field_class_declarators, siblings) {
+        field_decl = NULL;
+        GQuark qfield_name;
+        const char *field_name;
+
+        ret = visit_field_class_declarator(ctx, cls_specifier_list, &qfield_name, iter, &field_decl,
+                                           NULL);
+        if (ret) {
+            BT_ASSERT(!field_decl);
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                                          "Cannot visit field class declarator: ret={}", ret);
+            goto error;
+        }
+
+        BT_ASSERT(field_decl);
+        field_name = g_quark_to_string(qfield_name);
+
+        /* Check if field with same name already exists */
+        if (ctf_field_class_variant_borrow_option_by_name(variant_decl, field_name)) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                                          "Duplicate field in variant field class: "
+                                          "field-name=\"{}\"",
+                                          field_name);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        /* Add field to structure */
+        ctf_field_class_variant_append_option(variant_decl, field_name, field_decl);
+        field_decl = NULL;
+    }
+
+    return 0;
+
+error:
+    ctf_field_class_destroy(field_decl);
+    field_decl = NULL;
+    return ret;
+}
+
+static int visit_field_class_def(struct ctf_visitor_generate_ir *ctx,
+                                 struct ctf_node *cls_specifier_list,
+                                 struct bt_list_head *field_class_declarators)
+{
+    int ret = 0;
+    GQuark qidentifier;
+    struct ctf_node *iter;
+    struct ctf_field_class *class_decl = NULL;
+
+    bt_list_for_each_entry (iter, field_class_declarators, siblings) {
+        ret = visit_field_class_declarator(ctx, cls_specifier_list, &qidentifier, iter, &class_decl,
+                                           NULL);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(iter, "Cannot visit field class declarator: ret={}", ret);
+            ret = -EINVAL;
+            goto end;
+        }
+
+        /* Do not allow field class def and alias of untagged variants */
+        if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
+            struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(class_decl);
+
+            if (var_fc->tag_path.path->len == 0) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    iter, "Type definition of untagged variant field class is not allowed.");
+                ret = -EPERM;
+                goto end;
+            }
+        }
+
+        ret = ctx_decl_scope_register_alias(ctx->current_scope, g_quark_to_string(qidentifier),
+                                            class_decl);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(iter, "Cannot register field class alias: name=\"{}\"",
+                                          g_quark_to_string(qidentifier));
+            goto end;
+        }
+    }
+
+end:
+    ctf_field_class_destroy(class_decl);
+    class_decl = NULL;
+    return ret;
+}
+
+static int visit_field_class_alias(struct ctf_visitor_generate_ir *ctx, struct ctf_node *target,
+                                   struct ctf_node *alias)
+{
+    int ret = 0;
+    GQuark qalias;
+    struct ctf_node *node;
+    GQuark qdummy_field_name;
+    struct ctf_field_class *class_decl = NULL;
+
+    /* Create target field class */
+    if (bt_list_empty(&target->u.field_class_alias_target.field_class_declarators)) {
+        node = NULL;
+    } else {
+        node = _BT_LIST_FIRST_ENTRY(&target->u.field_class_alias_target.field_class_declarators,
+                                    struct ctf_node, siblings);
+    }
+
+    ret = visit_field_class_declarator(
+        ctx, target->u.field_class_alias_target.field_class_specifier_list, &qdummy_field_name,
+        node, &class_decl, NULL);
+    if (ret) {
+        BT_ASSERT(!class_decl);
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Cannot visit field class declarator: ret={}", ret);
+        goto end;
+    }
+
+    /* Do not allow field class def and alias of untagged variants */
+    if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
+        struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(class_decl);
+
+        if (var_fc->tag_path.path->len == 0) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                target, "Type definition of untagged variant field class is not allowed.");
+            ret = -EPERM;
+            goto end;
+        }
+    }
+
+    /*
+     * The semantic validator does not check whether the target is
+     * abstract or not (if it has an identifier). Check it here.
+     */
+    if (qdummy_field_name != 0) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(target, "Expecting empty identifier: id=\"{}\"",
+                                      g_quark_to_string(qdummy_field_name));
+        ret = -EINVAL;
+        goto end;
+    }
+
+    /* Create alias identifier */
+    node = _BT_LIST_FIRST_ENTRY(&alias->u.field_class_alias_name.field_class_declarators,
+                                struct ctf_node, siblings);
+    qalias = create_class_alias_identifier(
+        ctx, alias->u.field_class_alias_name.field_class_specifier_list, node);
+    ret = ctx_decl_scope_register_alias(ctx->current_scope, g_quark_to_string(qalias), class_decl);
+    if (ret) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Cannot register class alias: name=\"{}\"",
+                                      g_quark_to_string(qalias));
+        goto end;
+    }
+
+end:
+    ctf_field_class_destroy(class_decl);
+    class_decl = NULL;
+    return ret;
+}
+
+static int visit_struct_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
+                                   struct ctf_field_class_struct *struct_decl)
+{
+    int ret = 0;
+
+    switch (entry_node->type) {
+    case NODE_TYPEDEF:
+        ret = visit_field_class_def(ctx, entry_node->u.field_class_def.field_class_specifier_list,
+                                    &entry_node->u.field_class_def.field_class_declarators);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Cannot add field class found in structure field class: ret={}", ret);
+            goto end;
+        }
+        break;
+    case NODE_TYPEALIAS:
+        ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
+                                      entry_node->u.field_class_alias.alias);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Cannot add field class alias found in structure field class: ret={}",
+                ret);
+            goto end;
+        }
+        break;
+    case NODE_STRUCT_OR_VARIANT_DECLARATION:
+        /* Field */
+        ret = visit_struct_decl_field(
+            ctx, struct_decl,
+            entry_node->u.struct_or_variant_declaration.field_class_specifier_list,
+            &entry_node->u.struct_or_variant_declaration.field_class_declarators);
+        if (ret) {
+            goto end;
+        }
+        break;
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node, "Unexpected node type: node-type={}",
+                                      entry_node->type);
+        ret = -EINVAL;
+        goto end;
+    }
+
+end:
+    return ret;
+}
+
+static int visit_variant_decl_entry(struct ctf_visitor_generate_ir *ctx,
+                                    struct ctf_node *entry_node,
+                                    struct ctf_field_class_variant *variant_decl)
+{
+    int ret = 0;
+
+    switch (entry_node->type) {
+    case NODE_TYPEDEF:
+        ret = visit_field_class_def(ctx, entry_node->u.field_class_def.field_class_specifier_list,
+                                    &entry_node->u.field_class_def.field_class_declarators);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Cannot add field class found in variant field class: ret={}", ret);
+            goto end;
+        }
+        break;
+    case NODE_TYPEALIAS:
+        ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
+                                      entry_node->u.field_class_alias.alias);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Cannot add field class alias found in variant field class: ret={}",
+                ret);
+            goto end;
+        }
+        break;
+    case NODE_STRUCT_OR_VARIANT_DECLARATION:
+        /* Field */
+        ret = visit_variant_decl_field(
+            ctx, variant_decl,
+            entry_node->u.struct_or_variant_declaration.field_class_specifier_list,
+            &entry_node->u.struct_or_variant_declaration.field_class_declarators);
+        if (ret) {
+            goto end;
+        }
+        break;
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node, "Unexpected node type: node-type={}",
+                                      entry_node->type);
+        ret = -EINVAL;
+        goto end;
+    }
+
+end:
+    return ret;
+}
+
+static int visit_struct_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
+                             struct bt_list_head *decl_list, int has_body,
+                             struct bt_list_head *min_align,
+                             struct ctf_field_class_struct **struct_decl)
+{
+    int ret = 0;
+
+    BT_ASSERT(struct_decl);
+    *struct_decl = NULL;
+
+    /* For named struct (without body), lookup in declaration scope */
+    if (!has_body) {
+        if (!name) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Bodyless structure field class: missing name.");
+            ret = -EPERM;
+            goto error;
+        }
+
+        *struct_decl = ctx_decl_scope_lookup_struct(ctx->current_scope, name, -1, true);
+        if (!*struct_decl) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger, "Cannot find structure field class: name=\"struct {}\"", name);
+            ret = -EINVAL;
+            goto error;
+        }
+    } else {
+        struct ctf_node *entry_node;
+        uint64_t min_align_value = 0;
+
+        if (name) {
+            if (ctx_decl_scope_lookup_struct(ctx->current_scope, name, 1, false)) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    ctx->logger,
+                    "Structure field class already declared in local scope: "
+                    "name=\"struct {}\"",
+                    name);
+                ret = -EINVAL;
+                goto error;
+            }
+        }
+
+        if (!bt_list_empty(min_align)) {
+            ret = get_unary_unsigned(ctx, min_align, &min_align_value);
+            if (ret) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    ctx->logger,
+                    "Unexpected unary expression for structure field class's `align` attribute: "
+                    "ret={}",
+                    ret);
+                goto error;
+            }
+        }
+
+        *struct_decl = ctf_field_class_struct_create();
+        BT_ASSERT(*struct_decl);
+
+        if (min_align_value != 0) {
+            (*struct_decl)->base.alignment = min_align_value;
+        }
+
+        _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+        bt_list_for_each_entry (entry_node, decl_list, siblings) {
+            ret = visit_struct_decl_entry(ctx, entry_node, *struct_decl);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node,
+                                              "Cannot visit structure field class entry: "
+                                              "ret={}",
+                                              ret);
+                ctx_pop_scope(ctx);
+                goto error;
+            }
+        }
+
+        ctx_pop_scope(ctx);
+
+        if (name) {
+            ret = ctx_decl_scope_register_struct(ctx->current_scope, name, *struct_decl);
+            if (ret) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    ctx->logger,
+                    "Cannot register structure field class in declaration scope: "
+                    "name=\"struct {}\", ret={}",
+                    name, ret);
+                goto error;
+            }
+        }
+    }
+
+    return 0;
+
+error:
+    ctf_field_class_destroy(&(*struct_decl)->base);
+    *struct_decl = NULL;
+    return ret;
+}
+
+static int visit_variant_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
+                              const char *tag, struct bt_list_head *decl_list, int has_body,
+                              struct ctf_field_class_variant **variant_decl)
+{
+    int ret = 0;
+    struct ctf_field_class_variant *untagged_variant_decl = NULL;
+
+    BT_ASSERT(variant_decl);
+    *variant_decl = NULL;
+
+    /* For named variant (without body), lookup in declaration scope */
+    if (!has_body) {
+        if (!name) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Bodyless variant field class: missing name.");
+            ret = -EPERM;
+            goto error;
+        }
+
+        untagged_variant_decl = ctx_decl_scope_lookup_variant(ctx->current_scope, name, -1, true);
+        if (!untagged_variant_decl) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger, "Cannot find variant field class: name=\"variant {}\"", name);
+            ret = -EINVAL;
+            goto error;
+        }
+    } else {
+        struct ctf_node *entry_node;
+
+        if (name) {
+            if (ctx_decl_scope_lookup_variant(ctx->current_scope, name, 1, false)) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                             "Variant field class already declared in local scope: "
+                                             "name=\"variant {}\"",
+                                             name);
+                ret = -EINVAL;
+                goto error;
+            }
+        }
+
+        untagged_variant_decl = ctf_field_class_variant_create();
+        BT_ASSERT(untagged_variant_decl);
+        _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+        bt_list_for_each_entry (entry_node, decl_list, siblings) {
+            ret = visit_variant_decl_entry(ctx, entry_node, untagged_variant_decl);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node,
+                                              "Cannot visit variant field class entry: "
+                                              "ret={}",
+                                              ret);
+                ctx_pop_scope(ctx);
+                goto error;
+            }
+        }
+
+        ctx_pop_scope(ctx);
+
+        if (name) {
+            ret = ctx_decl_scope_register_variant(ctx->current_scope, name, untagged_variant_decl);
+            if (ret) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    ctx->logger,
+                    "Cannot register variant field class in declaration scope: "
+                    "name=\"variant {}\", ret={}",
+                    name, ret);
+                goto error;
+            }
+        }
+    }
+
+    /*
+     * If tagged, create tagged variant and return; otherwise
+     * return untagged variant.
+     */
+    if (!tag) {
+        *variant_decl = untagged_variant_decl;
+        untagged_variant_decl = NULL;
+    } else {
+        /*
+         * At this point, we have a fresh untagged variant; nobody
+         * else owns it. Set its tag now.
+         */
+        g_string_assign(untagged_variant_decl->tag_ref, tag);
+        *variant_decl = untagged_variant_decl;
+        untagged_variant_decl = NULL;
+    }
+
+    BT_ASSERT(!untagged_variant_decl);
+    BT_ASSERT(*variant_decl);
+    return 0;
+
+error:
+    ctf_field_class_destroy(&untagged_variant_decl->base);
+    untagged_variant_decl = NULL;
+    ctf_field_class_destroy(&(*variant_decl)->base);
+    *variant_decl = NULL;
+    return ret;
+}
+
+struct uori
+{
+    bool is_signed;
+    union
+    {
+        uint64_t u;
+        uint64_t i;
+    } value;
+};
+
+static int visit_enum_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *enumerator,
+                                 struct ctf_field_class_enum *enum_decl, struct uori *last)
+{
+    int ret = 0;
+    int nr_vals = 0;
+    struct ctf_node *iter;
+    struct uori start = {
+        .is_signed = false,
+        .value =
+            {
+                .u = 0,
+            },
+    };
+    struct uori end = {
+        .is_signed = false,
+        .value =
+            {
+                .u = 0,
+            },
+    };
+    const char *label = enumerator->u.enumerator.id;
+    struct bt_list_head *values = &enumerator->u.enumerator.values;
+
+    bt_list_for_each_entry (iter, values, siblings) {
+        struct uori *target;
+
+        if (iter->type != NODE_UNARY_EXPRESSION) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(iter,
+                                          "Wrong expression for enumeration field class label: "
+                                          "node-type={}, label=\"{}\"",
+                                          iter->type, label);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (nr_vals == 0) {
+            target = &start;
+        } else {
+            target = &end;
+        }
+
+        switch (iter->u.unary_expression.type) {
+        case UNARY_SIGNED_CONSTANT:
+            target->is_signed = true;
+            target->value.i = iter->u.unary_expression.u.signed_constant;
+            break;
+        case UNARY_UNSIGNED_CONSTANT:
+            target->is_signed = false;
+            target->value.u = iter->u.unary_expression.u.unsigned_constant;
+            break;
+        default:
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(iter,
+                                          "Invalid enumeration field class entry: "
+                                          "expecting constant signed or unsigned integer: "
+                                          "node-type={}, label=\"{}\"",
+                                          iter->u.unary_expression.type, label);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (nr_vals > 1) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                iter, "Invalid enumeration field class entry: label=\"{}\"", label);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        nr_vals++;
+    }
+
+    if (nr_vals == 0) {
+        start = *last;
+    }
+
+    if (nr_vals <= 1) {
+        end = start;
+    }
+
+    if (end.is_signed) {
+        last->value.i = end.value.i + 1;
+    } else {
+        last->value.u = end.value.u + 1;
+    }
+
+    ctf_field_class_enum_map_range(enum_decl, label, start.value.u, end.value.u);
+    return 0;
+
+error:
+    return ret;
+}
+
+static int visit_enum_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
+                           struct ctf_node *container_cls, struct bt_list_head *enumerator_list,
+                           int has_body, struct ctf_field_class_enum **enum_decl)
+{
+    int ret = 0;
+    GQuark qdummy_id;
+    struct ctf_field_class_int *integer_decl = NULL;
+
+    BT_ASSERT(enum_decl);
+    *enum_decl = NULL;
+
+    /* For named enum (without body), lookup in declaration scope */
+    if (!has_body) {
+        if (!name) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Bodyless enumeration field class: missing name.");
+            ret = -EPERM;
+            goto error;
+        }
+
+        *enum_decl = ctx_decl_scope_lookup_enum(ctx->current_scope, name, -1, true);
+        if (!*enum_decl) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                         "Cannot find enumeration field class: "
+                                         "name=\"enum {}\"",
+                                         name);
+            ret = -EINVAL;
+            goto error;
+        }
+    } else {
+        struct ctf_node *iter;
+        struct uori last_value = {
+            .is_signed = false,
+            .value =
+                {
+                    .u = 0,
+                },
+        };
+
+        if (name) {
+            if (ctx_decl_scope_lookup_enum(ctx->current_scope, name, 1, false)) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    ctx->logger,
+                    "Enumeration field class already declared in local scope: "
+                    "name=\"enum {}\"",
+                    name);
+                ret = -EINVAL;
+                goto error;
+            }
+        }
+
+        if (!container_cls) {
+            integer_decl = ctf_field_class_as_int(
+                ctx_decl_scope_lookup_alias(ctx->current_scope, "int", -1, true));
+            if (!integer_decl) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    ctx->logger,
+                    "Cannot find implicit `int` field class alias for enumeration field class.");
+                ret = -EINVAL;
+                goto error;
+            }
+        } else {
+            ctf_field_class *decl;
+
+            ret = visit_field_class_declarator(ctx, container_cls, &qdummy_id, NULL, &decl, NULL);
+            if (ret) {
+                BT_ASSERT(!decl);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            integer_decl = ctf_field_class_as_int(decl);
+        }
+
+        BT_ASSERT(integer_decl);
+
+        if (integer_decl->base.base.type != CTF_FIELD_CLASS_TYPE_INT) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger,
+                "Container field class for enumeration field class is not an integer field class: "
+                "fc-type={}",
+                integer_decl->base.base.type);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        *enum_decl = ctf_field_class_enum_create();
+        BT_ASSERT(*enum_decl);
+        (*enum_decl)->base.base.base.alignment = integer_decl->base.base.alignment;
+        ctf_field_class_int_copy_content(&(*enum_decl)->base, integer_decl);
+        last_value.is_signed = (*enum_decl)->base.is_signed;
+
+        bt_list_for_each_entry (iter, enumerator_list, siblings) {
+            ret = visit_enum_decl_entry(ctx, iter, *enum_decl, &last_value);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(iter,
+                                              "Cannot visit enumeration field class entry: "
+                                              "ret={}",
+                                              ret);
+                goto error;
+            }
+        }
+
+        if (name) {
+            ret = ctx_decl_scope_register_enum(ctx->current_scope, name, *enum_decl);
+            if (ret) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    ctx->logger,
+                    "Cannot register enumeration field class in declaration scope: "
+                    "ret={}",
+                    ret);
+                goto error;
+            }
+        }
+    }
+
+    goto end;
+
+error:
+    ctf_field_class_destroy(&(*enum_decl)->base.base.base);
+    *enum_decl = NULL;
+
+end:
+    ctf_field_class_destroy(&integer_decl->base.base);
+    integer_decl = NULL;
+    return ret;
+}
+
+static int visit_field_class_specifier(struct ctf_visitor_generate_ir *ctx,
+                                       struct ctf_node *cls_specifier_list,
+                                       struct ctf_field_class **decl)
+{
+    int ret = 0;
+    GString *str = NULL;
+
+    *decl = NULL;
+    str = g_string_new("");
+    ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
+    if (ret) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                                      "Cannot get field class specifier list's name: ret={}", ret);
+        goto error;
+    }
+
+    *decl = ctx_decl_scope_lookup_alias(ctx->current_scope, str->str, -1, true);
+    if (!*decl) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                                      "Cannot find field class alias: name=\"{}\"", str->str);
+        ret = -EINVAL;
+        goto error;
+    }
+
+    goto end;
+
+error:
+    ctf_field_class_destroy(*decl);
+    *decl = NULL;
+
+end:
+    if (str) {
+        g_string_free(str, TRUE);
+    }
+
+    return ret;
+}
+
+static int visit_integer_decl(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *expressions,
+                              struct ctf_field_class_int **integer_decl)
+{
+    int set = 0;
+    int ret = 0;
+    int signedness = 0;
+    struct ctf_node *expression;
+    uint64_t alignment = 0, size = 0;
+    struct ctf_clock_class *mapped_clock_class = NULL;
+    enum ctf_encoding encoding = CTF_ENCODING_NONE;
+    bt_field_class_integer_preferred_display_base base =
+        BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+    enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
+
+    *integer_decl = NULL;
+
+    bt_list_for_each_entry (expression, expressions, siblings) {
+        struct ctf_node *left, *right;
+
+        left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, struct ctf_node, siblings);
+        right =
+            _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.right, struct ctf_node, siblings);
+
+        if (left->u.unary_expression.type != UNARY_STRING) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(left, "Unexpected unary expression type: type={}",
+                                          left->u.unary_expression.type);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (strcmp(left->u.unary_expression.u.string, "signed") == 0) {
+            if (_IS_SET(&set, _INTEGER_SIGNED_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "signed", "integer field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            signedness = get_boolean(ctx, right);
+            if (signedness < 0) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Invalid boolean value for integer field class's `signed` attribute: "
+                    "ret={}",
+                    ret);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            _SET(&set, _INTEGER_SIGNED_SET);
+        } else if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
+            if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order", "integer field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            byte_order = get_real_byte_order(ctx, right);
+            if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Invalid `byte_order` attribute in integer field class: "
+                    "ret={}",
+                    ret);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            _SET(&set, _INTEGER_BYTE_ORDER_SET);
+        } else if (strcmp(left->u.unary_expression.u.string, "size") == 0) {
+            if (_IS_SET(&set, _INTEGER_SIZE_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "size", "integer field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(right,
+                                              "Invalid `size` attribute in integer field class: "
+                                              "expecting unsigned constant integer: "
+                                              "node-type={}",
+                                              right->u.unary_expression.type);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            size = right->u.unary_expression.u.unsigned_constant;
+            if (size == 0) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(right,
+                                              "Invalid `size` attribute in integer field class: "
+                                              "expecting positive constant integer: "
+                                              "size={}",
+                                              size);
+                ret = -EINVAL;
+                goto error;
+            } else if (size > 64) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Invalid `size` attribute in integer field class: "
+                    "integer fields over 64 bits are not supported as of this version: "
+                    "size={}",
+                    size);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            _SET(&set, _INTEGER_SIZE_SET);
+        } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
+            if (_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "align", "integer field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(right,
+                                              "Invalid `align` attribute in integer field class: "
+                                              "expecting unsigned constant integer: "
+                                              "node-type={}",
+                                              right->u.unary_expression.type);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            alignment = right->u.unary_expression.u.unsigned_constant;
+            if (!is_align_valid(alignment)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(right,
+                                              "Invalid `align` attribute in integer field class: "
+                                              "expecting power of two: "
+                                              "align={}",
+                                              alignment);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            _SET(&set, _INTEGER_ALIGN_SET);
+        } else if (strcmp(left->u.unary_expression.u.string, "base") == 0) {
+            if (_IS_SET(&set, _INTEGER_BASE_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "base", "integer field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            switch (right->u.unary_expression.type) {
+            case UNARY_UNSIGNED_CONSTANT:
+            {
+                uint64_t constant = right->u.unary_expression.u.unsigned_constant;
+
+                switch (constant) {
+                case 2:
+                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
+                    break;
+                case 8:
+                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
+                    break;
+                case 10:
+                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+                    break;
+                case 16:
+                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
+                    break;
+                default:
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                        right,
+                        "Invalid `base` attribute in integer field class: "
+                        "base={}",
+                        right->u.unary_expression.u.unsigned_constant);
+                    ret = -EINVAL;
+                    goto error;
+                }
+                break;
+            }
+            case UNARY_STRING:
+            {
+                char *s_right =
+                    ctf_ast_concatenate_unary_strings(&expression->u.ctf_expression.right);
+                if (!s_right) {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                        right,
+                        "Unexpected unary expression for integer field class's `base` attribute.");
+                    ret = -EINVAL;
+                    goto error;
+                }
+
+                if (strcmp(s_right, "decimal") == 0 || strcmp(s_right, "dec") == 0 ||
+                    strcmp(s_right, "d") == 0 || strcmp(s_right, "i") == 0 ||
+                    strcmp(s_right, "u") == 0) {
+                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+                } else if (strcmp(s_right, "hexadecimal") == 0 || strcmp(s_right, "hex") == 0 ||
+                           strcmp(s_right, "x") == 0 || strcmp(s_right, "X") == 0 ||
+                           strcmp(s_right, "p") == 0) {
+                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
+                } else if (strcmp(s_right, "octal") == 0 || strcmp(s_right, "oct") == 0 ||
+                           strcmp(s_right, "o") == 0) {
+                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
+                } else if (strcmp(s_right, "binary") == 0 || strcmp(s_right, "b") == 0) {
+                    base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
+                } else {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                        right,
+                        "Unexpected unary expression for integer field class's `base` attribute: "
+                        "base=\"{}\"",
+                        s_right);
+                    g_free(s_right);
+                    ret = -EINVAL;
+                    goto error;
+                }
+
+                g_free(s_right);
+                break;
+            }
+            default:
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right, "Invalid `base` attribute in integer field class: "
+                           "expecting unsigned constant integer or unary string.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            _SET(&set, _INTEGER_BASE_SET);
+        } else if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
+            char *s_right;
+
+            if (_IS_SET(&set, _INTEGER_ENCODING_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding", "integer field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            if (right->u.unary_expression.type != UNARY_STRING) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right, "Invalid `encoding` attribute in integer field class: "
+                           "expecting unary string.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            s_right = ctf_ast_concatenate_unary_strings(&expression->u.ctf_expression.right);
+            if (!s_right) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Unexpected unary expression for integer field class's `encoding` attribute.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (strcmp(s_right, "UTF8") == 0 || strcmp(s_right, "utf8") == 0 ||
+                strcmp(s_right, "utf-8") == 0 || strcmp(s_right, "UTF-8") == 0 ||
+                strcmp(s_right, "ASCII") == 0 || strcmp(s_right, "ascii") == 0) {
+                encoding = CTF_ENCODING_UTF8;
+            } else if (strcmp(s_right, "none") == 0) {
+                encoding = CTF_ENCODING_NONE;
+            } else {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Invalid `encoding` attribute in integer field class: "
+                    "unknown encoding: encoding=\"{}\"",
+                    s_right);
+                g_free(s_right);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            g_free(s_right);
+            _SET(&set, _INTEGER_ENCODING_SET);
+        } else if (strcmp(left->u.unary_expression.u.string, "map") == 0) {
+            const char *clock_name;
+
+            if (_IS_SET(&set, _INTEGER_MAP_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "map", "integer field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            if (right->u.unary_expression.type != UNARY_STRING) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(right,
+                                              "Invalid `map` attribute in integer field class: "
+                                              "expecting unary string.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            clock_name = get_map_clock_name_value(&expression->u.ctf_expression.right);
+            if (!clock_name) {
+                char *s_right =
+                    ctf_ast_concatenate_unary_strings(&expression->u.ctf_expression.right);
+
+                if (!s_right) {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                        right,
+                        "Unexpected unary expression for integer field class's `map` attribute.");
+                    ret = -EINVAL;
+                    goto error;
+                }
+
+                _BT_CPPLOGE_NODE(right,
+                                 "Invalid `map` attribute in integer field class: "
+                                 "cannot find clock class at this point: name=\"{}\"",
+                                 s_right);
+                _SET(&set, _INTEGER_MAP_SET);
+                g_free(s_right);
+                continue;
+            }
+
+            mapped_clock_class =
+                ctf_trace_class_borrow_clock_class_by_name(ctx->ctf_tc, clock_name);
+            if (!mapped_clock_class) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(right,
+                                              "Invalid `map` attribute in integer field class: "
+                                              "cannot find clock class at this point: name=\"{}\"",
+                                              clock_name);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            _SET(&set, _INTEGER_MAP_SET);
+        } else {
+            _BT_CPPLOGW_NODE(left,
+                             "Unknown attribute in integer field class: "
+                             "attr-name=\"{}\"",
+                             left->u.unary_expression.u.string);
+        }
+    }
+
+    if (!_IS_SET(&set, _INTEGER_SIZE_SET)) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "Missing `size` attribute in integer field class.");
+        ret = -EPERM;
+        goto error;
+    }
+
+    if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+        if (size % CHAR_BIT) {
+            /* Bit-packed alignment */
+            alignment = 1;
+        } else {
+            /* Byte-packed alignment */
+            alignment = CHAR_BIT;
+        }
+    }
+
+    *integer_decl = ctf_field_class_int_create();
+    BT_ASSERT(*integer_decl);
+    (*integer_decl)->base.base.alignment = alignment;
+    (*integer_decl)->base.byte_order = byte_order;
+    (*integer_decl)->base.size = size;
+    (*integer_decl)->is_signed = (signedness > 0);
+    (*integer_decl)->disp_base = base;
+    (*integer_decl)->encoding = encoding;
+    (*integer_decl)->mapped_clock_class = mapped_clock_class;
+    return 0;
+
+error:
+    ctf_field_class_destroy(&(*integer_decl)->base.base);
+    *integer_decl = NULL;
+    return ret;
+}
+
+static int visit_floating_point_number_decl(struct ctf_visitor_generate_ir *ctx,
+                                            struct bt_list_head *expressions,
+                                            struct ctf_field_class_float **float_decl)
+{
+    int set = 0;
+    int ret = 0;
+    struct ctf_node *expression;
+    uint64_t alignment = 1, exp_dig = 0, mant_dig = 0;
+    enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
+
+    *float_decl = NULL;
+
+    bt_list_for_each_entry (expression, expressions, siblings) {
+        struct ctf_node *left, *right;
+
+        left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, struct ctf_node, siblings);
+        right =
+            _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.right, struct ctf_node, siblings);
+
+        if (left->u.unary_expression.type != UNARY_STRING) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(left, "Unexpected unary expression type: type={}",
+                                          left->u.unary_expression.type);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
+            if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order",
+                                                  "floating point number field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            byte_order = get_real_byte_order(ctx, right);
+            if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Invalid `byte_order` attribute in floating point number field class: "
+                    "ret={}",
+                    ret);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            _SET(&set, _FLOAT_BYTE_ORDER_SET);
+        } else if (strcmp(left->u.unary_expression.u.string, "exp_dig") == 0) {
+            if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "exp_dig",
+                                                  "floating point number field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Invalid `exp_dig` attribute in floating point number field class: "
+                    "expecting unsigned constant integer: "
+                    "node-type={}",
+                    right->u.unary_expression.type);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            exp_dig = right->u.unary_expression.u.unsigned_constant;
+            _SET(&set, _FLOAT_EXP_DIG_SET);
+        } else if (strcmp(left->u.unary_expression.u.string, "mant_dig") == 0) {
+            if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "mant_dig",
+                                                  "floating point number field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Invalid `mant_dig` attribute in floating point number field class: "
+                    "expecting unsigned constant integer: "
+                    "node-type={}",
+                    right->u.unary_expression.type);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            mant_dig = right->u.unary_expression.u.unsigned_constant;
+            _SET(&set, _FLOAT_MANT_DIG_SET);
+        } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
+            if (_IS_SET(&set, _FLOAT_ALIGN_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "align",
+                                                  "floating point number field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            if (right->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Invalid `align` attribute in floating point number field class: "
+                    "expecting unsigned constant integer: "
+                    "node-type={}",
+                    right->u.unary_expression.type);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            alignment = right->u.unary_expression.u.unsigned_constant;
+
+            if (!is_align_valid(alignment)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Invalid `align` attribute in floating point number field class: "
+                    "expecting power of two: "
+                    "align={}",
+                    alignment);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            _SET(&set, _FLOAT_ALIGN_SET);
+        } else {
+            _BT_CPPLOGW_NODE(left,
+                             "Unknown attribute in floating point number field class: "
+                             "attr-name=\"{}\"",
+                             left->u.unary_expression.u.string);
+        }
+    }
+
+    if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            ctx->logger, "Missing `mant_dig` attribute in floating point number field class.");
+        ret = -EPERM;
+        goto error;
+    }
+
+    if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            ctx->logger, "Missing `exp_dig` attribute in floating point number field class.");
+        ret = -EPERM;
+        goto error;
+    }
+
+    if (mant_dig != 24 && mant_dig != 53) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "`mant_dig` attribute: expecting 24 or 53.");
+        ret = -EPERM;
+        goto error;
+    }
+
+    if (mant_dig == 24 && exp_dig != 8) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "`exp_dig` attribute: expecting 8 because `mant_dig` is 24.");
+        ret = -EPERM;
+        goto error;
+    }
+
+    if (mant_dig == 53 && exp_dig != 11) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
+                                     "`exp_dig` attribute: expecting 11 because `mant_dig` is 53.");
+        ret = -EPERM;
+        goto error;
+    }
+
+    if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+        if ((mant_dig + exp_dig) % CHAR_BIT) {
+            /* Bit-packed alignment */
+            alignment = 1;
+        } else {
+            /* Byte-packed alignment */
+            alignment = CHAR_BIT;
+        }
+    }
+
+    *float_decl = ctf_field_class_float_create();
+    BT_ASSERT(*float_decl);
+    (*float_decl)->base.base.alignment = alignment;
+    (*float_decl)->base.byte_order = byte_order;
+    (*float_decl)->base.size = mant_dig + exp_dig;
+    return 0;
+
+error:
+    ctf_field_class_destroy(&(*float_decl)->base.base);
+    *float_decl = NULL;
+    return ret;
+}
+
+static int visit_string_decl(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *expressions,
+                             struct ctf_field_class_string **string_decl)
+{
+    int set = 0;
+    int ret = 0;
+    struct ctf_node *expression;
+    enum ctf_encoding encoding = CTF_ENCODING_UTF8;
+
+    *string_decl = NULL;
+
+    bt_list_for_each_entry (expression, expressions, siblings) {
+        struct ctf_node *left, *right;
+
+        left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, struct ctf_node, siblings);
+        right =
+            _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.right, struct ctf_node, siblings);
+
+        if (left->u.unary_expression.type != UNARY_STRING) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(left, "Unexpected unary expression type: type={}",
+                                          left->u.unary_expression.type);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
+            char *s_right;
+
+            if (_IS_SET(&set, _STRING_ENCODING_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding", "string field class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            if (right->u.unary_expression.type != UNARY_STRING) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(right,
+                                              "Invalid `encoding` attribute in string field class: "
+                                              "expecting unary string.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            s_right = ctf_ast_concatenate_unary_strings(&expression->u.ctf_expression.right);
+            if (!s_right) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    right,
+                    "Unexpected unary expression for string field class's `encoding` attribute.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (strcmp(s_right, "UTF8") == 0 || strcmp(s_right, "utf8") == 0 ||
+                strcmp(s_right, "utf-8") == 0 || strcmp(s_right, "UTF-8") == 0 ||
+                strcmp(s_right, "ASCII") == 0 || strcmp(s_right, "ascii") == 0) {
+                encoding = CTF_ENCODING_UTF8;
+            } else if (strcmp(s_right, "none") == 0) {
+                encoding = CTF_ENCODING_NONE;
+            } else {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(right,
+                                              "Invalid `encoding` attribute in string field class: "
+                                              "unknown encoding: encoding=\"{}\"",
+                                              s_right);
+                g_free(s_right);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            g_free(s_right);
+            _SET(&set, _STRING_ENCODING_SET);
+        } else {
+            _BT_CPPLOGW_NODE(left,
+                             "Unknown attribute in string field class: "
+                             "attr-name=\"{}\"",
+                             left->u.unary_expression.u.string);
+        }
+    }
+
+    *string_decl = ctf_field_class_string_create();
+    BT_ASSERT(*string_decl);
+    (*string_decl)->encoding = encoding;
+    return 0;
+
+error:
+    ctf_field_class_destroy(&(*string_decl)->base);
+    *string_decl = NULL;
+    return ret;
+}
+
+static int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
+                                            struct ctf_node *ts_list, struct ctf_field_class **decl)
+{
+    int ret = 0;
+    struct ctf_node *first, *node;
+
+    *decl = NULL;
+
+    if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(ts_list, "Unexpected node type: node-type={}", ts_list->type);
+        ret = -EINVAL;
+        goto error;
+    }
+
+    first = _BT_LIST_FIRST_ENTRY(&ts_list->u.field_class_specifier_list.head, struct ctf_node,
+                                 siblings);
+    if (first->type != NODE_TYPE_SPECIFIER) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(first, "Unexpected node type: node-type={}", first->type);
+        ret = -EINVAL;
+        goto error;
+    }
+
+    node = first->u.field_class_specifier.node;
+
+    switch (first->u.field_class_specifier.type) {
+    case TYPESPEC_INTEGER:
+    {
+        ctf_field_class_int *int_decl;
+
+        ret = visit_integer_decl(ctx, &node->u.integer.expressions, &int_decl);
+        if (ret) {
+            BT_ASSERT(!int_decl);
+            goto error;
+        }
+
+        *decl = &int_decl->base.base;
+        break;
+    }
+    case TYPESPEC_FLOATING_POINT:
+    {
+        ctf_field_class_float *float_decl;
+
+        ret =
+            visit_floating_point_number_decl(ctx, &node->u.floating_point.expressions, &float_decl);
+        if (ret) {
+            BT_ASSERT(!float_decl);
+            goto error;
+        }
+
+        *decl = &float_decl->base.base;
+        break;
+    }
+    case TYPESPEC_STRING:
+    {
+        ctf_field_class_string *string_decl;
+
+        ret = visit_string_decl(ctx, &node->u.string.expressions, &string_decl);
+        if (ret) {
+            BT_ASSERT(!string_decl);
+            goto error;
+        }
+
+        *decl = &string_decl->base;
+        break;
+    }
+    case TYPESPEC_STRUCT:
+    {
+        ctf_field_class_struct *struct_decl;
+
+        ret = visit_struct_decl(ctx, node->u._struct.name, &node->u._struct.declaration_list,
+                                node->u._struct.has_body, &node->u._struct.min_align, &struct_decl);
+        if (ret) {
+            BT_ASSERT(!struct_decl);
+            goto error;
+        }
+
+        *decl = &struct_decl->base;
+        break;
+    }
+    case TYPESPEC_VARIANT:
+    {
+        ctf_field_class_variant *variant_decl;
+
+        ret = visit_variant_decl(ctx, node->u.variant.name, node->u.variant.choice,
+                                 &node->u.variant.declaration_list, node->u.variant.has_body,
+                                 &variant_decl);
+        if (ret) {
+            BT_ASSERT(!variant_decl);
+            goto error;
+        }
+
+        *decl = &variant_decl->base;
+        break;
+    }
+    case TYPESPEC_ENUM:
+    {
+        ctf_field_class_enum *enum_decl;
+
+        ret = visit_enum_decl(ctx, node->u._enum.enum_id, node->u._enum.container_field_class,
+                              &node->u._enum.enumerator_list, node->u._enum.has_body, &enum_decl);
+        if (ret) {
+            BT_ASSERT(!enum_decl);
+            goto error;
+        }
+
+        *decl = &enum_decl->base.base.base;
+        break;
+    }
+    case TYPESPEC_VOID:
+    case TYPESPEC_CHAR:
+    case TYPESPEC_SHORT:
+    case TYPESPEC_INT:
+    case TYPESPEC_LONG:
+    case TYPESPEC_FLOAT:
+    case TYPESPEC_DOUBLE:
+    case TYPESPEC_SIGNED:
+    case TYPESPEC_UNSIGNED:
+    case TYPESPEC_BOOL:
+    case TYPESPEC_COMPLEX:
+    case TYPESPEC_IMAGINARY:
+    case TYPESPEC_CONST:
+    case TYPESPEC_ID_TYPE:
+        ret = visit_field_class_specifier(ctx, ts_list, decl);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(first, "Cannot visit field class specifier: ret={}", ret);
+            BT_ASSERT(!*decl);
+            goto error;
+        }
+        break;
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(first, "Unexpected field class specifier type: node-type={}",
+                                      first->u.field_class_specifier.type);
+        ret = -EINVAL;
+        goto error;
+    }
+
+    BT_ASSERT(*decl);
+    return 0;
+
+error:
+    ctf_field_class_destroy(*decl);
+    *decl = NULL;
+    return ret;
+}
+
+static int visit_event_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
+                                  struct ctf_event_class *event_class, uint64_t *stream_id,
+                                  int *set)
+{
+    int ret = 0;
+    char *left = NULL;
+
+    switch (node->type) {
+    case NODE_TYPEDEF:
+        ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
+                                    &node->u.field_class_def.field_class_declarators);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Cannot add field class found in event class.");
+            goto error;
+        }
+        break;
+    case NODE_TYPEALIAS:
+        ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
+                                      node->u.field_class_alias.alias);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                          "Cannot add field class alias found in event class.");
+            goto error;
+        }
+        break;
+    case NODE_CTF_EXPRESSION:
+    {
+        left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+        if (!left) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (strcmp(left, "name") == 0) {
+            /* This is already known at this stage */
+            if (_IS_SET(set, _EVENT_NAME_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "name", "event class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            _SET(set, _EVENT_NAME_SET);
+        } else if (strcmp(left, "id") == 0) {
+            int64_t id = -1;
+
+            if (_IS_SET(set, _EVENT_ID_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "id", "event class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, (uint64_t *) &id);
+            /* Only read "id" if get_unary_unsigned() succeeded. */
+            if (ret || (!ret && id < 0)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node, "Unexpected unary expression for event class's `id` attribute.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            event_class->id = id;
+            _SET(set, _EVENT_ID_SET);
+        } else if (strcmp(left, "stream_id") == 0) {
+            if (_IS_SET(set, _EVENT_STREAM_ID_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "stream_id", "event class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, stream_id);
+
+            /*
+             * Only read "stream_id" if get_unary_unsigned()
+             * succeeded.
+             */
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node, "Unexpected unary expression for event class's `stream_id` attribute.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            _SET(set, _EVENT_STREAM_ID_SET);
+        } else if (strcmp(left, "context") == 0) {
+            if (_IS_SET(set, _EVENT_CONTEXT_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Duplicate `context` entry in event class.");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = visit_field_class_specifier_list(
+                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
+                &event_class->spec_context_fc);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                              "Cannot create event class's context field class.");
+                goto error;
+            }
+
+            BT_ASSERT(event_class->spec_context_fc);
+            _SET(set, _EVENT_CONTEXT_SET);
+        } else if (strcmp(left, "fields") == 0) {
+            if (_IS_SET(set, _EVENT_FIELDS_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Duplicate `fields` entry in event class.");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = visit_field_class_specifier_list(
+                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
+                &event_class->payload_fc);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                              "Cannot create event class's payload field class.");
+                goto error;
+            }
+
+            BT_ASSERT(event_class->payload_fc);
+            _SET(set, _EVENT_FIELDS_SET);
+        } else if (strcmp(left, "loglevel") == 0) {
+            uint64_t loglevel_value;
+            bool is_log_level_known = true;
+            bt_event_class_log_level log_level;
+
+            if (_IS_SET(set, _EVENT_LOG_LEVEL_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "loglevel", "event class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, &loglevel_value);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node, "Unexpected unary expression for event class's `loglevel` attribute.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            switch (loglevel_value) {
+            case 0:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY;
+                break;
+            case 1:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_ALERT;
+                break;
+            case 2:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_CRITICAL;
+                break;
+            case 3:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_ERROR;
+                break;
+            case 4:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_WARNING;
+                break;
+            case 5:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_NOTICE;
+                break;
+            case 6:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_INFO;
+                break;
+            case 7:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM;
+                break;
+            case 8:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM;
+                break;
+            case 9:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS;
+                break;
+            case 10:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE;
+                break;
+            case 11:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT;
+                break;
+            case 12:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION;
+                break;
+            case 13:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE;
+                break;
+            case 14:
+                log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG;
+                break;
+            default:
+                is_log_level_known = false;
+                _BT_CPPLOGW_NODE(
+                    node,
+                    "Not setting event class's log level because its value is unknown: "
+                    "log-level={}",
+                    loglevel_value);
+            }
+
+            if (is_log_level_known) {
+                ctf_event_class_set_log_level(event_class, log_level);
+            }
+
+            _SET(set, _EVENT_LOG_LEVEL_SET);
+        } else if (strcmp(left, "model.emf.uri") == 0) {
+            char *right;
+
+            if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "model.emf.uri", "event class");
+                ret = -EPERM;
+                goto error;
+            }
+
+            right = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.right);
+            if (!right) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node,
+                    "Unexpected unary expression for event class's `model.emf.uri` attribute.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (strlen(right) == 0) {
+                _BT_CPPLOGW_NODE(node, "Not setting event class's EMF URI because it's empty.");
+            } else {
+                g_string_assign(event_class->emf_uri, right);
+            }
+
+            g_free(right);
+            _SET(set, _EVENT_MODEL_EMF_URI_SET);
+        } else {
+            _BT_CPPLOGW_NODE(node,
+                             "Unknown attribute in event class: "
+                             "attr-name=\"{}\"",
+                             left);
+        }
+
+        g_free(left);
+        left = NULL;
+        break;
+    }
+    default:
+        ret = -EPERM;
+        goto error;
+    }
+
+    goto end;
+
+error:
+    g_free(left);
+
+end:
+    return ret;
+}
+
+static char *get_event_decl_name(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+    char *left = NULL;
+    char *name = NULL;
+    struct ctf_node *iter;
+    struct bt_list_head *decl_list = &node->u.event.declaration_list;
+
+    bt_list_for_each_entry (iter, decl_list, siblings) {
+        if (iter->type != NODE_CTF_EXPRESSION) {
+            continue;
+        }
+
+        left = ctf_ast_concatenate_unary_strings(&iter->u.ctf_expression.left);
+        if (!left) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(iter, "Cannot concatenate unary strings.");
+            goto error;
+        }
+
+        if (strcmp(left, "name") == 0) {
+            name = ctf_ast_concatenate_unary_strings(&iter->u.ctf_expression.right);
+            if (!name) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    iter, "Unexpected unary expression for event class's `name` attribute.");
+                goto error;
+            }
+        }
+
+        g_free(left);
+        left = NULL;
+
+        if (name) {
+            break;
+        }
+    }
+
+    return name;
+
+error:
+    g_free(left);
+    return NULL;
+}
+
+static int visit_event_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+    int ret = 0;
+    int set = 0;
+    struct ctf_node *iter;
+    uint64_t stream_id = 0;
+    char *event_name = NULL;
+    struct ctf_event_class *event_class = NULL;
+    struct ctf_stream_class *stream_class = NULL;
+    struct bt_list_head *decl_list = &node->u.event.declaration_list;
+    bool pop_scope = false;
+
+    if (node->visited) {
+        goto end;
+    }
+
+    node->visited = TRUE;
+    event_name = get_event_decl_name(ctx, node);
+    if (!event_name) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Missing `name` attribute in event class.");
+        ret = -EPERM;
+        goto error;
+    }
+
+    event_class = ctf_event_class_create();
+    BT_ASSERT(event_class);
+    g_string_assign(event_class->name, event_name);
+    _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+    pop_scope = true;
+
+    bt_list_for_each_entry (iter, decl_list, siblings) {
+        ret = visit_event_decl_entry(ctx, iter, event_class, &stream_id, &set);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(iter,
+                                          "Cannot visit event class's entry: "
+                                          "ret={}",
+                                          ret);
+            goto error;
+        }
+    }
+
+    if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) {
+        /*
+         * Allow missing stream_id if there is only a single
+         * stream class.
+         */
+        switch (ctx->ctf_tc->stream_classes->len) {
+        case 0:
+            /* Create implicit stream class if there's none */
+            stream_id = 0;
+            stream_class = ctf_stream_class_create();
+            BT_ASSERT(stream_class);
+            stream_class->id = stream_id;
+            g_ptr_array_add(ctx->ctf_tc->stream_classes, stream_class);
+            break;
+        case 1:
+            /* Single stream class: get its ID */
+            stream_class = (ctf_stream_class *) ctx->ctf_tc->stream_classes->pdata[0];
+            stream_id = stream_class->id;
+            break;
+        default:
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Missing `stream_id` attribute in event class.");
+            ret = -EPERM;
+            goto error;
+        }
+    }
+
+    /* We have the stream ID now; get the stream class if found */
+    if (!stream_class) {
+        stream_class = ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc, stream_id);
+        if (!stream_class) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                          "Cannot find stream class at this point: "
+                                          "id={}",
+                                          stream_id);
+            ret = -EINVAL;
+            goto error;
+        }
+    }
+
+    BT_ASSERT(stream_class);
+
+    if (!_IS_SET(&set, _EVENT_ID_SET)) {
+        /* Allow only one event without ID per stream */
+        if (stream_class->event_classes->len != 0) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Missing `id` attribute in event class.");
+            ret = -EPERM;
+            goto error;
+        }
+
+        /* Automatic ID */
+        event_class->id = 0;
+    }
+
+    if (ctf_stream_class_borrow_event_class_by_id(stream_class, event_class->id)) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                      "Duplicate event class (same ID) in the same stream class: "
+                                      "id={}",
+                                      event_class->id);
+        ret = -EEXIST;
+        goto error;
+    }
+
+    ctf_stream_class_append_event_class(stream_class, event_class);
+    event_class = NULL;
+    goto end;
+
+error:
+    ctf_event_class_destroy(event_class);
+    event_class = NULL;
+
+    if (ret >= 0) {
+        ret = -1;
+    }
+
+end:
+    if (pop_scope) {
+        ctx_pop_scope(ctx);
+    }
+
+    g_free(event_name);
+
+    return ret;
+}
+
+static int auto_map_field_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
+                                               struct ctf_field_class *fc)
+{
+    struct ctf_clock_class *clock_class_to_map_to = NULL;
+    uint64_t clock_class_count;
+
+    if (!fc) {
+        return 0;
+    }
+
+    if (fc->type != CTF_FIELD_CLASS_TYPE_INT && fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+        return 0;
+    }
+
+    ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+    if (int_fc->mapped_clock_class) {
+        /* Already mapped */
+        return 0;
+    }
+
+    clock_class_count = ctx->ctf_tc->clock_classes->len;
+
+    switch (clock_class_count) {
+    case 0:
+        /*
+         * No clock class exists in the trace at this point. Create an
+         * implicit one at 1 GHz, named `default`, and use this clock
+         * class.
+         */
+        clock_class_to_map_to = ctf_clock_class_create();
+        BT_ASSERT(clock_class_to_map_to);
+        clock_class_to_map_to->frequency = UINT64_C(1000000000);
+        g_string_assign(clock_class_to_map_to->name, "default");
+        g_ptr_array_add(ctx->ctf_tc->clock_classes, clock_class_to_map_to);
+        break;
+    case 1:
+        /*
+         * Only one clock class exists in the trace at this point: use
+         * this one.
+         */
+        clock_class_to_map_to = (ctf_clock_class *) ctx->ctf_tc->clock_classes->pdata[0];
+        break;
+    default:
+        /*
+         * Timestamp field not mapped to a clock class and there's more
+         * than one clock class in the trace: this is an error.
+         */
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            ctx->logger, "Timestamp field found with no mapped clock class, "
+                         "but there's more than one clock class in the trace at this point.");
+        return -1;
+    }
+
+    BT_ASSERT(clock_class_to_map_to);
+    int_fc->mapped_clock_class = clock_class_to_map_to;
+
+    return 0;
+}
+
+static int auto_map_fields_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
+                                                struct ctf_field_class *root_fc,
+                                                const char *field_name)
+{
+    int ret = 0;
+    uint64_t i, count;
+    struct ctf_field_class_struct *struct_fc = (ctf_field_class_struct *) root_fc;
+    struct ctf_field_class_variant *var_fc = (ctf_field_class_variant *) root_fc;
+
+    if (!root_fc) {
+        goto end;
+    }
+
+    if (root_fc->type != CTF_FIELD_CLASS_TYPE_STRUCT &&
+        root_fc->type != CTF_FIELD_CLASS_TYPE_VARIANT) {
+        goto end;
+    }
+
+    if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
+        count = struct_fc->members->len;
+    } else {
+        count = var_fc->options->len;
+    }
+
+    for (i = 0; i < count; i++) {
+        struct ctf_named_field_class *named_fc = NULL;
+
+        if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
+            named_fc = ctf_field_class_struct_borrow_member_by_index(struct_fc, i);
+        } else if (root_fc->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
+            named_fc = ctf_field_class_variant_borrow_option_by_index(var_fc, i);
+        } else {
+            bt_common_abort();
+        }
+
+        if (strcmp(named_fc->name->str, field_name) == 0) {
+            ret = auto_map_field_to_trace_clock_class(ctx, named_fc->fc);
+            if (ret) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    ctx->logger,
+                    "Cannot automatically map field to trace's clock class: "
+                    "field-name=\"{}\"",
+                    field_name);
+                goto end;
+            }
+        }
+
+        ret = auto_map_fields_to_trace_clock_class(ctx, named_fc->fc, field_name);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctx->logger,
+                "Cannot automatically map structure or variant field class's fields to trace's clock class: "
+                "field-name=\"{}\", root-field-name=\"{}\"",
+                field_name, named_fc->name->str);
+            goto end;
+        }
+    }
+
+end:
+    return ret;
+}
+
+static int visit_stream_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
+                                   struct ctf_stream_class *stream_class, int *set)
+{
+    int ret = 0;
+    char *left = NULL;
+
+    switch (node->type) {
+    case NODE_TYPEDEF:
+        ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
+                                    &node->u.field_class_def.field_class_declarators);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Cannot add field class found in stream class.");
+            goto error;
+        }
+        break;
+    case NODE_TYPEALIAS:
+        ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
+                                      node->u.field_class_alias.alias);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                          "Cannot add field class alias found in stream class.");
+            goto error;
+        }
+        break;
+    case NODE_CTF_EXPRESSION:
+    {
+        left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+        if (!left) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (strcmp(left, "id") == 0) {
+            int64_t id;
+
+            if (_IS_SET(set, _STREAM_ID_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "id", "stream declaration");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, (uint64_t *) &id);
+
+            /* Only read "id" if get_unary_unsigned() succeeded. */
+            if (ret || (!ret && id < 0)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node, "Unexpected unary expression for stream class's `id` attribute.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc, id)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Duplicate stream class (same ID): id={}", id);
+                ret = -EEXIST;
+                goto error;
+            }
+
+            stream_class->id = id;
+            _SET(set, _STREAM_ID_SET);
+        } else if (strcmp(left, "event.header") == 0) {
+            if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                              "Duplicate `event.header` entry in stream class.");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = visit_field_class_specifier_list(
+                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
+                &stream_class->event_header_fc);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node, "Cannot create stream class's event header field class.");
+                goto error;
+            }
+
+            BT_ASSERT(stream_class->event_header_fc);
+            ret = auto_map_fields_to_trace_clock_class(ctx, stream_class->event_header_fc,
+                                                       "timestamp");
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node,
+                    "Cannot automatically map specific event header field class fields named `timestamp` to trace's clock class.");
+                goto error;
+            }
+
+            _SET(set, _STREAM_EVENT_HEADER_SET);
+        } else if (strcmp(left, "event.context") == 0) {
+            if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                              "Duplicate `event.context` entry in stream class.");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = visit_field_class_specifier_list(
+                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
+                &stream_class->event_common_context_fc);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node, "Cannot create stream class's event context field class.");
+                goto error;
+            }
+
+            BT_ASSERT(stream_class->event_common_context_fc);
+            _SET(set, _STREAM_EVENT_CONTEXT_SET);
+        } else if (strcmp(left, "packet.context") == 0) {
+            if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                              "Duplicate `packet.context` entry in stream class.");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = visit_field_class_specifier_list(
+                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
+                &stream_class->packet_context_fc);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node, "Cannot create stream class's packet context field class.");
+                goto error;
+            }
+
+            BT_ASSERT(stream_class->packet_context_fc);
+            ret = auto_map_fields_to_trace_clock_class(ctx, stream_class->packet_context_fc,
+                                                       "timestamp_begin");
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node,
+                    "Cannot automatically map specific packet context field class fields named `timestamp_begin` to trace's clock class.");
+                goto error;
+            }
+
+            ret = auto_map_fields_to_trace_clock_class(ctx, stream_class->packet_context_fc,
+                                                       "timestamp_end");
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node,
+                    "Cannot automatically map specific packet context field class fields named `timestamp_end` to trace's clock class.");
+                goto error;
+            }
+
+            _SET(set, _STREAM_PACKET_CONTEXT_SET);
+        } else {
+            _BT_CPPLOGW_NODE(node,
+                             "Unknown attribute in stream class: "
+                             "attr-name=\"{}\"",
+                             left);
+        }
+
+        g_free(left);
+        left = NULL;
+        break;
+    }
+
+    default:
+        ret = -EPERM;
+        goto error;
+    }
+
+    return 0;
+
+error:
+    g_free(left);
+    return ret;
+}
+
+static int visit_stream_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+    int set = 0;
+    int ret = 0;
+    struct ctf_node *iter;
+    struct ctf_stream_class *stream_class = NULL;
+    struct bt_list_head *decl_list = &node->u.stream.declaration_list;
+
+    if (node->visited) {
+        goto end;
+    }
+
+    node->visited = TRUE;
+    stream_class = ctf_stream_class_create();
+    BT_ASSERT(stream_class);
+    _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+    bt_list_for_each_entry (iter, decl_list, siblings) {
+        ret = visit_stream_decl_entry(ctx, iter, stream_class, &set);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(iter,
+                                          "Cannot visit stream class's entry: "
+                                          "ret={}",
+                                          ret);
+            ctx_pop_scope(ctx);
+            goto error;
+        }
+    }
+
+    ctx_pop_scope(ctx);
+
+    if (_IS_SET(&set, _STREAM_ID_SET)) {
+        /* Check that packet header has `stream_id` field */
+        struct ctf_named_field_class *named_fc = NULL;
+
+        if (!ctx->ctf_tc->packet_header_fc) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Stream class has a `id` attribute, "
+                                                "but trace has no packet header field class.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        named_fc = ctf_field_class_struct_borrow_member_by_name(
+            ctf_field_class_as_struct(ctx->ctf_tc->packet_header_fc), "stream_id");
+        if (!named_fc) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                node, "Stream class has a `id` attribute, "
+                      "but trace's packet header field class has no `stream_id` field.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (named_fc->fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+            named_fc->fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                node,
+                "Stream class has a `id` attribute, "
+                "but trace's packet header field class's `stream_id` field is not an integer field class.");
+            ret = -EINVAL;
+            goto error;
+        }
+    } else {
+        /* Allow only _one_ ID-less stream */
+        if (ctx->ctf_tc->stream_classes->len != 0) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                node,
+                "Missing `id` attribute in stream class as there's more than one stream class in the trace.");
+            ret = -EPERM;
+            goto error;
+        }
+
+        /* Automatic ID: 0 */
+        stream_class->id = 0;
+    }
+
+    /*
+     * Make sure that this stream class's ID is currently unique in
+     * the trace.
+     */
+    if (ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc, stream_class->id)) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Duplicate stream class (same ID): id={}",
+                                      stream_class->id);
+        ret = -EINVAL;
+        goto error;
+    }
+
+    g_ptr_array_add(ctx->ctf_tc->stream_classes, stream_class);
+    stream_class = NULL;
+    goto end;
+
+error:
+    ctf_stream_class_destroy(stream_class);
+    stream_class = NULL;
+
+end:
+    return ret;
+}
+
+static int visit_trace_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
+                                  int *set)
+{
+    int ret = 0;
+    char *left = NULL;
+    uint64_t val;
+
+    switch (node->type) {
+    case NODE_TYPEDEF:
+        ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
+                                    &node->u.field_class_def.field_class_declarators);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                          "Cannot add field class found in trace (`trace` block).");
+            goto error;
+        }
+        break;
+    case NODE_TYPEALIAS:
+        ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
+                                      node->u.field_class_alias.alias);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                node, "Cannot add field class alias found in trace (`trace` block).");
+            goto error;
+        }
+        break;
+    case NODE_CTF_EXPRESSION:
+    {
+        left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+        if (!left) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (strcmp(left, "major") == 0) {
+            if (_IS_SET(set, _TRACE_MAJOR_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "major", "trace");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, &val);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node, "Unexpected unary expression for trace's `major` attribute.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (val != 1) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                              "Invalid trace's `minor` attribute: expecting 1.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            ctx->ctf_tc->major = val;
+            _SET(set, _TRACE_MAJOR_SET);
+        } else if (strcmp(left, "minor") == 0) {
+            if (_IS_SET(set, _TRACE_MINOR_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "minor", "trace");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = get_unary_unsigned(ctx, &node->u.ctf_expression.right, &val);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    node, "Unexpected unary expression for trace's `minor` attribute.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (val != 8) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                              "Invalid trace's `minor` attribute: expecting 8.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            ctx->ctf_tc->minor = val;
+            _SET(set, _TRACE_MINOR_SET);
+        } else if (strcmp(left, "uuid") == 0) {
+            if (_IS_SET(set, _TRACE_UUID_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "uuid", "trace");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = get_unary_uuid(ctx, &node->u.ctf_expression.right, ctx->ctf_tc->uuid);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Invalid trace's `uuid` attribute.");
+                goto error;
+            }
+
+            ctx->ctf_tc->is_uuid_set = true;
+            _SET(set, _TRACE_UUID_SET);
+        } else if (strcmp(left, "byte_order") == 0) {
+            /* Default byte order is already known at this stage */
+            if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order", "trace");
+                ret = -EPERM;
+                goto error;
+            }
+
+            BT_ASSERT(ctx->ctf_tc->default_byte_order != CTF_BYTE_ORDER_UNKNOWN);
+            _SET(set, _TRACE_BYTE_ORDER_SET);
+        } else if (strcmp(left, "packet.header") == 0) {
+            if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Duplicate `packet.header` entry in trace.");
+                ret = -EPERM;
+                goto error;
+            }
+
+            ret = visit_field_class_specifier_list(
+                ctx, _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings),
+                &ctx->ctf_tc->packet_header_fc);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                              "Cannot create trace's packet header field class.");
+                goto error;
+            }
+
+            BT_ASSERT(ctx->ctf_tc->packet_header_fc);
+            _SET(set, _TRACE_PACKET_HEADER_SET);
+        } else {
+            _BT_CPPLOGW_NODE(node,
+                             "Unknown attribute in stream class: "
+                             "attr-name=\"{}\"",
+                             left);
+        }
+
+        g_free(left);
+        left = NULL;
+        break;
+    }
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Unknown expression in trace.");
+        ret = -EINVAL;
+        goto error;
+    }
+
+    return 0;
+
+error:
+    g_free(left);
+    return ret;
+}
+
+static int visit_trace_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+    int ret = 0;
+    int set = 0;
+    struct ctf_node *iter;
+    struct bt_list_head *decl_list = &node->u.trace.declaration_list;
+
+    if (node->visited) {
+        goto end;
+    }
+
+    node->visited = TRUE;
+
+    if (ctx->is_trace_visited) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Duplicate trace (`trace` block).");
+        ret = -EEXIST;
+        goto error;
+    }
+
+    _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+    bt_list_for_each_entry (iter, decl_list, siblings) {
+        ret = visit_trace_decl_entry(ctx, iter, &set);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(iter,
+                                          "Cannot visit trace's entry (`trace` block): "
+                                          "ret={}",
+                                          ret);
+            ctx_pop_scope(ctx);
+            goto error;
+        }
+    }
+
+    ctx_pop_scope(ctx);
+
+    if (!_IS_SET(&set, _TRACE_MAJOR_SET)) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Missing `major` attribute in trace (`trace` block).");
+        ret = -EPERM;
+        goto error;
+    }
+
+    if (!_IS_SET(&set, _TRACE_MINOR_SET)) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Missing `minor` attribute in trace (`trace` block).");
+        ret = -EPERM;
+        goto error;
+    }
+
+    if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                      "Missing `byte_order` attribute in trace (`trace` block).");
+        ret = -EPERM;
+        goto error;
+    }
+
+    ctx->is_trace_visited = true;
+
+end:
+    return 0;
+
+error:
+    return ret;
+}
+
+static int visit_env(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+    int ret = 0;
+    char *left = NULL;
+    struct ctf_node *entry_node;
+    struct bt_list_head *decl_list = &node->u.env.declaration_list;
+
+    if (node->visited) {
+        goto end;
+    }
+
+    node->visited = TRUE;
+
+    bt_list_for_each_entry (entry_node, decl_list, siblings) {
+        struct bt_list_head *right_head = &entry_node->u.ctf_expression.right;
+
+        if (entry_node->type != NODE_CTF_EXPRESSION) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node,
+                                          "Wrong expression in environment entry: "
+                                          "node-type={}",
+                                          entry_node->type);
+            ret = -EPERM;
+            goto error;
+        }
+
+        left = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.left);
+        if (!left) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node, "Cannot get environment entry's name.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (is_unary_string(right_head)) {
+            char *right = ctf_ast_concatenate_unary_strings(right_head);
+
+            if (!right) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    entry_node,
+                    "Unexpected unary expression for environment entry's value: "
+                    "name=\"{}\"",
+                    left);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (strcmp(left, "tracer_name") == 0) {
+                if (strncmp(right, "lttng", 5) == 0) {
+                    BT_CPPLOGI_SPEC(ctx->logger,
+                                    "Detected LTTng trace from `{}` environment value: "
+                                    "tracer-name=\"{}\"",
+                                    left, right);
+                    ctx->is_lttng = true;
+                }
+            }
+
+            ctf_trace_class_append_env_entry(ctx->ctf_tc, left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR,
+                                             right, 0);
+            g_free(right);
+        } else if (is_unary_unsigned(right_head) || is_unary_signed(right_head)) {
+            int64_t v;
+
+            if (is_unary_unsigned(right_head)) {
+                ret = get_unary_unsigned(ctx, right_head, (uint64_t *) &v);
+            } else {
+                ret = get_unary_signed(right_head, &v);
+            }
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    entry_node,
+                    "Unexpected unary expression for environment entry's value: "
+                    "name=\"{}\"",
+                    left);
+                ret = -EINVAL;
+                goto error;
+            }
+
+            ctf_trace_class_append_env_entry(ctx->ctf_tc, left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT,
+                                             NULL, v);
+        } else {
+            _BT_CPPLOGW_NODE(entry_node,
+                             "Environment entry has unknown type: "
+                             "name=\"{}\"",
+                             left);
+        }
+
+        g_free(left);
+        left = NULL;
+    }
+
+end:
+    return 0;
+
+error:
+    g_free(left);
+    return ret;
+}
+
+static int set_trace_byte_order(struct ctf_visitor_generate_ir *ctx, struct ctf_node *trace_node)
+{
+    int ret = 0;
+    int set = 0;
+    char *left = NULL;
+    struct ctf_node *node;
+    struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
+
+    bt_list_for_each_entry (node, decl_list, siblings) {
+        if (node->type == NODE_CTF_EXPRESSION) {
+            struct ctf_node *right_node;
+
+            left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+            if (!left) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+                ret = -EINVAL;
+                goto error;
+            }
+
+            if (strcmp(left, "byte_order") == 0) {
+                enum ctf_byte_order bo;
+
+                if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+                    _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order", "trace");
+                    ret = -EPERM;
+                    goto error;
+                }
+
+                _SET(&set, _TRACE_BYTE_ORDER_SET);
+                right_node =
+                    _BT_LIST_FIRST_ENTRY(&node->u.ctf_expression.right, struct ctf_node, siblings);
+                bo = byte_order_from_unary_expr(ctx, right_node);
+                if (bo == CTF_BYTE_ORDER_UNKNOWN) {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                        node, "Invalid `byte_order` attribute in trace (`trace` block): "
+                              "expecting `le`, `be`, or `network`.");
+                    ret = -EINVAL;
+                    goto error;
+                } else if (bo == CTF_BYTE_ORDER_DEFAULT) {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                        node, "Invalid `byte_order` attribute in trace (`trace` block): "
+                              "cannot be set to `native` here.");
+                    ret = -EPERM;
+                    goto error;
+                }
+
+                ctx->ctf_tc->default_byte_order = bo;
+            }
+
+            g_free(left);
+            left = NULL;
+        }
+    }
+
+    if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(trace_node,
+                                      "Missing `byte_order` attribute in trace (`trace` block).");
+        ret = -EINVAL;
+        goto error;
+    }
+
+    return 0;
+
+error:
+    g_free(left);
+    return ret;
+}
+
+static int visit_clock_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
+                                  struct ctf_clock_class *clock, int *set, int64_t *offset_seconds,
+                                  uint64_t *offset_cycles)
+{
+    int ret = 0;
+    char *left = NULL;
+
+    if (entry_node->type != NODE_CTF_EXPRESSION) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node, "Unexpected node type: node-type={}",
+                                      entry_node->type);
+        ret = -EPERM;
+        goto error;
+    }
+
+    left = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.left);
+    if (!left) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node, "Cannot concatenate unary strings.");
+        ret = -EINVAL;
+        goto error;
+    }
+
+    if (strcmp(left, "name") == 0) {
+        char *right;
+
+        if (_IS_SET(set, _CLOCK_NAME_SET)) {
+            _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "name", "clock class");
+            ret = -EPERM;
+            goto error;
+        }
+
+        right = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.right);
+        if (!right) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Unexpected unary expression for clock class's `name` attribute.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        g_string_assign(clock->name, right);
+        g_free(right);
+        _SET(set, _CLOCK_NAME_SET);
+    } else if (strcmp(left, "uuid") == 0) {
+        bt_uuid_t uuid;
+
+        if (_IS_SET(set, _CLOCK_UUID_SET)) {
+            _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "uuid", "clock class");
+            ret = -EPERM;
+            goto error;
+        }
+
+        ret = get_unary_uuid(ctx, &entry_node->u.ctf_expression.right, uuid);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node, "Invalid clock class's `uuid` attribute.");
+            goto error;
+        }
+
+        clock->has_uuid = true;
+        bt_uuid_copy(clock->uuid, uuid);
+        _SET(set, _CLOCK_UUID_SET);
+    } else if (strcmp(left, "description") == 0) {
+        char *right;
+
+        if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) {
+            _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "description", "clock class");
+            ret = -EPERM;
+            goto error;
+        }
+
+        right = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.right);
+        if (!right) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node,
+                "Unexpected unary expression for clock class's `description` attribute.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        g_string_assign(clock->description, right);
+        g_free(right);
+        _SET(set, _CLOCK_DESCRIPTION_SET);
+    } else if (strcmp(left, "freq") == 0) {
+        uint64_t freq = UINT64_C(-1);
+
+        if (_IS_SET(set, _CLOCK_FREQ_SET)) {
+            _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "freq", "clock class");
+            ret = -EPERM;
+            goto error;
+        }
+
+        ret = get_unary_unsigned(ctx, &entry_node->u.ctf_expression.right, &freq);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Unexpected unary expression for clock class's `freq` attribute.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        if (freq == UINT64_C(-1) || freq == 0) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node, "Invalid clock class frequency: freq={}",
+                                          freq);
+            ret = -EINVAL;
+            goto error;
+        }
+
+        clock->frequency = freq;
+        _SET(set, _CLOCK_FREQ_SET);
+    } else if (strcmp(left, "precision") == 0) {
+        uint64_t precision;
+
+        if (_IS_SET(set, _CLOCK_PRECISION_SET)) {
+            _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "precision", "clock class");
+            ret = -EPERM;
+            goto error;
+        }
+
+        ret = get_unary_unsigned(ctx, &entry_node->u.ctf_expression.right, &precision);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Unexpected unary expression for clock class's `precision` attribute.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        clock->precision = precision;
+        _SET(set, _CLOCK_PRECISION_SET);
+    } else if (strcmp(left, "offset_s") == 0) {
+        if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) {
+            _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset_s", "clock class");
+            ret = -EPERM;
+            goto error;
+        }
+
+        ret = get_unary_signed(&entry_node->u.ctf_expression.right, offset_seconds);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Unexpected unary expression for clock class's `offset_s` attribute.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        _SET(set, _CLOCK_OFFSET_S_SET);
+    } else if (strcmp(left, "offset") == 0) {
+        if (_IS_SET(set, _CLOCK_OFFSET_SET)) {
+            _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset", "clock class");
+            ret = -EPERM;
+            goto error;
+        }
+
+        ret = get_unary_unsigned(ctx, &entry_node->u.ctf_expression.right, offset_cycles);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Unexpected unary expression for clock class's `offset` attribute.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        _SET(set, _CLOCK_OFFSET_SET);
+    } else if (strcmp(left, "absolute") == 0) {
+        struct ctf_node *right;
+
+        if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) {
+            _BT_CPPLOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "absolute", "clock class");
+            ret = -EPERM;
+            goto error;
+        }
+
+        right =
+            _BT_LIST_FIRST_ENTRY(&entry_node->u.ctf_expression.right, struct ctf_node, siblings);
+        ret = get_boolean(ctx, right);
+        if (ret < 0) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                entry_node, "Unexpected unary expression for clock class's `absolute` attribute.");
+            ret = -EINVAL;
+            goto error;
+        }
+
+        clock->is_absolute = ret;
+        _SET(set, _CLOCK_ABSOLUTE_SET);
+    } else {
+        _BT_CPPLOGW_NODE(entry_node, "Unknown attribute in clock class: attr-name=\"{}\"", left);
+    }
+
+    g_free(left);
+    left = NULL;
+    return 0;
+
+error:
+    g_free(left);
+    return ret;
+}
+
+static inline uint64_t cycles_from_ns(uint64_t frequency, uint64_t ns)
+{
+    uint64_t cycles;
+
+    /* 1GHz */
+    if (frequency == UINT64_C(1000000000)) {
+        cycles = ns;
+    } else {
+        cycles = (uint64_t) (((double) ns * (double) frequency) / 1e9);
+    }
+
+    return cycles;
+}
+
+static void calibrate_clock_class_offsets(int64_t *offset_seconds, uint64_t *offset_cycles,
+                                          uint64_t freq)
+{
+    if (*offset_cycles >= freq) {
+        const uint64_t s_in_offset_cycles = *offset_cycles / freq;
+
+        *offset_seconds += (int64_t) s_in_offset_cycles;
+        *offset_cycles -= (s_in_offset_cycles * freq);
+    }
+}
+
+static void apply_clock_class_is_absolute(struct ctf_visitor_generate_ir *ctx,
+                                          struct ctf_clock_class *clock)
+{
+    if (ctx->decoder_config.clkClsCfg.forceOriginIsUnixEpoch) {
+        clock->is_absolute = true;
+    }
+
+    return;
+}
+
+static void apply_clock_class_offset(struct ctf_visitor_generate_ir *ctx,
+                                     struct ctf_clock_class *clock)
+{
+    uint64_t freq;
+    int64_t offset_s_to_apply = ctx->decoder_config.clkClsCfg.offsetSec;
+    uint64_t offset_ns_to_apply;
+    int64_t cur_offset_s;
+    uint64_t cur_offset_cycles;
+
+    if (ctx->decoder_config.clkClsCfg.offsetSec == 0 &&
+        ctx->decoder_config.clkClsCfg.offsetNanoSec == 0) {
+        goto end;
+    }
+
+    /* Transfer nanoseconds to seconds as much as possible */
+    if (ctx->decoder_config.clkClsCfg.offsetNanoSec < 0) {
+        const int64_t abs_ns = -ctx->decoder_config.clkClsCfg.offsetNanoSec;
+        const int64_t abs_extra_s = abs_ns / INT64_C(1000000000) + 1;
+        const int64_t extra_s = -abs_extra_s;
+        const int64_t offset_ns =
+            ctx->decoder_config.clkClsCfg.offsetNanoSec - (extra_s * INT64_C(1000000000));
+
+        BT_ASSERT(offset_ns > 0);
+        offset_ns_to_apply = (uint64_t) offset_ns;
+        offset_s_to_apply += extra_s;
+    } else {
+        const int64_t extra_s = ctx->decoder_config.clkClsCfg.offsetNanoSec / INT64_C(1000000000);
+        const int64_t offset_ns =
+            ctx->decoder_config.clkClsCfg.offsetNanoSec - (extra_s * INT64_C(1000000000));
+
+        BT_ASSERT(offset_ns >= 0);
+        offset_ns_to_apply = (uint64_t) offset_ns;
+        offset_s_to_apply += extra_s;
+    }
+
+    freq = clock->frequency;
+    cur_offset_s = clock->offset_seconds;
+    cur_offset_cycles = clock->offset_cycles;
+
+    /* Apply offsets */
+    cur_offset_s += offset_s_to_apply;
+    cur_offset_cycles += cycles_from_ns(freq, offset_ns_to_apply);
+
+    /*
+     * Recalibrate offsets because the part in cycles can be greater
+     * than the frequency at this point.
+     */
+    calibrate_clock_class_offsets(&cur_offset_s, &cur_offset_cycles, freq);
+
+    /* Set final offsets */
+    clock->offset_seconds = cur_offset_s;
+    clock->offset_cycles = cur_offset_cycles;
+
+end:
+    return;
+}
+
+static int visit_clock_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *clock_node)
+{
+    int ret = 0;
+    int set = 0;
+    struct ctf_clock_class *clock;
+    struct ctf_node *entry_node;
+    struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list;
+    const char *clock_class_name;
+    int64_t offset_seconds = 0;
+    uint64_t offset_cycles = 0;
+    uint64_t freq;
+
+    if (clock_node->visited) {
+        return 0;
+    }
+
+    clock_node->visited = TRUE;
+
+    /* CTF 1.8's default frequency for a clock class is 1 GHz */
+    clock = ctf_clock_class_create();
+    if (!clock) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(clock_node, "Cannot create default clock class.");
+        ret = -ENOMEM;
+        goto end;
+    }
+
+    bt_list_for_each_entry (entry_node, decl_list, siblings) {
+        ret = visit_clock_decl_entry(ctx, entry_node, clock, &set, &offset_seconds, &offset_cycles);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(entry_node, "Cannot visit clock class's entry: ret={}",
+                                          ret);
+            goto end;
+        }
+    }
+
+    if (!_IS_SET(&set, _CLOCK_NAME_SET)) {
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(clock_node, "Missing `name` attribute in clock class.");
+        ret = -EPERM;
+        goto end;
+    }
+
+    clock_class_name = clock->name->str;
+    BT_ASSERT(clock_class_name);
+    if (ctx->is_lttng && strcmp(clock_class_name, "monotonic") == 0) {
+        /*
+         * Old versions of LTTng forgot to set its clock class
+         * as absolute, even if it is. This is important because
+         * it's a condition to be able to sort messages
+         * from different sources.
+         */
+        clock->is_absolute = true;
+    }
+
+    /*
+     * Adjust offsets so that the part in cycles is less than the
+     * frequency (move to the part in seconds).
+     */
+    freq = clock->frequency;
+    calibrate_clock_class_offsets(&offset_seconds, &offset_cycles, freq);
+    BT_ASSERT(offset_cycles < clock->frequency);
+    clock->offset_seconds = offset_seconds;
+    clock->offset_cycles = offset_cycles;
+    apply_clock_class_offset(ctx, clock);
+    apply_clock_class_is_absolute(ctx, clock);
+    g_ptr_array_add(ctx->ctf_tc->clock_classes, clock);
+    clock = NULL;
+
+end:
+    if (clock) {
+        ctf_clock_class_destroy(clock);
+    }
+
+    return ret;
+}
+
+static int visit_root_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *root_decl_node)
+{
+    int ret = 0;
+
+    if (root_decl_node->visited) {
+        goto end;
+    }
+
+    root_decl_node->visited = TRUE;
+
+    switch (root_decl_node->type) {
+    case NODE_TYPEDEF:
+        ret =
+            visit_field_class_def(ctx, root_decl_node->u.field_class_def.field_class_specifier_list,
+                                  &root_decl_node->u.field_class_def.field_class_declarators);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(root_decl_node,
+                                          "Cannot add field class found in root scope.");
+            goto end;
+        }
+        break;
+    case NODE_TYPEALIAS:
+        ret = visit_field_class_alias(ctx, root_decl_node->u.field_class_alias.target,
+                                      root_decl_node->u.field_class_alias.alias);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(root_decl_node,
+                                          "Cannot add field class alias found in root scope.");
+            goto end;
+        }
+        break;
+    case NODE_TYPE_SPECIFIER_LIST:
+    {
+        struct ctf_field_class *decl = NULL;
+
+        /*
+         * Just add the field class specifier to the root
+         * declaration scope. Put local reference.
+         */
+        ret = visit_field_class_specifier_list(ctx, root_decl_node, &decl);
+        if (ret) {
+            _BT_CPPLOGE_APPEND_CAUSE_NODE(root_decl_node,
+                                          "Cannot visit root scope's field class: "
+                                          "ret={}",
+                                          ret);
+            BT_ASSERT(!decl);
+            goto end;
+        }
+
+        ctf_field_class_destroy(decl);
+        decl = NULL;
+        break;
+    }
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(root_decl_node, "Unexpected node type: node-type={}",
+                                      root_decl_node->type);
+        ret = -EPERM;
+        goto end;
+    }
+
+end:
+    return ret;
+}
+
+ctf_visitor_generate_ir::UP
+ctf_visitor_generate_ir_create(const struct ctf_metadata_decoder_config *decoder_config)
+{
+    bt2c::Logger logger {decoder_config->logger, "PLUGIN/CTF/META/IR-VISITOR"};
+
+    /* Create visitor's context */
+    ctf_visitor_generate_ir::UP ctx = ctx_create(decoder_config, logger);
+
+    if (!ctx) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Cannot create visitor's context.");
+        goto error;
+    }
+
+    goto end;
+
+error:
+    ctx.reset();
+
+end:
+    return ctx;
+}
+
+bt2::TraceClass::Shared
+ctf_visitor_generate_ir_get_ir_trace_class(struct ctf_visitor_generate_ir *ctx)
+{
+    BT_ASSERT_DBG(ctx);
+
+    return ctx->trace_class;
+}
+
+struct ctf_trace_class *
+ctf_visitor_generate_ir_borrow_ctf_trace_class(struct ctf_visitor_generate_ir *ctx)
+{
+    BT_ASSERT_DBG(ctx);
+    BT_ASSERT_DBG(ctx->ctf_tc);
+    return ctx->ctf_tc;
+}
+
+int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+    int ret = 0;
+
+    BT_CPPLOGI_STR_SPEC(ctx->logger, "Visiting metadata's AST to generate CTF IR objects.");
+
+    switch (node->type) {
+    case NODE_ROOT:
+    {
+        struct ctf_node *iter;
+        bool got_trace_decl = false;
+
+        /*
+         * The first thing we need is the native byte order of
+         * the trace block, because early class aliases can have
+         * a `byte_order` attribute set to `native`. If we don't
+         * have the native byte order yet, and we don't have any
+         * trace block yet, then fail with EINCOMPLETE.
+         */
+        if (ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_UNKNOWN) {
+            bt_list_for_each_entry (iter, &node->u.root.trace, siblings) {
+                if (got_trace_decl) {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Duplicate trace (`trace` block).");
+                    ret = -1;
+                    goto end;
+                }
+
+                ret = set_trace_byte_order(ctx, iter);
+                if (ret) {
+                    _BT_CPPLOGE_APPEND_CAUSE_NODE(node,
+                                                  "Cannot set trace's native byte order: "
+                                                  "ret={}",
+                                                  ret);
+                    goto end;
+                }
+
+                got_trace_decl = true;
+            }
+
+            if (!got_trace_decl) {
+                BT_CPPLOGD_STR_SPEC(ctx->logger, "Incomplete AST: need trace (`trace` block).");
+                ret = -EINCOMPLETE;
+                goto end;
+            }
+        }
+
+        BT_ASSERT(ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_LITTLE ||
+                  ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_BIG);
+        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
+
+        /* Environment */
+        bt_list_for_each_entry (iter, &node->u.root.env, siblings) {
+            ret = visit_env(ctx, iter);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(
+                    iter,
+                    "Cannot visit trace's environment (`env` block) entry: "
+                    "ret={}",
+                    ret);
+                goto end;
+            }
+        }
+
+        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
+
+        /*
+         * Visit clock blocks.
+         */
+        bt_list_for_each_entry (iter, &node->u.root.clock, siblings) {
+            ret = visit_clock_decl(ctx, iter);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(iter, "Cannot visit clock class: ret={}", ret);
+                goto end;
+            }
+        }
+
+        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
+
+        /*
+         * Visit root declarations next, as they can be used by any
+         * following entity.
+         */
+        bt_list_for_each_entry (iter, &node->u.root.declaration_list, siblings) {
+            ret = visit_root_decl(ctx, iter);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(iter, "Cannot visit root entry: ret={}", ret);
+                goto end;
+            }
+        }
+
+        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
+
+        /* Callsite blocks are not supported */
+        bt_list_for_each_entry (iter, &node->u.root.callsite, siblings) {
+            _BT_CPPLOGW_NODE(iter, "\"callsite\" blocks are not supported as of this version.");
+        }
+
+        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
+
+        /* Trace */
+        bt_list_for_each_entry (iter, &node->u.root.trace, siblings) {
+            ret = visit_trace_decl(ctx, iter);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(iter,
+                                              "Cannot visit trace (`trace` block): "
+                                              "ret={}",
+                                              ret);
+                goto end;
+            }
+        }
+
+        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
+
+        /* Streams */
+        bt_list_for_each_entry (iter, &node->u.root.stream, siblings) {
+            ret = visit_stream_decl(ctx, iter);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(iter, "Cannot visit stream class: ret={}", ret);
+                goto end;
+            }
+        }
+
+        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
+
+        /* Events */
+        bt_list_for_each_entry (iter, &node->u.root.event, siblings) {
+            ret = visit_event_decl(ctx, iter);
+            if (ret) {
+                _BT_CPPLOGE_APPEND_CAUSE_NODE(iter, "Cannot visit event class: ret={}", ret);
+                goto end;
+            }
+        }
+
+        BT_ASSERT(ctx->current_scope && !ctx->current_scope->parent_scope);
+        break;
+    }
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_NODE(node, "Unexpected node type: node-type={}", node->type);
+        ret = -EINVAL;
+        goto end;
+    }
+
+    /* Update default clock classes */
+    ret = ctf_trace_class_update_default_clock_classes(ctx->ctf_tc, ctx->logger);
+    if (ret) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    /* Update trace class meanings */
+    ret = ctf_trace_class_update_meanings(ctx->ctf_tc);
+    if (ret) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    /* Update stream class configuration */
+    ret = ctf_trace_class_update_stream_class_config(ctx->ctf_tc);
+    if (ret) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    /* Update text arrays and sequences */
+    ret = ctf_trace_class_update_text_array_sequence(ctx->ctf_tc);
+    if (ret) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    /* Update structure/array/sequence alignments */
+    ret = ctf_trace_class_update_alignments(ctx->ctf_tc);
+    if (ret) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    /* Resolve sequence lengths and variant tags */
+    ret = ctf_trace_class_resolve_field_classes(ctx->ctf_tc, ctx->logger);
+    if (ret) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    if (ctx->trace_class) {
+        /*
+         * Update "in IR" for field classes.
+         *
+         * If we have no IR trace class, then we'll have no way
+         * to create IR fields anyway, so we leave all the
+         * `in_ir` members false.
+         */
+        ret = ctf_trace_class_update_in_ir(ctx->ctf_tc);
+        if (ret) {
+            ret = -EINVAL;
+            goto end;
+        }
+    }
+
+    /* Update saved value indexes */
+    ret = ctf_trace_class_update_value_storing_indexes(ctx->ctf_tc);
+    if (ret) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    /* Validate what we have so far */
+    ret = ctf_trace_class_validate(ctx->ctf_tc, ctx->logger);
+    if (ret) {
+        ret = -EINVAL;
+        goto end;
+    }
+
+    /*
+     * If there are fields which are not related to the CTF format
+     * itself in the packet header and in event header field
+     * classes, warn about it because they are never translated.
+     */
+    ctf_trace_class_warn_meaningless_header_fields(ctx->ctf_tc, ctx->logger);
+
+    if (ctx->trace_class) {
+        /* Copy new CTF metadata -> new IR metadata */
+        ret = ctf_trace_class_translate(ctx->decoder_config.self_comp,
+                                        ctx->trace_class->libObjPtr(), ctx->ctf_tc);
+        if (ret) {
+            ret = -EINVAL;
+            goto end;
+        }
+    }
+
+end:
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/visitor-parent-links.cpp b/src/plugins/ctf/common/src/metadata/tsdl/visitor-parent-links.cpp
new file mode 100644 (file)
index 0000000..90faaee
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Metadata Parent Link Creator.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "logging.hpp"
+
+#include "common/list.h"
+
+#include "ast.hpp"
+
+static int ctf_visitor_unary_expression(int depth, struct ctf_node *node,
+                                        const bt2c::Logger& logger)
+{
+    int ret = 0;
+
+    switch (node->u.unary_expression.link) {
+    case UNARY_LINK_UNKNOWN:
+    case UNARY_DOTLINK:
+    case UNARY_ARROWLINK:
+    case UNARY_DOTDOTDOT:
+        break;
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                        "Unknown expression link type: type={}\n",
+                                        (int) node->u.unary_expression.link);
+        return -EINVAL;
+    }
+
+    switch (node->u.unary_expression.type) {
+    case UNARY_STRING:
+    case UNARY_SIGNED_CONSTANT:
+    case UNARY_UNSIGNED_CONSTANT:
+        break;
+    case UNARY_SBRAC:
+        node->u.unary_expression.u.sbrac_exp->parent = node;
+        ret = ctf_visitor_unary_expression(depth + 1, node->u.unary_expression.u.sbrac_exp, logger);
+        if (ret)
+            return ret;
+        break;
+
+    case UNARY_UNKNOWN:
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                        "Unknown expression link type: type={}\n",
+                                        (int) node->u.unary_expression.link);
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int ctf_visitor_type_specifier(int depth, struct ctf_node *node, const bt2c::Logger& logger)
+{
+    int ret;
+
+    switch (node->u.field_class_specifier.type) {
+    case TYPESPEC_VOID:
+    case TYPESPEC_CHAR:
+    case TYPESPEC_SHORT:
+    case TYPESPEC_INT:
+    case TYPESPEC_LONG:
+    case TYPESPEC_FLOAT:
+    case TYPESPEC_DOUBLE:
+    case TYPESPEC_SIGNED:
+    case TYPESPEC_UNSIGNED:
+    case TYPESPEC_BOOL:
+    case TYPESPEC_COMPLEX:
+    case TYPESPEC_IMAGINARY:
+    case TYPESPEC_CONST:
+    case TYPESPEC_ID_TYPE:
+        break;
+    case TYPESPEC_FLOATING_POINT:
+    case TYPESPEC_INTEGER:
+    case TYPESPEC_STRING:
+    case TYPESPEC_STRUCT:
+    case TYPESPEC_VARIANT:
+    case TYPESPEC_ENUM:
+        node->u.field_class_specifier.node->parent = node;
+        ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_specifier.node, logger);
+        if (ret)
+            return ret;
+        break;
+
+    case TYPESPEC_UNKNOWN:
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno, "Unknown type specifier: type={}\n",
+                                        (int) node->u.field_class_specifier.type);
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node,
+                                              const bt2c::Logger& logger)
+{
+    int ret = 0;
+    struct ctf_node *iter;
+
+    depth++;
+
+    bt_list_for_each_entry (iter, &node->u.field_class_declarator.pointers, siblings) {
+        iter->parent = node;
+        ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+        if (ret)
+            return ret;
+    }
+
+    switch (node->u.field_class_declarator.type) {
+    case TYPEDEC_ID:
+        break;
+    case TYPEDEC_NESTED:
+        if (node->u.field_class_declarator.u.nested.field_class_declarator) {
+            node->u.field_class_declarator.u.nested.field_class_declarator->parent = node;
+            ret = ctf_visitor_parent_links(
+                depth + 1, node->u.field_class_declarator.u.nested.field_class_declarator, logger);
+            if (ret)
+                return ret;
+        }
+        if (!node->u.field_class_declarator.u.nested.abstract_array) {
+            bt_list_for_each_entry (iter, &node->u.field_class_declarator.u.nested.length,
+                                    siblings) {
+                iter->parent = node;
+                ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+                if (ret)
+                    return ret;
+            }
+        }
+        if (node->u.field_class_declarator.bitfield_len) {
+            node->u.field_class_declarator.bitfield_len = node;
+            ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_declarator.bitfield_len,
+                                           logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case TYPEDEC_UNKNOWN:
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno, "Unknown type declarator: type={}\n",
+                                        (int) node->u.field_class_declarator.type);
+        return -EINVAL;
+    }
+    depth--;
+    return 0;
+}
+
+int ctf_visitor_parent_links(int depth, struct ctf_node *node, const bt2c::Logger& parentLogger)
+{
+    int ret = 0;
+    struct ctf_node *iter;
+
+    if (node->visited)
+        return 0;
+
+    bt2c::Logger logger {parentLogger, "PLUGIN/CTF/META/PARENT-LINKS-VISITOR"};
+
+    switch (node->type) {
+    case NODE_ROOT:
+        bt_list_for_each_entry (iter, &node->u.root.declaration_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.root.trace, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.root.stream, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.root.event, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.root.clock, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.root.callsite, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+
+    case NODE_EVENT:
+        bt_list_for_each_entry (iter, &node->u.event.declaration_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_STREAM:
+        bt_list_for_each_entry (iter, &node->u.stream.declaration_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_ENV:
+        bt_list_for_each_entry (iter, &node->u.env.declaration_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_TRACE:
+        bt_list_for_each_entry (iter, &node->u.trace.declaration_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_CLOCK:
+        bt_list_for_each_entry (iter, &node->u.clock.declaration_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_CALLSITE:
+        bt_list_for_each_entry (iter, &node->u.callsite.declaration_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+
+    case NODE_CTF_EXPRESSION:
+        depth++;
+        bt_list_for_each_entry (iter, &node->u.ctf_expression.left, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.ctf_expression.right, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        depth--;
+        break;
+    case NODE_UNARY_EXPRESSION:
+        return ctf_visitor_unary_expression(depth, node, logger);
+
+    case NODE_TYPEDEF:
+        depth++;
+        node->u.field_class_def.field_class_specifier_list->parent = node;
+        ret = ctf_visitor_parent_links(depth + 1,
+                                       node->u.field_class_def.field_class_specifier_list, logger);
+        if (ret)
+            return ret;
+        bt_list_for_each_entry (iter, &node->u.field_class_def.field_class_declarators, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        depth--;
+        break;
+    case NODE_TYPEALIAS_TARGET:
+        depth++;
+        node->u.field_class_alias_target.field_class_specifier_list->parent = node;
+        ret = ctf_visitor_parent_links(
+            depth + 1, node->u.field_class_alias_target.field_class_specifier_list, logger);
+        if (ret)
+            return ret;
+        bt_list_for_each_entry (iter, &node->u.field_class_alias_target.field_class_declarators,
+                                siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        depth--;
+        break;
+    case NODE_TYPEALIAS_ALIAS:
+        depth++;
+        node->u.field_class_alias_name.field_class_specifier_list->parent = node;
+        ret = ctf_visitor_parent_links(
+            depth + 1, node->u.field_class_alias_name.field_class_specifier_list, logger);
+        if (ret)
+            return ret;
+        bt_list_for_each_entry (iter, &node->u.field_class_alias_name.field_class_declarators,
+                                siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        depth--;
+        break;
+    case NODE_TYPEALIAS:
+        node->u.field_class_alias.target->parent = node;
+        ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias.target, logger);
+        if (ret)
+            return ret;
+        node->u.field_class_alias.alias->parent = node;
+        ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias.alias, logger);
+        if (ret)
+            return ret;
+        break;
+
+    case NODE_TYPE_SPECIFIER_LIST:
+        bt_list_for_each_entry (iter, &node->u.field_class_specifier_list.head, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+
+    case NODE_TYPE_SPECIFIER:
+        ret = ctf_visitor_type_specifier(depth, node, logger);
+        if (ret)
+            return ret;
+        break;
+    case NODE_POINTER:
+        break;
+    case NODE_TYPE_DECLARATOR:
+        ret = ctf_visitor_field_class_declarator(depth, node, logger);
+        if (ret)
+            return ret;
+        break;
+
+    case NODE_FLOATING_POINT:
+        bt_list_for_each_entry (iter, &node->u.floating_point.expressions, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_INTEGER:
+        bt_list_for_each_entry (iter, &node->u.integer.expressions, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_STRING:
+        bt_list_for_each_entry (iter, &node->u.string.expressions, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_ENUMERATOR:
+        bt_list_for_each_entry (iter, &node->u.enumerator.values, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_ENUM:
+        depth++;
+        if (node->u._enum.container_field_class) {
+            ret = ctf_visitor_parent_links(depth + 1, node->u._enum.container_field_class, logger);
+            if (ret)
+                return ret;
+        }
+
+        bt_list_for_each_entry (iter, &node->u._enum.enumerator_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        depth--;
+        break;
+    case NODE_STRUCT_OR_VARIANT_DECLARATION:
+        node->u.struct_or_variant_declaration.field_class_specifier_list->parent = node;
+        ret = ctf_visitor_parent_links(
+            depth + 1, node->u.struct_or_variant_declaration.field_class_specifier_list, logger);
+        if (ret)
+            return ret;
+        bt_list_for_each_entry (
+            iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_VARIANT:
+        bt_list_for_each_entry (iter, &node->u.variant.declaration_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_STRUCT:
+        bt_list_for_each_entry (iter, &node->u._struct.declaration_list, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u._struct.min_align, siblings) {
+            iter->parent = node;
+            ret = ctf_visitor_parent_links(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+
+    case NODE_UNKNOWN:
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno, "Unknown node type: type={}\n",
+                                        (int) node->type);
+        return -EINVAL;
+    }
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/visitor-semantic-validator.cpp b/src/plugins/ctf/common/src/metadata/tsdl/visitor-semantic-validator.cpp
new file mode 100644 (file)
index 0000000..59cec93
--- /dev/null
@@ -0,0 +1,999 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Metadata Semantic Validator.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "logging.hpp"
+
+#include "common/list.h"
+
+#include "ast.hpp"
+
+#define _bt_list_first_entry(ptr, type, member) bt_list_entry((ptr)->next, type, member)
+
+static int _ctf_visitor_semantic_check(int depth, struct ctf_node *node,
+                                       const bt2c::Logger& logger);
+
+static int ctf_visitor_unary_expression(int, struct ctf_node *node, const bt2c::Logger& logger)
+{
+    struct ctf_node *iter;
+    int is_ctf_exp = 0, is_ctf_exp_left = 0;
+
+    switch (node->parent->type) {
+    case NODE_CTF_EXPRESSION:
+        is_ctf_exp = 1;
+        bt_list_for_each_entry (iter, &node->parent->u.ctf_expression.left, siblings) {
+            if (iter == node) {
+                is_ctf_exp_left = 1;
+                /*
+                 * We are a left child of a ctf expression.
+                 * We are only allowed to be a string.
+                 */
+                if (node->u.unary_expression.type != UNARY_STRING) {
+                    _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                        logger, node->lineno,
+                        "Left child of a CTF expression is only allowed to be a string.");
+                    goto errperm;
+                }
+                break;
+            }
+        }
+        /* Right child of a ctf expression can be any type of unary exp. */
+        break; /* OK */
+    case NODE_TYPE_DECLARATOR:
+        /*
+         * We are the length of a type declarator.
+         */
+        switch (node->u.unary_expression.type) {
+        case UNARY_UNSIGNED_CONSTANT:
+        case UNARY_STRING:
+            break;
+        default:
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                logger, node->lineno,
+                "Children of field class declarator and `enum` can only be unsigned numeric constants or references to fields (e.g., `a.b.c`).");
+            goto errperm;
+        }
+        break; /* OK */
+
+    case NODE_STRUCT:
+        /*
+         * We are the size of a struct align attribute.
+         */
+        switch (node->u.unary_expression.type) {
+        case UNARY_UNSIGNED_CONSTANT:
+            break;
+        default:
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                logger, node->lineno,
+                "Structure alignment attribute can only be an unsigned numeric constant.");
+            goto errperm;
+        }
+        break;
+
+    case NODE_ENUMERATOR:
+        /* The enumerator's parent has validated its validity already. */
+        break; /* OK */
+
+    case NODE_UNARY_EXPRESSION:
+        /*
+         * We disallow nested unary expressions and "sbrac" unary
+         * expressions.
+         */
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                        "Nested unary expressions not allowed (`()` and `[]`).");
+        goto errperm;
+
+    case NODE_ROOT:
+    case NODE_EVENT:
+    case NODE_STREAM:
+    case NODE_ENV:
+    case NODE_TRACE:
+    case NODE_CLOCK:
+    case NODE_CALLSITE:
+    case NODE_TYPEDEF:
+    case NODE_TYPEALIAS_TARGET:
+    case NODE_TYPEALIAS_ALIAS:
+    case NODE_TYPEALIAS:
+    case NODE_TYPE_SPECIFIER:
+    case NODE_POINTER:
+    case NODE_FLOATING_POINT:
+    case NODE_INTEGER:
+    case NODE_STRING:
+    case NODE_ENUM:
+    case NODE_STRUCT_OR_VARIANT_DECLARATION:
+    case NODE_VARIANT:
+    default:
+        goto errinval;
+    }
+
+    switch (node->u.unary_expression.link) {
+    case UNARY_LINK_UNKNOWN:
+        /* We don't allow empty link except on the first node of the list */
+        if (is_ctf_exp &&
+            _bt_list_first_entry(is_ctf_exp_left ? &node->parent->u.ctf_expression.left :
+                                                   &node->parent->u.ctf_expression.right,
+                                 struct ctf_node, siblings) != node) {
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                logger, node->lineno,
+                "Empty link is not allowed except on first node of unary expression (need to separate nodes with `.` or `->`).");
+            goto errperm;
+        }
+        break; /* OK */
+    case UNARY_DOTLINK:
+    case UNARY_ARROWLINK:
+        /* We only allow -> and . links between children of ctf_expression. */
+        if (node->parent->type != NODE_CTF_EXPRESSION) {
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                logger, node->lineno,
+                "Links `.` and `->` are only allowed as children of CTF expression.");
+            goto errperm;
+        }
+        /*
+         * Only strings can be separated linked by . or ->.
+         * This includes "", '' and non-quoted identifiers.
+         */
+        if (node->u.unary_expression.type != UNARY_STRING) {
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                logger, node->lineno,
+                "Links `.` and `->` are only allowed to separate strings and identifiers.");
+            goto errperm;
+        }
+        /* We don't allow link on the first node of the list */
+        if (is_ctf_exp &&
+            _bt_list_first_entry(is_ctf_exp_left ? &node->parent->u.ctf_expression.left :
+                                                   &node->parent->u.ctf_expression.right,
+                                 struct ctf_node, siblings) == node) {
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                logger, node->lineno,
+                "Links `.` and `->` are not allowed before first node of the unary expression list.");
+            goto errperm;
+        }
+        break;
+    case UNARY_DOTDOTDOT:
+        /* We only allow ... link between children of enumerator. */
+        if (node->parent->type != NODE_ENUMERATOR) {
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                            "Link `...` is only allowed within enumerator.");
+            goto errperm;
+        }
+        /* We don't allow link on the first node of the list */
+        if (_bt_list_first_entry(&node->parent->u.enumerator.values, struct ctf_node, siblings) ==
+            node) {
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                logger, node->lineno,
+                "Link `...` is not allowed on the first node of the unary expression list.");
+            goto errperm;
+        }
+        break;
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                        "Unknown expression link type: type={}",
+                                        (int) node->u.unary_expression.link);
+        return -EINVAL;
+    }
+    return 0;
+
+errinval:
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+        logger, node->lineno, "Incoherent parent node's type: node-type={}, parent-node-type={}",
+        node_type(node), node_type(node->parent));
+    return -EINVAL; /* Incoherent structure */
+
+errperm:
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                    "Semantic error: node-type={}, parent-node-type={}",
+                                    node_type(node), node_type(node->parent));
+    return -EPERM; /* Structure not allowed */
+}
+
+static int ctf_visitor_field_class_specifier_list(int, struct ctf_node *node,
+                                                  const bt2c::Logger& logger)
+{
+    switch (node->parent->type) {
+    case NODE_CTF_EXPRESSION:
+    case NODE_TYPE_DECLARATOR:
+    case NODE_TYPEDEF:
+    case NODE_TYPEALIAS_TARGET:
+    case NODE_TYPEALIAS_ALIAS:
+    case NODE_ENUM:
+    case NODE_STRUCT_OR_VARIANT_DECLARATION:
+    case NODE_ROOT:
+        break; /* OK */
+
+    case NODE_EVENT:
+    case NODE_STREAM:
+    case NODE_ENV:
+    case NODE_TRACE:
+    case NODE_CLOCK:
+    case NODE_CALLSITE:
+    case NODE_UNARY_EXPRESSION:
+    case NODE_TYPEALIAS:
+    case NODE_TYPE_SPECIFIER:
+    case NODE_TYPE_SPECIFIER_LIST:
+    case NODE_POINTER:
+    case NODE_FLOATING_POINT:
+    case NODE_INTEGER:
+    case NODE_STRING:
+    case NODE_ENUMERATOR:
+    case NODE_VARIANT:
+    case NODE_STRUCT:
+    default:
+        goto errinval;
+    }
+    return 0;
+errinval:
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+        logger, node->lineno, "Incoherent parent node's type: node-type={}, parent-node-type={}",
+        node_type(node), node_type(node->parent));
+    return -EINVAL; /* Incoherent structure */
+}
+
+static int ctf_visitor_field_class_specifier(int, struct ctf_node *node, const bt2c::Logger& logger)
+{
+    switch (node->parent->type) {
+    case NODE_TYPE_SPECIFIER_LIST:
+        break; /* OK */
+
+    case NODE_CTF_EXPRESSION:
+    case NODE_TYPE_DECLARATOR:
+    case NODE_TYPEDEF:
+    case NODE_TYPEALIAS_TARGET:
+    case NODE_TYPEALIAS_ALIAS:
+    case NODE_ENUM:
+    case NODE_STRUCT_OR_VARIANT_DECLARATION:
+    case NODE_ROOT:
+    case NODE_EVENT:
+    case NODE_STREAM:
+    case NODE_ENV:
+    case NODE_TRACE:
+    case NODE_CLOCK:
+    case NODE_CALLSITE:
+    case NODE_UNARY_EXPRESSION:
+    case NODE_TYPEALIAS:
+    case NODE_TYPE_SPECIFIER:
+    case NODE_POINTER:
+    case NODE_FLOATING_POINT:
+    case NODE_INTEGER:
+    case NODE_STRING:
+    case NODE_ENUMERATOR:
+    case NODE_VARIANT:
+    case NODE_STRUCT:
+    default:
+        goto errinval;
+    }
+    return 0;
+errinval:
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+        logger, node->lineno, "Incoherent parent node's type: node-type={}, parent-node-type={}",
+        node_type(node), node_type(node->parent));
+    return -EINVAL; /* Incoherent structure */
+}
+
+static int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node,
+                                              const bt2c::Logger& logger)
+{
+    int ret = 0;
+    struct ctf_node *iter;
+
+    depth++;
+
+    switch (node->parent->type) {
+    case NODE_TYPE_DECLARATOR:
+        /*
+         * A nested field class declarator is not allowed to
+         * contain pointers.
+         */
+        if (!bt_list_empty(&node->u.field_class_declarator.pointers))
+            goto errperm;
+        break; /* OK */
+    case NODE_TYPEALIAS_TARGET:
+        break; /* OK */
+    case NODE_TYPEALIAS_ALIAS:
+        /*
+         * Only accept alias name containing:
+         * - identifier
+         * - identifier *   (any number of pointers)
+         * NOT accepting alias names containing [] (would otherwise
+         * cause semantic clash for later declarations of
+         * arrays/sequences of elements, where elements could be
+         * arrays/sequences themselves (if allowed in field class alias).
+         * NOT accepting alias with identifier. The declarator should
+         * be either empty or contain pointer(s).
+         */
+        if (node->u.field_class_declarator.type == TYPEDEC_NESTED)
+            goto errperm;
+        bt_list_for_each_entry (iter,
+                                &node->parent->u.field_class_alias_name.field_class_specifier_list
+                                     ->u.field_class_specifier_list.head,
+                                siblings) {
+            switch (iter->u.field_class_specifier.type) {
+            case TYPESPEC_FLOATING_POINT:
+            case TYPESPEC_INTEGER:
+            case TYPESPEC_STRING:
+            case TYPESPEC_STRUCT:
+            case TYPESPEC_VARIANT:
+            case TYPESPEC_ENUM:
+                if (bt_list_empty(&node->u.field_class_declarator.pointers))
+                    goto errperm;
+                break;
+            default:
+                break;
+            }
+        }
+        if (node->u.field_class_declarator.type == TYPEDEC_ID &&
+            node->u.field_class_declarator.u.id)
+            goto errperm;
+        break; /* OK */
+    case NODE_TYPEDEF:
+    case NODE_STRUCT_OR_VARIANT_DECLARATION:
+        break; /* OK */
+
+    case NODE_ROOT:
+    case NODE_EVENT:
+    case NODE_STREAM:
+    case NODE_ENV:
+    case NODE_TRACE:
+    case NODE_CLOCK:
+    case NODE_CALLSITE:
+    case NODE_CTF_EXPRESSION:
+    case NODE_UNARY_EXPRESSION:
+    case NODE_TYPEALIAS:
+    case NODE_TYPE_SPECIFIER:
+    case NODE_POINTER:
+    case NODE_FLOATING_POINT:
+    case NODE_INTEGER:
+    case NODE_STRING:
+    case NODE_ENUMERATOR:
+    case NODE_ENUM:
+    case NODE_VARIANT:
+    case NODE_STRUCT:
+    default:
+        goto errinval;
+    }
+
+    bt_list_for_each_entry (iter, &node->u.field_class_declarator.pointers, siblings) {
+        ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+        if (ret)
+            return ret;
+    }
+
+    switch (node->u.field_class_declarator.type) {
+    case TYPEDEC_ID:
+        break;
+    case TYPEDEC_NESTED:
+    {
+        if (node->u.field_class_declarator.u.nested.field_class_declarator) {
+            ret = _ctf_visitor_semantic_check(
+                depth + 1, node->u.field_class_declarator.u.nested.field_class_declarator, logger);
+            if (ret)
+                return ret;
+        }
+        if (!node->u.field_class_declarator.u.nested.abstract_array) {
+            bt_list_for_each_entry (iter, &node->u.field_class_declarator.u.nested.length,
+                                    siblings) {
+                if (iter->type != NODE_UNARY_EXPRESSION) {
+                    _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                        logger, node->lineno, "Expecting unary expression as length: node-type={}",
+                        node_type(iter));
+                    return -EINVAL;
+                }
+                ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+                if (ret)
+                    return ret;
+            }
+        } else {
+            if (node->parent->type == NODE_TYPEALIAS_TARGET) {
+                _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                    logger, node->lineno,
+                    "Abstract array declarator not permitted as target of field class alias.");
+                return -EINVAL;
+            }
+        }
+        if (node->u.field_class_declarator.bitfield_len) {
+            ret = _ctf_visitor_semantic_check(depth + 1,
+                                              node->u.field_class_declarator.bitfield_len, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    }
+    case TYPEDEC_UNKNOWN:
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                        "Unknown field class declarator: type={}",
+                                        (int) node->u.field_class_declarator.type);
+        return -EINVAL;
+    }
+    depth--;
+    return 0;
+
+errinval:
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+        logger, node->lineno, "Incoherent parent node's type: node-type={}, parent-node-type={}",
+        node_type(node), node_type(node->parent));
+    return -EINVAL; /* Incoherent structure */
+
+errperm:
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                    "Semantic error: node-type={}, parent-node-type={}",
+                                    node_type(node), node_type(node->parent));
+    return -EPERM; /* Structure not allowed */
+}
+
+static int _ctf_visitor_semantic_check(int depth, struct ctf_node *node, const bt2c::Logger& logger)
+{
+    int ret = 0;
+    struct ctf_node *iter;
+
+    if (node->visited)
+        return 0;
+
+    switch (node->type) {
+    case NODE_ROOT:
+        bt_list_for_each_entry (iter, &node->u.root.declaration_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.root.trace, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.root.stream, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.root.event, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+
+    case NODE_EVENT:
+        switch (node->parent->type) {
+        case NODE_ROOT:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+
+        bt_list_for_each_entry (iter, &node->u.event.declaration_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_STREAM:
+        switch (node->parent->type) {
+        case NODE_ROOT:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+
+        bt_list_for_each_entry (iter, &node->u.stream.declaration_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_ENV:
+        switch (node->parent->type) {
+        case NODE_ROOT:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+
+        bt_list_for_each_entry (iter, &node->u.env.declaration_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_TRACE:
+        switch (node->parent->type) {
+        case NODE_ROOT:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+
+        bt_list_for_each_entry (iter, &node->u.trace.declaration_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_CLOCK:
+        switch (node->parent->type) {
+        case NODE_ROOT:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+
+        bt_list_for_each_entry (iter, &node->u.clock.declaration_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_CALLSITE:
+        switch (node->parent->type) {
+        case NODE_ROOT:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+
+        bt_list_for_each_entry (iter, &node->u.callsite.declaration_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+
+    case NODE_CTF_EXPRESSION:
+        switch (node->parent->type) {
+        case NODE_ROOT:
+        case NODE_EVENT:
+        case NODE_STREAM:
+        case NODE_ENV:
+        case NODE_TRACE:
+        case NODE_CLOCK:
+        case NODE_CALLSITE:
+        case NODE_FLOATING_POINT:
+        case NODE_INTEGER:
+        case NODE_STRING:
+            break; /* OK */
+
+        case NODE_CTF_EXPRESSION:
+        case NODE_UNARY_EXPRESSION:
+        case NODE_TYPEDEF:
+        case NODE_TYPEALIAS_TARGET:
+        case NODE_TYPEALIAS_ALIAS:
+        case NODE_STRUCT_OR_VARIANT_DECLARATION:
+        case NODE_TYPEALIAS:
+        case NODE_TYPE_SPECIFIER:
+        case NODE_TYPE_SPECIFIER_LIST:
+        case NODE_POINTER:
+        case NODE_TYPE_DECLARATOR:
+        case NODE_ENUMERATOR:
+        case NODE_ENUM:
+        case NODE_VARIANT:
+        case NODE_STRUCT:
+        default:
+            goto errinval;
+        }
+
+        depth++;
+        bt_list_for_each_entry (iter, &node->u.ctf_expression.left, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        bt_list_for_each_entry (iter, &node->u.ctf_expression.right, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        depth--;
+        break;
+    case NODE_UNARY_EXPRESSION:
+        return ctf_visitor_unary_expression(depth, node, logger);
+
+    case NODE_TYPEDEF:
+        switch (node->parent->type) {
+        case NODE_ROOT:
+        case NODE_EVENT:
+        case NODE_STREAM:
+        case NODE_TRACE:
+        case NODE_VARIANT:
+        case NODE_STRUCT:
+            break; /* OK */
+
+        case NODE_CTF_EXPRESSION:
+        case NODE_UNARY_EXPRESSION:
+        case NODE_TYPEDEF:
+        case NODE_TYPEALIAS_TARGET:
+        case NODE_TYPEALIAS_ALIAS:
+        case NODE_TYPEALIAS:
+        case NODE_STRUCT_OR_VARIANT_DECLARATION:
+        case NODE_TYPE_SPECIFIER:
+        case NODE_TYPE_SPECIFIER_LIST:
+        case NODE_POINTER:
+        case NODE_TYPE_DECLARATOR:
+        case NODE_FLOATING_POINT:
+        case NODE_INTEGER:
+        case NODE_STRING:
+        case NODE_ENUMERATOR:
+        case NODE_ENUM:
+        case NODE_CLOCK:
+        case NODE_CALLSITE:
+        case NODE_ENV:
+        default:
+            goto errinval;
+        }
+
+        depth++;
+        ret = _ctf_visitor_semantic_check(
+            depth + 1, node->u.field_class_def.field_class_specifier_list, logger);
+        if (ret)
+            return ret;
+        bt_list_for_each_entry (iter, &node->u.field_class_def.field_class_declarators, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        depth--;
+        break;
+    case NODE_TYPEALIAS_TARGET:
+    {
+        int nr_declarators;
+
+        switch (node->parent->type) {
+        case NODE_TYPEALIAS:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+
+        depth++;
+        ret = _ctf_visitor_semantic_check(
+            depth + 1, node->u.field_class_alias_target.field_class_specifier_list, logger);
+        if (ret)
+            return ret;
+        nr_declarators = 0;
+        bt_list_for_each_entry (iter, &node->u.field_class_alias_target.field_class_declarators,
+                                siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+            nr_declarators++;
+        }
+        if (nr_declarators > 1) {
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                logger, node->lineno,
+                "Too many declarators in field class alias's name (maximum is 1): count={}",
+                nr_declarators);
+            return -EINVAL;
+        }
+        depth--;
+        break;
+    }
+    case NODE_TYPEALIAS_ALIAS:
+    {
+        int nr_declarators;
+
+        switch (node->parent->type) {
+        case NODE_TYPEALIAS:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+
+        depth++;
+        ret = _ctf_visitor_semantic_check(
+            depth + 1, node->u.field_class_alias_name.field_class_specifier_list, logger);
+        if (ret)
+            return ret;
+        nr_declarators = 0;
+        bt_list_for_each_entry (iter, &node->u.field_class_alias_name.field_class_declarators,
+                                siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+            nr_declarators++;
+        }
+        if (nr_declarators > 1) {
+            _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                logger, node->lineno,
+                "Too many declarators in field class alias's name (maximum is 1): count={}",
+                nr_declarators);
+            return -EINVAL;
+        }
+        depth--;
+        break;
+    }
+    case NODE_TYPEALIAS:
+        switch (node->parent->type) {
+        case NODE_ROOT:
+        case NODE_EVENT:
+        case NODE_STREAM:
+        case NODE_TRACE:
+        case NODE_VARIANT:
+        case NODE_STRUCT:
+            break; /* OK */
+
+        case NODE_CTF_EXPRESSION:
+        case NODE_UNARY_EXPRESSION:
+        case NODE_TYPEDEF:
+        case NODE_TYPEALIAS_TARGET:
+        case NODE_TYPEALIAS_ALIAS:
+        case NODE_TYPEALIAS:
+        case NODE_STRUCT_OR_VARIANT_DECLARATION:
+        case NODE_TYPE_SPECIFIER:
+        case NODE_TYPE_SPECIFIER_LIST:
+        case NODE_POINTER:
+        case NODE_TYPE_DECLARATOR:
+        case NODE_FLOATING_POINT:
+        case NODE_INTEGER:
+        case NODE_STRING:
+        case NODE_ENUMERATOR:
+        case NODE_ENUM:
+        case NODE_CLOCK:
+        case NODE_CALLSITE:
+        case NODE_ENV:
+        default:
+            goto errinval;
+        }
+
+        ret = _ctf_visitor_semantic_check(depth + 1, node->u.field_class_alias.target, logger);
+        if (ret)
+            return ret;
+        ret = _ctf_visitor_semantic_check(depth + 1, node->u.field_class_alias.alias, logger);
+        if (ret)
+            return ret;
+        break;
+
+    case NODE_TYPE_SPECIFIER_LIST:
+        ret = ctf_visitor_field_class_specifier_list(depth, node, logger);
+        if (ret)
+            return ret;
+        break;
+    case NODE_TYPE_SPECIFIER:
+        ret = ctf_visitor_field_class_specifier(depth, node, logger);
+        if (ret)
+            return ret;
+        break;
+    case NODE_POINTER:
+        switch (node->parent->type) {
+        case NODE_TYPE_DECLARATOR:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+        break;
+    case NODE_TYPE_DECLARATOR:
+        ret = ctf_visitor_field_class_declarator(depth, node, logger);
+        if (ret)
+            return ret;
+        break;
+
+    case NODE_FLOATING_POINT:
+        switch (node->parent->type) {
+        case NODE_TYPE_SPECIFIER:
+            break; /* OK */
+        default:
+            goto errinval;
+
+        case NODE_UNARY_EXPRESSION:
+            goto errperm;
+        }
+        bt_list_for_each_entry (iter, &node->u.floating_point.expressions, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_INTEGER:
+        switch (node->parent->type) {
+        case NODE_TYPE_SPECIFIER:
+            break; /* OK */
+        default:
+            goto errinval;
+        }
+
+        bt_list_for_each_entry (iter, &node->u.integer.expressions, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_STRING:
+        switch (node->parent->type) {
+        case NODE_TYPE_SPECIFIER:
+            break; /* OK */
+        default:
+            goto errinval;
+
+        case NODE_UNARY_EXPRESSION:
+            goto errperm;
+        }
+
+        bt_list_for_each_entry (iter, &node->u.string.expressions, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_ENUMERATOR:
+        switch (node->parent->type) {
+        case NODE_ENUM:
+            break;
+        default:
+            goto errinval;
+        }
+        /*
+         * Enumerators are only allows to contain:
+         *    numeric unary expression
+         * or num. unary exp. ... num. unary exp
+         */
+        {
+            int count = 0;
+
+            bt_list_for_each_entry (iter, &node->u.enumerator.values, siblings) {
+                switch (count++) {
+                case 0:
+                    if (iter->type != NODE_UNARY_EXPRESSION ||
+                        (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT &&
+                         iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) ||
+                        iter->u.unary_expression.link != UNARY_LINK_UNKNOWN) {
+                        _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                            logger, iter->lineno,
+                            "First unary expression of enumerator is unexpected.");
+                        goto errperm;
+                    }
+                    break;
+                case 1:
+                    if (iter->type != NODE_UNARY_EXPRESSION ||
+                        (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT &&
+                         iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) ||
+                        iter->u.unary_expression.link != UNARY_DOTDOTDOT) {
+                        _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+                            logger, iter->lineno,
+                            "Second unary expression of enumerator is unexpected.");
+                        goto errperm;
+                    }
+                    break;
+                default:
+                    goto errperm;
+                }
+            }
+        }
+
+        bt_list_for_each_entry (iter, &node->u.enumerator.values, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_ENUM:
+        switch (node->parent->type) {
+        case NODE_TYPE_SPECIFIER:
+            break; /* OK */
+        default:
+            goto errinval;
+
+        case NODE_UNARY_EXPRESSION:
+            goto errperm;
+        }
+
+        depth++;
+        ret = _ctf_visitor_semantic_check(depth + 1, node->u._enum.container_field_class, logger);
+        if (ret)
+            return ret;
+
+        bt_list_for_each_entry (iter, &node->u._enum.enumerator_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        depth--;
+        break;
+    case NODE_STRUCT_OR_VARIANT_DECLARATION:
+        switch (node->parent->type) {
+        case NODE_STRUCT:
+        case NODE_VARIANT:
+            break;
+        default:
+            goto errinval;
+        }
+        ret = _ctf_visitor_semantic_check(
+            depth + 1, node->u.struct_or_variant_declaration.field_class_specifier_list, logger);
+        if (ret)
+            return ret;
+        bt_list_for_each_entry (
+            iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+    case NODE_VARIANT:
+        switch (node->parent->type) {
+        case NODE_TYPE_SPECIFIER:
+            break; /* OK */
+        default:
+            goto errinval;
+
+        case NODE_UNARY_EXPRESSION:
+            goto errperm;
+        }
+        bt_list_for_each_entry (iter, &node->u.variant.declaration_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+
+    case NODE_STRUCT:
+        switch (node->parent->type) {
+        case NODE_TYPE_SPECIFIER:
+            break; /* OK */
+        default:
+            goto errinval;
+
+        case NODE_UNARY_EXPRESSION:
+            goto errperm;
+        }
+        bt_list_for_each_entry (iter, &node->u._struct.declaration_list, siblings) {
+            ret = _ctf_visitor_semantic_check(depth + 1, iter, logger);
+            if (ret)
+                return ret;
+        }
+        break;
+
+    case NODE_UNKNOWN:
+    default:
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno, "Unknown node type: type={}",
+                                        (int) node->type);
+        return -EINVAL;
+    }
+    return ret;
+
+errinval:
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(
+        logger, node->lineno, "Incoherent parent node's type: node-type={}, parent-node-type={}",
+        node_type(node), node_type(node->parent));
+    return -EINVAL; /* Incoherent structure */
+
+errperm:
+    _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                    "Semantic error: node-type={}, parent-node-type={}",
+                                    node_type(node), node_type(node->parent));
+    return -EPERM; /* Structure not allowed */
+}
+
+int ctf_visitor_semantic_check(int depth, struct ctf_node *node, const bt2c::Logger& parentLogger)
+{
+    int ret = 0;
+    bt2c::Logger logger {parentLogger, "PLUGIN/CTF/META/SEMANTIC-VALIDATOR-VISITOR"};
+
+    /*
+     * First make sure we create the parent links for all children. Let's
+     * take the safe route and recreate them at each validation, just in
+     * case the structure has changed.
+     */
+    ret = ctf_visitor_parent_links(depth, node, logger);
+    if (ret) {
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                        "Cannot create parent links in metadata's AST: "
+                                        "ret={}",
+                                        ret);
+        goto end;
+    }
+
+    ret = _ctf_visitor_semantic_check(depth, node, logger);
+    if (ret) {
+        _BT_CPPLOGE_APPEND_CAUSE_LINENO(logger, node->lineno,
+                                        "Cannot check metadata's AST semantics: "
+                                        "ret={}",
+                                        ret);
+        goto end;
+    }
+
+end:
+    return ret;
+}
diff --git a/src/plugins/ctf/common/src/msg-iter/msg-iter.cpp b/src/plugins/ctf/common/src/msg-iter/msg-iter.cpp
new file mode 100644 (file)
index 0000000..4ab970b
--- /dev/null
@@ -0,0 +1,3062 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2015-2018 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2018 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Babeltrace - CTF message iterator
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "common/assert.h"
+#include "common/common.h"
+#include "cpp-common/bt2c/fmt.hpp"
+#include "cpp-common/bt2c/logging.hpp"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "../bfcr/bfcr.hpp"
+#include "msg-iter.hpp"
+
+/* A visit stack entry */
+struct stack_entry
+{
+    /*
+     * Current base field, one of:
+     *
+     *   * string
+     *   * structure
+     *   * array
+     *   * sequence
+     *   * variant
+     *
+     * Field is borrowed.
+     */
+    bt_field *base;
+
+    /* Index of next field to set */
+    size_t index;
+};
+
+/* Visit stack */
+struct stack
+{
+    struct ctf_msg_iter *msg_it;
+
+    /* Entries (struct stack_entry) */
+    GArray *entries;
+
+    /* Number of active entries */
+    size_t size;
+};
+
+/* State */
+enum state
+{
+    STATE_INIT,
+    STATE_SWITCH_PACKET,
+    STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN,
+    STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE,
+    STATE_AFTER_TRACE_PACKET_HEADER,
+    STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN,
+    STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
+    STATE_AFTER_STREAM_PACKET_CONTEXT,
+    STATE_EMIT_MSG_STREAM_BEGINNING,
+    STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS,
+    STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS,
+    STATE_EMIT_MSG_DISCARDED_EVENTS,
+    STATE_EMIT_MSG_DISCARDED_PACKETS,
+    STATE_EMIT_MSG_PACKET_BEGINNING,
+    STATE_DSCOPE_EVENT_HEADER_BEGIN,
+    STATE_DSCOPE_EVENT_HEADER_CONTINUE,
+    STATE_AFTER_EVENT_HEADER,
+    STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN,
+    STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE,
+    STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN,
+    STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE,
+    STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
+    STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE,
+    STATE_EMIT_MSG_EVENT,
+    STATE_EMIT_QUEUED_MSG_EVENT,
+    STATE_SKIP_PACKET_PADDING,
+    STATE_EMIT_MSG_PACKET_END_MULTI,
+    STATE_EMIT_MSG_PACKET_END_SINGLE,
+    STATE_EMIT_QUEUED_MSG_PACKET_END,
+    STATE_CHECK_EMIT_MSG_STREAM_END,
+    STATE_EMIT_MSG_STREAM_END,
+    STATE_DONE,
+};
+
+static __attribute__((used)) const char *format_as(state state)
+{
+    switch (state) {
+    case STATE_INIT:
+        return "STATE_INIT";
+
+    case STATE_SWITCH_PACKET:
+        return "STATE_SWITCH_PACKET";
+
+    case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
+        return "STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN";
+
+    case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
+        return "STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE";
+
+    case STATE_AFTER_TRACE_PACKET_HEADER:
+        return "STATE_AFTER_TRACE_PACKET_HEADER";
+
+    case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
+        return "STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN";
+
+    case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
+        return "STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE";
+
+    case STATE_AFTER_STREAM_PACKET_CONTEXT:
+        return "STATE_AFTER_STREAM_PACKET_CONTEXT";
+
+    case STATE_EMIT_MSG_STREAM_BEGINNING:
+        return "STATE_EMIT_MSG_STREAM_BEGINNING";
+
+    case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
+        return "STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS";
+
+    case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
+        return "STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS";
+
+    case STATE_EMIT_MSG_DISCARDED_EVENTS:
+        return "STATE_EMIT_MSG_DISCARDED_EVENTS";
+
+    case STATE_EMIT_MSG_DISCARDED_PACKETS:
+        return "STATE_EMIT_MSG_DISCARDED_PACKETS";
+
+    case STATE_EMIT_MSG_PACKET_BEGINNING:
+        return "STATE_EMIT_MSG_PACKET_BEGINNING";
+
+    case STATE_DSCOPE_EVENT_HEADER_BEGIN:
+        return "STATE_DSCOPE_EVENT_HEADER_BEGIN";
+
+    case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
+        return "STATE_DSCOPE_EVENT_HEADER_CONTINUE";
+
+    case STATE_AFTER_EVENT_HEADER:
+        return "STATE_AFTER_EVENT_HEADER";
+
+    case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
+        return "STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN";
+
+    case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
+        return "STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE";
+
+    case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
+        return "STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN";
+
+    case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
+        return "STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE";
+
+    case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
+        return "STATE_DSCOPE_EVENT_PAYLOAD_BEGIN";
+
+    case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
+        return "STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE";
+
+    case STATE_EMIT_MSG_EVENT:
+        return "STATE_EMIT_MSG_EVENT";
+
+    case STATE_EMIT_QUEUED_MSG_EVENT:
+        return "STATE_EMIT_QUEUED_MSG_EVENT";
+
+    case STATE_SKIP_PACKET_PADDING:
+        return "STATE_SKIP_PACKET_PADDING";
+
+    case STATE_EMIT_MSG_PACKET_END_MULTI:
+        return "STATE_EMIT_MSG_PACKET_END_MULTI";
+
+    case STATE_EMIT_MSG_PACKET_END_SINGLE:
+        return "STATE_EMIT_MSG_PACKET_END_SINGLE";
+
+    case STATE_EMIT_QUEUED_MSG_PACKET_END:
+        return "STATE_EMIT_QUEUED_MSG_PACKET_END";
+
+    case STATE_CHECK_EMIT_MSG_STREAM_END:
+        return "STATE_CHECK_EMIT_MSG_STREAM_END";
+
+    case STATE_EMIT_MSG_STREAM_END:
+        return "STATE_EMIT_MSG_STREAM_END";
+
+    case STATE_DONE:
+        return "STATE_DONE";
+    }
+
+    bt_common_abort();
+}
+
+struct end_of_packet_snapshots
+{
+    uint64_t discarded_events = 0;
+    uint64_t packets = 0;
+    uint64_t beginning_clock = 0;
+    uint64_t end_clock = 0;
+};
+
+/* CTF message iterator */
+struct ctf_msg_iter
+{
+    explicit ctf_msg_iter(bt2c::Logger loggerParam) noexcept : logger {std::move(loggerParam)}
+    {
+    }
+
+    /* Visit stack */
+    struct stack *stack = nullptr;
+
+    /* Current message iterator to create messages (weak) */
+    bt_self_message_iterator *self_msg_iter = nullptr;
+
+    /*
+     * True if library objects are unavailable during the decoding and
+     * should not be created/used.
+     */
+    bool dry_run = false;
+
+    /*
+     * Current dynamic scope field pointer.
+     *
+     * This is set by read_dscope_begin_state() and contains the
+     * value of one of the pointers in `dscopes` below.
+     */
+    bt_field *cur_dscope_field = nullptr;
+
+    /*
+     * True if we're done filling a string field from a text
+     * array/sequence payload.
+     */
+    bool done_filling_string = false;
+
+    /* Trace and classes */
+    /* True to set IR fields */
+    bool set_ir_fields = false;
+
+    struct
+    {
+        struct ctf_trace_class *tc = nullptr;
+        struct ctf_stream_class *sc = nullptr;
+        struct ctf_event_class *ec = nullptr;
+    } meta;
+
+    /* Current packet (NULL if not created yet) */
+    bt_packet *packet = nullptr;
+
+    /* Current stream (NULL if not set yet) */
+    bt_stream *stream = nullptr;
+
+    /* Current event (NULL if not created yet) */
+    bt_event *event = nullptr;
+
+    /* Current event message (NULL if not created yet) */
+    bt_message *event_msg = nullptr;
+
+    /*
+     * True if we need to emit a packet beginning message before we emit
+     * the next event message or the packet end message.
+     */
+    bool emit_delayed_packet_beginning_msg = false;
+
+    /*
+     * True if this is the first packet we are reading, and therefore if we
+     * should emit a stream beginning message.
+     */
+    bool emit_stream_beginning_message = false;
+
+    /*
+     * True if we need to emit a stream end message at the end of the
+     * current stream. A live stream may never receive any data and thus
+     * never send a stream beginning message which removes the need to emit
+     * a stream end message.
+     */
+    bool emit_stream_end_message = false;
+
+    /* Database of current dynamic scopes */
+    struct
+    {
+        bt_field *stream_packet_context = nullptr;
+        bt_field *event_common_context = nullptr;
+        bt_field *event_spec_context = nullptr;
+        bt_field *event_payload = nullptr;
+    } dscopes;
+
+    /* Current state */
+    enum state state = STATE_INIT;
+
+    /* Current medium buffer data */
+    struct
+    {
+        /* Last address provided by medium */
+        const uint8_t *addr = nullptr;
+
+        /* Buffer size provided by medium (bytes) */
+        size_t sz = 0;
+
+        /* Offset within whole packet of addr (bits) */
+        size_t packet_offset = 0;
+
+        /* Current position from addr (bits) */
+        size_t at = 0;
+
+        /* Position of the last event header from addr (bits) */
+        size_t last_eh_at = 0;
+    } buf;
+
+    /* Binary type reader */
+    struct bt_bfcr *bfcr = nullptr;
+
+    /* Current medium data */
+    struct
+    {
+        struct ctf_msg_iter_medium_ops medops;
+        size_t max_request_sz = 0;
+        void *data = nullptr;
+    } medium;
+
+    /* Current packet size (bits) (-1 if unknown) */
+    int64_t cur_exp_packet_total_size = 0;
+
+    /* Current content size (bits) (-1 if unknown) */
+    int64_t cur_exp_packet_content_size = 0;
+
+    /* Current stream class ID */
+    int64_t cur_stream_class_id = 0;
+
+    /* Current event class ID */
+    int64_t cur_event_class_id = 0;
+
+    /* Current data stream ID */
+    int64_t cur_data_stream_id = 0;
+
+    /*
+     * Offset, in the underlying media, of the current packet's
+     * start (-1 if unknown).
+     */
+    off_t cur_packet_offset = 0;
+
+    /* Default clock's current value */
+    uint64_t default_clock_snapshot = 0;
+
+    /* End of current packet snapshots */
+    struct end_of_packet_snapshots snapshots;
+
+    /* End of previous packet snapshots */
+    struct end_of_packet_snapshots prev_packet_snapshots;
+
+    /* Stored values (for sequence lengths, variant tags) */
+    GArray *stored_values = nullptr;
+
+    bt2c::Logger logger;
+};
+
+static struct stack *stack_new(struct ctf_msg_iter *msg_it)
+{
+    struct stack *stack = NULL;
+
+    stack = g_new0(struct stack, 1);
+    if (!stack) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger, "Failed to allocate one stack.");
+        goto error;
+    }
+
+    stack->msg_it = msg_it;
+    stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry));
+    if (!stack->entries) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger, "Failed to allocate a GArray.");
+        goto error;
+    }
+
+    BT_CPPLOGD_SPEC(msg_it->logger, "Created stack: msg-it-addr={}, stack-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(stack));
+    goto end;
+
+error:
+    g_free(stack);
+    stack = NULL;
+
+end:
+    return stack;
+}
+
+static void stack_destroy(struct stack *stack)
+{
+    struct ctf_msg_iter *msg_it;
+
+    BT_ASSERT_DBG(stack);
+    msg_it = stack->msg_it;
+    BT_CPPLOGD_SPEC(msg_it->logger, "Destroying stack: addr={}", fmt::ptr(stack));
+
+    if (stack->entries) {
+        g_array_free(stack->entries, TRUE);
+    }
+
+    g_free(stack);
+}
+
+static void stack_push(struct stack *stack, bt_field *base)
+{
+    struct stack_entry *entry;
+    struct ctf_msg_iter *msg_it;
+
+    BT_ASSERT_DBG(stack);
+    msg_it = stack->msg_it;
+    BT_ASSERT_DBG(base);
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Pushing base field on stack: stack-addr={}, "
+                    "stack-size-before={}, stack-size-after={}",
+                    fmt::ptr(stack), stack->size, stack->size + 1);
+
+    if (stack->entries->len == stack->size) {
+        g_array_set_size(stack->entries, stack->size + 1);
+    }
+
+    entry = &bt_g_array_index(stack->entries, struct stack_entry, stack->size);
+    entry->base = base;
+    entry->index = 0;
+    stack->size++;
+}
+
+static inline unsigned int stack_size(struct stack *stack)
+{
+    BT_ASSERT_DBG(stack);
+    return stack->size;
+}
+
+static void stack_pop(struct stack *stack)
+{
+    struct ctf_msg_iter *msg_it;
+
+    BT_ASSERT_DBG(stack);
+    BT_ASSERT_DBG(stack_size(stack));
+    msg_it = stack->msg_it;
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Popping from stack: "
+                    "stack-addr={}, stack-size-before={}, stack-size-after={}",
+                    fmt::ptr(stack), stack->size, stack->size - 1);
+    stack->size--;
+}
+
+static inline struct stack_entry *stack_top(struct stack *stack)
+{
+    BT_ASSERT_DBG(stack);
+    BT_ASSERT_DBG(stack_size(stack));
+    return &bt_g_array_index(stack->entries, struct stack_entry, stack->size - 1);
+}
+
+static inline bool stack_empty(struct stack *stack)
+{
+    return stack_size(stack) == 0;
+}
+
+static void stack_clear(struct stack *stack)
+{
+    BT_ASSERT_DBG(stack);
+    stack->size = 0;
+}
+
+static inline enum ctf_msg_iter_status
+msg_iter_status_from_m_status(enum ctf_msg_iter_medium_status m_status)
+{
+    /* They are the same */
+    return (ctf_msg_iter_status) m_status;
+}
+
+static inline size_t buf_size_bits(struct ctf_msg_iter *msg_it)
+{
+    return msg_it->buf.sz * 8;
+}
+
+static inline size_t buf_available_bits(struct ctf_msg_iter *msg_it)
+{
+    return buf_size_bits(msg_it) - msg_it->buf.at;
+}
+
+static inline size_t packet_at(struct ctf_msg_iter *msg_it)
+{
+    return msg_it->buf.packet_offset + msg_it->buf.at;
+}
+
+static inline void buf_consume_bits(struct ctf_msg_iter *msg_it, size_t incr)
+{
+    BT_CPPLOGT_SPEC(msg_it->logger, "Advancing cursor: msg-it-addr={}, cur-before={}, cur-after={}",
+                    fmt::ptr(msg_it), msg_it->buf.at, msg_it->buf.at + incr);
+    msg_it->buf.at += incr;
+}
+
+static enum ctf_msg_iter_status request_medium_bytes(struct ctf_msg_iter *msg_it)
+{
+    uint8_t *buffer_addr = NULL;
+    size_t buffer_sz = 0;
+    enum ctf_msg_iter_medium_status m_status;
+
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Calling user function (request bytes): msg-it-addr={}, "
+                    "request-size={}",
+                    fmt::ptr(msg_it), msg_it->medium.max_request_sz);
+    m_status = msg_it->medium.medops.request_bytes(msg_it->medium.max_request_sz, &buffer_addr,
+                                                   &buffer_sz, msg_it->medium.data);
+    BT_CPPLOGD_SPEC(msg_it->logger, "User function returned: status={}, buf-addr={}, buf-size={}",
+                    m_status, fmt::ptr(buffer_addr), buffer_sz);
+    if (m_status == CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+        BT_ASSERT(buffer_sz != 0);
+
+        /* New packet offset is old one + old size (in bits) */
+        msg_it->buf.packet_offset += buf_size_bits(msg_it);
+
+        /* Restart at the beginning of the new medium buffer */
+        msg_it->buf.at = 0;
+        msg_it->buf.last_eh_at = SIZE_MAX;
+
+        /* New medium buffer size */
+        msg_it->buf.sz = buffer_sz;
+
+        /* New medium buffer address */
+        msg_it->buf.addr = buffer_addr;
+
+        BT_CPPLOGD_SPEC(msg_it->logger,
+                        "User function returned new bytes: "
+                        "packet-offset={}, cur={}, size={}, addr={}",
+                        msg_it->buf.packet_offset, msg_it->buf.at, msg_it->buf.sz,
+                        fmt::ptr(msg_it->buf.addr));
+        BT_CPPLOGT_MEM_SPEC(msg_it->logger, buffer_addr, buffer_sz,
+                            "Returned bytes at {}:", fmt::ptr(buffer_addr));
+    } else if (m_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
+        /*
+         * User returned end of stream: validate that we're not
+         * in the middle of a packet header, packet context, or
+         * event.
+         */
+        if (msg_it->cur_exp_packet_total_size >= 0) {
+            if (packet_at(msg_it) == msg_it->cur_exp_packet_total_size) {
+                goto end;
+            }
+        } else {
+            if (packet_at(msg_it) == 0) {
+                goto end;
+            }
+
+            if (msg_it->buf.last_eh_at != SIZE_MAX && msg_it->buf.at == msg_it->buf.last_eh_at) {
+                goto end;
+            }
+        }
+
+        /* All other states are invalid */
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            msg_it->logger,
+            "User function returned {}, but message iterator is in an unexpected state: "
+            "state={}, cur-packet-size={}, cur={}, "
+            "packet-cur={}, last-eh-at={}",
+            m_status, msg_it->state, msg_it->cur_exp_packet_total_size, msg_it->buf.at,
+            packet_at(msg_it), msg_it->buf.last_eh_at);
+        m_status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+    } else if (m_status < 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "User function failed: "
+                                     "status={}",
+                                     m_status);
+    }
+
+end:
+    return msg_iter_status_from_m_status(m_status);
+}
+
+static inline enum ctf_msg_iter_status buf_ensure_available_bits(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+    if (G_UNLIKELY(buf_available_bits(msg_it) == 0)) {
+        /*
+         * This _cannot_ return CTF_MSG_ITER_STATUS_OK
+         * _and_ no bits.
+         */
+        status = request_medium_bytes(msg_it);
+    }
+
+    return status;
+}
+
+static enum ctf_msg_iter_status
+read_dscope_begin_state(struct ctf_msg_iter *msg_it, struct ctf_field_class *dscope_fc,
+                        enum state done_state, enum state continue_state, bt_field *dscope_field)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    enum bt_bfcr_status bfcr_status;
+    size_t consumed_bits;
+
+    msg_it->cur_dscope_field = dscope_field;
+    BT_CPPLOGT_SPEC(msg_it->logger, "Starting BFCR: msg-it-addr={}, bfcr-addr={}, fc-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(dscope_fc));
+    consumed_bits = bt_bfcr_start(msg_it->bfcr, dscope_fc, msg_it->buf.addr, msg_it->buf.at,
+                                  packet_at(msg_it), msg_it->buf.sz, &bfcr_status);
+    BT_CPPLOGT_SPEC(msg_it->logger, "BFCR consumed bits: size={}", consumed_bits);
+
+    switch (bfcr_status) {
+    case BT_BFCR_STATUS_OK:
+        /* Field class was read completely */
+        BT_CPPLOGT_STR_SPEC(msg_it->logger, "Field was completely decoded.");
+        msg_it->state = done_state;
+        break;
+    case BT_BFCR_STATUS_EOF:
+        BT_CPPLOGT_STR_SPEC(msg_it->logger, "BFCR needs more data to decode field completely.");
+        msg_it->state = continue_state;
+        break;
+    default:
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "BFCR failed to start: msg-it-addr={}, bfcr-addr={}, "
+                                     "status={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), bfcr_status);
+        status = CTF_MSG_ITER_STATUS_ERROR;
+        goto end;
+    }
+
+    /* Consume bits now since we know we're not in an error state */
+    buf_consume_bits(msg_it, consumed_bits);
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_dscope_continue_state(struct ctf_msg_iter *msg_it,
+                                                           enum state done_state)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    enum bt_bfcr_status bfcr_status;
+    size_t consumed_bits;
+
+    BT_CPPLOGT_SPEC(msg_it->logger, "Continuing BFCR: msg-it-addr={}, bfcr-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr));
+
+    status = buf_ensure_available_bits(msg_it);
+    if (status != CTF_MSG_ITER_STATUS_OK) {
+        if (status < 0) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                         "Cannot ensure that buffer has at least one byte: "
+                                         "msg-addr={}, status={}",
+                                         fmt::ptr(msg_it), status);
+        } else {
+            BT_CPPLOGT_SPEC(msg_it->logger,
+                            "Cannot ensure that buffer has at least one byte: "
+                            "msg-addr={}, status={}",
+                            fmt::ptr(msg_it), status);
+        }
+
+        goto end;
+    }
+
+    consumed_bits = bt_bfcr_continue(msg_it->bfcr, msg_it->buf.addr, msg_it->buf.sz, &bfcr_status);
+    BT_CPPLOGT_SPEC(msg_it->logger, "BFCR consumed bits: size={}", consumed_bits);
+
+    switch (bfcr_status) {
+    case BT_BFCR_STATUS_OK:
+        /* Type was read completely. */
+        BT_CPPLOGT_STR_SPEC(msg_it->logger, "Field was completely decoded.");
+        msg_it->state = done_state;
+        break;
+    case BT_BFCR_STATUS_EOF:
+        /* Stay in this continue state. */
+        BT_CPPLOGT_STR_SPEC(msg_it->logger, "BFCR needs more data to decode field completely.");
+        break;
+    default:
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "BFCR failed to continue: msg-it-addr={}, bfcr-addr={}, "
+                                     "status={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), bfcr_status);
+        status = CTF_MSG_ITER_STATUS_ERROR;
+        goto end;
+    }
+
+    /* Consume bits now since we know we're not in an error state. */
+    buf_consume_bits(msg_it, consumed_bits);
+end:
+    return status;
+}
+
+static void release_event_dscopes(struct ctf_msg_iter *msg_it)
+{
+    msg_it->dscopes.event_common_context = NULL;
+    msg_it->dscopes.event_spec_context = NULL;
+    msg_it->dscopes.event_payload = NULL;
+}
+
+static void release_all_dscopes(struct ctf_msg_iter *msg_it)
+{
+    msg_it->dscopes.stream_packet_context = NULL;
+
+    release_event_dscopes(msg_it);
+}
+
+static enum ctf_msg_iter_status switch_packet_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status;
+
+    /*
+     * We don't put the stream class here because we need to make
+     * sure that all the packets processed by the same message
+     * iterator refer to the same stream class (the first one).
+     */
+    BT_ASSERT(msg_it);
+
+    if (msg_it->cur_exp_packet_total_size != -1) {
+        msg_it->cur_packet_offset += msg_it->cur_exp_packet_total_size;
+    }
+
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Switching packet: msg-it-addr={}, cur={}, "
+                    "packet-offset={}",
+                    fmt::ptr(msg_it), msg_it->buf.at, msg_it->cur_packet_offset);
+    stack_clear(msg_it->stack);
+    msg_it->meta.ec = NULL;
+    BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
+    BT_MESSAGE_PUT_REF_AND_RESET(msg_it->event_msg);
+    release_all_dscopes(msg_it);
+    msg_it->cur_dscope_field = NULL;
+
+    if (msg_it->medium.medops.switch_packet) {
+        enum ctf_msg_iter_medium_status medium_status;
+
+        medium_status = msg_it->medium.medops.switch_packet(msg_it->medium.data);
+        if (medium_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
+            /* No more packets. */
+            msg_it->state = STATE_CHECK_EMIT_MSG_STREAM_END;
+            status = CTF_MSG_ITER_STATUS_OK;
+            goto end;
+        } else if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+            status = msg_iter_status_from_m_status(medium_status);
+            goto end;
+        }
+
+        /*
+         * After the packet switch, the medium might want to give us a
+         * different buffer for the new packet.
+         */
+        status = request_medium_bytes(msg_it);
+        if (status != CTF_MSG_ITER_STATUS_OK) {
+            goto end;
+        }
+    }
+
+    /*
+     * Adjust current buffer so that addr points to the beginning of the new
+     * packet.
+     */
+    if (msg_it->buf.addr) {
+        size_t consumed_bytes = (size_t) (msg_it->buf.at / CHAR_BIT);
+
+        /* Packets are assumed to start on a byte frontier. */
+        if (msg_it->buf.at % CHAR_BIT) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                msg_it->logger,
+                "Cannot switch packet: current position is not a multiple of 8: "
+                "msg-it-addr={}, cur={}",
+                fmt::ptr(msg_it), msg_it->buf.at);
+            status = CTF_MSG_ITER_STATUS_ERROR;
+            goto end;
+        }
+
+        msg_it->buf.addr += consumed_bytes;
+        msg_it->buf.sz -= consumed_bytes;
+        msg_it->buf.at = 0;
+        msg_it->buf.packet_offset = 0;
+        BT_CPPLOGD_SPEC(msg_it->logger, "Adjusted buffer: addr={}, size={}",
+                        fmt::ptr(msg_it->buf.addr), msg_it->buf.sz);
+    }
+
+    msg_it->cur_exp_packet_content_size = -1;
+    msg_it->cur_exp_packet_total_size = -1;
+    msg_it->cur_stream_class_id = -1;
+    msg_it->cur_event_class_id = -1;
+    msg_it->cur_data_stream_id = -1;
+    msg_it->prev_packet_snapshots = msg_it->snapshots;
+    msg_it->snapshots.discarded_events = UINT64_C(-1);
+    msg_it->snapshots.packets = UINT64_C(-1);
+    msg_it->snapshots.beginning_clock = UINT64_C(-1);
+    msg_it->snapshots.end_clock = UINT64_C(-1);
+    msg_it->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN;
+
+    status = CTF_MSG_ITER_STATUS_OK;
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_packet_header_begin_state(struct ctf_msg_iter *msg_it)
+{
+    struct ctf_field_class *packet_header_fc = NULL;
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+    /*
+     * Make sure at least one bit is available for this packet. An
+     * empty packet is impossible. If we reach the end of the medium
+     * at this point, then it's considered the end of the stream.
+     */
+    status = buf_ensure_available_bits(msg_it);
+    switch (status) {
+    case CTF_MSG_ITER_STATUS_OK:
+        break;
+    case CTF_MSG_ITER_STATUS_EOF:
+        status = CTF_MSG_ITER_STATUS_OK;
+        msg_it->state = STATE_CHECK_EMIT_MSG_STREAM_END;
+        goto end;
+    default:
+        goto end;
+    }
+
+    /* Packet header class is common to the whole trace class. */
+    packet_header_fc = msg_it->meta.tc->packet_header_fc;
+    if (!packet_header_fc) {
+        msg_it->state = STATE_AFTER_TRACE_PACKET_HEADER;
+        goto end;
+    }
+
+    msg_it->cur_stream_class_id = -1;
+    msg_it->cur_event_class_id = -1;
+    msg_it->cur_data_stream_id = -1;
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Decoding packet header field: "
+                    "msg-it-addr={}, trace-class-addr={}, fc-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.tc), fmt::ptr(packet_header_fc));
+    status = read_dscope_begin_state(msg_it, packet_header_fc, STATE_AFTER_TRACE_PACKET_HEADER,
+                                     STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, NULL);
+    if (status < 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot decode packet header field: "
+                                     "msg-it-addr={}, trace-class-addr={}, "
+                                     "fc-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->meta.tc),
+                                     fmt::ptr(packet_header_fc));
+    }
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_packet_header_continue_state(struct ctf_msg_iter *msg_it)
+{
+    return read_dscope_continue_state(msg_it, STATE_AFTER_TRACE_PACKET_HEADER);
+}
+
+static inline enum ctf_msg_iter_status set_current_stream_class(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    struct ctf_stream_class *new_stream_class = NULL;
+
+    if (msg_it->cur_stream_class_id == -1) {
+        /*
+         * No current stream class ID field, therefore only one
+         * stream class.
+         */
+        if (msg_it->meta.tc->stream_classes->len != 1) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                         "Need exactly one stream class since there's "
+                                         "no stream class ID field: "
+                                         "msg-it-addr={}",
+                                         fmt::ptr(msg_it));
+            status = CTF_MSG_ITER_STATUS_ERROR;
+            goto end;
+        }
+
+        new_stream_class = (ctf_stream_class *) msg_it->meta.tc->stream_classes->pdata[0];
+        msg_it->cur_stream_class_id = new_stream_class->id;
+    }
+
+    new_stream_class =
+        ctf_trace_class_borrow_stream_class_by_id(msg_it->meta.tc, msg_it->cur_stream_class_id);
+    if (!new_stream_class) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            msg_it->logger,
+            "No stream class with ID of stream class ID to use in trace class: "
+            "msg-it-addr={}, stream-class-id={}, "
+            "trace-class-addr={}",
+            fmt::ptr(msg_it), msg_it->cur_stream_class_id, fmt::ptr(msg_it->meta.tc));
+        status = CTF_MSG_ITER_STATUS_ERROR;
+        goto end;
+    }
+
+    if (msg_it->meta.sc) {
+        if (new_stream_class != msg_it->meta.sc) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                msg_it->logger,
+                "Two packets refer to two different stream classes within the same packet sequence: "
+                "msg-it-addr={}, prev-stream-class-addr={}, "
+                "prev-stream-class-id={}, "
+                "next-stream-class-addr={}, "
+                "next-stream-class-id={}, "
+                "trace-addr={}",
+                fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc), msg_it->meta.sc->id,
+                fmt::ptr(new_stream_class), new_stream_class->id, fmt::ptr(msg_it->meta.tc));
+            status = CTF_MSG_ITER_STATUS_ERROR;
+            goto end;
+        }
+    } else {
+        msg_it->meta.sc = new_stream_class;
+    }
+
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Set current stream class: "
+                    "msg-it-addr={}, stream-class-addr={}, "
+                    "stream-class-id={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc), msg_it->meta.sc->id);
+
+end:
+    return status;
+}
+
+static inline enum ctf_msg_iter_status set_current_stream(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    bt_stream *stream = NULL;
+
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Calling user function (get stream): msg-it-addr={}, "
+                    "stream-class-addr={}, stream-class-id={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc), msg_it->meta.sc->id);
+    stream = msg_it->medium.medops.borrow_stream(msg_it->meta.sc->ir_sc, msg_it->cur_data_stream_id,
+                                                 msg_it->medium.data);
+    bt_stream_get_ref(stream);
+    BT_CPPLOGD_SPEC(msg_it->logger, "User function returned: stream-addr={}", fmt::ptr(stream));
+    if (!stream) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            msg_it->logger,
+            "User function failed to return a stream object for the given stream class.");
+        status = CTF_MSG_ITER_STATUS_ERROR;
+        goto end;
+    }
+
+    if (msg_it->stream && stream != msg_it->stream) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            msg_it->logger,
+            "User function returned a different stream than the previous one for the same sequence of packets.");
+        status = CTF_MSG_ITER_STATUS_ERROR;
+        goto end;
+    }
+
+    BT_STREAM_MOVE_REF(msg_it->stream, stream);
+
+end:
+    bt_stream_put_ref(stream);
+    return status;
+}
+
+static inline enum ctf_msg_iter_status set_current_packet(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    bt_packet *packet = NULL;
+
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Creating packet from stream: "
+                    "msg-it-addr={}, stream-addr={}, "
+                    "stream-class-addr={}, "
+                    "stream-class-id={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->stream), fmt::ptr(msg_it->meta.sc),
+                    msg_it->meta.sc->id);
+
+    /* Create packet */
+    BT_ASSERT(msg_it->stream);
+    packet = bt_packet_create(msg_it->stream);
+    if (!packet) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot create packet from stream: "
+                                     "msg-it-addr={}, stream-addr={}, "
+                                     "stream-class-addr={}, "
+                                     "stream-class-id={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->stream),
+                                     fmt::ptr(msg_it->meta.sc), msg_it->meta.sc->id);
+        goto error;
+    }
+
+    goto end;
+
+error:
+    BT_PACKET_PUT_REF_AND_RESET(packet);
+    status = CTF_MSG_ITER_STATUS_ERROR;
+
+end:
+    BT_PACKET_MOVE_REF(msg_it->packet, packet);
+    return status;
+}
+
+static enum ctf_msg_iter_status after_packet_header_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status;
+
+    status = set_current_stream_class(msg_it);
+    if (status != CTF_MSG_ITER_STATUS_OK) {
+        goto end;
+    }
+
+    if (!msg_it->dry_run) {
+        status = set_current_stream(msg_it);
+        if (status != CTF_MSG_ITER_STATUS_OK) {
+            goto end;
+        }
+
+        status = set_current_packet(msg_it);
+        if (status != CTF_MSG_ITER_STATUS_OK) {
+            goto end;
+        }
+    }
+
+    msg_it->state = STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN;
+
+    status = CTF_MSG_ITER_STATUS_OK;
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_packet_context_begin_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    struct ctf_field_class *packet_context_fc;
+
+    BT_ASSERT(msg_it->meta.sc);
+    packet_context_fc = msg_it->meta.sc->packet_context_fc;
+    if (!packet_context_fc) {
+        BT_CPPLOGD_SPEC(msg_it->logger,
+                        "No packet packet context field class in stream class: continuing: "
+                        "msg-it-addr={}, stream-class-addr={}, "
+                        "stream-class-id={}",
+                        fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc), msg_it->meta.sc->id);
+        msg_it->state = STATE_AFTER_STREAM_PACKET_CONTEXT;
+        goto end;
+    }
+
+    if (packet_context_fc->in_ir && !msg_it->dry_run) {
+        BT_ASSERT(!msg_it->dscopes.stream_packet_context);
+        BT_ASSERT(msg_it->packet);
+        msg_it->dscopes.stream_packet_context = bt_packet_borrow_context_field(msg_it->packet);
+        BT_ASSERT(msg_it->dscopes.stream_packet_context);
+    }
+
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Decoding packet context field: "
+                    "msg-it-addr={}, stream-class-addr={}, "
+                    "stream-class-id={}, fc-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc), msg_it->meta.sc->id,
+                    fmt::ptr(packet_context_fc));
+    status = read_dscope_begin_state(msg_it, packet_context_fc, STATE_AFTER_STREAM_PACKET_CONTEXT,
+                                     STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
+                                     msg_it->dscopes.stream_packet_context);
+    if (status < 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot decode packet context field: "
+                                     "msg-it-addr={}, stream-class-addr={}, "
+                                     "stream-class-id={}, fc-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc),
+                                     msg_it->meta.sc->id, fmt::ptr(packet_context_fc));
+    }
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_packet_context_continue_state(struct ctf_msg_iter *msg_it)
+{
+    return read_dscope_continue_state(msg_it, STATE_AFTER_STREAM_PACKET_CONTEXT);
+}
+
+static enum ctf_msg_iter_status set_current_packet_content_sizes(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+    if (msg_it->cur_exp_packet_total_size == -1) {
+        if (msg_it->cur_exp_packet_content_size != -1) {
+            msg_it->cur_exp_packet_total_size = msg_it->cur_exp_packet_content_size;
+        }
+    } else {
+        if (msg_it->cur_exp_packet_content_size == -1) {
+            msg_it->cur_exp_packet_content_size = msg_it->cur_exp_packet_total_size;
+        }
+    }
+
+    BT_ASSERT(
+        (msg_it->cur_exp_packet_total_size >= 0 && msg_it->cur_exp_packet_content_size >= 0) ||
+        (msg_it->cur_exp_packet_total_size < 0 && msg_it->cur_exp_packet_content_size < 0));
+
+    if (msg_it->cur_exp_packet_content_size > msg_it->cur_exp_packet_total_size) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            msg_it->logger,
+            "Invalid packet or content size: "
+            "content size is greater than packet size: "
+            "msg-it-addr={}, packet-context-field-addr={}, "
+            "packet-size={}, content-size={}",
+            fmt::ptr(msg_it), fmt::ptr(msg_it->dscopes.stream_packet_context),
+            msg_it->cur_exp_packet_total_size, msg_it->cur_exp_packet_content_size);
+        status = CTF_MSG_ITER_STATUS_ERROR;
+        goto end;
+    }
+
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Set current packet and content sizes: "
+                    "msg-it-addr={}, packet-size={}, content-size={}",
+                    fmt::ptr(msg_it), msg_it->cur_exp_packet_total_size,
+                    msg_it->cur_exp_packet_content_size);
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status after_packet_context_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status;
+
+    status = set_current_packet_content_sizes(msg_it);
+    if (status != CTF_MSG_ITER_STATUS_OK) {
+        goto end;
+    }
+
+    if (msg_it->emit_stream_beginning_message) {
+        msg_it->state = STATE_EMIT_MSG_STREAM_BEGINNING;
+    } else {
+        msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS;
+    }
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_event_header_begin_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    struct ctf_field_class *event_header_fc = NULL;
+
+    /* Reset the position of the last event header */
+    msg_it->buf.last_eh_at = msg_it->buf.at;
+    msg_it->cur_event_class_id = -1;
+
+    /* Check if we have some content left */
+    if (msg_it->cur_exp_packet_content_size >= 0) {
+        if (G_UNLIKELY(packet_at(msg_it) == msg_it->cur_exp_packet_content_size)) {
+            /* No more events! */
+            BT_CPPLOGD_SPEC(msg_it->logger,
+                            "Reached end of packet: msg-it-addr={}, "
+                            "cur={}",
+                            fmt::ptr(msg_it), packet_at(msg_it));
+            msg_it->state = STATE_EMIT_MSG_PACKET_END_MULTI;
+            goto end;
+        } else if (G_UNLIKELY(packet_at(msg_it) > msg_it->cur_exp_packet_content_size)) {
+            /* That's not supposed to happen */
+            BT_CPPLOGD_SPEC(
+                msg_it->logger,
+                "Before decoding event header field: cursor is passed the packet's content: "
+                "msg-it-addr={}, content-size={}, "
+                "cur={}",
+                fmt::ptr(msg_it), msg_it->cur_exp_packet_content_size, packet_at(msg_it));
+            status = CTF_MSG_ITER_STATUS_ERROR;
+            goto end;
+        }
+    } else {
+        /*
+         * "Infinite" content: we're done when the medium has
+         * nothing else for us.
+         */
+        status = buf_ensure_available_bits(msg_it);
+        switch (status) {
+        case CTF_MSG_ITER_STATUS_OK:
+            break;
+        case CTF_MSG_ITER_STATUS_EOF:
+            status = CTF_MSG_ITER_STATUS_OK;
+            msg_it->state = STATE_EMIT_MSG_PACKET_END_SINGLE;
+            goto end;
+        default:
+            goto end;
+        }
+    }
+
+    release_event_dscopes(msg_it);
+    BT_ASSERT(msg_it->meta.sc);
+    event_header_fc = msg_it->meta.sc->event_header_fc;
+    if (!event_header_fc) {
+        msg_it->state = STATE_AFTER_EVENT_HEADER;
+        goto end;
+    }
+
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Decoding event header field: "
+                    "msg-it-addr={}, stream-class-addr={}, "
+                    "stream-class-id={}, "
+                    "fc-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc), msg_it->meta.sc->id,
+                    fmt::ptr(event_header_fc));
+    status = read_dscope_begin_state(msg_it, event_header_fc, STATE_AFTER_EVENT_HEADER,
+                                     STATE_DSCOPE_EVENT_HEADER_CONTINUE, NULL);
+    if (status < 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot decode event header field: "
+                                     "msg-it-addr={}, stream-class-addr={}, "
+                                     "stream-class-id={}, fc-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc),
+                                     msg_it->meta.sc->id, fmt::ptr(event_header_fc));
+    }
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_event_header_continue_state(struct ctf_msg_iter *msg_it)
+{
+    return read_dscope_continue_state(msg_it, STATE_AFTER_EVENT_HEADER);
+}
+
+static inline enum ctf_msg_iter_status set_current_event_class(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+    struct ctf_event_class *new_event_class = NULL;
+
+    if (msg_it->cur_event_class_id == -1) {
+        /*
+         * No current event class ID field, therefore only one
+         * event class.
+         */
+        if (msg_it->meta.sc->event_classes->len != 1) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                msg_it->logger,
+                "Need exactly one event class since there's no event class ID field: "
+                "msg-it-addr={}",
+                fmt::ptr(msg_it));
+            status = CTF_MSG_ITER_STATUS_ERROR;
+            goto end;
+        }
+
+        new_event_class = (ctf_event_class *) msg_it->meta.sc->event_classes->pdata[0];
+        msg_it->cur_event_class_id = new_event_class->id;
+    }
+
+    new_event_class =
+        ctf_stream_class_borrow_event_class_by_id(msg_it->meta.sc, msg_it->cur_event_class_id);
+    if (!new_event_class) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            msg_it->logger,
+            "No event class with ID of event class ID to use in stream class: "
+            "msg-it-addr={}, stream-class-id={}, "
+            "event-class-id={}, "
+            "trace-class-addr={}",
+            fmt::ptr(msg_it), msg_it->meta.sc->id, msg_it->cur_event_class_id,
+            fmt::ptr(msg_it->meta.tc));
+        status = CTF_MSG_ITER_STATUS_ERROR;
+        goto end;
+    }
+
+    msg_it->meta.ec = new_event_class;
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Set current event class: "
+                    "msg-it-addr={}, event-class-addr={}, "
+                    "event-class-id={}, "
+                    "event-class-name=\"{}\"",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.ec), msg_it->meta.ec->id,
+                    msg_it->meta.ec->name->str);
+
+end:
+    return status;
+}
+
+static inline enum ctf_msg_iter_status set_current_event_message(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    bt_message *msg = NULL;
+
+    BT_ASSERT_DBG(msg_it->meta.ec);
+    BT_ASSERT_DBG(msg_it->packet);
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Creating event message from event class and packet: "
+                    "msg-it-addr={}, ec-addr={}, ec-name=\"{}\", packet-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.ec), msg_it->meta.ec->name->str,
+                    fmt::ptr(msg_it->packet));
+    BT_ASSERT_DBG(msg_it->self_msg_iter);
+    BT_ASSERT_DBG(msg_it->meta.sc);
+
+    if (bt_stream_class_borrow_default_clock_class(msg_it->meta.sc->ir_sc)) {
+        msg = bt_message_event_create_with_packet_and_default_clock_snapshot(
+            msg_it->self_msg_iter, msg_it->meta.ec->ir_ec, msg_it->packet,
+            msg_it->default_clock_snapshot);
+    } else {
+        msg = bt_message_event_create_with_packet(msg_it->self_msg_iter, msg_it->meta.ec->ir_ec,
+                                                  msg_it->packet);
+    }
+
+    if (!msg) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot create event message: "
+                                     "msg-it-addr={}, ec-addr={}, ec-name=\"{}\", "
+                                     "packet-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->meta.ec),
+                                     msg_it->meta.ec->name->str, fmt::ptr(msg_it->packet));
+        goto error;
+    }
+
+    goto end;
+
+error:
+    BT_MESSAGE_PUT_REF_AND_RESET(msg);
+    status = CTF_MSG_ITER_STATUS_ERROR;
+
+end:
+    BT_MESSAGE_MOVE_REF(msg_it->event_msg, msg);
+    return status;
+}
+
+static enum ctf_msg_iter_status after_event_header_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status;
+
+    status = set_current_event_class(msg_it);
+    if (status != CTF_MSG_ITER_STATUS_OK) {
+        goto end;
+    }
+
+    if (G_UNLIKELY(msg_it->dry_run)) {
+        goto next_state;
+    }
+
+    status = set_current_event_message(msg_it);
+    if (status != CTF_MSG_ITER_STATUS_OK) {
+        goto end;
+    }
+
+    msg_it->event = bt_message_event_borrow_event(msg_it->event_msg);
+    BT_ASSERT_DBG(msg_it->event);
+
+next_state:
+    msg_it->state = STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN;
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_event_common_context_begin_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    struct ctf_field_class *event_common_context_fc;
+
+    event_common_context_fc = msg_it->meta.sc->event_common_context_fc;
+    if (!event_common_context_fc) {
+        msg_it->state = STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN;
+        goto end;
+    }
+
+    if (event_common_context_fc->in_ir && !msg_it->dry_run) {
+        BT_ASSERT_DBG(!msg_it->dscopes.event_common_context);
+        msg_it->dscopes.event_common_context = bt_event_borrow_common_context_field(msg_it->event);
+        BT_ASSERT_DBG(msg_it->dscopes.event_common_context);
+    }
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Decoding event common context field: "
+                    "msg-it-addr={}, stream-class-addr={}, "
+                    "stream-class-id={}, "
+                    "fc-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc), msg_it->meta.sc->id,
+                    fmt::ptr(event_common_context_fc));
+    status = read_dscope_begin_state(
+        msg_it, event_common_context_fc, STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN,
+        STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE, msg_it->dscopes.event_common_context);
+    if (status < 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot decode event common context field: "
+                                     "msg-it-addr={}, stream-class-addr={}, "
+                                     "stream-class-id={}, fc-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->meta.sc),
+                                     msg_it->meta.sc->id, fmt::ptr(event_common_context_fc));
+    }
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status
+read_event_common_context_continue_state(struct ctf_msg_iter *msg_it)
+{
+    return read_dscope_continue_state(msg_it, STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN);
+}
+
+static enum ctf_msg_iter_status read_event_spec_context_begin_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    struct ctf_field_class *event_spec_context_fc;
+
+    event_spec_context_fc = msg_it->meta.ec->spec_context_fc;
+    if (!event_spec_context_fc) {
+        msg_it->state = STATE_DSCOPE_EVENT_PAYLOAD_BEGIN;
+        goto end;
+    }
+
+    if (event_spec_context_fc->in_ir && !msg_it->dry_run) {
+        BT_ASSERT_DBG(!msg_it->dscopes.event_spec_context);
+        msg_it->dscopes.event_spec_context = bt_event_borrow_specific_context_field(msg_it->event);
+        BT_ASSERT_DBG(msg_it->dscopes.event_spec_context);
+    }
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Decoding event specific context field: "
+                    "msg-it-addr={}, event-class-addr={}, "
+                    "event-class-name=\"{}\", event-class-id={}, "
+                    "fc-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.ec), msg_it->meta.ec->name->str,
+                    msg_it->meta.ec->id, fmt::ptr(event_spec_context_fc));
+    status = read_dscope_begin_state(
+        msg_it, event_spec_context_fc, STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
+        STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE, msg_it->dscopes.event_spec_context);
+    if (status < 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot decode event specific context field: "
+                                     "msg-it-addr={}, event-class-addr={}, "
+                                     "event-class-name=\"{}\", "
+                                     "event-class-id={}, fc-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->meta.ec),
+                                     msg_it->meta.ec->name->str, msg_it->meta.ec->id,
+                                     fmt::ptr(event_spec_context_fc));
+    }
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_event_spec_context_continue_state(struct ctf_msg_iter *msg_it)
+{
+    return read_dscope_continue_state(msg_it, STATE_DSCOPE_EVENT_PAYLOAD_BEGIN);
+}
+
+static enum ctf_msg_iter_status read_event_payload_begin_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    struct ctf_field_class *event_payload_fc;
+
+    event_payload_fc = msg_it->meta.ec->payload_fc;
+    if (!event_payload_fc) {
+        msg_it->state = STATE_EMIT_MSG_EVENT;
+        goto end;
+    }
+
+    if (event_payload_fc->in_ir && !msg_it->dry_run) {
+        BT_ASSERT_DBG(!msg_it->dscopes.event_payload);
+        msg_it->dscopes.event_payload = bt_event_borrow_payload_field(msg_it->event);
+        BT_ASSERT_DBG(msg_it->dscopes.event_payload);
+    }
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Decoding event payload field: "
+                    "msg-it-addr={}, event-class-addr={}, "
+                    "event-class-name=\"{}\", event-class-id={}, "
+                    "fc-addr={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->meta.ec), msg_it->meta.ec->name->str,
+                    msg_it->meta.ec->id, fmt::ptr(event_payload_fc));
+    status =
+        read_dscope_begin_state(msg_it, event_payload_fc, STATE_EMIT_MSG_EVENT,
+                                STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, msg_it->dscopes.event_payload);
+    if (status < 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot decode event payload field: "
+                                     "msg-it-addr={}, event-class-addr={}, "
+                                     "event-class-name=\"{}\", "
+                                     "event-class-id={}, fc-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->meta.ec),
+                                     msg_it->meta.ec->name->str, msg_it->meta.ec->id,
+                                     fmt::ptr(event_payload_fc));
+    }
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_event_payload_continue_state(struct ctf_msg_iter *msg_it)
+{
+    return read_dscope_continue_state(msg_it, STATE_EMIT_MSG_EVENT);
+}
+
+static enum ctf_msg_iter_status skip_packet_padding_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    size_t bits_to_skip;
+    const enum state next_state = STATE_SWITCH_PACKET;
+
+    BT_ASSERT(msg_it->cur_exp_packet_total_size > 0);
+    bits_to_skip = msg_it->cur_exp_packet_total_size - packet_at(msg_it);
+    if (bits_to_skip == 0) {
+        msg_it->state = next_state;
+        goto end;
+    } else {
+        size_t bits_to_consume;
+
+        BT_CPPLOGD_SPEC(msg_it->logger,
+                        "Trying to skip {} bits of padding: msg-it-addr={}, size={}", bits_to_skip,
+                        fmt::ptr(msg_it), bits_to_skip);
+        status = buf_ensure_available_bits(msg_it);
+        if (status != CTF_MSG_ITER_STATUS_OK) {
+            goto end;
+        }
+
+        bits_to_consume = MIN(buf_available_bits(msg_it), bits_to_skip);
+        BT_CPPLOGD_SPEC(msg_it->logger, "Skipping {} bits of padding: msg-it-addr={}, size={}",
+                        bits_to_consume, fmt::ptr(msg_it), bits_to_consume);
+        buf_consume_bits(msg_it, bits_to_consume);
+        bits_to_skip = msg_it->cur_exp_packet_total_size - packet_at(msg_it);
+        if (bits_to_skip == 0) {
+            msg_it->state = next_state;
+            goto end;
+        }
+    }
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status check_emit_msg_discarded_events(struct ctf_msg_iter *msg_it)
+{
+    msg_it->state = STATE_EMIT_MSG_DISCARDED_EVENTS;
+
+    if (!msg_it->meta.sc->has_discarded_events) {
+        msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
+        goto end;
+    }
+
+    if (msg_it->prev_packet_snapshots.discarded_events == UINT64_C(-1)) {
+        if (msg_it->snapshots.discarded_events == 0 ||
+            msg_it->snapshots.discarded_events == UINT64_C(-1)) {
+            /*
+             * Stream's first packet with no discarded
+             * events or no information about discarded
+             * events: do not emit.
+             */
+            msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
+        }
+    } else {
+        /*
+         * If the previous packet has a value for this counter,
+         * then this counter is defined for the whole stream.
+         */
+        BT_ASSERT(msg_it->snapshots.discarded_events != UINT64_C(-1));
+
+        if (msg_it->snapshots.discarded_events - msg_it->prev_packet_snapshots.discarded_events ==
+            0) {
+            /*
+             * No discarded events since previous packet: do
+             * not emit.
+             */
+            msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
+        }
+    }
+
+end:
+    return CTF_MSG_ITER_STATUS_OK;
+}
+
+static enum ctf_msg_iter_status check_emit_msg_discarded_packets(struct ctf_msg_iter *msg_it)
+{
+    msg_it->state = STATE_EMIT_MSG_DISCARDED_PACKETS;
+
+    if (!msg_it->meta.sc->has_discarded_packets) {
+        msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
+        goto end;
+    }
+
+    if (msg_it->prev_packet_snapshots.packets == UINT64_C(-1)) {
+        /*
+         * Stream's first packet or no information about
+         * discarded packets: do not emit. In other words, if
+         * this is the first packet and its sequence number is
+         * not 0, do not consider that packets were previously
+         * lost: we might be reading a partial stream (LTTng
+         * snapshot for example).
+         */
+        msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
+    } else {
+        /*
+         * If the previous packet has a value for this counter,
+         * then this counter is defined for the whole stream.
+         */
+        BT_ASSERT(msg_it->snapshots.packets != UINT64_C(-1));
+
+        if (msg_it->snapshots.packets - msg_it->prev_packet_snapshots.packets <= 1) {
+            /*
+             * No discarded packets since previous packet:
+             * do not emit.
+             */
+            msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
+        }
+    }
+
+end:
+    return CTF_MSG_ITER_STATUS_OK;
+}
+
+static inline enum state check_emit_msg_stream_end(struct ctf_msg_iter *msg_it)
+{
+    enum state next_state;
+
+    if (msg_it->emit_stream_end_message) {
+        next_state = STATE_EMIT_MSG_STREAM_END;
+    } else {
+        next_state = STATE_DONE;
+    }
+
+    return next_state;
+}
+
+static inline enum ctf_msg_iter_status handle_state(struct ctf_msg_iter *msg_it)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    const enum state state = msg_it->state;
+
+    BT_CPPLOGT_SPEC(msg_it->logger, "Handling state: msg-it-addr={}, state={}", fmt::ptr(msg_it),
+                    state);
+
+    // TODO: optimalize!
+    switch (state) {
+    case STATE_INIT:
+        msg_it->state = STATE_SWITCH_PACKET;
+        break;
+    case STATE_SWITCH_PACKET:
+        status = switch_packet_state(msg_it);
+        break;
+    case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
+        status = read_packet_header_begin_state(msg_it);
+        break;
+    case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
+        status = read_packet_header_continue_state(msg_it);
+        break;
+    case STATE_AFTER_TRACE_PACKET_HEADER:
+        status = after_packet_header_state(msg_it);
+        break;
+    case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
+        status = read_packet_context_begin_state(msg_it);
+        break;
+    case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
+        status = read_packet_context_continue_state(msg_it);
+        break;
+    case STATE_AFTER_STREAM_PACKET_CONTEXT:
+        status = after_packet_context_state(msg_it);
+        break;
+    case STATE_EMIT_MSG_STREAM_BEGINNING:
+        msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS;
+        break;
+    case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
+        status = check_emit_msg_discarded_events(msg_it);
+        break;
+    case STATE_EMIT_MSG_DISCARDED_EVENTS:
+        msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
+        break;
+    case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
+        status = check_emit_msg_discarded_packets(msg_it);
+        break;
+    case STATE_EMIT_MSG_DISCARDED_PACKETS:
+        msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
+        break;
+    case STATE_EMIT_MSG_PACKET_BEGINNING:
+        msg_it->state = STATE_DSCOPE_EVENT_HEADER_BEGIN;
+        break;
+    case STATE_DSCOPE_EVENT_HEADER_BEGIN:
+        status = read_event_header_begin_state(msg_it);
+        break;
+    case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
+        status = read_event_header_continue_state(msg_it);
+        break;
+    case STATE_AFTER_EVENT_HEADER:
+        status = after_event_header_state(msg_it);
+        break;
+    case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
+        status = read_event_common_context_begin_state(msg_it);
+        break;
+    case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
+        status = read_event_common_context_continue_state(msg_it);
+        break;
+    case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
+        status = read_event_spec_context_begin_state(msg_it);
+        break;
+    case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
+        status = read_event_spec_context_continue_state(msg_it);
+        break;
+    case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
+        status = read_event_payload_begin_state(msg_it);
+        break;
+    case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
+        status = read_event_payload_continue_state(msg_it);
+        break;
+    case STATE_EMIT_MSG_EVENT:
+        msg_it->state = STATE_DSCOPE_EVENT_HEADER_BEGIN;
+        break;
+    case STATE_EMIT_QUEUED_MSG_EVENT:
+        msg_it->state = STATE_EMIT_MSG_EVENT;
+        break;
+    case STATE_SKIP_PACKET_PADDING:
+        status = skip_packet_padding_state(msg_it);
+        break;
+    case STATE_EMIT_MSG_PACKET_END_MULTI:
+        msg_it->state = STATE_SKIP_PACKET_PADDING;
+        break;
+    case STATE_EMIT_MSG_PACKET_END_SINGLE:
+        msg_it->state = STATE_EMIT_MSG_STREAM_END;
+        break;
+    case STATE_EMIT_QUEUED_MSG_PACKET_END:
+        msg_it->state = STATE_EMIT_MSG_PACKET_END_SINGLE;
+        break;
+    case STATE_CHECK_EMIT_MSG_STREAM_END:
+        msg_it->state = check_emit_msg_stream_end(msg_it);
+        break;
+    case STATE_EMIT_MSG_STREAM_END:
+        msg_it->state = STATE_DONE;
+        break;
+    case STATE_DONE:
+        break;
+    default:
+        BT_CPPLOGF_SPEC(msg_it->logger,
+                        "Unknown CTF plugin message iterator state: "
+                        "msg-it-addr={}, state={}",
+                        fmt::ptr(msg_it), msg_it->state);
+        bt_common_abort();
+    }
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Handled state: msg-it-addr={}, status={}, "
+                    "prev-state={}, cur-state={}",
+                    fmt::ptr(msg_it), status, state, msg_it->state);
+    return status;
+}
+
+void ctf_msg_iter_reset_for_next_stream_file(struct ctf_msg_iter *msg_it)
+{
+    BT_ASSERT(msg_it);
+    BT_CPPLOGD_SPEC(msg_it->logger, "Resetting message iterator: addr={}", fmt::ptr(msg_it));
+    stack_clear(msg_it->stack);
+    msg_it->meta.sc = NULL;
+    msg_it->meta.ec = NULL;
+    BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
+    BT_STREAM_PUT_REF_AND_RESET(msg_it->stream);
+    BT_MESSAGE_PUT_REF_AND_RESET(msg_it->event_msg);
+    release_all_dscopes(msg_it);
+    msg_it->cur_dscope_field = NULL;
+
+    msg_it->buf.addr = NULL;
+    msg_it->buf.sz = 0;
+    msg_it->buf.at = 0;
+    msg_it->buf.last_eh_at = SIZE_MAX;
+    msg_it->buf.packet_offset = 0;
+    msg_it->state = STATE_INIT;
+    msg_it->cur_exp_packet_content_size = -1;
+    msg_it->cur_exp_packet_total_size = -1;
+    msg_it->cur_packet_offset = -1;
+    msg_it->cur_event_class_id = -1;
+    msg_it->snapshots.beginning_clock = UINT64_C(-1);
+    msg_it->snapshots.end_clock = UINT64_C(-1);
+}
+
+/**
+ * Resets the internal state of a CTF message iterator.
+ */
+void ctf_msg_iter_reset(struct ctf_msg_iter *msg_it)
+{
+    ctf_msg_iter_reset_for_next_stream_file(msg_it);
+    msg_it->cur_stream_class_id = -1;
+    msg_it->cur_data_stream_id = -1;
+    msg_it->snapshots.discarded_events = UINT64_C(-1);
+    msg_it->snapshots.packets = UINT64_C(-1);
+    msg_it->prev_packet_snapshots.discarded_events = UINT64_C(-1);
+    msg_it->prev_packet_snapshots.packets = UINT64_C(-1);
+    msg_it->prev_packet_snapshots.beginning_clock = UINT64_C(-1);
+    msg_it->prev_packet_snapshots.end_clock = UINT64_C(-1);
+    msg_it->emit_stream_beginning_message = true;
+    msg_it->emit_stream_end_message = false;
+}
+
+static bt_field *borrow_next_field(struct ctf_msg_iter *msg_it)
+{
+    bt_field *next_field = NULL;
+    bt_field *base_field;
+    const bt_field_class *base_fc;
+    bt_field_class_type base_fc_type;
+    size_t index;
+
+    BT_ASSERT_DBG(!stack_empty(msg_it->stack));
+    index = stack_top(msg_it->stack)->index;
+    base_field = stack_top(msg_it->stack)->base;
+    BT_ASSERT_DBG(base_field);
+    base_fc = bt_field_borrow_class_const(base_field);
+    BT_ASSERT_DBG(base_fc);
+    base_fc_type = bt_field_class_get_type(base_fc);
+
+    if (base_fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
+        BT_ASSERT_DBG(index < bt_field_class_structure_get_member_count(
+                                  bt_field_borrow_class_const(base_field)));
+        next_field = bt_field_structure_borrow_member_field_by_index(base_field, index);
+    } else if (bt_field_class_type_is(base_fc_type, BT_FIELD_CLASS_TYPE_ARRAY)) {
+        BT_ASSERT_DBG(index < bt_field_array_get_length(base_field));
+        next_field = bt_field_array_borrow_element_field_by_index(base_field, index);
+    } else if (bt_field_class_type_is(base_fc_type, BT_FIELD_CLASS_TYPE_VARIANT)) {
+        BT_ASSERT_DBG(index == 0);
+        next_field = bt_field_variant_borrow_selected_option_field(base_field);
+    } else {
+        bt_common_abort();
+    }
+
+    BT_ASSERT_DBG(next_field);
+    return next_field;
+}
+
+static void update_default_clock(struct ctf_msg_iter *msg_it, uint64_t new_val,
+                                 uint64_t new_val_size)
+{
+    uint64_t new_val_mask;
+    uint64_t cur_value_masked;
+
+    BT_ASSERT_DBG(new_val_size > 0);
+
+    /*
+     * Special case for a 64-bit new value, which is the limit
+     * of a clock value as of this version: overwrite the
+     * current value directly.
+     */
+    if (new_val_size == 64) {
+        msg_it->default_clock_snapshot = new_val;
+        goto end;
+    }
+
+    new_val_mask = (1ULL << new_val_size) - 1;
+    cur_value_masked = msg_it->default_clock_snapshot & new_val_mask;
+
+    if (new_val < cur_value_masked) {
+        /*
+         * It looks like a wrap happened on the number of bits
+         * of the requested new value. Assume that the clock
+         * value wrapped only one time.
+         */
+        msg_it->default_clock_snapshot += new_val_mask + 1;
+    }
+
+    /* Clear the low bits of the current clock value. */
+    msg_it->default_clock_snapshot &= ~new_val_mask;
+
+    /* Set the low bits of the current clock value. */
+    msg_it->default_clock_snapshot |= new_val;
+
+end:
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Updated default clock's value from integer field's value: "
+                    "value={}",
+                    msg_it->default_clock_snapshot);
+}
+
+/*
+ * Ensure the message iterator's `stored_values` array is large enough to
+ * accommodate `storing_index`.
+ *
+ * We may need more slots in the array than initially allocated if more
+ * metadata arrives along the way.
+ */
+static void ensure_stored_values_size(ctf_msg_iter *msg_it, uint64_t storing_index)
+{
+    if (G_UNLIKELY(storing_index >= msg_it->stored_values->len)) {
+        g_array_set_size(msg_it->stored_values, msg_it->meta.tc->stored_value_count);
+    }
+}
+
+static enum bt_bfcr_status bfcr_unsigned_int_cb(uint64_t value, struct ctf_field_class *fc,
+                                                void *data)
+{
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+    bt_field *field = NULL;
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Unsigned integer function called from BFCR: "
+                    "msg-it-addr={}, bfcr-addr={}, fc-addr={}, "
+                    "fc-type={}, fc-in-ir={}, value={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(fc), fc->type, fc->in_ir,
+                    value);
+
+    ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+    if (G_LIKELY(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE)) {
+        goto update_def_clock;
+    }
+
+    switch (int_fc->meaning) {
+    case CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID:
+        msg_it->cur_event_class_id = value;
+        break;
+    case CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID:
+        msg_it->cur_data_stream_id = value;
+        break;
+    case CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME:
+        msg_it->snapshots.beginning_clock = value;
+        break;
+    case CTF_FIELD_CLASS_MEANING_PACKET_END_TIME:
+        msg_it->snapshots.end_clock = value;
+        break;
+    case CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID:
+        msg_it->cur_stream_class_id = value;
+        break;
+    case CTF_FIELD_CLASS_MEANING_MAGIC:
+        if (value != 0xc1fc1fc1) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                         "Invalid CTF magic number: msg-it-addr={}, magic={}",
+                                         fmt::ptr(msg_it), value);
+            status = BT_BFCR_STATUS_ERROR;
+            goto end;
+        }
+
+        break;
+    case CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT:
+        msg_it->snapshots.packets = value;
+        break;
+    case CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT:
+        msg_it->snapshots.discarded_events = value;
+        break;
+    case CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE:
+        msg_it->cur_exp_packet_total_size = value;
+        break;
+    case CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE:
+        msg_it->cur_exp_packet_content_size = value;
+        break;
+    default:
+        bt_common_abort();
+    }
+
+update_def_clock:
+    if (G_UNLIKELY(int_fc->mapped_clock_class)) {
+        update_default_clock(msg_it, value, int_fc->base.size);
+    }
+
+    if (G_UNLIKELY(int_fc->storing_index >= 0)) {
+        ensure_stored_values_size(msg_it, int_fc->storing_index);
+        bt_g_array_index(msg_it->stored_values, uint64_t, (uint64_t) int_fc->storing_index) = value;
+    }
+
+    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+        goto end;
+    }
+
+    field = borrow_next_field(msg_it);
+    BT_ASSERT_DBG(field);
+    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+    BT_ASSERT_DBG(bt_field_class_type_is(bt_field_get_class_type(field),
+                                         BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER));
+    bt_field_integer_unsigned_set_value(field, value);
+    stack_top(msg_it->stack)->index++;
+
+end:
+    return status;
+}
+
+static enum bt_bfcr_status bfcr_unsigned_int_char_cb(uint64_t value, struct ctf_field_class *fc,
+                                                     void *data)
+{
+    int ret;
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+    bt_field *string_field = NULL;
+    char str[2] = {'\0', '\0'};
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Unsigned integer character function called from BFCR: "
+                    "msg-it-addr={}, bfcr-addr={}, fc-addr={}, "
+                    "fc-type={}, fc-in-ir={}, value={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(fc), fc->type, fc->in_ir,
+                    value);
+
+    ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+    BT_ASSERT_DBG(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE);
+    BT_ASSERT_DBG(!int_fc->mapped_clock_class);
+    BT_ASSERT_DBG(int_fc->storing_index < 0);
+
+    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+        goto end;
+    }
+
+    if (msg_it->done_filling_string) {
+        goto end;
+    }
+
+    if (value == 0) {
+        msg_it->done_filling_string = true;
+        goto end;
+    }
+
+    string_field = stack_top(msg_it->stack)->base;
+    BT_ASSERT_DBG(bt_field_get_class_type(string_field) == BT_FIELD_CLASS_TYPE_STRING);
+
+    /* Append character */
+    str[0] = (char) value;
+    ret = bt_field_string_append_with_length(string_field, str, 1);
+    if (ret) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot append character to string field's value: "
+                                     "msg-it-addr={}, field-addr={}, ret={}",
+                                     fmt::ptr(msg_it), fmt::ptr(string_field), ret);
+        status = BT_BFCR_STATUS_ERROR;
+        goto end;
+    }
+
+end:
+    return status;
+}
+
+static enum bt_bfcr_status bfcr_signed_int_cb(int64_t value, struct ctf_field_class *fc, void *data)
+{
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+    bt_field *field = NULL;
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Signed integer function called from BFCR: "
+                    "msg-it-addr={}, bfcr-addr={}, fc-addr={}, "
+                    "fc-type={}, fc-in-ir={}, value={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(fc), fc->type, fc->in_ir,
+                    value);
+
+    ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+    BT_ASSERT_DBG(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE);
+
+    if (G_UNLIKELY(int_fc->storing_index >= 0)) {
+        ensure_stored_values_size(msg_it, int_fc->storing_index);
+        bt_g_array_index(msg_it->stored_values, uint64_t, (uint64_t) int_fc->storing_index) =
+            (uint64_t) value;
+    }
+
+    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+        goto end;
+    }
+
+    field = borrow_next_field(msg_it);
+    BT_ASSERT_DBG(field);
+    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+    BT_ASSERT_DBG(
+        bt_field_class_type_is(bt_field_get_class_type(field), BT_FIELD_CLASS_TYPE_SIGNED_INTEGER));
+    bt_field_integer_signed_set_value(field, value);
+    stack_top(msg_it->stack)->index++;
+
+end:
+    return status;
+}
+
+static enum bt_bfcr_status bfcr_floating_point_cb(double value, struct ctf_field_class *fc,
+                                                  void *data)
+{
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+    bt_field *field = NULL;
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+    bt_field_class_type type;
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Floating point number function called from BFCR: "
+                    "msg-it-addr={}, bfcr-addr={}, fc-addr={}, "
+                    "fc-type={}, fc-in-ir={}, value={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(fc), fc->type, fc->in_ir,
+                    value);
+
+    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+        goto end;
+    }
+
+    field = borrow_next_field(msg_it);
+    type = bt_field_get_class_type(field);
+    BT_ASSERT_DBG(field);
+    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+    BT_ASSERT_DBG(bt_field_class_type_is(type, BT_FIELD_CLASS_TYPE_REAL));
+
+    if (type == BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL) {
+        bt_field_real_single_precision_set_value(field, (float) value);
+    } else {
+        bt_field_real_double_precision_set_value(field, value);
+    }
+    stack_top(msg_it->stack)->index++;
+
+end:
+    return status;
+}
+
+static enum bt_bfcr_status bfcr_string_begin_cb(struct ctf_field_class *fc, void *data)
+{
+    bt_field *field = NULL;
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "String (beginning) function called from BFCR: "
+                    "msg-it-addr={}, bfcr-addr={}, fc-addr={}, "
+                    "fc-type={}, fc-in-ir={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(fc), fc->type, fc->in_ir);
+
+    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+        goto end;
+    }
+
+    field = borrow_next_field(msg_it);
+    BT_ASSERT_DBG(field);
+    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+    BT_ASSERT_DBG(bt_field_get_class_type(field) == BT_FIELD_CLASS_TYPE_STRING);
+    bt_field_string_clear(field);
+
+    /*
+     * Push on stack. Not a compound class per se, but we know that
+     * only bfcr_string_cb() may be called between this call and a
+     * subsequent call to bfcr_string_end_cb().
+     */
+    stack_push(msg_it->stack, field);
+
+end:
+    return BT_BFCR_STATUS_OK;
+}
+
+static enum bt_bfcr_status bfcr_string_cb(const char *value, size_t len, struct ctf_field_class *fc,
+                                          void *data)
+{
+    enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+    bt_field *field = NULL;
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+    int ret;
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "String (substring) function called from BFCR: "
+                    "msg-it-addr={}, bfcr-addr={}, fc-addr={}, "
+                    "fc-type={}, fc-in-ir={}, string-length={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(fc), fc->type, fc->in_ir,
+                    len);
+
+    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+        goto end;
+    }
+
+    field = stack_top(msg_it->stack)->base;
+    BT_ASSERT_DBG(field);
+
+    /* Append current substring */
+    ret = bt_field_string_append_with_length(field, value, len);
+    if (ret) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot append substring to string field's value: "
+                                     "msg-it-addr={}, field-addr={}, string-length={}, "
+                                     "ret={}",
+                                     fmt::ptr(msg_it), fmt::ptr(field), len, ret);
+        status = BT_BFCR_STATUS_ERROR;
+        goto end;
+    }
+
+end:
+    return status;
+}
+
+static enum bt_bfcr_status bfcr_string_end_cb(struct ctf_field_class *fc, void *data)
+{
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "String (end) function called from BFCR: "
+                    "msg-it-addr={}, bfcr-addr={}, fc-addr={}, "
+                    "fc-type={}, fc-in-ir={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(fc), fc->type, fc->in_ir);
+
+    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+        goto end;
+    }
+
+    /* Pop string field */
+    stack_pop(msg_it->stack);
+
+    /* Go to next field */
+    stack_top(msg_it->stack)->index++;
+
+end:
+    return BT_BFCR_STATUS_OK;
+}
+
+static enum bt_bfcr_status bfcr_compound_begin_cb(struct ctf_field_class *fc, void *data)
+{
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+    bt_field *field;
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Compound (beginning) function called from BFCR: "
+                    "msg-it-addr={}, bfcr-addr={}, fc-addr={}, "
+                    "fc-type={}, fc-in-ir={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(fc), fc->type, fc->in_ir);
+
+    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+        goto end;
+    }
+
+    /* Borrow field */
+    if (stack_empty(msg_it->stack)) {
+        /* Root: already set by read_dscope_begin_state() */
+        field = msg_it->cur_dscope_field;
+    } else {
+        field = borrow_next_field(msg_it);
+        BT_ASSERT_DBG(field);
+    }
+
+    /* Push field */
+    BT_ASSERT_DBG(field);
+    BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+    stack_push(msg_it->stack, field);
+
+    /*
+     * Change BFCR "unsigned int" callback if it's a text
+     * array/sequence.
+     */
+    if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+        ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        if (array_fc->is_text) {
+            BT_ASSERT_DBG(bt_field_get_class_type(field) == BT_FIELD_CLASS_TYPE_STRING);
+            msg_it->done_filling_string = false;
+            bt_field_string_clear(field);
+            bt_bfcr_set_unsigned_int_cb(msg_it->bfcr, bfcr_unsigned_int_char_cb);
+        }
+    }
+
+end:
+    return BT_BFCR_STATUS_OK;
+}
+
+static enum bt_bfcr_status bfcr_compound_end_cb(struct ctf_field_class *fc, void *data)
+{
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+
+    BT_CPPLOGT_SPEC(msg_it->logger,
+                    "Compound (end) function called from BFCR: "
+                    "msg-it-addr={}, bfcr-addr={}, fc-addr={}, "
+                    "fc-type={}, fc-in-ir={}",
+                    fmt::ptr(msg_it), fmt::ptr(msg_it->bfcr), fmt::ptr(fc), fc->type, fc->in_ir);
+
+    if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+        goto end;
+    }
+
+    BT_ASSERT_DBG(!stack_empty(msg_it->stack));
+    BT_ASSERT_DBG(bt_field_borrow_class_const(stack_top(msg_it->stack)->base) == fc->ir_fc);
+
+    /*
+     * Reset BFCR "unsigned int" callback if it's a text
+     * array/sequence.
+     */
+    if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+        ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+        if (array_fc->is_text) {
+            BT_ASSERT_DBG(bt_field_get_class_type(stack_top(msg_it->stack)->base) ==
+                          BT_FIELD_CLASS_TYPE_STRING);
+            bt_bfcr_set_unsigned_int_cb(msg_it->bfcr, bfcr_unsigned_int_cb);
+        }
+    }
+
+    /* Pop stack */
+    stack_pop(msg_it->stack);
+
+    /* If the stack is not empty, increment the base's index */
+    if (!stack_empty(msg_it->stack)) {
+        stack_top(msg_it->stack)->index++;
+    }
+
+end:
+    return BT_BFCR_STATUS_OK;
+}
+
+static int64_t bfcr_get_sequence_length_cb(struct ctf_field_class *fc, void *data)
+{
+    bt_field *seq_field;
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+    struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+    int64_t length;
+    int ret;
+
+    length =
+        (uint64_t) bt_g_array_index(msg_it->stored_values, uint64_t, seq_fc->stored_length_index);
+
+    if (G_UNLIKELY(msg_it->dry_run)) {
+        goto end;
+    }
+
+    seq_field = stack_top(msg_it->stack)->base;
+    BT_ASSERT_DBG(seq_field);
+
+    /*
+     * bfcr_get_sequence_length_cb() also gets called back for a
+     * text sequence, but the destination field is a string field.
+     * Only set the field's sequence length if the destination field
+     * is a sequence field.
+     */
+    if (!seq_fc->base.is_text) {
+        BT_ASSERT_DBG(bt_field_class_type_is(bt_field_get_class_type(seq_field),
+                                             BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY));
+        ret = bt_field_array_dynamic_set_length(seq_field, (uint64_t) length);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                         "Cannot set dynamic array field's length field: "
+                                         "msg-it-addr={}, field-addr={}, "
+                                         "length={}",
+                                         fmt::ptr(msg_it), fmt::ptr(seq_field), length);
+            length = -1;
+        }
+    }
+
+end:
+    return length;
+}
+
+static struct ctf_field_class *
+bfcr_borrow_variant_selected_field_class_cb(struct ctf_field_class *fc, void *data)
+{
+    int ret;
+    uint64_t i;
+    int64_t option_index = -1;
+    ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+    ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+    struct ctf_named_field_class *selected_option = NULL;
+    struct ctf_field_class *ret_fc = NULL;
+    union
+    {
+        uint64_t u;
+        int64_t i;
+    } tag;
+
+    /* Get variant's tag */
+    tag.u = bt_g_array_index(msg_it->stored_values, uint64_t, var_fc->stored_tag_index);
+
+    /*
+     * Check each range to find the selected option's index.
+     */
+    if (var_fc->tag_fc->base.is_signed) {
+        for (i = 0; i < var_fc->ranges->len; i++) {
+            struct ctf_field_class_variant_range *range =
+                ctf_field_class_variant_borrow_range_by_index(var_fc, i);
+
+            if (tag.i >= range->range.lower.i && tag.i <= range->range.upper.i) {
+                option_index = (int64_t) range->option_index;
+                break;
+            }
+        }
+    } else {
+        for (i = 0; i < var_fc->ranges->len; i++) {
+            struct ctf_field_class_variant_range *range =
+                ctf_field_class_variant_borrow_range_by_index(var_fc, i);
+
+            if (tag.u >= range->range.lower.u && tag.u <= range->range.upper.u) {
+                option_index = (int64_t) range->option_index;
+                break;
+            }
+        }
+    }
+
+    if (option_index < 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot find variant field class's option: "
+                                     "msg-it-addr={}, var-fc-addr={}, u-tag={}, "
+                                     "i-tag={}",
+                                     fmt::ptr(msg_it), fmt::ptr(var_fc), tag.u, tag.i);
+        ret_fc = NULL;
+        goto end;
+    }
+
+    selected_option =
+        ctf_field_class_variant_borrow_option_by_index(var_fc, (uint64_t) option_index);
+
+    if (selected_option->fc->in_ir && !msg_it->dry_run) {
+        bt_field *var_field = stack_top(msg_it->stack)->base;
+
+        ret = bt_field_variant_select_option_by_index(var_field, option_index);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                         "Cannot select variant field's option field: "
+                                         "msg-it-addr={}, var-field-addr={}, "
+                                         "opt-index={}",
+                                         fmt::ptr(msg_it), fmt::ptr(var_field), option_index);
+            ret_fc = NULL;
+            goto end;
+        }
+    }
+
+    ret_fc = selected_option->fc;
+
+end:
+    return ret_fc;
+}
+
+static bt_message *create_msg_stream_beginning(struct ctf_msg_iter *msg_it)
+{
+    bt_message *msg;
+
+    BT_ASSERT(msg_it->stream);
+    BT_ASSERT(msg_it->self_msg_iter);
+    msg = bt_message_stream_beginning_create(msg_it->self_msg_iter, msg_it->stream);
+    if (!msg) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot create stream beginning message: "
+                                     "msg-it-addr={}, stream-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->stream));
+    }
+
+    return msg;
+}
+
+static bt_message *create_msg_stream_end(struct ctf_msg_iter *msg_it)
+{
+    bt_message *msg;
+
+    if (!msg_it->stream) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot create stream end message because stream is NULL: "
+                                     "msg-it-addr={}",
+                                     fmt::ptr(msg_it));
+        msg = NULL;
+        goto end;
+    }
+
+    BT_ASSERT(msg_it->self_msg_iter);
+    msg = bt_message_stream_end_create(msg_it->self_msg_iter, msg_it->stream);
+    if (!msg) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot create stream end message: "
+                                     "msg-it-addr={}, stream-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->stream));
+    }
+
+end:
+    return msg;
+}
+
+static bt_message *create_msg_packet_beginning(struct ctf_msg_iter *msg_it, bool use_default_cs)
+{
+    bt_message *msg;
+    const bt_stream_class *sc = msg_it->meta.sc->ir_sc;
+
+    BT_ASSERT(msg_it->packet);
+    BT_ASSERT(sc);
+    BT_ASSERT(msg_it->self_msg_iter);
+
+    if (msg_it->meta.sc->packets_have_ts_begin) {
+        BT_ASSERT(msg_it->snapshots.beginning_clock != UINT64_C(-1));
+        uint64_t raw_cs_value;
+
+        /*
+         * Either use the decoded packet `timestamp_begin` field or the
+         * current stream's default clock_snapshot.
+         */
+        if (use_default_cs) {
+            raw_cs_value = msg_it->default_clock_snapshot;
+        } else {
+            raw_cs_value = msg_it->snapshots.beginning_clock;
+        }
+
+        msg = bt_message_packet_beginning_create_with_default_clock_snapshot(
+            msg_it->self_msg_iter, msg_it->packet, raw_cs_value);
+    } else {
+        msg = bt_message_packet_beginning_create(msg_it->self_msg_iter, msg_it->packet);
+    }
+
+    if (!msg) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot create packet beginning message: "
+                                     "msg-it-addr={}, packet-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->packet));
+        goto end;
+    }
+
+end:
+    return msg;
+}
+
+static bt_message *emit_delayed_packet_beg_msg(struct ctf_msg_iter *msg_it)
+{
+    bool packet_beg_ts_need_fix_up;
+
+    msg_it->emit_delayed_packet_beginning_msg = false;
+
+    /*
+     * Only fix the packet's timestamp_begin if it's larger than the first
+     * event of the packet. If there was no event in the packet, the
+     * `default_clock_snapshot` field will be either equal or greater than
+     * `snapshots.beginning_clock` so there is not fix needed.
+     */
+    packet_beg_ts_need_fix_up = msg_it->default_clock_snapshot < msg_it->snapshots.beginning_clock;
+
+    /* create_msg_packet_beginning() logs errors */
+    return create_msg_packet_beginning(msg_it, packet_beg_ts_need_fix_up);
+}
+
+static bt_message *create_msg_packet_end(struct ctf_msg_iter *msg_it)
+{
+    bt_message *msg;
+    bool update_default_cs = true;
+
+    if (!msg_it->packet) {
+        msg = NULL;
+        goto end;
+    }
+
+    /*
+     * Check if we need to emit the delayed packet
+     * beginning message instead of the packet end message.
+     */
+    if (G_UNLIKELY(msg_it->emit_delayed_packet_beginning_msg)) {
+        msg = emit_delayed_packet_beg_msg(msg_it);
+        /* Don't forget to emit the packet end message. */
+        msg_it->state = STATE_EMIT_QUEUED_MSG_PACKET_END;
+        goto end;
+    }
+
+    /* Check if may be affected by lttng-crash timestamp_end quirk. */
+    if (G_UNLIKELY(msg_it->meta.tc->quirks.lttng_crash)) {
+        /*
+         * Check if the `timestamp_begin` field is non-zero but
+         * `timestamp_end` is zero. It means the trace is affected by
+         * the lttng-crash packet `timestamp_end` quirk and must be
+         * fixed up by omitting to update the default clock snapshot to
+         * the `timestamp_end` as is typically done.
+         */
+        if (msg_it->snapshots.beginning_clock != 0 && msg_it->snapshots.end_clock == 0) {
+            update_default_cs = false;
+        }
+    }
+
+    /*
+     * Check if may be affected by lttng event-after-packet `timestamp_end`
+     * quirk.
+     */
+    if (msg_it->meta.tc->quirks.lttng_event_after_packet) {
+        /*
+         * Check if `timestamp_end` is smaller then the current
+         * default_clock_snapshot (which is set to the last event
+         * decoded). It means the trace is affected by the lttng
+         * `event-after-packet` packet `timestamp_end` quirk and must
+         * be fixed up by omitting to update the default clock snapshot
+         * to the `timestamp_end` as is typically done.
+         */
+        if (msg_it->snapshots.end_clock < msg_it->default_clock_snapshot) {
+            update_default_cs = false;
+        }
+    }
+
+    /* Update default clock from packet's end time. */
+    if (msg_it->snapshots.end_clock != UINT64_C(-1) && update_default_cs) {
+        msg_it->default_clock_snapshot = msg_it->snapshots.end_clock;
+    }
+
+    BT_ASSERT(msg_it->self_msg_iter);
+
+    if (msg_it->meta.sc->packets_have_ts_end) {
+        BT_ASSERT(msg_it->snapshots.end_clock != UINT64_C(-1));
+        msg = bt_message_packet_end_create_with_default_clock_snapshot(
+            msg_it->self_msg_iter, msg_it->packet, msg_it->default_clock_snapshot);
+    } else {
+        msg = bt_message_packet_end_create(msg_it->self_msg_iter, msg_it->packet);
+    }
+
+    if (!msg) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot create packet end message: "
+                                     "msg-it-addr={}, packet-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->packet));
+        goto end;
+    }
+
+    BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
+
+end:
+    return msg;
+}
+
+static bt_message *create_msg_discarded_events(struct ctf_msg_iter *msg_it)
+{
+    bt_message *msg;
+    uint64_t beginning_raw_value = UINT64_C(-1);
+    uint64_t end_raw_value = UINT64_C(-1);
+
+    BT_ASSERT(msg_it->self_msg_iter);
+    BT_ASSERT(msg_it->stream);
+    BT_ASSERT(msg_it->meta.sc->has_discarded_events);
+
+    if (msg_it->meta.sc->discarded_events_have_default_cs) {
+        if (msg_it->prev_packet_snapshots.discarded_events == UINT64_C(-1)) {
+            /*
+             * We discarded events, but before (and possibly
+             * including) the current packet: use this packet's time
+             * range, and do not have a specific count.
+             */
+            beginning_raw_value = msg_it->snapshots.beginning_clock;
+            end_raw_value = msg_it->snapshots.end_clock;
+        } else {
+            beginning_raw_value = msg_it->prev_packet_snapshots.end_clock;
+            end_raw_value = msg_it->snapshots.end_clock;
+        }
+
+        BT_ASSERT(beginning_raw_value != UINT64_C(-1));
+        BT_ASSERT(end_raw_value != UINT64_C(-1));
+        msg = bt_message_discarded_events_create_with_default_clock_snapshots(
+            msg_it->self_msg_iter, msg_it->stream, beginning_raw_value, end_raw_value);
+    } else {
+        msg = bt_message_discarded_events_create(msg_it->self_msg_iter, msg_it->stream);
+    }
+
+    if (!msg) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot create discarded events message: "
+                                     "msg-it-addr={}, stream-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->stream));
+        goto end;
+    }
+
+    if (msg_it->prev_packet_snapshots.discarded_events != UINT64_C(-1)) {
+        bt_message_discarded_events_set_count(msg,
+                                              msg_it->snapshots.discarded_events -
+                                                  msg_it->prev_packet_snapshots.discarded_events);
+    }
+
+end:
+    return msg;
+}
+
+static bt_message *create_msg_discarded_packets(struct ctf_msg_iter *msg_it)
+{
+    bt_message *msg;
+
+    BT_ASSERT(msg_it->self_msg_iter);
+    BT_ASSERT(msg_it->stream);
+    BT_ASSERT(msg_it->meta.sc->has_discarded_packets);
+    BT_ASSERT(msg_it->prev_packet_snapshots.packets != UINT64_C(-1));
+
+    if (msg_it->meta.sc->discarded_packets_have_default_cs) {
+        BT_ASSERT(msg_it->prev_packet_snapshots.end_clock != UINT64_C(-1));
+        BT_ASSERT(msg_it->snapshots.beginning_clock != UINT64_C(-1));
+        msg = bt_message_discarded_packets_create_with_default_clock_snapshots(
+            msg_it->self_msg_iter, msg_it->stream, msg_it->prev_packet_snapshots.end_clock,
+            msg_it->snapshots.beginning_clock);
+    } else {
+        msg = bt_message_discarded_packets_create(msg_it->self_msg_iter, msg_it->stream);
+    }
+
+    if (!msg) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Cannot create discarded packets message: "
+                                     "msg-it-addr={}, stream-addr={}",
+                                     fmt::ptr(msg_it), fmt::ptr(msg_it->stream));
+        goto end;
+    }
+
+    bt_message_discarded_packets_set_count(msg, msg_it->snapshots.packets -
+                                                    msg_it->prev_packet_snapshots.packets - 1);
+
+end:
+    return msg;
+}
+
+ctf_msg_iter_up ctf_msg_iter_create(struct ctf_trace_class *tc, size_t max_request_sz,
+                                    struct ctf_msg_iter_medium_ops medops, void *data,
+                                    bt_self_message_iterator *self_msg_iter,
+                                    const bt2c::Logger& parentLogger)
+{
+    struct bt_bfcr_cbs cbs = {
+        .classes =
+            {
+                .signed_int = bfcr_signed_int_cb,
+                .unsigned_int = bfcr_unsigned_int_cb,
+                .floating_point = bfcr_floating_point_cb,
+                .string_begin = bfcr_string_begin_cb,
+                .string = bfcr_string_cb,
+                .string_end = bfcr_string_end_cb,
+                .compound_begin = bfcr_compound_begin_cb,
+                .compound_end = bfcr_compound_end_cb,
+            },
+        .query =
+            {
+                .get_sequence_length = bfcr_get_sequence_length_cb,
+                .borrow_variant_selected_field_class = bfcr_borrow_variant_selected_field_class_cb,
+            },
+    };
+
+    BT_ASSERT(tc);
+    BT_ASSERT(medops.request_bytes);
+    BT_ASSERT(medops.borrow_stream);
+    BT_ASSERT(max_request_sz > 0);
+
+    bt2c::Logger logger {parentLogger, "PLUGIN/CTF/MSG-ITER"};
+    BT_CPPLOGD_SPEC(logger,
+                    "Creating CTF plugin message iterator: "
+                    "trace-addr={}, max-request-size={}, "
+                    "data={}, log-level={}",
+                    fmt::ptr(tc), max_request_sz, fmt::ptr(data), logger.level());
+
+    ctf_msg_iter_up msg_it {new ctf_msg_iter {std::move(logger)}};
+    msg_it->self_msg_iter = self_msg_iter;
+    msg_it->meta.tc = tc;
+    msg_it->medium.medops = medops;
+    msg_it->medium.max_request_sz = max_request_sz;
+    msg_it->medium.data = data;
+    msg_it->stack = stack_new(msg_it.get());
+    msg_it->stored_values = g_array_new(FALSE, TRUE, sizeof(uint64_t));
+    g_array_set_size(msg_it->stored_values, tc->stored_value_count);
+
+    if (!msg_it->stack) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger, "Failed to create field stack.");
+        goto error;
+    }
+
+    msg_it->bfcr = bt_bfcr_create(cbs, msg_it.get(), msg_it->logger);
+    if (!msg_it->bfcr) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                     "Failed to create binary class reader (BFCR).");
+        goto error;
+    }
+
+    ctf_msg_iter_reset(msg_it.get());
+    BT_CPPLOGD_SPEC(msg_it->logger,
+                    "Created CTF plugin message iterator: "
+                    "trace-addr={}, max-request-size={}, "
+                    "data={}, msg-it-addr={}, log-level={}",
+                    fmt::ptr(tc), max_request_sz, fmt::ptr(data), fmt::ptr(msg_it),
+                    msg_it->logger.level());
+    msg_it->cur_packet_offset = 0;
+
+end:
+    return msg_it;
+
+error:
+    msg_it.reset();
+    goto end;
+}
+
+void ctf_msg_iter_destroy(struct ctf_msg_iter *msg_it)
+{
+    BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
+    BT_STREAM_PUT_REF_AND_RESET(msg_it->stream);
+    release_all_dscopes(msg_it);
+
+    BT_CPPLOGD_SPEC(msg_it->logger, "Destroying CTF plugin message iterator: addr={}",
+                    fmt::ptr(msg_it));
+
+    if (msg_it->stack) {
+        BT_CPPLOGD_STR_SPEC(msg_it->logger, "Destroying field stack.");
+        stack_destroy(msg_it->stack);
+    }
+
+    if (msg_it->bfcr) {
+        BT_CPPLOGD_SPEC(msg_it->logger, "Destroying BFCR: bfcr-addr={}", fmt::ptr(msg_it->bfcr));
+        bt_bfcr_destroy(msg_it->bfcr);
+    }
+
+    if (msg_it->stored_values) {
+        g_array_free(msg_it->stored_values, TRUE);
+    }
+
+    delete msg_it;
+}
+
+void ctf_msg_iter_deleter::operator()(ctf_msg_iter *iter) noexcept
+{
+    ctf_msg_iter_destroy(iter);
+}
+
+enum ctf_msg_iter_status ctf_msg_iter_get_next_message(struct ctf_msg_iter *msg_it,
+                                                       const bt_message **message)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+    BT_ASSERT_DBG(msg_it);
+    BT_ASSERT_DBG(message);
+    BT_CPPLOGD_SPEC(msg_it->logger, "Getting next message: msg-it-addr={}", fmt::ptr(msg_it));
+
+    while (true) {
+        status = handle_state(msg_it);
+        if (G_UNLIKELY(status == CTF_MSG_ITER_STATUS_AGAIN)) {
+            BT_CPPLOGD_STR_SPEC(msg_it->logger, "Medium returned CTF_MSG_ITER_STATUS_AGAIN.");
+            goto end;
+        } else if (G_UNLIKELY(status != CTF_MSG_ITER_STATUS_OK)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                         "Cannot handle state: msg-it-addr={}, state={}",
+                                         fmt::ptr(msg_it), msg_it->state);
+            goto end;
+        }
+
+        switch (msg_it->state) {
+        case STATE_EMIT_MSG_EVENT:
+            BT_ASSERT_DBG(msg_it->event_msg);
+
+            /*
+             * Check if we need to emit the delayed packet
+             * beginning message instead of the event message.
+             */
+            if (G_UNLIKELY(msg_it->emit_delayed_packet_beginning_msg)) {
+                *message = emit_delayed_packet_beg_msg(msg_it);
+                if (!*message) {
+                    status = CTF_MSG_ITER_STATUS_ERROR;
+                }
+
+                /*
+                 * Don't forget to emit the event message of
+                 * the event record that was just decoded.
+                 */
+                msg_it->state = STATE_EMIT_QUEUED_MSG_EVENT;
+
+            } else {
+                *message = msg_it->event_msg;
+                msg_it->event_msg = NULL;
+            }
+            goto end;
+        case STATE_EMIT_MSG_DISCARDED_EVENTS:
+            /* create_msg_discarded_events() logs errors */
+            *message = create_msg_discarded_events(msg_it);
+
+            if (!*message) {
+                status = CTF_MSG_ITER_STATUS_ERROR;
+            }
+
+            goto end;
+        case STATE_EMIT_MSG_DISCARDED_PACKETS:
+            /* create_msg_discarded_packets() logs errors */
+            *message = create_msg_discarded_packets(msg_it);
+
+            if (!*message) {
+                status = CTF_MSG_ITER_STATUS_ERROR;
+            }
+
+            goto end;
+        case STATE_EMIT_MSG_PACKET_BEGINNING:
+            if (G_UNLIKELY(msg_it->meta.tc->quirks.barectf_event_before_packet)) {
+                msg_it->emit_delayed_packet_beginning_msg = true;
+                /*
+                 * There is no message to return yet as this
+                 * packet beginning message is delayed until we
+                 * decode the first event message of the
+                 * packet.
+                 */
+                break;
+            } else {
+                /* create_msg_packet_beginning() logs errors */
+                *message = create_msg_packet_beginning(msg_it, false);
+                if (!*message) {
+                    status = CTF_MSG_ITER_STATUS_ERROR;
+                }
+            }
+
+            goto end;
+        case STATE_EMIT_MSG_PACKET_END_SINGLE:
+        case STATE_EMIT_MSG_PACKET_END_MULTI:
+            /* create_msg_packet_end() logs errors */
+            *message = create_msg_packet_end(msg_it);
+
+            if (!*message) {
+                status = CTF_MSG_ITER_STATUS_ERROR;
+            }
+
+            goto end;
+        case STATE_EMIT_MSG_STREAM_BEGINNING:
+            /* create_msg_stream_beginning() logs errors */
+            *message = create_msg_stream_beginning(msg_it);
+            msg_it->emit_stream_beginning_message = false;
+            msg_it->emit_stream_end_message = true;
+
+            if (!*message) {
+                status = CTF_MSG_ITER_STATUS_ERROR;
+            }
+
+            goto end;
+        case STATE_EMIT_MSG_STREAM_END:
+            /* create_msg_stream_end() logs errors */
+            *message = create_msg_stream_end(msg_it);
+            msg_it->emit_stream_end_message = false;
+
+            if (!*message) {
+                status = CTF_MSG_ITER_STATUS_ERROR;
+            }
+
+            goto end;
+        case STATE_DONE:
+            status = CTF_MSG_ITER_STATUS_EOF;
+            goto end;
+        default:
+            /* Non-emitting state: continue */
+            break;
+        }
+    }
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status decode_until_state(struct ctf_msg_iter *msg_it,
+                                                   enum state target_state_1,
+                                                   enum state target_state_2)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+    BT_ASSERT_DBG(msg_it);
+
+    do {
+        /*
+         * Check if we reached the state at which we want to stop
+         * decoding.
+         */
+        if (msg_it->state == target_state_1 || msg_it->state == target_state_2) {
+            goto end;
+        }
+
+        status = handle_state(msg_it);
+        if (G_UNLIKELY(status == CTF_MSG_ITER_STATUS_AGAIN)) {
+            BT_CPPLOGD_STR_SPEC(msg_it->logger, "Medium returned CTF_MSG_ITER_STATUS_AGAIN.");
+            goto end;
+        } else if (G_UNLIKELY(status != CTF_MSG_ITER_STATUS_OK)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_it->logger,
+                                         "Cannot handle state: msg-it-addr={}, state={}",
+                                         fmt::ptr(msg_it), msg_it->state);
+            goto end;
+        }
+
+        switch (msg_it->state) {
+        case STATE_INIT:
+        case STATE_SWITCH_PACKET:
+        case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
+        case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
+        case STATE_AFTER_TRACE_PACKET_HEADER:
+        case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
+        case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
+        case STATE_AFTER_STREAM_PACKET_CONTEXT:
+        case STATE_EMIT_MSG_STREAM_BEGINNING:
+        case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
+        case STATE_EMIT_MSG_DISCARDED_EVENTS:
+        case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
+        case STATE_EMIT_MSG_DISCARDED_PACKETS:
+        case STATE_EMIT_MSG_PACKET_BEGINNING:
+        case STATE_DSCOPE_EVENT_HEADER_BEGIN:
+        case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
+        case STATE_AFTER_EVENT_HEADER:
+        case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
+        case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
+        case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
+        case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
+        case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
+        case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
+        case STATE_EMIT_MSG_EVENT:
+        case STATE_EMIT_QUEUED_MSG_EVENT:
+        case STATE_SKIP_PACKET_PADDING:
+        case STATE_EMIT_MSG_PACKET_END_MULTI:
+        case STATE_EMIT_MSG_PACKET_END_SINGLE:
+        case STATE_EMIT_QUEUED_MSG_PACKET_END:
+        case STATE_EMIT_MSG_STREAM_END:
+            break;
+        case STATE_DONE:
+            /* fall-through */
+        default:
+            /* We should never get to the STATE_DONE state. */
+            BT_CPPLOGF_SPEC(msg_it->logger, "Unexpected state: msg-it-addr={}, state={}",
+                            fmt::ptr(msg_it), msg_it->state);
+            bt_common_abort();
+        }
+    } while (true);
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status read_packet_header_context_fields(struct ctf_msg_iter *msg_it)
+{
+    int ret;
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+    status = decode_until_state(msg_it, STATE_EMIT_MSG_PACKET_BEGINNING, (state) -1);
+    if (status != CTF_MSG_ITER_STATUS_OK) {
+        goto end;
+    }
+
+    ret = set_current_packet_content_sizes(msg_it);
+    if (ret) {
+        status = CTF_MSG_ITER_STATUS_ERROR;
+        goto end;
+    }
+
+end:
+    return status;
+}
+
+enum ctf_msg_iter_status ctf_msg_iter_seek(struct ctf_msg_iter *msg_it, off_t offset)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+    enum ctf_msg_iter_medium_status medium_status;
+
+    BT_ASSERT(msg_it);
+    BT_ASSERT(offset >= 0);
+    BT_ASSERT(msg_it->medium.medops.seek);
+
+    medium_status = msg_it->medium.medops.seek(offset, msg_it->medium.data);
+    if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+        if (medium_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
+            status = CTF_MSG_ITER_STATUS_EOF;
+        } else {
+            status = CTF_MSG_ITER_STATUS_ERROR;
+            goto end;
+        }
+    }
+
+    ctf_msg_iter_reset(msg_it);
+    msg_it->cur_packet_offset = offset;
+
+end:
+    return status;
+}
+
+static enum ctf_msg_iter_status clock_snapshot_at_msg_iter_state(struct ctf_msg_iter *msg_it,
+                                                                 enum state target_state_1,
+                                                                 enum state target_state_2,
+                                                                 uint64_t *clock_snapshot)
+{
+    enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+    BT_ASSERT_DBG(msg_it);
+    BT_ASSERT_DBG(clock_snapshot);
+    status = decode_until_state(msg_it, target_state_1, target_state_2);
+    if (status != CTF_MSG_ITER_STATUS_OK) {
+        goto end;
+    }
+
+    *clock_snapshot = msg_it->default_clock_snapshot;
+end:
+    return status;
+}
+
+enum ctf_msg_iter_status
+ctf_msg_iter_curr_packet_first_event_clock_snapshot(struct ctf_msg_iter *msg_it,
+                                                    uint64_t *first_clock_snapshot)
+{
+    return clock_snapshot_at_msg_iter_state(msg_it, STATE_AFTER_EVENT_HEADER, (state) -1,
+                                            first_clock_snapshot);
+}
+
+enum ctf_msg_iter_status
+ctf_msg_iter_curr_packet_last_event_clock_snapshot(struct ctf_msg_iter *msg_it,
+                                                   uint64_t *last_clock_snapshot)
+{
+    return clock_snapshot_at_msg_iter_state(msg_it, STATE_EMIT_MSG_PACKET_END_SINGLE,
+                                            STATE_EMIT_MSG_PACKET_END_MULTI, last_clock_snapshot);
+}
+
+enum ctf_msg_iter_status
+ctf_msg_iter_get_packet_properties(struct ctf_msg_iter *msg_it,
+                                   struct ctf_msg_iter_packet_properties *props)
+{
+    enum ctf_msg_iter_status status;
+
+    BT_ASSERT_DBG(msg_it);
+    BT_ASSERT_DBG(props);
+    status = read_packet_header_context_fields(msg_it);
+    if (status != CTF_MSG_ITER_STATUS_OK) {
+        goto end;
+    }
+
+    props->exp_packet_total_size = msg_it->cur_exp_packet_total_size;
+    props->exp_packet_content_size = msg_it->cur_exp_packet_content_size;
+    props->stream_class_id = (uint64_t) msg_it->cur_stream_class_id;
+    props->data_stream_id = msg_it->cur_data_stream_id;
+    props->snapshots.discarded_events = msg_it->snapshots.discarded_events;
+    props->snapshots.packets = msg_it->snapshots.packets;
+    props->snapshots.beginning_clock = msg_it->snapshots.beginning_clock;
+    props->snapshots.end_clock = msg_it->snapshots.end_clock;
+
+end:
+    return status;
+}
+
+void ctf_msg_iter_set_dry_run(struct ctf_msg_iter *msg_it, bool val)
+{
+    msg_it->dry_run = val;
+}
diff --git a/src/plugins/ctf/common/src/msg-iter/msg-iter.hpp b/src/plugins/ctf/common/src/msg-iter/msg-iter.hpp
new file mode 100644 (file)
index 0000000..f9bb7f2
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Babeltrace - CTF message iterator
+ */
+
+#ifndef CTF_MSG_ITER_H
+#define CTF_MSG_ITER_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "../metadata/tsdl/ctf-meta.hpp"
+
+namespace bt2c {
+
+class Logger;
+
+} /* namespace bt2c */
+
+/**
+ * @file ctf-msg-iter.h
+ *
+ * CTF message iterator
+ *
+ * This is a common internal API used by CTF source plugins. It allows
+ * one to get messages from a user-provided medium.
+ */
+
+/**
+ * Medium operations status codes.  These use the same values as
+ * libbabeltrace2.
+ */
+enum ctf_msg_iter_medium_status
+{
+    /**
+     * End of file.
+     *
+     * The medium function called by the message iterator
+     * function reached the end of the file.
+     */
+    CTF_MSG_ITER_MEDIUM_STATUS_EOF = 1,
+
+    /**
+     * There is no data available right now, try again later.
+     */
+    CTF_MSG_ITER_MEDIUM_STATUS_AGAIN = 11,
+
+    /** General error. */
+    CTF_MSG_ITER_MEDIUM_STATUS_ERROR = -1,
+
+    /** Memory error. */
+    CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR = -12,
+
+    /** Everything okay. */
+    CTF_MSG_ITER_MEDIUM_STATUS_OK = 0,
+};
+
+inline const char *format_as(const ctf_msg_iter_medium_status status) noexcept
+{
+    switch (status) {
+    case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
+        return "EOF";
+
+    case CTF_MSG_ITER_MEDIUM_STATUS_AGAIN:
+        return "AGAIN";
+
+    case CTF_MSG_ITER_MEDIUM_STATUS_ERROR:
+        return "ERROR";
+
+    case CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR:
+        return "MEMORY_ERROR";
+
+    case CTF_MSG_ITER_MEDIUM_STATUS_OK:
+        return "OK";
+    }
+
+    bt_common_abort();
+}
+
+/**
+ * CTF message iterator API status code.
+ */
+enum ctf_msg_iter_status
+{
+    /**
+     * End of file.
+     *
+     * The medium function called by the message iterator
+     * function reached the end of the file.
+     */
+    CTF_MSG_ITER_STATUS_EOF = CTF_MSG_ITER_MEDIUM_STATUS_EOF,
+
+    /**
+     * There is no data available right now, try again later.
+     *
+     * Some condition resulted in the
+     * ctf_msg_iter_medium_ops::request_bytes() user function not
+     * having access to any data now. You should retry calling the
+     * last called message iterator function once the situation
+     * is resolved.
+     */
+    CTF_MSG_ITER_STATUS_AGAIN = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN,
+
+    /** General error. */
+    CTF_MSG_ITER_STATUS_ERROR = CTF_MSG_ITER_MEDIUM_STATUS_ERROR,
+
+    /** Memory error. */
+    CTF_MSG_ITER_STATUS_MEMORY_ERROR = CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR,
+
+    /** Everything okay. */
+    CTF_MSG_ITER_STATUS_OK = CTF_MSG_ITER_MEDIUM_STATUS_OK,
+};
+
+inline const char *format_as(ctf_msg_iter_status status) noexcept
+{
+    switch (status) {
+    case CTF_MSG_ITER_STATUS_EOF:
+        return "EOF";
+
+    case CTF_MSG_ITER_STATUS_AGAIN:
+        return "AGAIN";
+
+    case CTF_MSG_ITER_STATUS_ERROR:
+        return "ERROR";
+
+    case CTF_MSG_ITER_STATUS_MEMORY_ERROR:
+        return "MEMORY_ERROR";
+
+    case CTF_MSG_ITER_STATUS_OK:
+        return "OK";
+    }
+
+    bt_common_abort();
+}
+
+/**
+ * Medium operations.
+ *
+ * Those user functions are called by the message iterator
+ * functions to request medium actions.
+ */
+struct ctf_msg_iter_medium_ops
+{
+    /**
+     * Returns the next byte buffer to be used by the binary file
+     * reader to deserialize binary data.
+     *
+     * This function \em must be defined.
+     *
+     * The purpose of this function is to return a buffer of bytes
+     * to the message iterator, of a maximum of \p request_sz
+     * bytes. If this function cannot return a buffer of at least
+     * \p request_sz bytes, it may return a smaller buffer. In
+     * either cases, \p buffer_sz must be set to the returned buffer
+     * size (in bytes).
+     *
+     * The returned buffer's ownership remains the medium, in that
+     * it won't be freed by the message iterator functions. The
+     * returned buffer won't be modified by the message
+     * iterator functions either.
+     *
+     * When this function is called for the first time for a given
+     * file, the offset within the file is considered to be 0. The
+     * next times this function is called, the returned buffer's
+     * byte offset within the complete file must be the previous
+     * offset plus the last returned value of \p buffer_sz by this
+     * medium.
+     *
+     * This function must return one of the following statuses:
+     *
+     *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_OK</b>: Everything
+     *     is okay, i.e. \p buffer_sz is set to a positive value
+     *     reflecting the number of available bytes in the buffer
+     *     starting at the address written in \p buffer_addr.
+     *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_AGAIN</b>: No data is
+     *     available right now. In this case, the message
+     *     iterator function called by the user returns
+     *     #CTF_MSG_ITER_STATUS_AGAIN, and it is the user's
+     *     responsibility to make sure enough data becomes available
+     *     before calling the \em same message iterator
+     *     function again to continue the decoding process.
+     *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_EOF</b>: The end of
+     *     the file was reached, and no more data will ever be
+     *     available for this file. In this case, the message
+     *     iterator function called by the user returns
+     *     #CTF_MSG_ITER_STATUS_EOF. This must \em not be
+     *     returned when returning at least one byte of data to the
+     *     caller, i.e. this must be returned when there's
+     *     absolutely nothing left; should the request size be
+     *     larger than what's left in the file, this function must
+     *     return what's left, setting \p buffer_sz to the number of
+     *     remaining bytes, and return
+     *     #CTF_MSG_ITER_MEDIUM_STATUS_EOF on the \em following
+     *     call.
+     *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_ERROR</b>: A fatal
+     *     error occurred during this operation. In this case, the
+     *     message iterator function called by the user returns
+     *     #CTF_MSG_ITER_STATUS_ERROR.
+     *
+     * If #CTF_MSG_ITER_MEDIUM_STATUS_OK is not returned, the
+     * values of \p buffer_sz and \p buffer_addr are \em ignored by
+     * the caller.
+     *
+     * @param request_sz       Requested buffer size (bytes)
+     * @param buffer_addr      Returned buffer address
+     * @param buffer_sz        Returned buffer's size (bytes)
+     * @param data             User data
+     * @returns                Status code (see description above)
+     */
+    enum ctf_msg_iter_medium_status (*request_bytes)(size_t request_sz, uint8_t **buffer_addr,
+                                                     size_t *buffer_sz, void *data);
+
+    /**
+     * Repositions the underlying stream's position.
+     *
+     * This *optional* method repositions the underlying stream
+     * to a given absolute position in the medium.
+     *
+     * @param offset   Offset to use for the given directive
+     * @param data             User data
+     * @returns                One of #ctf_msg_iter_medium_status values
+     */
+    enum ctf_msg_iter_medium_status (*seek)(off_t offset, void *data);
+
+    /**
+     * Called when the message iterator wishes to inform the medium that it
+     * is about to start a new packet.
+     *
+     * After the iterator has called switch_packet, the following call to
+     * request_bytes must return the content at the start of the next
+     * packet.  */
+    enum ctf_msg_iter_medium_status (*switch_packet)(void *data);
+
+    /**
+     * Returns a stream instance (weak reference) for the given
+     * stream class.
+     *
+     * This is called after a packet header is read, and the
+     * corresponding stream class is found by the message
+     * iterator.
+     *
+     * @param stream_class     Stream class of the stream to get
+     * @param stream_id        Stream (instance) ID of the stream
+     *                 to get (-1ULL if not available)
+     * @param data             User data
+     * @returns                Stream instance (weak reference) or
+     *                 \c NULL on error
+     */
+    bt_stream *(*borrow_stream)(bt_stream_class *stream_class, int64_t stream_id, void *data);
+};
+
+/** CTF message iterator. */
+struct ctf_msg_iter;
+
+struct ctf_msg_iter_deleter
+{
+    void operator()(ctf_msg_iter *iter) noexcept;
+};
+
+using ctf_msg_iter_up = std::unique_ptr<ctf_msg_iter, ctf_msg_iter_deleter>;
+
+/**
+ * Creates a CTF message iterator.
+ *
+ * Upon successful completion, the reference count of \p trace is
+ * incremented.
+ *
+ * @param trace                        Trace to read
+ * @param max_request_sz       Maximum buffer size, in bytes, to
+ *                             request to
+ *                             ctf_msg_iter_medium_ops::request_bytes()
+ *                             at a time
+ * @param medops               Medium operations
+ * @param medops_data          User data (passed to medium operations)
+ * @returns                    New CTF message iterator on
+ *                             success, or \c NULL on error
+ */
+ctf_msg_iter_up ctf_msg_iter_create(struct ctf_trace_class *tc, size_t max_request_sz,
+                                    struct ctf_msg_iter_medium_ops medops, void *medops_data,
+                                    bt_self_message_iterator *self_msg_iter,
+                                    const bt2c::Logger& logger);
+
+/**
+ * Destroys a CTF message iterator, freeing all internal resources.
+ *
+ * The registered trace's reference count is decremented.
+ *
+ * @param msg_iter             CTF message iterator
+ */
+void ctf_msg_iter_destroy(struct ctf_msg_iter *msg_iter);
+
+/**
+ * Returns the next message from a CTF message iterator.
+ *
+ * Upon successful completion, #CTF_MSG_ITER_STATUS_OK is
+ * returned, and the next message is written to \p msg.
+ * In this case, the caller is responsible for calling
+ * bt_message_put() on the returned message.
+ *
+ * If this function returns #CTF_MSG_ITER_STATUS_AGAIN, the caller
+ * should make sure that data becomes available to its medium, and
+ * call this function again, until another status is returned.
+ *
+ * @param msg_iter             CTF message iterator
+ * @param message              Returned message if the function's
+ *                             return value is #CTF_MSG_ITER_STATUS_OK
+ * @returns                    One of #ctf_msg_iter_status values
+ */
+enum ctf_msg_iter_status ctf_msg_iter_get_next_message(struct ctf_msg_iter *msg_it,
+                                                       const bt_message **message);
+
+struct ctf_msg_iter_packet_properties
+{
+    int64_t exp_packet_total_size;
+    int64_t exp_packet_content_size;
+    uint64_t stream_class_id;
+    int64_t data_stream_id;
+
+    struct
+    {
+        uint64_t discarded_events;
+        uint64_t packets;
+        uint64_t beginning_clock;
+        uint64_t end_clock;
+    } snapshots;
+};
+
+enum ctf_msg_iter_status
+ctf_msg_iter_get_packet_properties(struct ctf_msg_iter *msg_it,
+                                   struct ctf_msg_iter_packet_properties *props);
+
+enum ctf_msg_iter_status
+ctf_msg_iter_curr_packet_first_event_clock_snapshot(struct ctf_msg_iter *msg_it,
+                                                    uint64_t *first_event_cs);
+
+enum ctf_msg_iter_status
+ctf_msg_iter_curr_packet_last_event_clock_snapshot(struct ctf_msg_iter *msg_it,
+                                                   uint64_t *last_event_cs);
+
+enum ctf_msg_iter_status ctf_msg_iter_seek(struct ctf_msg_iter *msg_it, off_t offset);
+
+/*
+ * Resets the iterator so that the next requested medium bytes are
+ * assumed to be the first bytes of a new stream. Depending on
+ * ctf_msg_iter_set_emit_stream_beginning_message(), the first message
+ * which this iterator emits after calling ctf_msg_iter_reset() is of
+ * type `CTF_MESSAGE_TYPE_STREAM_BEGINNING`.
+ */
+void ctf_msg_iter_reset(struct ctf_msg_iter *msg_it);
+
+/*
+ * Like ctf_msg_iter_reset(), but preserves stream-dependent state.
+ */
+void ctf_msg_iter_reset_for_next_stream_file(struct ctf_msg_iter *msg_it);
+
+void ctf_msg_iter_set_dry_run(struct ctf_msg_iter *msg_it, bool val);
+
+#endif /* CTF_MSG_ITER_H */
diff --git a/src/plugins/ctf/fs-sink/Makefile.am b/src/plugins/ctf/fs-sink/Makefile.am
deleted file mode 100644 (file)
index ef6e399..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-fs-sink.la
-
-libbabeltrace2_plugin_ctf_fs_sink_la_LIBADD =
-libbabeltrace2_plugin_ctf_fs_sink_la_SOURCES = \
-       fs-sink.cpp \
-       fs-sink.hpp \
-       fs-sink-ctf-meta.hpp \
-       translate-trace-ir-to-ctf-ir.cpp \
-       translate-trace-ir-to-ctf-ir.hpp \
-       translate-ctf-ir-to-tsdl.cpp \
-       translate-ctf-ir-to-tsdl.hpp \
-       fs-sink-stream.cpp \
-       fs-sink-stream.hpp \
-       fs-sink-trace.cpp \
-       fs-sink-trace.hpp
index e56ab5269db78448922f8765279d265b9df8e26c..7161fb35c52770c06d20988578e48876970f58e4 100644 (file)
@@ -7,15 +7,15 @@
 #ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H
 #define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H
 
-#include <babeltrace2/babeltrace.h>
-#include "common/common.h"
-#include "common/assert.h"
-#include "common/uuid.h"
 #include <glib.h>
 #include <stdint.h>
 #include <string.h>
-#include <stdbool.h>
-#include <ctype.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "common/common.h"
+#include "common/uuid.h"
 
 enum fs_sink_ctf_field_class_type
 {
@@ -39,9 +39,6 @@ struct fs_sink_ctf_field_class
     const bt_field_class *ir_fc;
 
     unsigned int alignment;
-
-    /* Index of the field class within its own parent */
-    uint64_t index_in_parent;
 };
 
 struct fs_sink_ctf_field_class_bit_array
@@ -288,33 +285,31 @@ static inline void fs_sink_ctf_field_class_destroy(struct fs_sink_ctf_field_clas
 static inline void _fs_sink_ctf_field_class_init(struct fs_sink_ctf_field_class *fc,
                                                  enum fs_sink_ctf_field_class_type type,
                                                  const bt_field_class *ir_fc,
-                                                 unsigned int alignment, uint64_t index_in_parent)
+                                                 unsigned int alignment)
 {
     BT_ASSERT(fc);
     fc->type = type;
     fc->ir_fc = ir_fc;
     fc->alignment = alignment;
-    fc->index_in_parent = index_in_parent;
 }
 
-static inline void _fs_sink_ctf_field_class_bit_array_init(
-    struct fs_sink_ctf_field_class_bit_array *fc, enum fs_sink_ctf_field_class_type type,
-    const bt_field_class *ir_fc, unsigned int size, uint64_t index_in_parent)
+static inline void
+_fs_sink_ctf_field_class_bit_array_init(struct fs_sink_ctf_field_class_bit_array *fc,
+                                        enum fs_sink_ctf_field_class_type type,
+                                        const bt_field_class *ir_fc, unsigned int size)
 {
-    _fs_sink_ctf_field_class_init(&fc->base, type, ir_fc, size % 8 == 0 ? 8 : 1, index_in_parent);
+    _fs_sink_ctf_field_class_init(&fc->base, type, ir_fc, size % 8 == 0 ? 8 : 1);
     fc->size = size;
 }
 
 static inline void _fs_sink_ctf_field_class_int_init(struct fs_sink_ctf_field_class_int *fc,
                                                      enum fs_sink_ctf_field_class_type type,
-                                                     const bt_field_class *ir_fc,
-                                                     uint64_t index_in_parent)
+                                                     const bt_field_class *ir_fc)
 {
     bt_field_class_type ir_fc_type = bt_field_class_get_type(ir_fc);
 
     _fs_sink_ctf_field_class_bit_array_init(
-        &fc->base, type, ir_fc, (unsigned int) bt_field_class_integer_get_field_value_range(ir_fc),
-        index_in_parent);
+        &fc->base, type, ir_fc, (unsigned int) bt_field_class_integer_get_field_value_range(ir_fc));
     fc->is_signed = bt_field_class_type_is(ir_fc_type, BT_FIELD_CLASS_TYPE_SIGNED_INTEGER);
 }
 
@@ -341,7 +336,7 @@ _fs_sink_ctf_named_field_class_fini(struct fs_sink_ctf_named_field_class *named_
 }
 
 static inline struct fs_sink_ctf_field_class_bit_array *
-fs_sink_ctf_field_class_bit_array_create(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_bit_array_create(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_bit_array *fc =
         g_new0(struct fs_sink_ctf_field_class_bit_array, 1);
@@ -349,12 +344,12 @@ fs_sink_ctf_field_class_bit_array_create(const bt_field_class *ir_fc, uint64_t i
     BT_ASSERT(fc);
     _fs_sink_ctf_field_class_bit_array_init(
         fc, FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY, ir_fc,
-        (unsigned int) bt_field_class_bit_array_get_length(ir_fc), index_in_parent);
+        (unsigned int) bt_field_class_bit_array_get_length(ir_fc));
     return fc;
 }
 
 static inline struct fs_sink_ctf_field_class_bool *
-fs_sink_ctf_field_class_bool_create(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_bool_create(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_bool *fc = g_new0(struct fs_sink_ctf_field_class_bool, 1);
 
@@ -364,79 +359,73 @@ fs_sink_ctf_field_class_bool_create(const bt_field_class *ir_fc, uint64_t index_
      * CTF 1.8 has no boolean field class type, so this component
      * translates it to an 8-bit unsigned integer field class.
      */
-    _fs_sink_ctf_field_class_bit_array_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_BOOL, ir_fc, 8,
-                                            index_in_parent);
+    _fs_sink_ctf_field_class_bit_array_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_BOOL, ir_fc, 8);
     return fc;
 }
 
 static inline struct fs_sink_ctf_field_class_int *
-fs_sink_ctf_field_class_int_create(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_int_create(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_int *fc = g_new0(struct fs_sink_ctf_field_class_int, 1);
 
     BT_ASSERT(fc);
-    _fs_sink_ctf_field_class_int_init(fc, FS_SINK_CTF_FIELD_CLASS_TYPE_INT, ir_fc, index_in_parent);
+    _fs_sink_ctf_field_class_int_init(fc, FS_SINK_CTF_FIELD_CLASS_TYPE_INT, ir_fc);
     return fc;
 }
 
 static inline struct fs_sink_ctf_field_class_float *
-fs_sink_ctf_field_class_float_create(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_float_create(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_float *fc = g_new0(struct fs_sink_ctf_field_class_float, 1);
 
     BT_ASSERT(fc);
     _fs_sink_ctf_field_class_bit_array_init(
         &fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT, ir_fc,
-        bt_field_class_get_type(ir_fc) == BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL ? 32 : 64,
-        index_in_parent);
+        bt_field_class_get_type(ir_fc) == BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL ? 32 : 64);
     return fc;
 }
 
 static inline struct fs_sink_ctf_field_class_string *
-fs_sink_ctf_field_class_string_create(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_string_create(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_string *fc = g_new0(struct fs_sink_ctf_field_class_string, 1);
 
     BT_ASSERT(fc);
-    _fs_sink_ctf_field_class_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_STRING, ir_fc, 8,
-                                  index_in_parent);
+    _fs_sink_ctf_field_class_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_STRING, ir_fc, 8);
     return fc;
 }
 
 static inline struct fs_sink_ctf_field_class_struct *
-fs_sink_ctf_field_class_struct_create_empty(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_struct_create_empty(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_struct *fc = g_new0(struct fs_sink_ctf_field_class_struct, 1);
 
     BT_ASSERT(fc);
-    _fs_sink_ctf_field_class_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT, ir_fc, 1,
-                                  index_in_parent);
+    _fs_sink_ctf_field_class_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT, ir_fc, 1);
     fc->members = g_array_new(FALSE, TRUE, sizeof(struct fs_sink_ctf_named_field_class));
     BT_ASSERT(fc->members);
     return fc;
 }
 
 static inline struct fs_sink_ctf_field_class_option *
-fs_sink_ctf_field_class_option_create_empty(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_option_create_empty(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_option *fc = g_new0(struct fs_sink_ctf_field_class_option, 1);
 
     BT_ASSERT(fc);
-    _fs_sink_ctf_field_class_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION, ir_fc, 1,
-                                  index_in_parent);
+    _fs_sink_ctf_field_class_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION, ir_fc, 1);
     fc->tag_ref = g_string_new(NULL);
     BT_ASSERT(fc->tag_ref);
     return fc;
 }
 
 static inline struct fs_sink_ctf_field_class_variant *
-fs_sink_ctf_field_class_variant_create_empty(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_variant_create_empty(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_variant *fc = g_new0(struct fs_sink_ctf_field_class_variant, 1);
 
     BT_ASSERT(fc);
-    _fs_sink_ctf_field_class_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT, ir_fc, 1,
-                                  index_in_parent);
+    _fs_sink_ctf_field_class_init(&fc->base, FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT, ir_fc, 1);
     fc->options = g_array_new(FALSE, TRUE, sizeof(struct fs_sink_ctf_named_field_class));
     BT_ASSERT(fc->options);
     fc->tag_ref = g_string_new(NULL);
@@ -447,26 +436,24 @@ fs_sink_ctf_field_class_variant_create_empty(const bt_field_class *ir_fc, uint64
 }
 
 static inline struct fs_sink_ctf_field_class_array *
-fs_sink_ctf_field_class_array_create_empty(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_array_create_empty(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_array *fc = g_new0(struct fs_sink_ctf_field_class_array, 1);
 
     BT_ASSERT(fc);
-    _fs_sink_ctf_field_class_init(&fc->base.base, FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY, ir_fc, 1,
-                                  index_in_parent);
+    _fs_sink_ctf_field_class_init(&fc->base.base, FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY, ir_fc, 1);
     fc->length = bt_field_class_array_static_get_length(ir_fc);
     return fc;
 }
 
 static inline struct fs_sink_ctf_field_class_sequence *
-fs_sink_ctf_field_class_sequence_create_empty(const bt_field_class *ir_fc, uint64_t index_in_parent)
+fs_sink_ctf_field_class_sequence_create_empty(const bt_field_class *ir_fc)
 {
     struct fs_sink_ctf_field_class_sequence *fc =
         g_new0(struct fs_sink_ctf_field_class_sequence, 1);
 
     BT_ASSERT(fc);
-    _fs_sink_ctf_field_class_init(&fc->base.base, FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE, ir_fc, 1,
-                                  index_in_parent);
+    _fs_sink_ctf_field_class_init(&fc->base.base, FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE, ir_fc, 1);
     fc->length_ref = g_string_new(NULL);
     BT_ASSERT(fc->length_ref);
     fc->length_is_before =
@@ -668,7 +655,7 @@ fs_sink_ctf_field_class_struct_borrow_member_by_index(struct fs_sink_ctf_field_c
 {
     BT_ASSERT_DBG(fc);
     BT_ASSERT_DBG(index < fc->members->len);
-    return &g_array_index(fc->members, struct fs_sink_ctf_named_field_class, index);
+    return &bt_g_array_index(fc->members, struct fs_sink_ctf_named_field_class, index);
 }
 
 static inline struct fs_sink_ctf_named_field_class *
@@ -759,7 +746,7 @@ fs_sink_ctf_field_class_struct_append_member(struct fs_sink_ctf_field_class_stru
     g_array_set_size(fc->members, fc->members->len + 1);
 
     named_fc =
-        &g_array_index(fc->members, struct fs_sink_ctf_named_field_class, fc->members->len - 1);
+        &bt_g_array_index(fc->members, struct fs_sink_ctf_named_field_class, fc->members->len - 1);
     _fs_sink_ctf_named_field_class_init(named_fc);
     g_string_assign(named_fc->name, name);
     named_fc->fc = member_fc;
@@ -772,7 +759,7 @@ fs_sink_ctf_field_class_variant_borrow_option_by_index(struct fs_sink_ctf_field_
 {
     BT_ASSERT_DBG(fc);
     BT_ASSERT_DBG(index < fc->options->len);
-    return &g_array_index(fc->options, struct fs_sink_ctf_named_field_class, index);
+    return &bt_g_array_index(fc->options, struct fs_sink_ctf_named_field_class, index);
 }
 
 static inline struct fs_sink_ctf_named_field_class *
@@ -811,7 +798,7 @@ fs_sink_ctf_field_class_variant_append_option(struct fs_sink_ctf_field_class_var
     g_array_set_size(fc->options, fc->options->len + 1);
 
     named_fc =
-        &g_array_index(fc->options, struct fs_sink_ctf_named_field_class, fc->options->len - 1);
+        &bt_g_array_index(fc->options, struct fs_sink_ctf_named_field_class, fc->options->len - 1);
     _fs_sink_ctf_named_field_class_init(named_fc);
     g_string_assign(named_fc->name, name);
     named_fc->fc = option_fc;
index a90d587b9587baa3b7cc53e2c9fea5113cd18554..e070e435aa5c4233c4979ae24f13e297a1a14c90 100644 (file)
@@ -4,25 +4,20 @@
  * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
  */
 
-#define BT_COMP_LOG_SELF_COMP (stream->trace->fs_sink->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (stream->log_level)
-#define BT_LOG_TAG            "PLUGIN/SINK.CTF.FS/STREAM"
-#include "logging/comp-logging.h"
+#include <glib.h>
+#include <stdio.h>
 
 #include <babeltrace2/babeltrace.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <glib.h>
+
 #include "common/assert.h"
+#include "compat/endian.h" /* IWYU pragma: keep  */
 #include "ctfser/ctfser.h"
-#include "compat/endian.h"
 
-#include "fs-sink.hpp"
-#include "fs-sink-trace.hpp"
+#include "fs-sink-ctf-meta.hpp"
 #include "fs-sink-stream.hpp"
+#include "fs-sink-trace.hpp"
 #include "translate-trace-ir-to-ctf-ir.hpp"
 
-BT_HIDDEN
 void fs_sink_stream_destroy(struct fs_sink_stream *stream)
 {
     if (!stream) {
@@ -37,7 +32,7 @@ void fs_sink_stream_destroy(struct fs_sink_stream *stream)
     }
 
     bt_packet_put_ref(stream->packet_state.packet);
-    g_free(stream);
+    delete stream;
 
 end:
     return;
@@ -120,19 +115,13 @@ static void set_stream_file_name(struct fs_sink_stream *stream)
     stream->file_name = make_unique_stream_file_name(stream->trace, base_name);
 }
 
-BT_HIDDEN
 struct fs_sink_stream *fs_sink_stream_create(struct fs_sink_trace *trace,
                                              const bt_stream *ir_stream)
 {
-    struct fs_sink_stream *stream = g_new0(struct fs_sink_stream, 1);
+    fs_sink_stream *stream = new fs_sink_stream {trace->logger};
     int ret;
     GString *path = g_string_new(trace->path->str);
 
-    if (!stream) {
-        goto end;
-    }
-
-    stream->log_level = trace->log_level;
     stream->trace = trace;
     stream->ir_stream = ir_stream;
     stream->packet_state.beginning_cs = UINT64_C(-1);
@@ -148,7 +137,7 @@ struct fs_sink_stream *fs_sink_stream_create(struct fs_sink_trace *trace,
 
     set_stream_file_name(stream);
     g_string_append_printf(path, "/%s", stream->file_name->str);
-    ret = bt_ctfser_init(&stream->ctfser, path->str, stream->log_level);
+    ret = bt_ctfser_init(&stream->ctfser, path->str, static_cast<int>(stream->logger.level()));
     if (ret) {
         goto error;
     }
@@ -230,9 +219,7 @@ static inline int write_float_field(struct fs_sink_stream *stream,
     return ret;
 }
 
-static inline int write_string_field(struct fs_sink_stream *stream,
-                                     struct fs_sink_ctf_field_class_string *fc,
-                                     const bt_field *field)
+static inline int write_string_field(struct fs_sink_stream *stream, const bt_field *field)
 {
     return bt_ctfser_write_string(&stream->ctfser, bt_field_string_get_value(field));
 }
@@ -379,7 +366,7 @@ static int write_field(struct fs_sink_stream *stream, struct fs_sink_ctf_field_c
         ret = write_float_field(stream, fs_sink_ctf_field_class_as_float(fc), field);
         break;
     case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING:
-        ret = write_string_field(stream, fs_sink_ctf_field_class_as_string(fc), field);
+        ret = write_string_field(stream, field);
         break;
     case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
         ret = write_struct_field(stream, fs_sink_ctf_field_class_as_struct(fc), field, true);
@@ -430,7 +417,6 @@ end:
     return ret;
 }
 
-BT_HIDDEN
 int fs_sink_stream_write_event(struct fs_sink_stream *stream, const bt_clock_snapshot *cs,
                                const bt_event *event, struct fs_sink_ctf_event_class *ec)
 {
@@ -552,7 +538,6 @@ end:
     return ret;
 }
 
-BT_HIDDEN
 int fs_sink_stream_open_packet(struct fs_sink_stream *stream, const bt_clock_snapshot *cs,
                                const bt_packet *packet)
 {
@@ -578,8 +563,8 @@ int fs_sink_stream_open_packet(struct fs_sink_stream *stream, const bt_clock_sna
     ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, UINT64_C(0xc1fc1fc1), 8, 32,
                                                     BYTE_ORDER);
     if (ret) {
-        BT_COMP_LOGE("Error writing packet header magic: stream-file-name=%s",
-                     stream->file_name->str);
+        BT_CPPLOGE_SPEC(stream->logger, "Error writing packet header magic: stream-file-name={}",
+                        stream->file_name->str);
         goto end;
     }
 
@@ -588,8 +573,8 @@ int fs_sink_stream_open_packet(struct fs_sink_stream *stream, const bt_clock_sna
         ret = bt_ctfser_write_byte_aligned_unsigned_int(
             &stream->ctfser, (uint64_t) stream->sc->trace->uuid[i], 8, 8, BYTE_ORDER);
         if (ret) {
-            BT_COMP_LOGE("Error writing packet header UUID: stream-file-name=%s",
-                         stream->file_name->str);
+            BT_CPPLOGE_SPEC(stream->logger, "Error writing packet header UUID: stream-file-name={}",
+                            stream->file_name->str);
             goto end;
         }
     }
@@ -598,9 +583,10 @@ int fs_sink_stream_open_packet(struct fs_sink_stream *stream, const bt_clock_sna
     ret = bt_ctfser_write_byte_aligned_unsigned_int(
         &stream->ctfser, bt_stream_class_get_id(stream->sc->ir_sc), 8, 64, BYTE_ORDER);
     if (ret) {
-        BT_COMP_LOGE("Error writing packet header stream class id: "
-                     "stream-file-name=%s, stream-class-id=%" PRIu64,
-                     stream->file_name->str, bt_stream_class_get_id(stream->sc->ir_sc));
+        BT_CPPLOGE_SPEC(stream->logger,
+                        "Error writing packet header stream class id: "
+                        "stream-file-name={}, stream-class-id={}",
+                        stream->file_name->str, bt_stream_class_get_id(stream->sc->ir_sc));
         goto end;
     }
 
@@ -608,9 +594,10 @@ int fs_sink_stream_open_packet(struct fs_sink_stream *stream, const bt_clock_sna
     ret = bt_ctfser_write_byte_aligned_unsigned_int(
         &stream->ctfser, bt_stream_get_id(stream->ir_stream), 8, 64, BYTE_ORDER);
     if (ret) {
-        BT_COMP_LOGE("Error writing packet header stream id: "
-                     "stream-file-name=%s, stream-id=%" PRIu64,
-                     stream->file_name->str, bt_stream_get_id(stream->ir_stream));
+        BT_CPPLOGE_SPEC(stream->logger,
+                        "Error writing packet header stream id: "
+                        "stream-file-name={}, stream-id={}",
+                        stream->file_name->str, bt_stream_get_id(stream->ir_stream));
         goto end;
     }
 
@@ -630,7 +617,6 @@ end:
     return ret;
 }
 
-BT_HIDDEN
 int fs_sink_stream_close_packet(struct fs_sink_stream *stream, const bt_clock_snapshot *cs)
 {
     int ret;
index 54657fe7c7dff2938d3e25e507d14319e53b9a90..8c309525c5619f55b1643cc5c148cadb9c8f3781 100644 (file)
@@ -7,30 +7,35 @@
 #ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H
 #define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H
 
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-#include "ctfser/ctfser.h"
 #include <glib.h>
-#include <stdbool.h>
 #include <stdint.h>
 
-#include "fs-sink-ctf-meta.hpp"
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/logging.hpp"
+#include "ctfser/ctfser.h"
 
 struct fs_sink_trace;
+struct fs_sink_ctf_stream_class;
 
 struct fs_sink_stream
 {
-    bt_logging_level log_level;
-    struct fs_sink_trace *trace;
-    struct bt_ctfser ctfser;
+    explicit fs_sink_stream(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SINK.CTF.FS/STREAM"}
+    {
+    }
+
+    bt2c::Logger logger;
+    fs_sink_trace *trace = nullptr;
+    bt_ctfser ctfser {};
 
     /* Stream's file name */
-    GString *file_name;
+    GString *file_name = nullptr;
 
     /* Weak */
-    const bt_stream *ir_stream;
+    const bt_stream *ir_stream = nullptr;
 
-    struct fs_sink_ctf_stream_class *sc;
+    fs_sink_ctf_stream_class *sc = nullptr;
 
     /* Current packet's state */
     struct
@@ -40,65 +45,65 @@ struct fs_sink_stream
          * packet (got a packet beginning message, but no
          * packet end message yet).
          */
-        bool is_open;
+        bool is_open = false;
 
         /*
          * Current beginning default clock snapshot for the
          * current packet (`UINT64_C(-1)` if not set).
          */
-        uint64_t beginning_cs;
+        uint64_t beginning_cs = 0;
 
         /*
          * Current end default clock snapshot for the current
          * packet (`UINT64_C(-1)` if not set).
          */
-        uint64_t end_cs;
+        uint64_t end_cs = 0;
 
         /*
          * Current packet's content size (bits) for the current
          * packet.
          */
-        uint64_t content_size;
+        uint64_t content_size = 0;
 
         /*
          * Current packet's total size (bits) for the current
          * packet.
          */
-        uint64_t total_size;
+        uint64_t total_size = 0;
 
         /*
          * Discarded events (free running) counter for the
          * current packet.
          */
-        uint64_t discarded_events_counter;
+        uint64_t discarded_events_counter = 0;
 
         /* Sequence number (free running) of the current packet */
-        uint64_t seq_num;
+        uint64_t seq_num = 0;
 
         /*
          * Offset of the packet context structure within the
          * current packet (bits).
          */
-        uint64_t context_offset_bits;
+        uint64_t context_offset_bits = 0;
 
         /*
          * Owned by this; `NULL` if the current packet is closed
          * or if the trace IR stream does not support packets.
          */
-        const bt_packet *packet;
+        const bt_packet *packet = nullptr;
     } packet_state;
 
     /* Previous packet's state */
     struct
     {
         /* End default clock snapshot (`UINT64_C(-1)` if not set) */
-        uint64_t end_cs;
+        uint64_t end_cs = 0;
 
         /* Discarded events (free running) counter */
-        uint64_t discarded_events_counter;
+        uint64_t discarded_events_counter = 0;
 
         /* Sequence number (free running) */
-        uint64_t seq_num;
+        uint64_t seq_num = 0;
     } prev_packet_state;
 
     /* State to handle discarded events */
@@ -120,14 +125,14 @@ struct fs_sink_stream
          *
          * * Its end time is the current packet's end time.
          */
-        bool in_range;
+        bool in_range = false;
 
         /*
          * Beginning and end times of the time range given by a
          * previously received discarded events message.
          */
-        uint64_t beginning_cs;
-        uint64_t end_cs;
+        uint64_t beginning_cs = 0;
+        uint64_t end_cs = 0;
     } discarded_events_state;
 
     /* State to handle discarded packets */
@@ -150,33 +155,28 @@ struct fs_sink_stream
          * * Its end time is the current packet's beginning
          *   time.
          */
-        bool in_range;
+        bool in_range = false;
 
         /*
          * Beginning and end times of the time range given by a
          * previously received discarded packets message.
          */
-        uint64_t beginning_cs;
-        uint64_t end_cs;
+        uint64_t beginning_cs = 0;
+        uint64_t end_cs = 0;
     } discarded_packets_state;
 };
 
-BT_HIDDEN
 struct fs_sink_stream *fs_sink_stream_create(struct fs_sink_trace *trace,
                                              const bt_stream *ir_stream);
 
-BT_HIDDEN
 void fs_sink_stream_destroy(struct fs_sink_stream *stream);
 
-BT_HIDDEN
 int fs_sink_stream_write_event(struct fs_sink_stream *stream, const bt_clock_snapshot *cs,
                                const bt_event *event, struct fs_sink_ctf_event_class *ec);
 
-BT_HIDDEN
 int fs_sink_stream_open_packet(struct fs_sink_stream *stream, const bt_clock_snapshot *cs,
                                const bt_packet *packet);
 
-BT_HIDDEN
 int fs_sink_stream_close_packet(struct fs_sink_stream *stream, const bt_clock_snapshot *cs);
 
 #endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H */
index e9dddc7b45150798ef92b7b5ac21760a633d8fda..79d4839ab85e637140cd163bd837b7c0d9741625 100644 (file)
@@ -4,23 +4,19 @@
  * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
  */
 
-#define BT_COMP_LOG_SELF_COMP (trace->fs_sink->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (trace->log_level)
-#define BT_LOG_TAG            "PLUGIN/SINK.CTF.FS/TRACE"
-#include "logging/comp-logging.h"
+#include <glib.h>
+#include <stdio.h>
 
 #include <babeltrace2/babeltrace.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <glib.h>
+
 #include "common/assert.h"
-#include "ctfser/ctfser.h"
 
-#include "translate-trace-ir-to-ctf-ir.hpp"
-#include "translate-ctf-ir-to-tsdl.hpp"
-#include "fs-sink.hpp"
-#include "fs-sink-trace.hpp"
+#include "fs-sink-ctf-meta.hpp"
 #include "fs-sink-stream.hpp"
+#include "fs-sink-trace.hpp"
+#include "fs-sink.hpp"
+#include "translate-ctf-ir-to-tsdl.hpp"
+#include "translate-trace-ir-to-ctf-ir.hpp"
 
 /*
  * Sanitizes `path` so as to:
@@ -140,7 +136,8 @@ static int lttng_validate_datetime(const struct fs_sink_trace *trace, const char
      * the format.
      */
     if (!g_time_val_from_iso8601(datetime, &tv)) {
-        BT_COMP_LOGI("Couldn't parse datetime as ISO 8601: date=\"%s\"", datetime);
+        BT_CPPLOGI_SPEC(trace->logger, "Couldn't parse datetime as ISO 8601: date=\"{}\"",
+                        datetime);
         goto end;
     }
 
@@ -160,7 +157,8 @@ static int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace, GS
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id");
     if (!v || !bt_value_is_signed_integer(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_id\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger,
+                            "Couldn't get environment value: name=\"tracer_buffering_id\"");
         goto error;
     }
 
@@ -168,7 +166,8 @@ static int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace, GS
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "architecture_bit_width");
     if (!v || !bt_value_is_signed_integer(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"architecture_bit_width\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger,
+                            "Couldn't get environment value: name=\"architecture_bit_width\"");
         goto error;
     }
 
@@ -194,7 +193,7 @@ static int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace, GS
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "procname");
     if (!v || !bt_value_is_string(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"procname\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger, "Couldn't get environment value: name=\"procname\"");
         goto error;
     }
 
@@ -202,7 +201,7 @@ static int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace, GS
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid");
     if (!v || !bt_value_is_signed_integer(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger, "Couldn't get environment value: name=\"vpid\"");
         goto error;
     }
 
@@ -210,7 +209,8 @@ static int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace, GS
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime");
     if (!v || !bt_value_is_string(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid_datetime\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger,
+                            "Couldn't get environment value: name=\"vpid_datetime\"");
         goto error;
     }
 
@@ -250,20 +250,20 @@ static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "tracer_name");
     if (!v || !bt_value_is_string(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_name\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger, "Couldn't get environment value: name=\"tracer_name\"");
         goto error;
     }
 
     tracer_name = bt_value_string_get(v);
 
     if (!g_str_equal(tracer_name, "lttng-ust") && !g_str_equal(tracer_name, "lttng-modules")) {
-        BT_COMP_LOGI("Unrecognized tracer name: name=\"%s\"", tracer_name);
+        BT_CPPLOGI_SPEC(trace->logger, "Unrecognized tracer name: name=\"{}\"", tracer_name);
         goto error;
     }
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "tracer_major");
     if (!v || !bt_value_is_signed_integer(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_major\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger, "Couldn't get environment value: name=\"tracer_major\"");
         goto error;
     }
 
@@ -271,22 +271,23 @@ static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "tracer_minor");
     if (!v || !bt_value_is_signed_integer(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_minor\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger, "Couldn't get environment value: name=\"tracer_minor\"");
         goto error;
     }
 
     tracer_minor = bt_value_integer_signed_get(v);
 
     if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) {
-        BT_COMP_LOGI("Unsupported LTTng version for automatic trace path: major=%" PRId64
-                     ", minor=%" PRId64,
-                     tracer_major, tracer_minor);
+        BT_CPPLOGI_SPEC(trace->logger,
+                        "Unsupported LTTng version for automatic trace path: major={}, minor={}",
+                        tracer_major, tracer_minor);
         goto error;
     }
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "hostname");
     if (!v || !bt_value_is_string(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_hostname\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger,
+                            "Couldn't get environment value: name=\"tracer_hostname\"");
         goto error;
     }
 
@@ -294,7 +295,7 @@ static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "trace_name");
     if (!v || !bt_value_is_string(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_name\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger, "Couldn't get environment value: name=\"trace_name\"");
         goto error;
     }
 
@@ -303,7 +304,8 @@ static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
     v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace,
                                                               "trace_creation_datetime");
     if (!v || !bt_value_is_string(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_creation_datetime\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger,
+                            "Couldn't get environment value: name=\"trace_creation_datetime\"");
         goto error;
     }
 
@@ -317,7 +319,7 @@ static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
 
     v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "domain");
     if (!v || !bt_value_is_string(v)) {
-        BT_COMP_LOGI_STR("Couldn't get environment value: name=\"domain\"");
+        BT_CPPLOGI_STR_SPEC(trace->logger, "Couldn't get environment value: name=\"domain\"");
         goto error;
     }
 
@@ -330,7 +332,8 @@ static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
         v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace,
                                                                   "tracer_buffering_scheme");
         if (!v || !bt_value_is_string(v)) {
-            BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\"");
+            BT_CPPLOGI_STR_SPEC(trace->logger,
+                                "Couldn't get environment value: name=\"tracer_buffering_scheme\"");
             goto error;
         }
 
@@ -347,13 +350,14 @@ static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
             }
         } else {
             /* Unknown buffering scheme. */
-            BT_COMP_LOGI("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"",
-                         tracer_buffering_scheme);
+            BT_CPPLOGI_SPEC(trace->logger,
+                            "Unknown buffering scheme: tracer_buffering_scheme=\"{}\"",
+                            tracer_buffering_scheme);
             goto error;
         }
     } else if (!g_str_equal(domain, "kernel")) {
         /* Unknown domain. */
-        BT_COMP_LOGI("Unknown domain: domain=\"%s\"", domain);
+        BT_CPPLOGI_SPEC(trace->logger, "Unknown domain: domain=\"{}\"", domain);
         goto error;
     }
 
@@ -454,7 +458,6 @@ end:
     return unique_full_path;
 }
 
-BT_HIDDEN
 void fs_sink_trace_destroy(struct fs_sink_trace *trace)
 {
     GString *tsdl = NULL;
@@ -488,17 +491,19 @@ void fs_sink_trace_destroy(struct fs_sink_trace *trace)
     BT_ASSERT(trace->metadata_path);
     fh = fopen(trace->metadata_path->str, "wb");
     if (!fh) {
-        BT_COMP_LOGF_ERRNO("In trace destruction listener: "
-                           "cannot open metadata file for writing",
-                           ": path=\"%s\"", trace->metadata_path->str);
+        BT_CPPLOGF_ERRNO_SPEC(trace->logger,
+                              "In trace destruction listener: "
+                              "cannot open metadata file for writing",
+                              ": path=\"{}\"", trace->metadata_path->str);
         bt_common_abort();
     }
 
     len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
     if (len != tsdl->len) {
-        BT_COMP_LOGF_ERRNO("In trace destruction listener: "
-                           "cannot write metadata file",
-                           ": path=\"%s\"", trace->metadata_path->str);
+        BT_CPPLOGF_ERRNO_SPEC(trace->logger,
+                              "In trace destruction listener: "
+                              "cannot write metadata file",
+                              ": path=\"{}\"", trace->metadata_path->str);
         bt_common_abort();
     }
 
@@ -515,9 +520,10 @@ void fs_sink_trace_destroy(struct fs_sink_trace *trace)
         int ret = fclose(fh);
 
         if (ret != 0) {
-            BT_COMP_LOGW_ERRNO("In trace destruction listener: "
-                               "cannot close metadata file",
-                               ": path=\"%s\"", trace->metadata_path->str);
+            BT_CPPLOGW_ERRNO_SPEC(trace->logger,
+                                  "In trace destruction listener: "
+                                  "cannot close metadata file",
+                                  ": path=\"{}\"", trace->metadata_path->str);
         }
     }
 
@@ -526,7 +532,7 @@ void fs_sink_trace_destroy(struct fs_sink_trace *trace)
 
     fs_sink_ctf_trace_destroy(trace->trace);
     trace->trace = NULL;
-    g_free(trace);
+    delete trace;
 
     g_string_free(tsdl, TRUE);
 
@@ -547,18 +553,12 @@ static void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
     g_hash_table_remove(trace->fs_sink->traces, ir_trace);
 }
 
-BT_HIDDEN
 struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, const bt_trace *ir_trace)
 {
     int ret;
-    struct fs_sink_trace *trace = g_new0(struct fs_sink_trace, 1);
+    fs_sink_trace *trace = new fs_sink_trace {fs_sink->logger};
     bt_trace_add_listener_status trace_status;
 
-    if (!trace) {
-        goto end;
-    }
-
-    trace->log_level = fs_sink->log_level;
     trace->fs_sink = fs_sink;
     trace->ir_trace = ir_trace;
     trace->ir_trace_destruction_listener_id = UINT64_C(-1);
@@ -571,8 +571,8 @@ struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, const b
     BT_ASSERT(trace->path);
     ret = g_mkdir_with_parents(trace->path->str, 0755);
     if (ret) {
-        BT_COMP_LOGE_ERRNO("Cannot create directories for trace directory", ": path=\"%s\"",
-                           trace->path->str);
+        BT_CPPLOGE_ERRNO_SPEC(trace->logger, "Cannot create directories for trace directory",
+                              ": path=\"{}\"", trace->path->str);
         goto error;
     }
 
index 9fe48d0838704cea81c00e86bf6aef66a1f08355..c9b164367f46158d4fde85f78176754cbad3acbc 100644 (file)
@@ -7,23 +7,27 @@
 #ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H
 #define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H
 
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-#include "ctfser/ctfser.h"
 #include <glib.h>
-#include <stdint.h>
 
-#include "fs-sink-ctf-meta.hpp"
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/logging.hpp"
 
 struct fs_sink_comp;
+struct fs_sink_ctf_trace;
 
 struct fs_sink_trace
 {
-    bt_logging_level log_level;
-    struct fs_sink_comp *fs_sink;
+    explicit fs_sink_trace(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SINK.CTF.FS/TRACE"}
+    {
+    }
+
+    bt2c::Logger logger;
+    fs_sink_comp *fs_sink = nullptr;
 
     /* Owned by this */
-    struct fs_sink_ctf_trace *trace;
+    fs_sink_ctf_trace *trace = nullptr;
 
     /*
      * Weak reference: this object does not own it, and `trace`
@@ -37,27 +41,25 @@ struct fs_sink_trace
      * could "leak" resources (memory, file descriptors) associated
      * to traces and streams which otherwise would not exist.
      */
-    const bt_trace *ir_trace;
+    const bt_trace *ir_trace = nullptr;
 
-    bt_listener_id ir_trace_destruction_listener_id;
+    bt_listener_id ir_trace_destruction_listener_id = 0;
 
     /* Trace's directory */
-    GString *path;
+    GString *path = nullptr;
 
     /* `metadata` file path */
-    GString *metadata_path;
+    GString *metadata_path = nullptr;
 
     /*
      * Hash table of `const bt_stream *` (weak) to
      * `struct fs_sink_stream *` (owned by hash table).
      */
-    GHashTable *streams;
+    GHashTable *streams = nullptr;
 };
 
-BT_HIDDEN
 struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, const bt_trace *ir_trace);
 
-BT_HIDDEN
 void fs_sink_trace_destroy(struct fs_sink_trace *trace);
 
 #endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H */
index 13264660763c2b8dfe2ccb76fbbd1d1cca5a87b4..52d3d426ad33da8e6d9ad21561aee893a7895383 100644 (file)
@@ -4,25 +4,22 @@
  * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
  */
 
-#define BT_COMP_LOG_SELF_COMP (fs_sink->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (fs_sink->log_level)
-#define BT_LOG_TAG            "PLUGIN/SINK.CTF.FS"
-#include "logging/comp-logging.h"
+#include <glib.h>
+#include <stdio.h>
 
 #include <babeltrace2/babeltrace.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <glib.h>
+
 #include "common/assert.h"
+#include "cpp-common/vendor/fmt/format.h"
 #include "ctfser/ctfser.h"
+
 #include "plugins/common/param-validation/param-validation.h"
 
-#include "fs-sink.hpp"
-#include "fs-sink-trace.hpp"
-#include "fs-sink-stream.hpp"
 #include "fs-sink-ctf-meta.hpp"
+#include "fs-sink-stream.hpp"
+#include "fs-sink-trace.hpp"
+#include "fs-sink.hpp"
 #include "translate-trace-ir-to-ctf-ir.hpp"
-#include "translate-ctf-ir-to-tsdl.hpp"
 
 static const char * const in_port_name = "in";
 
@@ -35,9 +32,9 @@ ensure_output_dir_exists(struct fs_sink_comp *fs_sink)
 
     ret = g_mkdir_with_parents(fs_sink->output_dir_path->str, 0755);
     if (ret) {
-        BT_COMP_LOGE_APPEND_CAUSE_ERRNO(fs_sink->self_comp,
-                                        "Cannot create directories for output directory",
-                                        ": output-dir-path=\"%s\"", fs_sink->output_dir_path->str);
+        BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(
+            fs_sink->logger, "Cannot create directories for output directory",
+            ": output-dir-path=\"{}\"", fs_sink->output_dir_path->str);
         status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -71,7 +68,7 @@ static bt_component_class_initialize_method_status configure_component(struct fs
         bt_param_validation_validate(params, fs_sink_params_descr, &validation_error);
     if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
         status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "%s", validation_error);
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "{}", validation_error);
         goto end;
     } else if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
         status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
@@ -125,83 +122,74 @@ static void destroy_fs_sink_comp(struct fs_sink_comp *fs_sink)
     }
 
     BT_MESSAGE_ITERATOR_PUT_REF_AND_RESET(fs_sink->upstream_iter);
-    g_free(fs_sink);
+    delete fs_sink;
 
 end:
     return;
 }
 
-BT_HIDDEN
-bt_component_class_initialize_method_status
-ctf_fs_sink_init(bt_self_component_sink *self_comp_sink,
-                 bt_self_component_sink_configuration *config, const bt_value *params,
-                 void *init_method_data)
+bt_component_class_initialize_method_status ctf_fs_sink_init(bt_self_component_sink *self_comp_sink,
+                                                             bt_self_component_sink_configuration *,
+                                                             const bt_value *params, void *)
 {
-    bt_component_class_initialize_method_status status;
-    bt_self_component_add_port_status add_port_status;
-    struct fs_sink_comp *fs_sink = NULL;
-    bt_self_component *self_comp = bt_self_component_sink_as_self_component(self_comp_sink);
-    bt_logging_level log_level =
-        bt_component_get_logging_level(bt_self_component_as_component(self_comp));
-
-    fs_sink = g_new0(struct fs_sink_comp, 1);
-    if (!fs_sink) {
-        BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                            "Failed to allocate one CTF FS sink structure.");
-        BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
-            self_comp, "Failed to allocate one CTF FS sink structure.");
-        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-        goto end;
-    }
-
-    fs_sink->log_level = log_level;
-    fs_sink->self_comp = self_comp;
-    fs_sink->output_dir_path = g_string_new(NULL);
-    status = configure_component(fs_sink, params);
-    if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
-        /* configure_component() logs errors */
-        goto end;
-    }
+    try {
+        bt_component_class_initialize_method_status status;
+        bt_self_component_add_port_status add_port_status;
+        struct fs_sink_comp *fs_sink = NULL;
+        bt_self_component *self_comp = bt_self_component_sink_as_self_component(self_comp_sink);
+
+        fs_sink = new fs_sink_comp {bt2::SelfSinkComponent {self_comp_sink}};
+        fs_sink->output_dir_path = g_string_new(NULL);
+        status = configure_component(fs_sink, params);
+        if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
+            /* configure_component() logs errors */
+            goto end;
+        }
 
-    if (fs_sink->assume_single_trace &&
-        g_file_test(fs_sink->output_dir_path->str, G_FILE_TEST_EXISTS)) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Single trace mode, but output path exists: output-path=\"%s\"",
-                                  fs_sink->output_dir_path->str);
-        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-        goto end;
-    }
+        if (fs_sink->assume_single_trace &&
+            g_file_test(fs_sink->output_dir_path->str, G_FILE_TEST_EXISTS)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                fs_sink->logger, "Single trace mode, but output path exists: output-path=\"{}\"",
+                fs_sink->output_dir_path->str);
+            status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+            goto end;
+        }
 
-    status = ensure_output_dir_exists(fs_sink);
-    if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
-        /* ensure_output_dir_exists() logs errors */
-        goto end;
-    }
+        status = ensure_output_dir_exists(fs_sink);
+        if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
+            /* ensure_output_dir_exists() logs errors */
+            goto end;
+        }
 
-    fs_sink->traces = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
-                                            (GDestroyNotify) fs_sink_trace_destroy);
-    if (!fs_sink->traces) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate one GHashTable.");
-        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-        goto end;
-    }
+        fs_sink->traces = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+                                                (GDestroyNotify) fs_sink_trace_destroy);
+        if (!fs_sink->traces) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to allocate one GHashTable.");
+            status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+            goto end;
+        }
 
-    add_port_status =
-        bt_self_component_sink_add_input_port(self_comp_sink, in_port_name, NULL, NULL);
-    if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
-        status = (bt_component_class_initialize_method_status) add_port_status;
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to add input port.");
-        goto end;
-    }
+        add_port_status =
+            bt_self_component_sink_add_input_port(self_comp_sink, in_port_name, NULL, NULL);
+        if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
+            status = (bt_component_class_initialize_method_status) add_port_status;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to add input port.");
+            goto end;
+        }
 
-    bt_self_component_set_data(self_comp, fs_sink);
+        bt_self_component_set_data(self_comp, fs_sink);
 
 end:
-    if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
-        destroy_fs_sink_comp(fs_sink);
-    }
+        if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
+            destroy_fs_sink_comp(fs_sink);
+        }
 
-    return status;
+        return status;
+    } catch (const std::bad_alloc&) {
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+    }
 }
 
 static inline struct fs_sink_stream *borrow_stream(struct fs_sink_comp *fs_sink,
@@ -214,10 +202,10 @@ static inline struct fs_sink_stream *borrow_stream(struct fs_sink_comp *fs_sink,
     trace = (fs_sink_trace *) g_hash_table_lookup(fs_sink->traces, ir_trace);
     if (G_UNLIKELY(!trace)) {
         if (fs_sink->assume_single_trace && g_hash_table_size(fs_sink->traces) > 0) {
-            BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                      "Single trace mode, but getting more than one trace: "
-                                      "stream-name=\"%s\"",
-                                      bt_stream_get_name(ir_stream));
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger,
+                                         "Single trace mode, but getting more than one trace: "
+                                         "stream-name=\"{}\"",
+                                         bt2c::maybeNull(bt_stream_get_name(ir_stream)));
             goto end;
         }
 
@@ -242,37 +230,39 @@ end:
 static inline bt_component_class_sink_consume_method_status
 handle_event_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
 {
-    int ret;
-    bt_component_class_sink_consume_method_status status =
-        BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
-    const bt_event *ir_event = bt_message_event_borrow_event_const(msg);
-    const bt_stream *ir_stream = bt_event_borrow_stream_const(ir_event);
-    struct fs_sink_stream *stream;
-    struct fs_sink_ctf_event_class *ec = NULL;
-    const bt_clock_snapshot *cs = NULL;
-
-    stream = borrow_stream(fs_sink, ir_stream);
-    if (G_UNLIKELY(!stream)) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to borrow stream.");
-        status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-        goto end;
-    }
+    try {
+        int ret;
+        bt_component_class_sink_consume_method_status status =
+            BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+        const bt_event *ir_event = bt_message_event_borrow_event_const(msg);
+        const bt_stream *ir_stream = bt_event_borrow_stream_const(ir_event);
+        struct fs_sink_stream *stream;
+        struct fs_sink_ctf_event_class *ec = NULL;
+        const bt_clock_snapshot *cs = NULL;
+
+        stream = borrow_stream(fs_sink, ir_stream);
+        if (G_UNLIKELY(!stream)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to borrow stream.");
+            status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+            goto end;
+        }
 
-    ret = try_translate_event_class_trace_ir_to_ctf_ir(fs_sink, stream->sc,
-                                                       bt_event_borrow_class_const(ir_event), &ec);
-    if (ret) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to translate event class to CTF IR.");
-        status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-        goto end;
-    }
+        ret = try_translate_event_class_trace_ir_to_ctf_ir(
+            fs_sink, stream->sc, bt_event_borrow_class_const(ir_event), &ec);
+        if (ret) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger,
+                                         "Failed to translate event class to CTF IR.");
+            status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+            goto end;
+        }
 
-    BT_ASSERT_DBG(ec);
+        BT_ASSERT_DBG(ec);
 
-    if (stream->sc->default_clock_class) {
-        cs = bt_message_event_borrow_default_clock_snapshot_const(msg);
-    }
+        if (stream->sc->default_clock_class) {
+            cs = bt_message_event_borrow_default_clock_snapshot_const(msg);
+        }
 
-    /*
+        /*
      * If this event's stream does not support packets, then we
      * lazily create artificial packets.
      *
@@ -281,43 +271,49 @@ handle_event_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
      * comes the time to write a new event and the packet's content
      * size is >= 4 MiB), except the last one which can be smaller.
      */
-    if (G_UNLIKELY(!stream->sc->has_packets)) {
-        if (stream->packet_state.is_open &&
-            bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser) / 8 >= 4 * 1024 * 1024) {
-            /*
+        if (G_UNLIKELY(!stream->sc->has_packets)) {
+            if (stream->packet_state.is_open &&
+                bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser) / 8 >=
+                    4 * 1024 * 1024) {
+                /*
              * Stream's current packet is larger than 4 MiB:
              * close it. A new packet will be opened just
              * below.
              */
-            ret = fs_sink_stream_close_packet(stream, NULL);
-            if (ret) {
-                BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to close packet.");
-                status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                goto end;
+                ret = fs_sink_stream_close_packet(stream, NULL);
+                if (ret) {
+                    BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to close packet.");
+                    status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                    goto end;
+                }
             }
-        }
 
-        if (!stream->packet_state.is_open) {
-            /* Stream's packet is not currently opened: open it */
-            ret = fs_sink_stream_open_packet(stream, NULL, NULL);
-            if (ret) {
-                BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to open packet.");
-                status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                goto end;
+            if (!stream->packet_state.is_open) {
+                /* Stream's packet is not currently opened: open it */
+                ret = fs_sink_stream_open_packet(stream, NULL, NULL);
+                if (ret) {
+                    BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to open packet.");
+                    status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                    goto end;
+                }
             }
         }
-    }
 
-    BT_ASSERT_DBG(stream->packet_state.is_open);
-    ret = fs_sink_stream_write_event(stream, cs, ir_event, ec);
-    if (G_UNLIKELY(ret)) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to write event.");
-        status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-        goto end;
-    }
+        BT_ASSERT_DBG(stream->packet_state.is_open);
+        ret = fs_sink_stream_write_event(stream, cs, ir_event, ec);
+        if (G_UNLIKELY(ret)) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to write event.");
+            status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+            goto end;
+        }
 
 end:
-    return status;
+        return status;
+    } catch (const std::bad_alloc&) {
+        return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+    }
 }
 
 static inline bt_component_class_sink_consume_method_status
@@ -333,7 +329,7 @@ handle_packet_beginning_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
 
     stream = borrow_stream(fs_sink, ir_stream);
     if (G_UNLIKELY(!stream)) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to borrow stream.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to borrow stream.");
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -383,17 +379,18 @@ handle_packet_beginning_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
         }
 
         if (stream->discarded_events_state.beginning_cs != expected_cs) {
-            BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                      "Incompatible discarded events message: "
-                                      "unexpected beginning time: "
-                                      "beginning-cs-val=%" PRIu64 ", "
-                                      "expected-beginning-cs-val=%" PRIu64 ", "
-                                      "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                                      "trace-name=\"%s\", path=\"%s/%s\"",
-                                      stream->discarded_events_state.beginning_cs, expected_cs,
-                                      bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                                      bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                                      stream->trace->path->str, stream->file_name->str);
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                fs_sink->logger,
+                "Incompatible discarded events message: "
+                "unexpected beginning time: "
+                "beginning-cs-val={}, "
+                "expected-beginning-cs-val={}, "
+                "stream-id={}, stream-name=\"{}\", "
+                "trace-name=\"{}\", path=\"{}/{}\"",
+                stream->discarded_events_state.beginning_cs, expected_cs,
+                bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+                bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+                stream->trace->path->str, stream->file_name->str);
             status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
             goto end;
         }
@@ -431,31 +428,32 @@ handle_packet_beginning_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
          * this case.
          */
         if (stream->prev_packet_state.end_cs == UINT64_C(-1)) {
-            BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                      "Incompatible discarded packets message "
-                                      "occurring before the stream's first packet: "
-                                      "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                                      "trace-name=\"%s\", path=\"%s/%s\"",
-                                      bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                                      bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                                      stream->trace->path->str, stream->file_name->str);
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                fs_sink->logger,
+                "Incompatible discarded packets message "
+                "occurring before the stream's first packet: "
+                "stream-id={}, stream-name=\"{}\", "
+                "trace-name=\"{}\", path=\"{}/{}\"",
+                bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+                bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+                stream->trace->path->str, stream->file_name->str);
             status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
             goto end;
         }
 
         if (stream->discarded_packets_state.beginning_cs != stream->prev_packet_state.end_cs) {
-            BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                      "Incompatible discarded packets message: "
-                                      "unexpected beginning time: "
-                                      "beginning-cs-val=%" PRIu64 ", "
-                                      "expected-beginning-cs-val=%" PRIu64 ", "
-                                      "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                                      "trace-name=\"%s\", path=\"%s/%s\"",
-                                      stream->discarded_packets_state.beginning_cs,
-                                      stream->prev_packet_state.end_cs, bt_stream_get_id(ir_stream),
-                                      bt_stream_get_name(ir_stream),
-                                      bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                                      stream->trace->path->str, stream->file_name->str);
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                fs_sink->logger,
+                "Incompatible discarded packets message: "
+                "unexpected beginning time: "
+                "beginning-cs-val={}, "
+                "expected-beginning-cs-val={}, "
+                "stream-id={}, stream-name=\"{}\", "
+                "trace-name=\"{}\", path=\"{}/{}\"",
+                stream->discarded_packets_state.beginning_cs, stream->prev_packet_state.end_cs,
+                bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+                bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+                stream->trace->path->str, stream->file_name->str);
             status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
             goto end;
         }
@@ -463,17 +461,18 @@ handle_packet_beginning_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
         expected_end_cs = bt_clock_snapshot_get_value(cs);
 
         if (stream->discarded_packets_state.end_cs != expected_end_cs) {
-            BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                      "Incompatible discarded packets message: "
-                                      "unexpected end time: "
-                                      "end-cs-val=%" PRIu64 ", "
-                                      "expected-end-cs-val=%" PRIu64 ", "
-                                      "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                                      "trace-name=\"%s\", path=\"%s/%s\"",
-                                      stream->discarded_packets_state.end_cs, expected_end_cs,
-                                      bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                                      bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                                      stream->trace->path->str, stream->file_name->str);
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                fs_sink->logger,
+                "Incompatible discarded packets message: "
+                "unexpected end time: "
+                "end-cs-val={}, "
+                "expected-end-cs-val={}, "
+                "stream-id={}, stream-name=\"{}\", "
+                "trace-name=\"{}\", path=\"{}/{}\"",
+                stream->discarded_packets_state.end_cs, expected_end_cs,
+                bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+                bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+                stream->trace->path->str, stream->file_name->str);
             status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
             goto end;
         }
@@ -489,7 +488,7 @@ handle_packet_beginning_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
 
     ret = fs_sink_stream_open_packet(stream, cs, ir_packet);
     if (ret) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to open packet.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to open packet.");
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -511,7 +510,7 @@ handle_packet_end_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
 
     stream = borrow_stream(fs_sink, ir_stream);
     if (G_UNLIKELY(!stream)) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to borrow stream.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to borrow stream.");
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -550,17 +549,18 @@ handle_packet_end_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
         expected_cs = bt_clock_snapshot_get_value(cs);
 
         if (stream->discarded_events_state.end_cs != expected_cs) {
-            BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                      "Incompatible discarded events message: "
-                                      "unexpected end time: "
-                                      "end-cs-val=%" PRIu64 ", "
-                                      "expected-end-cs-val=%" PRIu64 ", "
-                                      "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                                      "trace-name=\"%s\", path=\"%s/%s\"",
-                                      stream->discarded_events_state.end_cs, expected_cs,
-                                      bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                                      bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                                      stream->trace->path->str, stream->file_name->str);
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                fs_sink->logger,
+                "Incompatible discarded events message: "
+                "unexpected end time: "
+                "end-cs-val={}, "
+                "expected-end-cs-val={}, "
+                "stream-id={}, stream-name=\"{}\", "
+                "trace-name=\"{}\", path=\"{}/{}\"",
+                stream->discarded_events_state.end_cs, expected_cs, bt_stream_get_id(ir_stream),
+                bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+                bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+                stream->trace->path->str, stream->file_name->str);
             status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
             goto end;
         }
@@ -568,7 +568,7 @@ handle_packet_end_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
 
     ret = fs_sink_stream_close_packet(stream, cs);
     if (ret) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to close packet.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to close packet.");
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -606,15 +606,15 @@ handle_stream_beginning_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
         BT_ASSERT(!bt_stream_class_supports_discarded_packets(ir_sc));
 
         if (!fs_sink->ignore_discarded_events && bt_stream_class_supports_discarded_events(ir_sc)) {
-            BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                      "Unsupported stream: "
-                                      "stream does not support packets, "
-                                      "but supports discarded events: "
-                                      "stream-addr=%p, "
-                                      "stream-id=%" PRIu64 ", "
-                                      "stream-name=\"%s\"",
-                                      ir_stream, bt_stream_get_id(ir_stream),
-                                      bt_stream_get_name(ir_stream));
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger,
+                                         "Unsupported stream: "
+                                         "stream does not support packets, "
+                                         "but supports discarded events: "
+                                         "stream-addr={}, "
+                                         "stream-id={}, "
+                                         "stream-name=\"{}\"",
+                                         fmt::ptr(ir_stream), bt_stream_get_id(ir_stream),
+                                         bt2c::maybeNull(bt_stream_get_name(ir_stream)));
             status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
             goto end;
         }
@@ -627,15 +627,15 @@ handle_stream_beginning_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
     if (!fs_sink->ignore_discarded_events &&
         bt_stream_class_discarded_events_have_default_clock_snapshots(ir_sc) &&
         !packets_have_beginning_end_cs) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                  "Unsupported stream: discarded events have "
-                                  "default clock snapshots, but packets have no "
-                                  "beginning and/or end default clock snapshots: "
-                                  "stream-addr=%p, "
-                                  "stream-id=%" PRIu64 ", "
-                                  "stream-name=\"%s\"",
-                                  ir_stream, bt_stream_get_id(ir_stream),
-                                  bt_stream_get_name(ir_stream));
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger,
+                                     "Unsupported stream: discarded events have "
+                                     "default clock snapshots, but packets have no "
+                                     "beginning and/or end default clock snapshots: "
+                                     "stream-addr={}, "
+                                     "stream-id={}, "
+                                     "stream-name=\"{}\"",
+                                     fmt::ptr(ir_stream), bt_stream_get_id(ir_stream),
+                                     bt2c::maybeNull(bt_stream_get_name(ir_stream)));
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -648,32 +648,33 @@ handle_stream_beginning_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
     if (!fs_sink->ignore_discarded_packets &&
         bt_stream_class_discarded_packets_have_default_clock_snapshots(ir_sc) &&
         !packets_have_beginning_end_cs) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                  "Unsupported stream: discarded packets have "
-                                  "default clock snapshots, but packets have no "
-                                  "beginning and/or end default clock snapshots: "
-                                  "stream-addr=%p, "
-                                  "stream-id=%" PRIu64 ", "
-                                  "stream-name=\"%s\"",
-                                  ir_stream, bt_stream_get_id(ir_stream),
-                                  bt_stream_get_name(ir_stream));
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger,
+                                     "Unsupported stream: discarded packets have "
+                                     "default clock snapshots, but packets have no "
+                                     "beginning and/or end default clock snapshots: "
+                                     "stream-addr={}, "
+                                     "stream-id={}, "
+                                     "stream-name=\"{}\"",
+                                     fmt::ptr(ir_stream), bt_stream_get_id(ir_stream),
+                                     bt2c::maybeNull(bt_stream_get_name(ir_stream)));
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
 
     stream = borrow_stream(fs_sink, ir_stream);
     if (!stream) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to borrow stream.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to borrow stream.");
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
 
-    BT_COMP_LOGI("Created new, empty stream file: "
-                 "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                 "trace-name=\"%s\", path=\"%s/%s\"",
-                 bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                 bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                 stream->trace->path->str, stream->file_name->str);
+    BT_CPPLOGI_SPEC(fs_sink->logger,
+                    "Created new, empty stream file: "
+                    "stream-id={}, stream-name=\"{}\", "
+                    "trace-name=\"{}\", path=\"{}/{}\"",
+                    bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+                    bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+                    stream->trace->path->str, stream->file_name->str);
 
 end:
     return status;
@@ -689,7 +690,7 @@ handle_stream_end_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
 
     stream = borrow_stream(fs_sink, ir_stream);
     if (!stream) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to borrow stream.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to borrow stream.");
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -699,18 +700,19 @@ handle_stream_end_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
         int ret = fs_sink_stream_close_packet(stream, NULL);
 
         if (ret) {
-            BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to close packet.");
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to close packet.");
             status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
             goto end;
         }
     }
 
-    BT_COMP_LOGI("Closing stream file: "
-                 "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                 "trace-name=\"%s\", path=\"%s/%s\"",
-                 bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                 bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                 stream->trace->path->str, stream->file_name->str);
+    BT_CPPLOGI_SPEC(fs_sink->logger,
+                    "Closing stream file: "
+                    "stream-id={}, stream-name=\"{}\", "
+                    "trace-name=\"{}\", path=\"{}/{}\"",
+                    bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+                    bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+                    stream->trace->path->str, stream->file_name->str);
 
     /*
      * This destroys the stream object and frees all its resources,
@@ -735,29 +737,31 @@ handle_discarded_events_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
 
     stream = borrow_stream(fs_sink, ir_stream);
     if (!stream) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to borrow stream.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to borrow stream.");
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
 
     if (fs_sink->ignore_discarded_events) {
-        BT_COMP_LOGI("Ignoring discarded events message: "
-                     "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                     "trace-name=\"%s\", path=\"%s/%s\"",
-                     bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                     bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                     stream->trace->path->str, stream->file_name->str);
+        BT_CPPLOGI_SPEC(fs_sink->logger,
+                        "Ignoring discarded events message: "
+                        "stream-id={}, stream-name=\"{}\", "
+                        "trace-name=\"{}\", path=\"{}/{}\"",
+                        bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+                        bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+                        stream->trace->path->str, stream->file_name->str);
         goto end;
     }
 
     if (stream->discarded_events_state.in_range) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                  "Unsupported contiguous discarded events message: "
-                                  "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                                  "trace-name=\"%s\", path=\"%s/%s\"",
-                                  bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                                  bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                                  stream->trace->path->str, stream->file_name->str);
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            fs_sink->logger,
+            "Unsupported contiguous discarded events message: "
+            "stream-id={}, stream-name=\"{}\", "
+            "trace-name=\"{}\", path=\"{}/{}\"",
+            bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+            bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+            stream->trace->path->str, stream->file_name->str);
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -771,14 +775,15 @@ handle_discarded_events_msg(struct fs_sink_comp *fs_sink, const bt_message *msg)
      * time.
      */
     if (stream->packet_state.is_open && stream->sc->discarded_events_has_ts) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                  "Unsupported discarded events message with "
-                                  "default clock snapshots occurring within a packet: "
-                                  "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                                  "trace-name=\"%s\", path=\"%s/%s\"",
-                                  bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                                  bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                                  stream->trace->path->str, stream->file_name->str);
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            fs_sink->logger,
+            "Unsupported discarded events message with "
+            "default clock snapshots occurring within a packet: "
+            "stream-id={}, stream-name=\"{}\", "
+            "trace-name=\"{}\", path=\"{}/{}\"",
+            bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+            bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+            stream->trace->path->str, stream->file_name->str);
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -834,29 +839,31 @@ handle_discarded_packets_msg(struct fs_sink_comp *fs_sink, const bt_message *msg
 
     stream = borrow_stream(fs_sink, ir_stream);
     if (!stream) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to borrow stream.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to borrow stream.");
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
 
     if (fs_sink->ignore_discarded_packets) {
-        BT_COMP_LOGI("Ignoring discarded packets message: "
-                     "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                     "trace-name=\"%s\", path=\"%s/%s\"",
-                     bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                     bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                     stream->trace->path->str, stream->file_name->str);
+        BT_CPPLOGI_SPEC(fs_sink->logger,
+                        "Ignoring discarded packets message: "
+                        "stream-id={}, stream-name=\"{}\", "
+                        "trace-name=\"{}\", path=\"{}/{}\"",
+                        bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+                        bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+                        stream->trace->path->str, stream->file_name->str);
         goto end;
     }
 
     if (stream->discarded_packets_state.in_range) {
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                  "Unsupported contiguous discarded packets message: "
-                                  "stream-id=%" PRIu64 ", stream-name=\"%s\", "
-                                  "trace-name=\"%s\", path=\"%s/%s\"",
-                                  bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
-                                  bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
-                                  stream->trace->path->str, stream->file_name->str);
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            fs_sink->logger,
+            "Unsupported contiguous discarded packets message: "
+            "stream-id={}, stream-name=\"{}\", "
+            "trace-name=\"{}\", path=\"{}/{}\"",
+            bt_stream_get_id(ir_stream), bt2c::maybeNull(bt_stream_get_name(ir_stream)),
+            bt2c::maybeNull(bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream))),
+            stream->trace->path->str, stream->file_name->str);
         status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
         goto end;
     }
@@ -913,7 +920,6 @@ static inline void put_messages(bt_message_array_const msgs, uint64_t count)
     }
 }
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status ctf_fs_sink_consume(bt_self_component_sink *self_comp)
 {
     bt_component_class_sink_consume_method_status status =
@@ -932,8 +938,8 @@ bt_component_class_sink_consume_method_status ctf_fs_sink_consume(bt_self_compon
     next_status = bt_message_iterator_next(fs_sink->upstream_iter, &msgs, &msg_count);
     if (next_status < 0) {
         status = (bt_component_class_sink_consume_method_status) next_status;
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                  "Failed to get next message from upstream iterator.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger,
+                                     "Failed to get next message from upstream iterator.");
         goto end;
     }
 
@@ -959,7 +965,8 @@ bt_component_class_sink_consume_method_status ctf_fs_sink_consume(bt_self_compon
                 break;
             case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
                 /* Ignore */
-                BT_COMP_LOGD_STR("Ignoring message iterator inactivity message.");
+                BT_CPPLOGD_STR_SPEC(fs_sink->logger,
+                                    "Ignoring message iterator inactivity message.");
                 break;
             case BT_MESSAGE_TYPE_STREAM_BEGINNING:
                 status = handle_stream_beginning_msg(fs_sink, msg);
@@ -980,11 +987,11 @@ bt_component_class_sink_consume_method_status ctf_fs_sink_consume(bt_self_compon
             BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]);
 
             if (status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK) {
-                BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                          "Failed to handle message: "
-                                          "generated CTF traces could be incomplete: "
-                                          "output-dir-path=\"%s\"",
-                                          fs_sink->output_dir_path->str);
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger,
+                                             "Failed to handle message: "
+                                             "generated CTF traces could be incomplete: "
+                                             "output-dir-path=\"{}\"",
+                                             fs_sink->output_dir_path->str);
                 goto error;
             }
         }
@@ -1012,30 +1019,34 @@ end:
     return status;
 }
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status
 ctf_fs_sink_graph_is_configured(bt_self_component_sink *self_comp)
 {
-    bt_component_class_sink_graph_is_configured_method_status status;
-    bt_message_iterator_create_from_sink_component_status msg_iter_status;
-    fs_sink_comp *fs_sink = (fs_sink_comp *) bt_self_component_get_data(
-        bt_self_component_sink_as_self_component(self_comp));
-
-    msg_iter_status = bt_message_iterator_create_from_sink_component(
-        self_comp, bt_self_component_sink_borrow_input_port_by_name(self_comp, in_port_name),
-        &fs_sink->upstream_iter);
-    if (msg_iter_status != BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK) {
-        status = (bt_component_class_sink_graph_is_configured_method_status) msg_iter_status;
-        BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp, "Failed to create upstream iterator.");
-        goto end;
-    }
+    try {
+        bt_component_class_sink_graph_is_configured_method_status status;
+        bt_message_iterator_create_from_sink_component_status msg_iter_status;
+        fs_sink_comp *fs_sink = (fs_sink_comp *) bt_self_component_get_data(
+            bt_self_component_sink_as_self_component(self_comp));
+
+        msg_iter_status = bt_message_iterator_create_from_sink_component(
+            self_comp, bt_self_component_sink_borrow_input_port_by_name(self_comp, in_port_name),
+            &fs_sink->upstream_iter);
+        if (msg_iter_status != BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK) {
+            status = (bt_component_class_sink_graph_is_configured_method_status) msg_iter_status;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(fs_sink->logger, "Failed to create upstream iterator.");
+            goto end;
+        }
 
-    status = BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK;
+        status = BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK;
 end:
-    return status;
+        return status;
+    } catch (const std::bad_alloc&) {
+        return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2c::Error&) {
+        return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_ERROR;
+    }
 }
 
-BT_HIDDEN
 void ctf_fs_sink_finalize(bt_self_component_sink *self_comp)
 {
     fs_sink_comp *fs_sink = (fs_sink_comp *) bt_self_component_get_data(
index d026cb291c899d14aecbc4c85d486c8afc1716ab..529ae346027a4b0a3f544a6864077668d5777655 100644 (file)
@@ -7,21 +7,26 @@
 #ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H
 #define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H
 
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-#include <stdbool.h>
 #include <glib.h>
 
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/logging.hpp"
+
 struct fs_sink_comp
 {
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
+    explicit fs_sink_comp(const bt2::SelfSinkComponent selfSinkComp) :
+        logger {selfSinkComp, "PLUGIN/SINK.CTF.FS/COMP"}
+    {
+    }
+
+    bt2c::Logger logger;
 
     /* Owned by this */
-    bt_message_iterator *upstream_iter;
+    bt_message_iterator *upstream_iter = nullptr;
 
     /* Base output directory path */
-    GString *output_dir_path;
+    GString *output_dir_path = nullptr;
 
     /*
      * True if the component assumes that it will only write a
@@ -29,41 +34,37 @@ struct fs_sink_comp
      * streams). This makes the component write the stream files
      * directly in the output directory (`output_dir_path` above).
      */
-    bool assume_single_trace;
+    bool assume_single_trace = false;
 
     /* True to completely ignore discarded events messages */
-    bool ignore_discarded_events;
+    bool ignore_discarded_events = false;
 
     /* True to completely ignore discarded packets messages */
-    bool ignore_discarded_packets;
+    bool ignore_discarded_packets = false;
 
     /*
      * True to make the component quiet (nothing printed to the
      * standard output).
      */
-    bool quiet;
+    bool quiet = false;
 
     /*
      * Hash table of `const bt_trace *` (weak) to
      * `struct fs_sink_trace *` (owned by hash table).
      */
-    GHashTable *traces;
+    GHashTable *traces = nullptr;
 };
 
-BT_HIDDEN
 bt_component_class_initialize_method_status
 ctf_fs_sink_init(bt_self_component_sink *component, bt_self_component_sink_configuration *config,
                  const bt_value *params, void *init_method_data);
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status
 ctf_fs_sink_consume(bt_self_component_sink *component);
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status
 ctf_fs_sink_graph_is_configured(bt_self_component_sink *component);
 
-BT_HIDDEN
 void ctf_fs_sink_finalize(bt_self_component_sink *component);
 
 #endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H */
index 1d673820247bce466162be0989ea847faae34b88..c45a16857a07dbc93045d71000ef78bef3681836 100644 (file)
@@ -4,26 +4,30 @@
  * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
  */
 
-#include "translate-ctf-ir-to-tsdl.hpp"
+#include <glib.h>
+#include <stdio.h>
 
 #include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <glib.h>
+
 #include "common/assert.h"
-#include "compat/endian.h"
+#include "compat/endian.h" /* IWYU pragma: keep  */
 
 #include "fs-sink-ctf-meta.hpp"
+#include "translate-ctf-ir-to-tsdl.hpp"
 
-struct ctx
+namespace ctf {
+namespace sink {
+
+struct CtfIrToTsdlCtx
 {
     unsigned int indent_level;
     GString *tsdl;
 };
 
-static inline void append_indent(struct ctx *ctx)
+} /* namespace sink */
+} /* namespace ctf */
+
+static inline void append_indent(ctf::sink::CtfIrToTsdlCtx *ctx)
 {
     unsigned int i;
 
@@ -32,12 +36,12 @@ static inline void append_indent(struct ctx *ctx)
     }
 }
 
-static void append_uuid(struct ctx *ctx, bt_uuid uuid)
+static void append_uuid(ctf::sink::CtfIrToTsdlCtx *ctx, bt_uuid uuid)
 {
     g_string_append_printf(ctx->tsdl, "\"" BT_UUID_FMT "\"", BT_UUID_FMT_VALUES(uuid));
 }
 
-static void append_quoted_string_content(struct ctx *ctx, const char *str)
+static void append_quoted_string_content(ctf::sink::CtfIrToTsdlCtx *ctx, const char *str)
 {
     const char *ch;
 
@@ -80,7 +84,7 @@ static void append_quoted_string_content(struct ctx *ctx, const char *str)
     }
 }
 
-static void append_quoted_string(struct ctx *ctx, const char *str)
+static void append_quoted_string(ctf::sink::CtfIrToTsdlCtx *ctx, const char *str)
 {
     g_string_append_c(ctx->tsdl, '"');
     append_quoted_string_content(ctx, str);
@@ -88,7 +92,7 @@ static void append_quoted_string(struct ctx *ctx, const char *str)
 }
 
 static void append_integer_field_class_from_props(
-    struct ctx *ctx, unsigned int size, unsigned int alignment, bool is_signed,
+    ctf::sink::CtfIrToTsdlCtx *ctx, unsigned int size, unsigned int alignment, bool is_signed,
     bt_field_class_integer_preferred_display_base disp_base, const char *mapped_clock_class_name,
     const char *field_name, bool end)
 {
@@ -133,27 +137,27 @@ static void append_integer_field_class_from_props(
     }
 }
 
-static void append_end_block(struct ctx *ctx)
+static void append_end_block(ctf::sink::CtfIrToTsdlCtx *ctx)
 {
     ctx->indent_level--;
     append_indent(ctx);
     g_string_append(ctx->tsdl, "}");
 }
 
-static void append_end_block_semi_nl(struct ctx *ctx)
+static void append_end_block_semi_nl(ctf::sink::CtfIrToTsdlCtx *ctx)
 {
     ctx->indent_level--;
     append_indent(ctx);
     g_string_append(ctx->tsdl, "};\n");
 }
 
-static void append_end_block_semi_nl_nl(struct ctx *ctx)
+static void append_end_block_semi_nl_nl(ctf::sink::CtfIrToTsdlCtx *ctx)
 {
     append_end_block_semi_nl(ctx);
     g_string_append_c(ctx->tsdl, '\n');
 }
 
-static void append_bool_field_class(struct ctx *ctx,
+static void append_bool_field_class(ctf::sink::CtfIrToTsdlCtx *ctx,
                                     __attribute__((unused)) struct fs_sink_ctf_field_class_bool *fc)
 {
     /*
@@ -165,7 +169,7 @@ static void append_bool_field_class(struct ctx *ctx,
                                           NULL, NULL, false);
 }
 
-static void append_bit_array_field_class(struct ctx *ctx,
+static void append_bit_array_field_class(ctf::sink::CtfIrToTsdlCtx *ctx,
                                          struct fs_sink_ctf_field_class_bit_array *fc)
 {
     /*
@@ -178,7 +182,8 @@ static void append_bit_array_field_class(struct ctx *ctx,
                                           NULL, NULL, false);
 }
 
-static void append_integer_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class_int *fc)
+static void append_integer_field_class(ctf::sink::CtfIrToTsdlCtx *ctx,
+                                       struct fs_sink_ctf_field_class_int *fc)
 {
     const bt_field_class *ir_fc = fc->base.base.ir_fc;
     bt_field_class_type type = bt_field_class_get_type(ir_fc);
@@ -271,7 +276,8 @@ static void append_integer_field_class(struct ctx *ctx, struct fs_sink_ctf_field
     }
 }
 
-static void append_float_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class_float *fc)
+static void append_float_field_class(ctf::sink::CtfIrToTsdlCtx *ctx,
+                                     struct fs_sink_ctf_field_class_float *fc)
 {
     unsigned int mant_dig, exp_dig;
 
@@ -287,14 +293,15 @@ static void append_float_field_class(struct ctx *ctx, struct fs_sink_ctf_field_c
                            mant_dig, exp_dig, fc->base.base.alignment);
 }
 
-static void append_string_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class_string *fc)
+static void append_string_field_class(ctf::sink::CtfIrToTsdlCtx *ctx)
 {
     g_string_append(ctx->tsdl, "string { encoding = UTF8; }");
 }
 
-static void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *fc);
+static void append_field_class(ctf::sink::CtfIrToTsdlCtx *ctx, struct fs_sink_ctf_field_class *fc);
 
-static void append_member(struct ctx *ctx, const char *name, struct fs_sink_ctf_field_class *fc)
+static void append_member(ctf::sink::CtfIrToTsdlCtx *ctx, const char *name,
+                          struct fs_sink_ctf_field_class *fc)
 {
     GString *lengths = NULL;
     const char *lengths_str = "";
@@ -335,7 +342,7 @@ static void append_member(struct ctx *ctx, const char *name, struct fs_sink_ctf_
     }
 }
 
-static void append_struct_field_class_members(struct ctx *ctx,
+static void append_struct_field_class_members(ctf::sink::CtfIrToTsdlCtx *ctx,
                                               struct fs_sink_ctf_field_class_struct *struct_fc)
 {
     uint64_t i;
@@ -433,7 +440,8 @@ static void append_struct_field_class_members(struct ctx *ctx,
     }
 }
 
-static void append_struct_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class_struct *fc)
+static void append_struct_field_class(ctf::sink::CtfIrToTsdlCtx *ctx,
+                                      struct fs_sink_ctf_field_class_struct *fc)
 {
     g_string_append(ctx->tsdl, "struct {\n");
     ctx->indent_level++;
@@ -442,7 +450,7 @@ static void append_struct_field_class(struct ctx *ctx, struct fs_sink_ctf_field_
     g_string_append_printf(ctx->tsdl, " align(%u)", fc->base.alignment);
 }
 
-static void append_option_field_class(struct ctx *ctx,
+static void append_option_field_class(ctf::sink::CtfIrToTsdlCtx *ctx,
                                       struct fs_sink_ctf_field_class_option *opt_fc)
 {
     g_string_append_printf(ctx->tsdl, "variant <%s> {\n", opt_fc->tag_ref->str);
@@ -454,7 +462,7 @@ static void append_option_field_class(struct ctx *ctx,
     append_end_block(ctx);
 }
 
-static void append_variant_field_class(struct ctx *ctx,
+static void append_variant_field_class(ctf::sink::CtfIrToTsdlCtx *ctx,
                                        struct fs_sink_ctf_field_class_variant *var_fc)
 {
     uint64_t i;
@@ -473,7 +481,7 @@ static void append_variant_field_class(struct ctx *ctx,
     append_end_block(ctx);
 }
 
-static void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *fc)
+static void append_field_class(ctf::sink::CtfIrToTsdlCtx *ctx, struct fs_sink_ctf_field_class *fc)
 {
     switch (fc->type) {
     case FS_SINK_CTF_FIELD_CLASS_TYPE_BOOL:
@@ -489,7 +497,7 @@ static void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *
         append_float_field_class(ctx, fs_sink_ctf_field_class_as_float(fc));
         break;
     case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING:
-        append_string_field_class(ctx, fs_sink_ctf_field_class_as_string(fc));
+        append_string_field_class(ctx);
         break;
     case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
         append_struct_field_class(ctx, fs_sink_ctf_field_class_as_struct(fc));
@@ -505,7 +513,7 @@ static void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *
     }
 }
 
-static void append_event_class(struct ctx *ctx, struct fs_sink_ctf_event_class *ec)
+static void append_event_class(ctf::sink::CtfIrToTsdlCtx *ctx, struct fs_sink_ctf_event_class *ec)
 {
     const char *str;
     bt_event_class_log_level log_level;
@@ -617,7 +625,7 @@ static void append_event_class(struct ctx *ctx, struct fs_sink_ctf_event_class *
     append_end_block_semi_nl_nl(ctx);
 }
 
-static void append_stream_class(struct ctx *ctx, struct fs_sink_ctf_stream_class *sc)
+static void append_stream_class(ctf::sink::CtfIrToTsdlCtx *ctx, struct fs_sink_ctf_stream_class *sc)
 {
     uint64_t i;
 
@@ -719,7 +727,7 @@ static void append_stream_class(struct ctx *ctx, struct fs_sink_ctf_stream_class
     }
 
     /*
-     * Unconditionnally write the packet sequence number as, even if
+     * Unconditionally write the packet sequence number as, even if
      * there's no possible discarded packets message, it's still
      * useful information to have.
      */
@@ -776,10 +784,9 @@ static void append_stream_class(struct ctx *ctx, struct fs_sink_ctf_stream_class
     }
 }
 
-BT_HIDDEN
 void translate_trace_ctf_ir_to_tsdl(struct fs_sink_ctf_trace *trace, GString *tsdl)
 {
-    struct ctx ctx = {
+    ctf::sink::CtfIrToTsdlCtx ctx = {
         .indent_level = 0,
         .tsdl = tsdl,
     };
index c980e9f936d483057888130af5c9ae39d6a66436..1efe100f3e9046c559c4fde81ee3ccba60ea6cff 100644 (file)
@@ -9,10 +9,6 @@
 
 #include <glib.h>
 
-#include "common/macros.h"
-#include "fs-sink-ctf-meta.hpp"
-
-BT_HIDDEN
 void translate_trace_ctf_ir_to_tsdl(struct fs_sink_ctf_trace *trace, GString *tsdl);
 
 #endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H */
index d7ff260f271d8b49e5d006b72fcf53ea6d2e26d6..5c8cf801da1ada833ed184ce3a08ccbc3da42b9f 100644 (file)
@@ -4,28 +4,25 @@
  * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
  */
 
-#define BT_COMP_LOG_SELF_COMP (ctx->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (ctx->log_level)
-#define BT_LOG_TAG            "PLUGIN/SINK.CTF.FS/TRANSLATE-TRACE-IR-TO-CTF-IR"
-#include "logging/comp-logging.h"
+#include <string>
 
-#include "translate-trace-ir-to-ctf-ir.hpp"
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
 
 #include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/common.h"
+
 #include "common/assert.h"
-#include <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <glib.h>
+#include "common/common.h"
+#include "cpp-common/bt2/field-path.hpp"
+#include "cpp-common/bt2c/fmt.hpp"
 
-#include "fs-sink.hpp"
 #include "fs-sink-ctf-meta.hpp"
+#include "fs-sink.hpp"
+#include "translate-trace-ir-to-ctf-ir.hpp"
 
 struct field_path_elem
 {
-    uint64_t index_in_parent;
     GString *name;
 
     /* Weak */
@@ -35,31 +32,42 @@ struct field_path_elem
     struct fs_sink_ctf_field_class *parent_fc;
 };
 
-struct ctx
+namespace ctf {
+namespace sink {
+
+struct TraceIrToCtfIrCtx
 {
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
+    explicit TraceIrToCtfIrCtx(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SINK.CTF.FS/TRANSLATE-TRACE-IR-TO-CTF-IR"}
+    {
+    }
+
+    bt2c::Logger logger;
 
     /* Weak */
-    struct fs_sink_ctf_stream_class *cur_sc;
+    struct fs_sink_ctf_stream_class *cur_sc = nullptr;
 
     /* Weak */
-    struct fs_sink_ctf_event_class *cur_ec;
+    struct fs_sink_ctf_event_class *cur_ec = nullptr;
 
-    bt_field_path_scope cur_scope;
+    bt_field_path_scope cur_scope = BT_FIELD_PATH_SCOPE_PACKET_CONTEXT;
 
     /*
      * Array of `struct field_path_elem` */
-    GArray *cur_path;
+    GArray *cur_path = nullptr;
 };
 
-static inline struct field_path_elem *cur_path_stack_at(struct ctx *ctx, uint64_t i)
+} /* namespace sink */
+} /* namespace ctf */
+
+static inline struct field_path_elem *cur_path_stack_at(ctf::sink::TraceIrToCtfIrCtx *ctx,
+                                                        uint64_t i)
 {
     BT_ASSERT(i < ctx->cur_path->len);
-    return &g_array_index(ctx->cur_path, struct field_path_elem, i);
+    return &bt_g_array_index(ctx->cur_path, struct field_path_elem, i);
 }
 
-static inline struct field_path_elem *cur_path_stack_top(struct ctx *ctx)
+static inline struct field_path_elem *cur_path_stack_top(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
     BT_ASSERT(ctx->cur_path->len > 0);
     return cur_path_stack_at(ctx, ctx->cur_path->len - 1);
@@ -151,7 +159,7 @@ end:
     return must_protect;
 }
 
-static inline int cur_path_stack_push(struct ctx *ctx, uint64_t index_in_parent, const char *name,
+static inline int cur_path_stack_push(ctf::sink::TraceIrToCtfIrCtx *ctx, const char *name,
                                       bool force_protect_name, const bt_field_class *ir_fc,
                                       struct fs_sink_ctf_field_class *parent_fc)
 {
@@ -160,7 +168,6 @@ static inline int cur_path_stack_push(struct ctx *ctx, uint64_t index_in_parent,
 
     g_array_set_size(ctx->cur_path, ctx->cur_path->len + 1);
     field_path_elem = cur_path_stack_top(ctx);
-    field_path_elem->index_in_parent = index_in_parent;
     field_path_elem->name = g_string_new(NULL);
 
     if (name) {
@@ -177,9 +184,10 @@ static inline int cur_path_stack_push(struct ctx *ctx, uint64_t index_in_parent,
                 is_reserved_member_name(name, "timestamp_end") ||
                 is_reserved_member_name(name, "events_discarded") ||
                 is_reserved_member_name(name, "packet_seq_num")) {
-                BT_COMP_LOGE("Unsupported reserved TSDL structure field class member "
-                             "or variant field class option name: name=\"%s\"",
-                             name);
+                BT_CPPLOGE_SPEC(ctx->logger,
+                                "Unsupported reserved TSDL structure field class member "
+                                "or variant field class option name: name=\"{}\"",
+                                name);
                 ret = -1;
                 goto end;
             }
@@ -187,9 +195,10 @@ static inline int cur_path_stack_push(struct ctx *ctx, uint64_t index_in_parent,
 
         if (!ist_valid_identifier(field_path_elem->name->str)) {
             ret = -1;
-            BT_COMP_LOGE("Unsupported non-TSDL structure field class member "
-                         "or variant field class option name: name=\"%s\"",
-                         field_path_elem->name->str);
+            BT_CPPLOGE_SPEC(ctx->logger,
+                            "Unsupported non-TSDL structure field class member "
+                            "or variant field class option name: name=\"{}\"",
+                            field_path_elem->name->str);
             goto end;
         }
     }
@@ -201,7 +210,7 @@ end:
     return ret;
 }
 
-static inline void cur_path_stack_pop(struct ctx *ctx)
+static inline void cur_path_stack_pop(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
     struct field_path_elem *field_path_elem;
 
@@ -228,8 +237,8 @@ static inline void cur_path_stack_pop(struct ctx *ctx)
  *
  * Returns a negative value if this resolving operation failed.
  */
-static int create_relative_field_ref(struct ctx *ctx, const bt_field_path *tgt_ir_field_path,
-                                     GString *tgt_field_ref,
+static int create_relative_field_ref(ctf::sink::TraceIrToCtfIrCtx *ctx,
+                                     const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref,
                                      struct fs_sink_ctf_field_class **user_tgt_fc)
 {
     int ret = 0;
@@ -393,8 +402,8 @@ end:
  *
  * Returns a negative value if this resolving operation failed.
  */
-static int create_absolute_field_ref(struct ctx *ctx, const bt_field_path *tgt_ir_field_path,
-                                     GString *tgt_field_ref,
+static int create_absolute_field_ref(ctf::sink::TraceIrToCtfIrCtx *ctx,
+                                     const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref,
                                      struct fs_sink_ctf_field_class **user_tgt_fc)
 {
     int ret = 0;
@@ -476,9 +485,9 @@ end:
  * created immediately before (in which case `tgt_field_ref` is
  * irrelevant).
  */
-static void resolve_field_class(struct ctx *ctx, const bt_field_path *tgt_ir_field_path,
-                                GString *tgt_field_ref, bool *create_before,
-                                struct fs_sink_ctf_field_class **user_tgt_fc)
+static void resolve_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx,
+                                const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref,
+                                bool *create_before, struct fs_sink_ctf_field_class **user_tgt_fc)
 {
     int ret;
     bt_field_path_scope tgt_scope;
@@ -526,9 +535,10 @@ end:
     return;
 }
 
-static int translate_field_class(struct ctx *ctx);
+static int translate_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx);
 
-static inline void append_to_parent_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *fc)
+static inline void append_to_parent_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx,
+                                                struct fs_sink_ctf_field_class *fc)
 {
     struct fs_sink_ctf_field_class *parent_fc = cur_path_stack_top(ctx)->parent_fc;
 
@@ -567,7 +577,8 @@ static inline void append_to_parent_field_class(struct ctx *ctx, struct fs_sink_
     }
 }
 
-static inline void update_parent_field_class_alignment(struct ctx *ctx, unsigned int alignment)
+static inline void update_parent_field_class_alignment(ctf::sink::TraceIrToCtfIrCtx *ctx,
+                                                       unsigned int alignment)
 {
     struct fs_sink_ctf_field_class *parent_fc = cur_path_stack_top(ctx)->parent_fc;
 
@@ -590,8 +601,10 @@ static inline void update_parent_field_class_alignment(struct ctx *ctx, unsigned
     }
 }
 
-static inline int translate_structure_field_class_members(
-    struct ctx *ctx, struct fs_sink_ctf_field_class_struct *struct_fc, const bt_field_class *ir_fc)
+static inline int
+translate_structure_field_class_members(ctf::sink::TraceIrToCtfIrCtx *ctx,
+                                        struct fs_sink_ctf_field_class_struct *struct_fc,
+                                        const bt_field_class *ir_fc)
 {
     int ret = 0;
     uint64_t i;
@@ -604,19 +617,21 @@ static inline int translate_structure_field_class_members(
         member = bt_field_class_structure_borrow_member_by_index_const(ir_fc, i);
         name = bt_field_class_structure_member_get_name(member);
         memb_ir_fc = bt_field_class_structure_member_borrow_field_class_const(member);
-        ret = cur_path_stack_push(ctx, i, name, true, memb_ir_fc, &struct_fc->base);
+        ret = cur_path_stack_push(ctx, name, true, memb_ir_fc, &struct_fc->base);
         if (ret) {
-            BT_COMP_LOGE("Cannot translate structure field class member: "
-                         "name=\"%s\"",
-                         name);
+            BT_CPPLOGE_SPEC(ctx->logger,
+                            "Cannot translate structure field class member: "
+                            "name=\"{}\"",
+                            name);
             goto end;
         }
 
         ret = translate_field_class(ctx);
         if (ret) {
-            BT_COMP_LOGE("Cannot translate structure field class member: "
-                         "name=\"%s\"",
-                         name);
+            BT_CPPLOGE_SPEC(ctx->logger,
+                            "Cannot translate structure field class member: "
+                            "name=\"{}\"",
+                            name);
             goto end;
         }
 
@@ -627,11 +642,11 @@ end:
     return ret;
 }
 
-static inline int translate_structure_field_class(struct ctx *ctx)
+static inline int translate_structure_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
     int ret;
-    struct fs_sink_ctf_field_class_struct *fc = fs_sink_ctf_field_class_struct_create_empty(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_struct *fc =
+        fs_sink_ctf_field_class_struct_create_empty(cur_path_stack_top(ctx)->ir_fc);
 
     BT_ASSERT(fc);
     append_to_parent_field_class(ctx, &fc->base);
@@ -841,10 +856,10 @@ end:
     return ret;
 }
 
-static inline int translate_option_field_class(struct ctx *ctx)
+static inline int translate_option_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
-    struct fs_sink_ctf_field_class_option *fc = fs_sink_ctf_field_class_option_create_empty(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_option *fc =
+        fs_sink_ctf_field_class_option_create_empty(cur_path_stack_top(ctx)->ir_fc);
     const bt_field_class *content_ir_fc =
         bt_field_class_option_borrow_field_class_const(fc->base.ir_fc);
     int ret;
@@ -863,15 +878,15 @@ static inline int translate_option_field_class(struct ctx *ctx)
      * unsigned enumeration field class).
      */
     append_to_parent_field_class(ctx, &fc->base);
-    ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, content_ir_fc, &fc->base);
+    ret = cur_path_stack_push(ctx, NULL, false, content_ir_fc, &fc->base);
     if (ret) {
-        BT_COMP_LOGE_STR("Cannot translate option field class content.");
+        BT_CPPLOGE_STR_SPEC(ctx->logger, "Cannot translate option field class content.");
         goto end;
     }
 
     ret = translate_field_class(ctx);
     if (ret) {
-        BT_COMP_LOGE_STR("Cannot translate option field class content.");
+        BT_CPPLOGE_STR_SPEC(ctx->logger, "Cannot translate option field class content.");
         goto end;
     }
 
@@ -882,12 +897,12 @@ end:
     return ret;
 }
 
-static inline int translate_variant_field_class(struct ctx *ctx)
+static inline int translate_variant_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
     int ret = 0;
     uint64_t i;
-    struct fs_sink_ctf_field_class_variant *fc = fs_sink_ctf_field_class_variant_create_empty(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_variant *fc =
+        fs_sink_ctf_field_class_variant_create_empty(cur_path_stack_top(ctx)->ir_fc);
     bt_field_class_type ir_fc_type;
     const bt_field_path *ir_selector_field_path = NULL;
     struct fs_sink_ctf_field_class *tgt_fc = NULL;
@@ -1014,19 +1029,21 @@ append_to_parent:
          * option name because it's already protected at this
          * point.
          */
-        ret = cur_path_stack_push(ctx, i, prot_opt_name, false, opt_ir_fc, &fc->base);
+        ret = cur_path_stack_push(ctx, prot_opt_name, false, opt_ir_fc, &fc->base);
         if (ret) {
-            BT_COMP_LOGE("Cannot translate variant field class option: "
-                         "name=\"%s\"",
-                         prot_opt_name);
+            BT_CPPLOGE_SPEC(ctx->logger,
+                            "Cannot translate variant field class option: "
+                            "name=\"{}\"",
+                            prot_opt_name);
             goto end;
         }
 
         ret = translate_field_class(ctx);
         if (ret) {
-            BT_COMP_LOGE("Cannot translate variant field class option: "
-                         "name=\"%s\"",
-                         prot_opt_name);
+            BT_CPPLOGE_SPEC(ctx->logger,
+                            "Cannot translate variant field class option: "
+                            "name=\"{}\"",
+                            prot_opt_name);
             goto end;
         }
 
@@ -1042,25 +1059,25 @@ end:
     return ret;
 }
 
-static inline int translate_static_array_field_class(struct ctx *ctx)
+static inline int translate_static_array_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
-    struct fs_sink_ctf_field_class_array *fc = fs_sink_ctf_field_class_array_create_empty(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_array *fc =
+        fs_sink_ctf_field_class_array_create_empty(cur_path_stack_top(ctx)->ir_fc);
     const bt_field_class *elem_ir_fc =
         bt_field_class_array_borrow_element_field_class_const(fc->base.base.ir_fc);
     int ret;
 
     BT_ASSERT(fc);
     append_to_parent_field_class(ctx, &fc->base.base);
-    ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, elem_ir_fc, &fc->base.base);
+    ret = cur_path_stack_push(ctx, NULL, false, elem_ir_fc, &fc->base.base);
     if (ret) {
-        BT_COMP_LOGE_STR("Cannot translate static array field class element.");
+        BT_CPPLOGE_STR_SPEC(ctx->logger, "Cannot translate static array field class element.");
         goto end;
     }
 
     ret = translate_field_class(ctx);
     if (ret) {
-        BT_COMP_LOGE_STR("Cannot translate static array field class element.");
+        BT_CPPLOGE_STR_SPEC(ctx->logger, "Cannot translate static array field class element.");
         goto end;
     }
 
@@ -1071,10 +1088,10 @@ end:
     return ret;
 }
 
-static inline int translate_dynamic_array_field_class(struct ctx *ctx)
+static inline int translate_dynamic_array_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
-    struct fs_sink_ctf_field_class_sequence *fc = fs_sink_ctf_field_class_sequence_create_empty(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_sequence *fc =
+        fs_sink_ctf_field_class_sequence_create_empty(cur_path_stack_top(ctx)->ir_fc);
     const bt_field_class *elem_ir_fc =
         bt_field_class_array_borrow_element_field_class_const(fc->base.base.ir_fc);
     int ret;
@@ -1092,15 +1109,15 @@ static inline int translate_dynamic_array_field_class(struct ctx *ctx)
     }
 
     append_to_parent_field_class(ctx, &fc->base.base);
-    ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, elem_ir_fc, &fc->base.base);
+    ret = cur_path_stack_push(ctx, NULL, false, elem_ir_fc, &fc->base.base);
     if (ret) {
-        BT_COMP_LOGE_STR("Cannot translate dynamic array field class element.");
+        BT_CPPLOGE_STR_SPEC(ctx->logger, "Cannot translate dynamic array field class element.");
         goto end;
     }
 
     ret = translate_field_class(ctx);
     if (ret) {
-        BT_COMP_LOGE_STR("Cannot translate dynamic array field class element.");
+        BT_CPPLOGE_STR_SPEC(ctx->logger, "Cannot translate dynamic array field class element.");
         goto end;
     }
 
@@ -1111,50 +1128,50 @@ end:
     return ret;
 }
 
-static inline int translate_bool_field_class(struct ctx *ctx)
+static inline int translate_bool_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
-    struct fs_sink_ctf_field_class_bool *fc = fs_sink_ctf_field_class_bool_create(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_bool *fc =
+        fs_sink_ctf_field_class_bool_create(cur_path_stack_top(ctx)->ir_fc);
 
     BT_ASSERT(fc);
     append_to_parent_field_class(ctx, &fc->base.base);
     return 0;
 }
 
-static inline int translate_bit_array_field_class(struct ctx *ctx)
+static inline int translate_bit_array_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
-    struct fs_sink_ctf_field_class_bit_array *fc = fs_sink_ctf_field_class_bit_array_create(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_bit_array *fc =
+        fs_sink_ctf_field_class_bit_array_create(cur_path_stack_top(ctx)->ir_fc);
 
     BT_ASSERT(fc);
     append_to_parent_field_class(ctx, &fc->base);
     return 0;
 }
 
-static inline int translate_integer_field_class(struct ctx *ctx)
+static inline int translate_integer_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
-    struct fs_sink_ctf_field_class_int *fc = fs_sink_ctf_field_class_int_create(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_int *fc =
+        fs_sink_ctf_field_class_int_create(cur_path_stack_top(ctx)->ir_fc);
 
     BT_ASSERT(fc);
     append_to_parent_field_class(ctx, &fc->base.base);
     return 0;
 }
 
-static inline int translate_real_field_class(struct ctx *ctx)
+static inline int translate_real_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
-    struct fs_sink_ctf_field_class_float *fc = fs_sink_ctf_field_class_float_create(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_float *fc =
+        fs_sink_ctf_field_class_float_create(cur_path_stack_top(ctx)->ir_fc);
 
     BT_ASSERT(fc);
     append_to_parent_field_class(ctx, &fc->base.base);
     return 0;
 }
 
-static inline int translate_string_field_class(struct ctx *ctx)
+static inline int translate_string_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
-    struct fs_sink_ctf_field_class_string *fc = fs_sink_ctf_field_class_string_create(
-        cur_path_stack_top(ctx)->ir_fc, cur_path_stack_top(ctx)->index_in_parent);
+    struct fs_sink_ctf_field_class_string *fc =
+        fs_sink_ctf_field_class_string_create(cur_path_stack_top(ctx)->ir_fc);
 
     BT_ASSERT(fc);
     append_to_parent_field_class(ctx, &fc->base);
@@ -1168,7 +1185,7 @@ static inline int translate_string_field_class(struct ctx *ctx)
  * within its parent are in the context's current path's top element
  * (cur_path_stack_top()).
  */
-static int translate_field_class(struct ctx *ctx)
+static int translate_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
     int ret;
     bt_field_class_type ir_fc_type = bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc);
@@ -1418,7 +1435,7 @@ end:
  * class and then calls translate_structure_field_class_members() to
  * fill it.
  */
-static int translate_scope_field_class(struct ctx *ctx, bt_field_path_scope scope,
+static int translate_scope_field_class(ctf::sink::TraceIrToCtfIrCtx *ctx, bt_field_path_scope scope,
                                        struct fs_sink_ctf_field_class **fc,
                                        const bt_field_class *ir_fc)
 {
@@ -1430,24 +1447,22 @@ static int translate_scope_field_class(struct ctx *ctx, bt_field_path_scope scop
 
     BT_ASSERT(bt_field_class_get_type(ir_fc) == BT_FIELD_CLASS_TYPE_STRUCTURE);
     BT_ASSERT(fc);
-    *fc = &fs_sink_ctf_field_class_struct_create_empty(ir_fc, UINT64_C(-1))->base;
+    *fc = &fs_sink_ctf_field_class_struct_create_empty(ir_fc)->base;
     BT_ASSERT(*fc);
     ctx->cur_scope = scope;
     BT_ASSERT(ctx->cur_path->len == 0);
-    ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, ir_fc, NULL);
+    ret = cur_path_stack_push(ctx, NULL, false, ir_fc, NULL);
     if (ret) {
-        BT_COMP_LOGE("Cannot translate scope structure field class: "
-                     "scope=%d",
-                     scope);
+        BT_CPPLOGE_SPEC(ctx->logger, "Cannot translate scope structure field class: scope={}",
+                        static_cast<bt2::FieldPathScope>(scope));
         goto end;
     }
 
     ret =
         translate_structure_field_class_members(ctx, fs_sink_ctf_field_class_as_struct(*fc), ir_fc);
     if (ret) {
-        BT_COMP_LOGE("Cannot translate scope structure field class: "
-                     "scope=%d",
-                     scope);
+        BT_CPPLOGE_SPEC(ctx->logger, "Cannot translate scope structure field class: scope={}",
+                        static_cast<bt2::FieldPathScope>(scope));
         goto end;
     }
 
@@ -1460,16 +1475,13 @@ end:
     return ret;
 }
 
-static inline void ctx_init(struct ctx *ctx, struct fs_sink_comp *fs_sink)
+static inline void ctx_init(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
-    memset(ctx, 0, sizeof(struct ctx));
     ctx->cur_path = g_array_new(FALSE, TRUE, sizeof(struct field_path_elem));
     BT_ASSERT(ctx->cur_path);
-    ctx->log_level = fs_sink->log_level;
-    ctx->self_comp = fs_sink->self_comp;
 }
 
-static inline void ctx_fini(struct ctx *ctx)
+static inline void ctx_fini(ctf::sink::TraceIrToCtfIrCtx *ctx)
 {
     if (ctx->cur_path) {
         g_array_free(ctx->cur_path, TRUE);
@@ -1482,13 +1494,13 @@ static int translate_event_class(struct fs_sink_comp *fs_sink, struct fs_sink_ct
                                  struct fs_sink_ctf_event_class **out_ec)
 {
     int ret = 0;
-    struct ctx ctx;
+    ctf::sink::TraceIrToCtfIrCtx ctx {fs_sink->logger};
     struct fs_sink_ctf_event_class *ec;
 
     BT_ASSERT(sc);
     BT_ASSERT(ir_ec);
 
-    ctx_init(&ctx, fs_sink);
+    ctx_init(&ctx);
     ec = fs_sink_ctf_event_class_create(sc, ir_ec);
     BT_ASSERT(ec);
     ctx.cur_sc = sc;
@@ -1512,7 +1524,6 @@ end:
     return ret;
 }
 
-BT_HIDDEN
 int try_translate_event_class_trace_ir_to_ctf_ir(struct fs_sink_comp *fs_sink,
                                                  struct fs_sink_ctf_stream_class *sc,
                                                  const bt_event_class *ir_ec,
@@ -1562,17 +1573,15 @@ end:
 static void make_unique_default_clock_class_name(struct fs_sink_ctf_stream_class *sc)
 {
     unsigned int suffix = 0;
-    char buf[16];
 
-    g_string_assign(sc->default_clock_class_name, "");
-    sprintf(buf, "default");
+    std::string name = "default";
 
-    while (default_clock_class_name_exists(sc->trace, buf)) {
-        sprintf(buf, "default%u", suffix);
+    while (default_clock_class_name_exists(sc->trace, name.c_str())) {
+        name = "default" + std::to_string(suffix);
         suffix++;
     }
 
-    g_string_assign(sc->default_clock_class_name, buf);
+    g_string_assign(sc->default_clock_class_name, name.c_str());
 }
 
 static int translate_stream_class(struct fs_sink_comp *fs_sink, struct fs_sink_ctf_trace *trace,
@@ -1580,11 +1589,11 @@ static int translate_stream_class(struct fs_sink_comp *fs_sink, struct fs_sink_c
                                   struct fs_sink_ctf_stream_class **out_sc)
 {
     int ret = 0;
-    struct ctx ctx;
+    ctf::sink::TraceIrToCtfIrCtx ctx {fs_sink->logger};
 
     BT_ASSERT(trace);
     BT_ASSERT(ir_sc);
-    ctx_init(&ctx, fs_sink);
+    ctx_init(&ctx);
     *out_sc = fs_sink_ctf_stream_class_create(trace, ir_sc);
     BT_ASSERT(*out_sc);
 
@@ -1647,7 +1656,6 @@ end:
     return ret;
 }
 
-BT_HIDDEN
 int try_translate_stream_class_trace_ir_to_ctf_ir(struct fs_sink_comp *fs_sink,
                                                   struct fs_sink_ctf_trace *trace,
                                                   const bt_stream_class *ir_sc,
@@ -1673,7 +1681,6 @@ end:
     return ret;
 }
 
-BT_HIDDEN
 struct fs_sink_ctf_trace *translate_trace_trace_ir_to_ctf_ir(struct fs_sink_comp *fs_sink,
                                                              const bt_trace *ir_trace)
 {
@@ -1690,10 +1697,10 @@ struct fs_sink_ctf_trace *translate_trace_trace_ir_to_ctf_ir(struct fs_sink_comp
         bt_trace_borrow_environment_entry_by_index_const(ir_trace, i, &name, &val);
 
         if (!ist_valid_identifier(name)) {
-            BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, fs_sink->log_level, fs_sink->self_comp,
-                                "Unsupported trace class's environment entry name: "
-                                "name=\"%s\"",
-                                name);
+            BT_CPPLOGE_SPEC(fs_sink->logger,
+                            "Unsupported trace class's environment entry name: "
+                            "name=\"{}\"",
+                            name);
             goto end;
         }
 
@@ -1702,10 +1709,10 @@ struct fs_sink_ctf_trace *translate_trace_trace_ir_to_ctf_ir(struct fs_sink_comp
         case BT_VALUE_TYPE_STRING:
             break;
         default:
-            BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, fs_sink->log_level, fs_sink->self_comp,
-                                "Unsupported trace class's environment entry value type: "
-                                "type=%s",
-                                bt_common_value_type_string(bt_value_get_type(val)));
+            BT_CPPLOGE_SPEC(fs_sink->logger,
+                            "Unsupported trace class's environment entry value type: "
+                            "type={}",
+                            static_cast<bt2::ValueType>(bt_value_get_type(val)));
             goto end;
         }
     }
index a1c7312a6f97fcd205aa5f9f131ffdda0c84e2b5..5a3fda6c456b5dcf4a6633731e2cda265e5c8cca 100644 (file)
@@ -7,25 +7,18 @@
 #ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H
 #define BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H
 
-#include "common/macros.h"
 #include <babeltrace2/babeltrace.h>
 
-#include "fs-sink.hpp"
-#include "fs-sink-ctf-meta.hpp"
-
-BT_HIDDEN
 int try_translate_event_class_trace_ir_to_ctf_ir(struct fs_sink_comp *fs_sink,
                                                  struct fs_sink_ctf_stream_class *sc,
                                                  const bt_event_class *ir_ec,
                                                  struct fs_sink_ctf_event_class **out_ec);
 
-BT_HIDDEN
 int try_translate_stream_class_trace_ir_to_ctf_ir(struct fs_sink_comp *fs_sink,
                                                   struct fs_sink_ctf_trace *trace,
                                                   const bt_stream_class *ir_sc,
                                                   struct fs_sink_ctf_stream_class **out_sc);
 
-BT_HIDDEN
 struct fs_sink_ctf_trace *translate_trace_trace_ir_to_ctf_ir(struct fs_sink_comp *fs_sink,
                                                              const bt_trace *ir_trace);
 
diff --git a/src/plugins/ctf/fs-src/Makefile.am b/src/plugins/ctf/fs-src/Makefile.am
deleted file mode 100644 (file)
index cb990b6..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-fs-src.la
-
-libbabeltrace2_plugin_ctf_fs_src_la_SOURCES = \
-       data-stream-file.cpp \
-       data-stream-file.hpp \
-       file.cpp \
-       file.hpp \
-       fs.cpp \
-       fs.hpp \
-       lttng-index.hpp \
-       metadata.cpp \
-       metadata.hpp \
-       query.hpp \
-       query.cpp
index 9a49fd9936878144090c6c43e3fbc5a1993be1c8..22aa309f93df6244727a48e2926e113cf3d59c1a 100644 (file)
@@ -6,27 +6,21 @@
  * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
  */
 
-#define BT_COMP_LOG_SELF_COMP (self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (log_level)
-#define BT_LOG_TAG            "PLUGIN/SRC.CTF.FS/DS"
-#include "logging/comp-logging.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
 #include <glib.h>
-#include <inttypes.h>
-#include "compat/mman.h"
-#include "compat/endian.h"
-#include <babeltrace2/babeltrace.h>
-#include "common/common.h"
-#include "file.hpp"
-#include "metadata.hpp"
-#include "../common/msg-iter/msg-iter.hpp"
-#include "common/assert.h"
+#include <stdint.h>
+#include <stdio.h>
+
+#include "compat/endian.h" /* IWYU pragma: keep  */
+#include "compat/mman.h"   /* IWYU: pragma keep  */
+#include "cpp-common/bt2c/glib-up.hpp"
+#include "cpp-common/bt2s/make-unique.hpp"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "../common/src/msg-iter/msg-iter.hpp"
 #include "data-stream-file.hpp"
-#include <string.h>
+#include "file.hpp"
+#include "fs.hpp"
+#include "lttng-index.hpp"
 
 static inline size_t remaining_mmap_bytes(struct ctf_fs_ds_file *ds_file)
 {
@@ -46,31 +40,24 @@ static bool offset_ist_mapped(struct ctf_fs_ds_file *ds_file, off_t offset_in_fi
 
 static enum ctf_msg_iter_medium_status ds_file_munmap(struct ctf_fs_ds_file *ds_file)
 {
-    enum ctf_msg_iter_medium_status status;
-    bt_self_component *self_comp = ds_file->self_comp;
-    bt_logging_level log_level = ds_file->log_level;
-
     BT_ASSERT(ds_file);
 
     if (!ds_file->mmap_addr) {
-        status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-        goto end;
+        return CTF_MSG_ITER_MEDIUM_STATUS_OK;
     }
 
     if (bt_munmap(ds_file->mmap_addr, ds_file->mmap_len)) {
-        BT_COMP_LOGE_ERRNO("Cannot memory-unmap file",
-                           ": address=%p, size=%zu, file_path=\"%s\", file=%p", ds_file->mmap_addr,
-                           ds_file->mmap_len, ds_file->file ? ds_file->file->path->str : "NULL",
-                           ds_file->file ? ds_file->file->fp : NULL);
-        status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_ERRNO_SPEC(ds_file->logger, "Cannot memory-unmap file",
+                              ": address={}, size={}, file_path=\"{}\", file={}",
+                              fmt::ptr(ds_file->mmap_addr), ds_file->mmap_len,
+                              ds_file->file ? ds_file->file->path : "NULL",
+                              ds_file->file ? fmt::ptr(ds_file->file->fp) : NULL);
+        return CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
     }
 
     ds_file->mmap_addr = NULL;
 
-    status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-end:
-    return status;
+    return CTF_MSG_ITER_MEDIUM_STATUS_OK;
 }
 
 /*
@@ -87,10 +74,6 @@ end:
 static enum ctf_msg_iter_medium_status ds_file_mmap(struct ctf_fs_ds_file *ds_file,
                                                     off_t requested_offset_in_file)
 {
-    enum ctf_msg_iter_medium_status status;
-    bt_self_component *self_comp = ds_file->self_comp;
-    bt_logging_level log_level = ds_file->log_level;
-
     /* Ensure the requested offset is in the file range. */
     BT_ASSERT(requested_offset_in_file >= 0);
     BT_ASSERT(requested_offset_in_file < ds_file->file->size);
@@ -102,14 +85,13 @@ static enum ctf_msg_iter_medium_status ds_file_mmap(struct ctf_fs_ds_file *ds_fi
     if (offset_ist_mapped(ds_file, requested_offset_in_file)) {
         ds_file->request_offset_in_mapping =
             requested_offset_in_file - ds_file->mmap_offset_in_file;
-        status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-        goto end;
+        return CTF_MSG_ITER_MEDIUM_STATUS_OK;
     }
 
     /* Unmap old region */
-    status = ds_file_munmap(ds_file);
+    ctf_msg_iter_medium_status status = ds_file_munmap(ds_file);
     if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-        goto end;
+        return status;
     }
 
     /*
@@ -117,7 +99,8 @@ static enum ctf_msg_iter_medium_status ds_file_mmap(struct ctf_fs_ds_file *ds_fi
      * contains `requested_offset_in_file`.
      */
     ds_file->request_offset_in_mapping =
-        requested_offset_in_file % bt_mmap_get_offset_align_size(ds_file->log_level);
+        requested_offset_in_file %
+        bt_mmap_get_offset_align_size(static_cast<int>(ds_file->logger.level()));
     ds_file->mmap_offset_in_file = requested_offset_in_file - ds_file->request_offset_in_mapping;
     ds_file->mmap_len =
         MIN(ds_file->file->size - ds_file->mmap_offset_in_file, ds_file->mmap_max_len);
@@ -125,20 +108,17 @@ static enum ctf_msg_iter_medium_status ds_file_mmap(struct ctf_fs_ds_file *ds_fi
     BT_ASSERT(ds_file->mmap_len > 0);
 
     ds_file->mmap_addr =
-        bt_mmap((void *) 0, ds_file->mmap_len, PROT_READ, MAP_PRIVATE, fileno(ds_file->file->fp),
-                ds_file->mmap_offset_in_file, ds_file->log_level);
+        bt_mmap(ds_file->mmap_len, PROT_READ, MAP_PRIVATE, fileno(ds_file->file->fp.get()),
+                ds_file->mmap_offset_in_file, static_cast<int>(ds_file->logger.level()));
     if (ds_file->mmap_addr == MAP_FAILED) {
-        BT_COMP_LOGE("Cannot memory-map address (size %zu) of file \"%s\" (%p) at offset %jd: %s",
-                     ds_file->mmap_len, ds_file->file->path->str, ds_file->file->fp,
-                     (intmax_t) ds_file->mmap_offset_in_file, strerror(errno));
-        status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_SPEC(ds_file->logger,
+                        "Cannot memory-map address (size {}) of file \"{}\" ({}) at offset {}: {}",
+                        ds_file->mmap_len, ds_file->file->path, fmt::ptr(ds_file->file->fp),
+                        (intmax_t) ds_file->mmap_offset_in_file, strerror(errno));
+        return CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
     }
 
-    status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-
-end:
-    return status;
+    return CTF_MSG_ITER_MEDIUM_STATUS_OK;
 }
 
 /*
@@ -154,8 +134,6 @@ end:
 
 static enum ctf_msg_iter_medium_status ds_file_mmap_next(struct ctf_fs_ds_file *ds_file)
 {
-    enum ctf_msg_iter_medium_status status;
-
     /*
      * If we're called, it's because more bytes are requested but we have
      * given all the bytes of the current mapping.
@@ -167,23 +145,16 @@ static enum ctf_msg_iter_medium_status ds_file_mmap_next(struct ctf_fs_ds_file *
      * no next mapping.
      */
     if (ds_file->mmap_offset_in_file + ds_file->mmap_len == ds_file->file->size) {
-        status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-        goto end;
+        return CTF_MSG_ITER_MEDIUM_STATUS_EOF;
     }
 
-    status = ds_file_mmap(ds_file, ds_file->mmap_offset_in_file + ds_file->mmap_len);
-
-end:
-    return status;
+    return ds_file_mmap(ds_file, ds_file->mmap_offset_in_file + ds_file->mmap_len);
 }
 
 static enum ctf_msg_iter_medium_status medop_request_bytes(size_t request_sz, uint8_t **buffer_addr,
                                                            size_t *buffer_sz, void *data)
 {
-    enum ctf_msg_iter_medium_status status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
     struct ctf_fs_ds_file *ds_file = (struct ctf_fs_ds_file *) data;
-    bt_self_component *self_comp = ds_file->self_comp;
-    bt_logging_level log_level = ds_file->log_level;
 
     BT_ASSERT(request_sz > 0);
 
@@ -194,22 +165,21 @@ static enum ctf_msg_iter_medium_status medop_request_bytes(size_t request_sz, ui
     if (remaining_mmap_bytes(ds_file) == 0) {
         /* Are we at the end of the file? */
         if (ds_file->mmap_offset_in_file >= ds_file->file->size) {
-            BT_COMP_LOGD("Reached end of file \"%s\" (%p)", ds_file->file->path->str,
-                         ds_file->file->fp);
-            status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-            goto end;
+            BT_CPPLOGD_SPEC(ds_file->logger, "Reached end of file \"{}\" ({})", ds_file->file->path,
+                            fmt::ptr(ds_file->file->fp));
+            return CTF_MSG_ITER_MEDIUM_STATUS_EOF;
         }
 
-        status = ds_file_mmap_next(ds_file);
+        ctf_msg_iter_medium_status status = ds_file_mmap_next(ds_file);
         switch (status) {
         case CTF_MSG_ITER_MEDIUM_STATUS_OK:
             break;
         case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
-            goto end;
+            return CTF_MSG_ITER_MEDIUM_STATUS_EOF;
         default:
-            BT_COMP_LOGE("Cannot memory-map next region of file \"%s\" (%p)",
-                         ds_file->file->path->str, ds_file->file->fp);
-            goto error;
+            BT_CPPLOGE_SPEC(ds_file->logger, "Cannot memory-map next region of file \"{}\" ({})",
+                            ds_file->file->path, fmt::ptr(ds_file->file->fp));
+            return status;
         }
     }
 
@@ -220,35 +190,26 @@ static enum ctf_msg_iter_medium_status medop_request_bytes(size_t request_sz, ui
     *buffer_addr = ((uint8_t *) ds_file->mmap_addr) + ds_file->request_offset_in_mapping;
 
     ds_file->request_offset_in_mapping += *buffer_sz;
-    goto end;
 
-error:
-    status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-
-end:
-    return status;
+    return CTF_MSG_ITER_MEDIUM_STATUS_OK;
 }
 
-static bt_stream *medop_borrow_stream(bt_stream_class *stream_class, int64_t stream_id, void *data)
+static bt_stream *medop_borrow_stream(bt_stream_class *stream_class, int64_t, void *data)
 {
     struct ctf_fs_ds_file *ds_file = (struct ctf_fs_ds_file *) data;
     bt_stream_class *ds_file_stream_class;
-    bt_stream *stream = NULL;
 
-    ds_file_stream_class = bt_stream_borrow_class(ds_file->stream);
+    ds_file_stream_class = ds_file->stream->cls().libObjPtr();
 
     if (stream_class != ds_file_stream_class) {
         /*
          * Not supported: two packets described by two different
          * stream classes within the same data stream file.
          */
-        goto end;
+        return nullptr;
     }
 
-    stream = ds_file->stream;
-
-end:
-    return stream;
+    return ds_file->stream->libObjPtr();
 }
 
 static enum ctf_msg_iter_medium_status medop_seek(off_t offset, void *data)
@@ -261,7 +222,6 @@ static enum ctf_msg_iter_medium_status medop_seek(off_t offset, void *data)
     return ds_file_mmap(ds_file, offset);
 }
 
-BT_HIDDEN
 struct ctf_msg_iter_medium_ops ctf_fs_ds_file_medops = {
     medop_request_bytes,
     medop_seek,
@@ -271,27 +231,31 @@ struct ctf_msg_iter_medium_ops ctf_fs_ds_file_medops = {
 
 struct ctf_fs_ds_group_medops_data
 {
+    explicit ctf_fs_ds_group_medops_data(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.FS/DS-GROUP-MEDOPS"}
+    {
+    }
+
+    bt2c::Logger logger;
+
     /* Weak, set once at creation time. */
-    struct ctf_fs_ds_file_group *ds_file_group;
+    struct ctf_fs_ds_file_group *ds_file_group = nullptr;
 
     /*
      * Index (as in element rank) of the index entry of ds_file_groups'
      * index we will read next (so, the one after the one we are reading
      * right now).
      */
-    guint next_index_entry_index;
+    guint next_index_entry_index = 0;
 
     /*
      * File we are currently reading.  Changes whenever we switch to
      * reading another data file.
-     *
-     * Owned by this.
      */
-    struct ctf_fs_ds_file *file;
+    ctf_fs_ds_file::UP file;
 
     /* Weak, for context / logging / appending causes. */
-    bt_self_message_iterator *self_msg_iter;
-    bt_logging_level log_level;
+    bt_self_message_iterator *self_msg_iter = nullptr;
 };
 
 static enum ctf_msg_iter_medium_status medop_group_request_bytes(size_t request_sz,
@@ -301,7 +265,7 @@ static enum ctf_msg_iter_medium_status medop_group_request_bytes(size_t request_
     struct ctf_fs_ds_group_medops_data *data = (struct ctf_fs_ds_group_medops_data *) void_data;
 
     /* Return bytes from the current file. */
-    return medop_request_bytes(request_sz, buffer_addr, buffer_sz, data->file);
+    return medop_request_bytes(request_sz, buffer_addr, buffer_sz, data->file.get());
 }
 
 static bt_stream *medop_group_borrow_stream(bt_stream_class *stream_class, int64_t stream_id,
@@ -309,7 +273,7 @@ static bt_stream *medop_group_borrow_stream(bt_stream_class *stream_class, int64
 {
     struct ctf_fs_ds_group_medops_data *data = (struct ctf_fs_ds_group_medops_data *) void_data;
 
-    return medop_borrow_stream(stream_class, stream_id, data->file);
+    return medop_borrow_stream(stream_class, stream_id, data->file.get());
 }
 
 /*
@@ -319,27 +283,20 @@ static bt_stream *medop_group_borrow_stream(bt_stream_class *stream_class, int64
 
 static enum ctf_msg_iter_medium_status
 ctf_fs_ds_group_medops_set_file(struct ctf_fs_ds_group_medops_data *data,
-                                struct ctf_fs_ds_index_entry *index_entry,
-                                bt_self_message_iterator *self_msg_iter, bt_logging_level log_level)
+                                struct ctf_fs_ds_index_entry *index_entry)
 {
-    enum ctf_msg_iter_medium_status status;
-
     BT_ASSERT(data);
     BT_ASSERT(index_entry);
 
     /* Check if that file is already the one mapped. */
-    if (!data->file || strcmp(index_entry->path, data->file->file->path->str) != 0) {
-        /* Destroy the previously used file. */
-        ctf_fs_ds_file_destroy(data->file);
-
+    if (!data->file || data->file->file->path != index_entry->path) {
         /* Create the new file. */
         data->file =
             ctf_fs_ds_file_create(data->ds_file_group->ctf_fs_trace, data->ds_file_group->stream,
-                                  index_entry->path, log_level);
+                                  index_entry->path, data->logger);
         if (!data->file) {
-            BT_MSG_ITER_LOGE_APPEND_CAUSE(self_msg_iter, "failed to create ctf_fs_ds_file.");
-            status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-            goto end;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(data->logger, "failed to create ctf_fs_ds_file.");
+            return CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
         }
     }
 
@@ -347,87 +304,50 @@ ctf_fs_ds_group_medops_set_file(struct ctf_fs_ds_group_medops_data *data,
      * Ensure the right portion of the file will be returned on the next
      * request_bytes call.
      */
-    status = ds_file_mmap(data->file, index_entry->offset);
-    if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-        goto end;
-    }
-
-    status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-
-end:
-    return status;
+    return ds_file_mmap(data->file.get(), index_entry->offset.bytes());
 }
 
 static enum ctf_msg_iter_medium_status medop_group_switch_packet(void *void_data)
 {
     struct ctf_fs_ds_group_medops_data *data = (struct ctf_fs_ds_group_medops_data *) void_data;
-    struct ctf_fs_ds_index_entry *index_entry;
-    enum ctf_msg_iter_medium_status status;
 
     /* If we have gone through all index entries, we are done. */
-    if (data->next_index_entry_index >= data->ds_file_group->index->entries->len) {
-        status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-        goto end;
+    if (data->next_index_entry_index >= data->ds_file_group->index.entries.size()) {
+        return CTF_MSG_ITER_MEDIUM_STATUS_EOF;
     }
 
     /*
      * Otherwise, look up the next index entry / packet and prepare it
      *  for reading.
      */
-    index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index(
-        data->ds_file_group->index->entries, data->next_index_entry_index);
-
-    status =
-        ctf_fs_ds_group_medops_set_file(data, index_entry, data->self_msg_iter, data->log_level);
+    ctf_msg_iter_medium_status status = ctf_fs_ds_group_medops_set_file(
+        data, &data->ds_file_group->index.entries[data->next_index_entry_index]);
     if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-        goto end;
+        return status;
     }
 
     data->next_index_entry_index++;
 
-    status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-end:
-    return status;
+    return CTF_MSG_ITER_MEDIUM_STATUS_OK;
 }
 
-BT_HIDDEN
-void ctf_fs_ds_group_medops_data_destroy(struct ctf_fs_ds_group_medops_data *data)
+void ctf_fs_ds_group_medops_data_deleter::operator()(ctf_fs_ds_group_medops_data *data) noexcept
 {
-    if (!data) {
-        goto end;
-    }
-
-    ctf_fs_ds_file_destroy(data->file);
-
-    g_free(data);
-
-end:
-    return;
+    delete data;
 }
 
 enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_data_create(
     struct ctf_fs_ds_file_group *ds_file_group, bt_self_message_iterator *self_msg_iter,
-    bt_logging_level log_level, struct ctf_fs_ds_group_medops_data **out)
+    const bt2c::Logger& parentLogger, ctf_fs_ds_group_medops_data_up& out)
 {
-    struct ctf_fs_ds_group_medops_data *data;
-    enum ctf_msg_iter_medium_status status;
-
     BT_ASSERT(self_msg_iter);
     BT_ASSERT(ds_file_group);
-    BT_ASSERT(ds_file_group->index);
-    BT_ASSERT(ds_file_group->index->entries->len > 0);
-
-    data = g_new0(struct ctf_fs_ds_group_medops_data, 1);
-    if (!data) {
-        BT_MSG_ITER_LOGE_APPEND_CAUSE(self_msg_iter,
-                                      "Failed to allocate a struct ctf_fs_ds_group_medops_data");
-        status = CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR;
-        goto error;
-    }
+    BT_ASSERT(!ds_file_group->index.entries.empty());
+
+    out.reset(new ctf_fs_ds_group_medops_data {parentLogger});
 
-    data->ds_file_group = ds_file_group;
-    data->self_msg_iter = self_msg_iter;
-    data->log_level = log_level;
+    out->ds_file_group = ds_file_group;
+    out->self_msg_iter = self_msg_iter;
 
     /*
      * No need to prepare the first file.  ctf_msg_iter will call
@@ -435,15 +355,7 @@ enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_data_create(
      * done then.
      */
 
-    *out = data;
-    status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-    goto end;
-
-error:
-    ctf_fs_ds_group_medops_data_destroy(data);
-
-end:
-    return status;
+    return CTF_MSG_ITER_MEDIUM_STATUS_OK;
 }
 
 void ctf_fs_ds_group_medops_data_reset(struct ctf_fs_ds_group_medops_data *data)
@@ -464,23 +376,6 @@ struct ctf_msg_iter_medium_ops ctf_fs_ds_group_medops = {
     .borrow_stream = medop_group_borrow_stream,
 };
 
-static struct ctf_fs_ds_index_entry *ctf_fs_ds_index_entry_create(bt_self_component *self_comp,
-                                                                  bt_logging_level log_level)
-{
-    struct ctf_fs_ds_index_entry *entry;
-
-    entry = g_new0(struct ctf_fs_ds_index_entry, 1);
-    if (!entry) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate a ctf_fs_ds_index_entry.");
-        goto end;
-    }
-
-    entry->packet_seq_num = UINT64_MAX;
-
-end:
-    return entry;
-}
-
 static int convert_cycles_to_ns(struct ctf_clock_class *clock_class, uint64_t cycles, int64_t *ns)
 {
     return bt_util_clock_cycles_to_ns_from_origin(cycles, clock_class->frequency,
@@ -488,70 +383,50 @@ static int convert_cycles_to_ns(struct ctf_clock_class *clock_class, uint64_t cy
                                                   clock_class->offset_cycles, ns);
 }
 
-static struct ctf_fs_ds_index *build_index_from_idx_file(struct ctf_fs_ds_file *ds_file,
-                                                         struct ctf_fs_ds_file_info *file_info,
-                                                         struct ctf_msg_iter *msg_iter)
+static bt2s::optional<ctf_fs_ds_index>
+build_index_from_idx_file(struct ctf_fs_ds_file *ds_file, struct ctf_fs_ds_file_info *file_info,
+                          struct ctf_msg_iter *msg_iter)
 {
-    int ret;
-    gchar *directory = NULL;
-    gchar *basename = NULL;
-    GString *index_basename = NULL;
-    gchar *index_file_path = NULL;
-    GMappedFile *mapped_file = NULL;
-    gsize filesize;
-    const char *mmap_begin = NULL, *file_pos = NULL;
-    const struct ctf_packet_index_file_hdr *header = NULL;
-    struct ctf_fs_ds_index *index = NULL;
-    struct ctf_fs_ds_index_entry *index_entry = NULL, *prev_index_entry = NULL;
-    uint64_t total_packets_size = 0;
-    size_t file_index_entry_size;
-    size_t file_entry_count;
-    size_t i;
-    struct ctf_stream_class *sc;
-    struct ctf_msg_iter_packet_properties props;
-    uint32_t version_major, version_minor;
-    bt_self_component *self_comp = ds_file->self_comp;
-    bt_logging_level log_level = ds_file->log_level;
-
-    BT_COMP_LOGI("Building index from .idx file of stream file %s", ds_file->file->path->str);
-    ret = ctf_msg_iter_get_packet_properties(msg_iter, &props);
+    BT_CPPLOGI_SPEC(ds_file->logger, "Building index from .idx file of stream file {}",
+                    ds_file->file->path);
+    ctf_msg_iter_packet_properties props;
+    int ret = ctf_msg_iter_get_packet_properties(msg_iter, &props);
     if (ret) {
-        BT_COMP_LOGI_STR("Cannot read first packet's header and context fields.");
-        goto error;
+        BT_CPPLOGI_STR_SPEC(ds_file->logger,
+                            "Cannot read first packet's header and context fields.");
+        return bt2s::nullopt;
     }
 
-    sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, props.stream_class_id);
+    ctf_stream_class *sc =
+        ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, props.stream_class_id);
     BT_ASSERT(sc);
     if (!sc->default_clock_class) {
-        BT_COMP_LOGI_STR("Cannot find stream class's default clock class.");
-        goto error;
+        BT_CPPLOGI_STR_SPEC(ds_file->logger, "Cannot find stream class's default clock class.");
+        return bt2s::nullopt;
     }
 
     /* Look for index file in relative path index/name.idx. */
-    basename = g_path_get_basename(ds_file->file->path->str);
+    bt2c::GCharUP basename {g_path_get_basename(ds_file->file->path.c_str())};
     if (!basename) {
-        BT_COMP_LOGE("Cannot get the basename of datastream file %s", ds_file->file->path->str);
-        goto error;
+        BT_CPPLOGE_SPEC(ds_file->logger, "Cannot get the basename of datastream file {}",
+                        ds_file->file->path);
+        return bt2s::nullopt;
     }
 
-    directory = g_path_get_dirname(ds_file->file->path->str);
+    bt2c::GCharUP directory {g_path_get_dirname(ds_file->file->path.c_str())};
     if (!directory) {
-        BT_COMP_LOGE("Cannot get dirname of datastream file %s", ds_file->file->path->str);
-        goto error;
+        BT_CPPLOGE_SPEC(ds_file->logger, "Cannot get dirname of datastream file {}",
+                        ds_file->file->path);
+        return bt2s::nullopt;
     }
 
-    index_basename = g_string_new(basename);
-    if (!index_basename) {
-        BT_COMP_LOGE_STR("Cannot allocate index file basename string");
-        goto error;
-    }
-
-    g_string_append(index_basename, ".idx");
-    index_file_path = g_build_filename(directory, "index", index_basename->str, NULL);
-    mapped_file = g_mapped_file_new(index_file_path, FALSE, NULL);
+    std::string index_basename = fmt::format("{}.idx", basename.get());
+    bt2c::GCharUP index_file_path {
+        g_build_filename(directory.get(), "index", index_basename.c_str(), NULL)};
+    bt2c::GMappedFileUP mapped_file {g_mapped_file_new(index_file_path.get(), FALSE, NULL)};
     if (!mapped_file) {
-        BT_COMP_LOGD("Cannot create new mapped file %s", index_file_path);
-        goto error;
+        BT_CPPLOGD_SPEC(ds_file->logger, "Cannot create new mapped file {}", index_file_path.get());
+        return bt2s::nullopt;
     }
 
     /*
@@ -559,411 +434,306 @@ static struct ctf_fs_ds_index *build_index_from_idx_file(struct ctf_fs_ds_file *
      * Traces with such large indexes have never been seen in the wild,
      * but this would need to be adjusted to support them.
      */
-    filesize = g_mapped_file_get_length(mapped_file);
-    if (filesize < sizeof(*header)) {
-        BT_COMP_LOGW("Invalid LTTng trace index file: "
-                     "file size (%zu bytes) < header size (%zu bytes)",
-                     filesize, sizeof(*header));
-        goto error;
+    gsize filesize = g_mapped_file_get_length(mapped_file.get());
+    if (filesize < sizeof(ctf_packet_index_file_hdr)) {
+        BT_CPPLOGW_SPEC(ds_file->logger,
+                        "Invalid LTTng trace index file: "
+                        "file size ({} bytes) < header size ({} bytes)",
+                        filesize, sizeof(ctf_packet_index_file_hdr));
+        return bt2s::nullopt;
     }
 
-    mmap_begin = g_mapped_file_get_contents(mapped_file);
-    header = (struct ctf_packet_index_file_hdr *) mmap_begin;
+    const char *mmap_begin = g_mapped_file_get_contents(mapped_file.get());
+    const ctf_packet_index_file_hdr *header = (const ctf_packet_index_file_hdr *) mmap_begin;
 
-    file_pos = g_mapped_file_get_contents(mapped_file) + sizeof(*header);
+    const char *file_pos = g_mapped_file_get_contents(mapped_file.get()) + sizeof(*header);
     if (be32toh(header->magic) != CTF_INDEX_MAGIC) {
-        BT_COMP_LOGW_STR("Invalid LTTng trace index: \"magic\" field validation failed");
-        goto error;
+        BT_CPPLOGW_STR_SPEC(ds_file->logger,
+                            "Invalid LTTng trace index: \"magic\" field validation failed");
+        return bt2s::nullopt;
     }
 
-    version_major = be32toh(header->index_major);
-    version_minor = be32toh(header->index_minor);
+    uint32_t version_major = be32toh(header->index_major);
+    uint32_t version_minor = be32toh(header->index_minor);
     if (version_major != 1) {
-        BT_COMP_LOGW("Unknown LTTng trace index version: "
-                     "major=%" PRIu32 ", minor=%" PRIu32,
-                     version_major, version_minor);
-        goto error;
+        BT_CPPLOGW_SPEC(ds_file->logger, "Unknown LTTng trace index version: major={}, minor={}",
+                        version_major, version_minor);
+        return bt2s::nullopt;
     }
 
-    file_index_entry_size = be32toh(header->packet_index_len);
+    size_t file_index_entry_size = be32toh(header->packet_index_len);
     if (file_index_entry_size < CTF_INDEX_1_0_SIZE) {
-        BT_COMP_LOGW(
+        BT_CPPLOGW_SPEC(
+            ds_file->logger,
             "Invalid `packet_index_len` in LTTng trace index file (`packet_index_len` < CTF index 1.0 index entry size): "
-            "packet_index_len=%zu, CTF_INDEX_1_0_SIZE=%zu",
+            "packet_index_len={}, CTF_INDEX_1_0_SIZE={}",
             file_index_entry_size, CTF_INDEX_1_0_SIZE);
-        goto error;
+        return bt2s::nullopt;
     }
 
-    file_entry_count = (filesize - sizeof(*header)) / file_index_entry_size;
+    size_t file_entry_count = (filesize - sizeof(*header)) / file_index_entry_size;
     if ((filesize - sizeof(*header)) % file_index_entry_size) {
-        BT_COMP_LOGW("Invalid LTTng trace index: the index's size after the header "
-                     "(%zu bytes) is not a multiple of the index entry size "
-                     "(%zu bytes)",
-                     (filesize - sizeof(*header)), sizeof(*header));
-        goto error;
+        BT_CPPLOGW_SPEC(ds_file->logger,
+                        "Invalid LTTng trace index: the index's size after the header "
+                        "({} bytes) is not a multiple of the index entry size "
+                        "({} bytes)",
+                        (filesize - sizeof(*header)), sizeof(*header));
+        return bt2s::nullopt;
     }
 
-    index = ctf_fs_ds_index_create(ds_file->log_level, ds_file->self_comp);
-    if (!index) {
-        goto error;
-    }
+    ctf_fs_ds_index index;
+    ctf_fs_ds_index_entry *prev_index_entry = nullptr;
+    auto totalPacketsSize = bt2c::DataLen::fromBytes(0);
 
-    for (i = 0; i < file_entry_count; i++) {
+    for (size_t i = 0; i < file_entry_count; i++) {
         struct ctf_packet_index *file_index = (struct ctf_packet_index *) file_pos;
-        uint64_t packet_size = be64toh(file_index->packet_size);
-
-        if (packet_size % CHAR_BIT) {
-            BT_COMP_LOGW("Invalid packet size encountered in LTTng trace index file");
-            goto error;
-        }
+        const auto packetSize = bt2c::DataLen::fromBits(be64toh(file_index->packet_size));
 
-        index_entry = ctf_fs_ds_index_entry_create(ds_file->self_comp, ds_file->log_level);
-        if (!index_entry) {
-            BT_COMP_LOGE_APPEND_CAUSE(ds_file->self_comp,
-                                      "Failed to create a ctf_fs_ds_index_entry.");
-            goto error;
+        if (packetSize.hasExtraBits()) {
+            BT_CPPLOGW_SPEC(ds_file->logger,
+                            "Invalid packet size encountered in LTTng trace index file");
+            return bt2s::nullopt;
         }
 
-        /* Set path to stream file. */
-        index_entry->path = file_info->path->str;
-
-        /* Convert size in bits to bytes. */
-        packet_size /= CHAR_BIT;
-        index_entry->packet_size = packet_size;
+        const auto offset = bt2c::DataLen::fromBytes(be64toh(file_index->offset));
 
-        index_entry->offset = be64toh(file_index->offset);
-        if (i != 0 && index_entry->offset < prev_index_entry->offset) {
-            BT_COMP_LOGW(
+        if (i != 0 && offset < prev_index_entry->offset) {
+            BT_CPPLOGW_SPEC(
+                ds_file->logger,
                 "Invalid, non-monotonic, packet offset encountered in LTTng trace index file: "
-                "previous offset=%" PRIu64 ", current offset=%" PRIu64,
-                prev_index_entry->offset, index_entry->offset);
-            goto error;
+                "previous offset={} bytes, current offset={} bytes",
+                prev_index_entry->offset.bytes(), offset.bytes());
+            return bt2s::nullopt;
         }
 
-        index_entry->timestamp_begin = be64toh(file_index->timestamp_begin);
-        index_entry->timestamp_end = be64toh(file_index->timestamp_end);
-        if (index_entry->timestamp_end < index_entry->timestamp_begin) {
-            BT_COMP_LOGW(
+        ctf_fs_ds_index_entry index_entry {file_info->path.c_str(), offset, packetSize};
+        index_entry.timestamp_begin = be64toh(file_index->timestamp_begin);
+        index_entry.timestamp_end = be64toh(file_index->timestamp_end);
+        if (index_entry.timestamp_end < index_entry.timestamp_begin) {
+            BT_CPPLOGW_SPEC(
+                ds_file->logger,
                 "Invalid packet time bounds encountered in LTTng trace index file (begin > end): "
-                "timestamp_begin=%" PRIu64 "timestamp_end=%" PRIu64,
-                index_entry->timestamp_begin, index_entry->timestamp_end);
-            goto error;
+                "timestamp_begin={}, timestamp_end={}",
+                index_entry.timestamp_begin, index_entry.timestamp_end);
+            return bt2s::nullopt;
         }
 
         /* Convert the packet's bound to nanoseconds since Epoch. */
-        ret = convert_cycles_to_ns(sc->default_clock_class, index_entry->timestamp_begin,
-                                   &index_entry->timestamp_begin_ns);
+        ret = convert_cycles_to_ns(sc->default_clock_class, index_entry.timestamp_begin,
+                                   &index_entry.timestamp_begin_ns);
         if (ret) {
-            BT_COMP_LOGI_STR(
+            BT_CPPLOGI_STR_SPEC(
+                ds_file->logger,
                 "Failed to convert raw timestamp to nanoseconds since Epoch during index parsing");
-            goto error;
+            return bt2s::nullopt;
         }
-        ret = convert_cycles_to_ns(sc->default_clock_class, index_entry->timestamp_end,
-                                   &index_entry->timestamp_end_ns);
+        ret = convert_cycles_to_ns(sc->default_clock_class, index_entry.timestamp_end,
+                                   &index_entry.timestamp_end_ns);
         if (ret) {
-            BT_COMP_LOGI_STR(
+            BT_CPPLOGI_STR_SPEC(
+                ds_file->logger,
                 "Failed to convert raw timestamp to nanoseconds since Epoch during LTTng trace index parsing");
-            goto error;
+            return bt2s::nullopt;
         }
 
         if (version_minor >= 1) {
-            index_entry->packet_seq_num = be64toh(file_index->packet_seq_num);
+            index_entry.packet_seq_num = be64toh(file_index->packet_seq_num);
         }
 
-        total_packets_size += packet_size;
+        totalPacketsSize += packetSize;
         file_pos += file_index_entry_size;
 
-        prev_index_entry = index_entry;
+        index.entries.emplace_back(index_entry);
 
-        /* Give ownership of `index_entry` to `index->entries`. */
-        g_ptr_array_add(index->entries, index_entry);
-        index_entry = NULL;
+        prev_index_entry = &index.entries.back();
     }
 
     /* Validate that the index addresses the complete stream. */
-    if (ds_file->file->size != total_packets_size) {
-        BT_COMP_LOGW("Invalid LTTng trace index file; indexed size != stream file size: "
-                     "file-size=%" PRIu64 ", total-packets-size=%" PRIu64,
-                     ds_file->file->size, total_packets_size);
-        goto error;
-    }
-end:
-    g_free(directory);
-    g_free(basename);
-    g_free(index_file_path);
-    if (index_basename) {
-        g_string_free(index_basename, TRUE);
-    }
-    if (mapped_file) {
-        g_mapped_file_unref(mapped_file);
+    if (ds_file->file->size != totalPacketsSize.bytes()) {
+        BT_CPPLOGW_SPEC(ds_file->logger,
+                        "Invalid LTTng trace index file; indexed size != stream file size: "
+                        "file-size={} bytes, total-packets-size={} bytes",
+                        ds_file->file->size, totalPacketsSize.bytes());
+        return bt2s::nullopt;
     }
+
     return index;
-error:
-    ctf_fs_ds_index_destroy(index);
-    g_free(index_entry);
-    index = NULL;
-    goto end;
 }
 
-static int init_index_entry(struct ctf_fs_ds_index_entry *entry, struct ctf_fs_ds_file *ds_file,
-                            struct ctf_msg_iter_packet_properties *props, off_t packet_size,
-                            off_t packet_offset)
+static int init_index_entry(ctf_fs_ds_index_entry& entry, struct ctf_fs_ds_file *ds_file,
+                            struct ctf_msg_iter_packet_properties *props)
 {
-    int ret = 0;
-    struct ctf_stream_class *sc;
-    bt_self_component *self_comp = ds_file->self_comp;
-    bt_logging_level log_level = ds_file->log_level;
-
-    sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, props->stream_class_id);
+    ctf_stream_class *sc =
+        ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, props->stream_class_id);
     BT_ASSERT(sc);
-    BT_ASSERT(packet_offset >= 0);
-    entry->offset = packet_offset;
-    BT_ASSERT(packet_size >= 0);
-    entry->packet_size = packet_size;
 
     if (props->snapshots.beginning_clock != UINT64_C(-1)) {
-        entry->timestamp_begin = props->snapshots.beginning_clock;
+        entry.timestamp_begin = props->snapshots.beginning_clock;
 
         /* Convert the packet's bound to nanoseconds since Epoch. */
-        ret = convert_cycles_to_ns(sc->default_clock_class, props->snapshots.beginning_clock,
-                                   &entry->timestamp_begin_ns);
+        int ret = convert_cycles_to_ns(sc->default_clock_class, props->snapshots.beginning_clock,
+                                       &entry.timestamp_begin_ns);
         if (ret) {
-            BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch.");
-            goto end;
+            BT_CPPLOGI_STR_SPEC(ds_file->logger,
+                                "Failed to convert raw timestamp to nanoseconds since Epoch.");
+            return ret;
         }
     } else {
-        entry->timestamp_begin = UINT64_C(-1);
-        entry->timestamp_begin_ns = UINT64_C(-1);
+        entry.timestamp_begin = UINT64_C(-1);
+        entry.timestamp_begin_ns = UINT64_C(-1);
     }
 
     if (props->snapshots.end_clock != UINT64_C(-1)) {
-        entry->timestamp_end = props->snapshots.end_clock;
+        entry.timestamp_end = props->snapshots.end_clock;
 
         /* Convert the packet's bound to nanoseconds since Epoch. */
-        ret = convert_cycles_to_ns(sc->default_clock_class, props->snapshots.end_clock,
-                                   &entry->timestamp_end_ns);
+        int ret = convert_cycles_to_ns(sc->default_clock_class, props->snapshots.end_clock,
+                                       &entry.timestamp_end_ns);
         if (ret) {
-            BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch.");
-            goto end;
+            BT_CPPLOGI_STR_SPEC(ds_file->logger,
+                                "Failed to convert raw timestamp to nanoseconds since Epoch.");
+            return ret;
         }
     } else {
-        entry->timestamp_end = UINT64_C(-1);
-        entry->timestamp_end_ns = UINT64_C(-1);
+        entry.timestamp_end = UINT64_C(-1);
+        entry.timestamp_end_ns = UINT64_C(-1);
     }
 
-end:
-    return ret;
+    return 0;
 }
 
-static struct ctf_fs_ds_index *build_index_from_stream_file(struct ctf_fs_ds_file *ds_file,
-                                                            struct ctf_fs_ds_file_info *file_info,
-                                                            struct ctf_msg_iter *msg_iter)
+static bt2s::optional<ctf_fs_ds_index>
+build_index_from_stream_file(struct ctf_fs_ds_file *ds_file, struct ctf_fs_ds_file_info *file_info,
+                             struct ctf_msg_iter *msg_iter)
 {
-    int ret;
-    struct ctf_fs_ds_index *index = NULL;
-    enum ctf_msg_iter_status iter_status = CTF_MSG_ITER_STATUS_OK;
-    off_t current_packet_offset_bytes = 0;
-    bt_self_component *self_comp = ds_file->self_comp;
-    bt_logging_level log_level = ds_file->log_level;
-
-    BT_COMP_LOGI("Indexing stream file %s", ds_file->file->path->str);
-
-    index = ctf_fs_ds_index_create(ds_file->log_level, ds_file->self_comp);
-    if (!index) {
-        goto error;
-    }
+    BT_CPPLOGI_SPEC(ds_file->logger, "Indexing stream file {}", ds_file->file->path);
+
+    ctf_fs_ds_index index;
+    auto currentPacketOffset = bt2c::DataLen::fromBytes(0);
 
     while (true) {
-        off_t current_packet_size_bytes;
-        struct ctf_fs_ds_index_entry *index_entry;
         struct ctf_msg_iter_packet_properties props;
 
-        if (current_packet_offset_bytes < 0) {
-            BT_COMP_LOGE_STR("Cannot get the current packet's offset.");
-            goto error;
-        } else if (current_packet_offset_bytes > ds_file->file->size) {
-            BT_COMP_LOGE_STR("Unexpected current packet's offset (larger than file).");
-            goto error;
-        } else if (current_packet_offset_bytes == ds_file->file->size) {
+        if (currentPacketOffset.bytes() > ds_file->file->size) {
+            BT_CPPLOGE_STR_SPEC(ds_file->logger,
+                                "Unexpected current packet's offset (larger than file).");
+            return bt2s::nullopt;
+        } else if (currentPacketOffset.bytes() == ds_file->file->size) {
             /* No more data */
             break;
         }
 
-        iter_status = ctf_msg_iter_seek(msg_iter, current_packet_offset_bytes);
+        ctf_msg_iter_status iter_status = ctf_msg_iter_seek(msg_iter, currentPacketOffset.bytes());
         if (iter_status != CTF_MSG_ITER_STATUS_OK) {
-            goto error;
+            return bt2s::nullopt;
         }
 
         iter_status = ctf_msg_iter_get_packet_properties(msg_iter, &props);
         if (iter_status != CTF_MSG_ITER_STATUS_OK) {
-            goto error;
+            return bt2s::nullopt;
         }
 
-        if (props.exp_packet_total_size >= 0) {
-            current_packet_size_bytes = (uint64_t) props.exp_packet_total_size / 8;
-        } else {
-            current_packet_size_bytes = ds_file->file->size;
-        }
-
-        if (current_packet_offset_bytes + current_packet_size_bytes > ds_file->file->size) {
-            BT_COMP_LOGW("Invalid packet size reported in file: stream=\"%s\", "
-                         "packet-offset=%jd, packet-size-bytes=%jd, "
-                         "file-size=%jd",
-                         ds_file->file->path->str, (intmax_t) current_packet_offset_bytes,
-                         (intmax_t) current_packet_size_bytes, (intmax_t) ds_file->file->size);
-            goto error;
-        }
-
-        index_entry = ctf_fs_ds_index_entry_create(ds_file->self_comp, ds_file->log_level);
-        if (!index_entry) {
-            BT_COMP_LOGE_APPEND_CAUSE(ds_file->self_comp,
-                                      "Failed to create a ctf_fs_ds_index_entry.");
-            goto error;
+        /*
+         * Get the current packet size from the packet header, if set.  Else,
+         * assume there is a single packet in the file, so take the file size
+         * as the packet size.
+         */
+        const auto currentPacketSize = props.exp_packet_total_size >= 0 ?
+                                           bt2c::DataLen::fromBits(props.exp_packet_total_size) :
+                                           bt2c::DataLen::fromBytes(ds_file->file->size);
+
+        if ((currentPacketOffset + currentPacketSize).bytes() > ds_file->file->size) {
+            BT_CPPLOGW_SPEC(ds_file->logger,
+                            "Invalid packet size reported in file: stream=\"{}\", "
+                            "packet-offset-bytes={}, packet-size-bytes={}, "
+                            "file-size-bytes={}",
+                            ds_file->file->path, currentPacketOffset.bytes(),
+                            currentPacketSize.bytes(), ds_file->file->size);
+            return bt2s::nullopt;
         }
 
-        /* Set path to stream file. */
-        index_entry->path = file_info->path->str;
+        ctf_fs_ds_index_entry index_entry {file_info->path, currentPacketOffset, currentPacketSize};
 
-        ret = init_index_entry(index_entry, ds_file, &props, current_packet_size_bytes,
-                               current_packet_offset_bytes);
+        int ret = init_index_entry(index_entry, ds_file, &props);
         if (ret) {
-            g_free(index_entry);
-            goto error;
+            return bt2s::nullopt;
         }
 
-        g_ptr_array_add(index->entries, index_entry);
+        index.entries.emplace_back(index_entry);
 
-        current_packet_offset_bytes += current_packet_size_bytes;
-        BT_COMP_LOGD("Seeking to next packet: current-packet-offset=%jd, "
-                     "next-packet-offset=%jd",
-                     (intmax_t) (current_packet_offset_bytes - current_packet_size_bytes),
-                     (intmax_t) current_packet_offset_bytes);
+        currentPacketOffset += currentPacketSize;
+        BT_CPPLOGD_SPEC(ds_file->logger,
+                        "Seeking to next packet: current-packet-offset-bytes={}, "
+                        "next-packet-offset-bytes={}",
+                        (currentPacketOffset - currentPacketSize).bytes(),
+                        currentPacketOffset.bytes());
     }
 
-end:
     return index;
-
-error:
-    ctf_fs_ds_index_destroy(index);
-    index = NULL;
-    goto end;
 }
 
-BT_HIDDEN
-struct ctf_fs_ds_file *ctf_fs_ds_file_create(struct ctf_fs_trace *ctf_fs_trace, bt_stream *stream,
-                                             const char *path, bt_logging_level log_level)
+ctf_fs_ds_file::UP ctf_fs_ds_file_create(struct ctf_fs_trace *ctf_fs_trace,
+                                         bt2::Stream::Shared stream, const char *path,
+                                         const bt2c::Logger& parentLogger)
 {
-    int ret;
-    const size_t offset_align = bt_mmap_get_offset_align_size(log_level);
-    struct ctf_fs_ds_file *ds_file = g_new0(struct ctf_fs_ds_file, 1);
-
-    if (!ds_file) {
-        goto error;
-    }
-
-    ds_file->log_level = log_level;
-    ds_file->self_comp = ctf_fs_trace->self_comp;
-    ds_file->file = ctf_fs_file_create(log_level, ds_file->self_comp);
-    if (!ds_file->file) {
-        goto error;
-    }
+    auto ds_file = bt2s::make_unique<ctf_fs_ds_file>(parentLogger);
 
-    ds_file->stream = stream;
-    bt_stream_get_ref(ds_file->stream);
-    ds_file->metadata = ctf_fs_trace->metadata;
-    g_string_assign(ds_file->file->path, path);
-    ret = ctf_fs_file_open(ds_file->file, "rb");
+    ds_file->file = bt2s::make_unique<ctf_fs_file>(parentLogger);
+    ds_file->stream = std::move(stream);
+    ds_file->metadata = ctf_fs_trace->metadata.get();
+    ds_file->file->path = path;
+    int ret = ctf_fs_file_open(ds_file->file.get(), "rb");
     if (ret) {
-        goto error;
+        return nullptr;
     }
 
+    const size_t offset_align =
+        bt_mmap_get_offset_align_size(static_cast<int>(ds_file->logger.level()));
     ds_file->mmap_max_len = offset_align * 2048;
 
-    goto end;
-
-error:
-    /* Do not touch "borrowed" file. */
-    ctf_fs_ds_file_destroy(ds_file);
-    ds_file = NULL;
-
-end:
     return ds_file;
 }
 
-BT_HIDDEN
-struct ctf_fs_ds_index *ctf_fs_ds_file_build_index(struct ctf_fs_ds_file *ds_file,
-                                                   struct ctf_fs_ds_file_info *file_info,
-                                                   struct ctf_msg_iter *msg_iter)
+bt2s::optional<ctf_fs_ds_index> ctf_fs_ds_file_build_index(struct ctf_fs_ds_file *ds_file,
+                                                           struct ctf_fs_ds_file_info *file_info,
+                                                           struct ctf_msg_iter *msg_iter)
 {
-    struct ctf_fs_ds_index *index;
-    bt_self_component *self_comp = ds_file->self_comp;
-    bt_logging_level log_level = ds_file->log_level;
-
-    index = build_index_from_idx_file(ds_file, file_info, msg_iter);
+    auto index = build_index_from_idx_file(ds_file, file_info, msg_iter);
     if (index) {
-        goto end;
+        return index;
     }
 
-    BT_COMP_LOGI("Failed to build index from .index file; "
-                 "falling back to stream indexing.");
-    index = build_index_from_stream_file(ds_file, file_info, msg_iter);
-end:
-    return index;
+    BT_CPPLOGI_SPEC(ds_file->logger, "Failed to build index from .index file; "
+                                     "falling back to stream indexing.");
+    return build_index_from_stream_file(ds_file, file_info, msg_iter);
 }
 
-BT_HIDDEN
-struct ctf_fs_ds_index *ctf_fs_ds_index_create(bt_logging_level log_level,
-                                               bt_self_component *self_comp)
+ctf_fs_ds_file::~ctf_fs_ds_file()
 {
-    struct ctf_fs_ds_index *index = g_new0(struct ctf_fs_ds_index, 1);
-
-    if (!index) {
-        BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp, "Failed to allocate index");
-        goto error;
-    }
-
-    index->entries = g_ptr_array_new_with_free_func((GDestroyNotify) g_free);
-    if (!index->entries) {
-        BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                            "Failed to allocate index entries.");
-        goto error;
-    }
-
-    goto end;
-
-error:
-    ctf_fs_ds_index_destroy(index);
-    index = NULL;
-end:
-    return index;
+    (void) ds_file_munmap(this);
 }
 
-BT_HIDDEN
-void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *ds_file)
+ctf_fs_ds_file_info::UP ctf_fs_ds_file_info_create(const char *path, int64_t begin_ns)
 {
-    if (!ds_file) {
-        return;
-    }
-
-    bt_stream_put_ref(ds_file->stream);
-    (void) ds_file_munmap(ds_file);
-
-    if (ds_file->file) {
-        ctf_fs_file_destroy(ds_file->file);
-    }
+    ctf_fs_ds_file_info::UP ds_file_info = bt2s::make_unique<ctf_fs_ds_file_info>();
 
-    g_free(ds_file);
+    ds_file_info->path = path;
+    ds_file_info->begin_ns = begin_ns;
+    return ds_file_info;
 }
 
-BT_HIDDEN
-void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index)
+void ctf_fs_ds_file_group::insert_ds_file_info_sorted(ctf_fs_ds_file_info::UP ds_file_info)
 {
-    if (!index) {
-        return;
-    }
+    /* Find the spot where to insert this ds_file_info. */
+    auto it = this->ds_file_infos.begin();
+
+    for (; it != this->ds_file_infos.end(); ++it) {
+        const ctf_fs_ds_file_info& other_ds_file_info = **it;
 
-    if (index->entries) {
-        g_ptr_array_free(index->entries, TRUE);
+        if (ds_file_info->begin_ns < other_ds_file_info.begin_ns) {
+            break;
+        }
     }
-    g_free(index);
+
+    this->ds_file_infos.insert(it, std::move(ds_file_info));
 }
index 5723d760e860a7cd1e47d71f20617f54af47817e..05a6db13538049d4ab194dd0695e03fa9a3f852e 100644 (file)
 #ifndef CTF_FS_DS_FILE_H
 #define CTF_FS_DS_FILE_H
 
-#include <stdio.h>
-#include <stdbool.h>
+#include <memory>
+#include <string>
+#include <vector>
+
 #include <glib.h>
-#include "common/macros.h"
+#include <stdio.h>
+
 #include <babeltrace2/babeltrace.h>
 
-#include "../common/msg-iter/msg-iter.hpp"
-#include "lttng-index.hpp"
+#include "cpp-common/bt2/trace-ir.hpp"
+#include "cpp-common/bt2c/data-len.hpp"
+#include "cpp-common/bt2c/logging.hpp"
 
-struct ctf_fs_component;
-struct ctf_fs_file;
-struct ctf_fs_trace;
-struct ctf_fs_ds_file;
-struct ctf_fs_ds_file_group;
-struct ctf_fs_ds_group_medops_data;
+#include "../common/src/msg-iter/msg-iter.hpp"
+#include "file.hpp"
 
 struct ctf_fs_ds_file_info
 {
-    /* Owned by this. */
-    GString *path;
+    using UP = std::unique_ptr<ctf_fs_ds_file_info>;
+
+    std::string path;
 
     /* Guaranteed to be set, as opposed to the index. */
-    int64_t begin_ns;
+    int64_t begin_ns = 0;
 };
 
-struct ctf_fs_metadata;
-
 struct ctf_fs_ds_file
 {
-    bt_logging_level log_level;
+    using UP = std::unique_ptr<ctf_fs_ds_file>;
 
-    /* Weak */
-    bt_self_component *self_comp;
+    explicit ctf_fs_ds_file(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.FS/DS"}
+    {
+    }
+
+    ctf_fs_ds_file(const ctf_fs_ds_file&) = delete;
+    ctf_fs_ds_file& operator=(const ctf_fs_ds_file&) = delete;
+    ~ctf_fs_ds_file();
+
+    bt2c::Logger logger;
 
     /* Weak */
-    struct ctf_fs_metadata *metadata;
+    struct ctf_fs_metadata *metadata = nullptr;
 
-    /* Owned by this */
-    struct ctf_fs_file *file;
+    ctf_fs_file::UP file;
 
-    /* Owned by this */
-    bt_stream *stream;
+    bt2::Stream::Shared stream;
 
-    void *mmap_addr;
+    void *mmap_addr = nullptr;
 
     /*
      * Max length of chunk to mmap() when updating the current mapping.
      * This value must be page-aligned.
      */
-    size_t mmap_max_len;
+    size_t mmap_max_len = 0;
 
     /* Length of the current mapping. Never exceeds the file's length. */
-    size_t mmap_len;
+    size_t mmap_len = 0;
 
     /* Offset in the file where the current mapping starts. */
-    off_t mmap_offset_in_file;
+    off_t mmap_offset_in_file = 0;
 
     /*
      * Offset, in the current mapping, of the address to return on the next
      * request.
      */
-    off_t request_offset_in_mapping;
+    off_t request_offset_in_mapping = 0;
+};
+
+struct ctf_fs_ds_index_entry
+{
+    ctf_fs_ds_index_entry(const bt2c::CStringView pathParam, const bt2c::DataLen offsetParam,
+                          const bt2c::DataLen packetSizeParam) noexcept :
+        path {pathParam},
+        offset {offsetParam}, packetSize {packetSizeParam}
+    {
+    }
+
+    /* Weak, belongs to ctf_fs_ds_file_info. */
+    const char *path;
+
+    /* Position of the packet from the beginning of the file. */
+    bt2c::DataLen offset;
+
+    /* Size of the packet. */
+    bt2c::DataLen packetSize;
+
+    /*
+     * Extracted from the packet context, relative to the respective fields'
+     * mapped clock classes (in cycles).
+     */
+    uint64_t timestamp_begin = 0, timestamp_end = 0;
+
+    /*
+     * Converted from the packet context, relative to the trace's EPOCH
+     * (in ns since EPOCH).
+     */
+    int64_t timestamp_begin_ns = 0, timestamp_end_ns = 0;
+
+    /*
+     * Packet sequence number, or UINT64_MAX if not present in the index.
+     */
+    uint64_t packet_seq_num = UINT64_MAX;
 };
 
-BT_HIDDEN
-struct ctf_fs_ds_file *ctf_fs_ds_file_create(struct ctf_fs_trace *ctf_fs_trace, bt_stream *stream,
-                                             const char *path, bt_logging_level log_level);
+struct ctf_fs_ds_index
+{
+    std::vector<ctf_fs_ds_index_entry> entries;
+};
 
-BT_HIDDEN
-void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *stream);
+struct ctf_fs_ds_file_group
+{
+    using UP = std::unique_ptr<ctf_fs_ds_file_group>;
 
-BT_HIDDEN
-struct ctf_fs_ds_index *ctf_fs_ds_file_build_index(struct ctf_fs_ds_file *ds_file,
-                                                   struct ctf_fs_ds_file_info *ds_file_info,
-                                                   struct ctf_msg_iter *msg_iter);
+    explicit ctf_fs_ds_file_group(struct ctf_fs_trace * const trace,
+                                  ctf_stream_class * const scParam, const uint64_t streamInstanceId,
+                                  ctf_fs_ds_index indexParam) noexcept :
 
-BT_HIDDEN
-struct ctf_fs_ds_index *ctf_fs_ds_index_create(bt_logging_level log_level,
-                                               bt_self_component *self_comp);
+        sc {scParam},
+        stream_id(streamInstanceId), ctf_fs_trace {trace}, index {std::move(indexParam)}
+
+    {
+    }
+
+    /*
+     * Insert ds_file_info in the list of ds_file_infos at the right
+     * place to keep it sorted.
+     */
+    void insert_ds_file_info_sorted(ctf_fs_ds_file_info::UP ds_file_info);
+
+    /*
+     * This is an _ordered_ array of data stream file infos which
+     * belong to this group (a single stream instance).
+     *
+     * You can call ctf_fs_ds_file_create() with one of those paths
+     * and the trace IR stream below.
+     */
+    std::vector<ctf_fs_ds_file_info::UP> ds_file_infos;
+
+    /* Owned by this */
+    struct ctf_stream_class *sc = nullptr;
 
-BT_HIDDEN
-void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index);
+    bt2::Stream::Shared stream;
+
+    /* Stream (instance) ID; -1ULL means none */
+    uint64_t stream_id = 0;
+
+    /* Weak, belongs to component */
+    struct ctf_fs_trace *ctf_fs_trace = nullptr;
+
+    ctf_fs_ds_index index;
+};
+
+ctf_fs_ds_file::UP ctf_fs_ds_file_create(ctf_fs_trace *ctf_fs_trace, bt2::Stream::Shared stream,
+                                         const char *path, const bt2c::Logger& logger);
+
+bt2s::optional<ctf_fs_ds_index> ctf_fs_ds_file_build_index(struct ctf_fs_ds_file *ds_file,
+                                                           struct ctf_fs_ds_file_info *ds_file_info,
+                                                           struct ctf_msg_iter *msg_iter);
+
+ctf_fs_ds_file_info::UP ctf_fs_ds_file_info_create(const char *path, int64_t begin_ns);
 
 /*
  * Medium operations to iterate on a single ctf_fs_ds_file.
@@ -105,18 +184,21 @@ extern struct ctf_msg_iter_medium_ops ctf_fs_ds_file_medops;
  * The data pointer when using these medops must be a pointer to a ctf_fs_ds
  * group_medops_data structure.
  */
-BT_HIDDEN
 extern struct ctf_msg_iter_medium_ops ctf_fs_ds_group_medops;
 
-BT_HIDDEN
-enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_data_create(
-    struct ctf_fs_ds_file_group *ds_file_group, bt_self_message_iterator *self_msg_iter,
-    bt_logging_level log_level, struct ctf_fs_ds_group_medops_data **out);
+struct ctf_fs_ds_group_medops_data_deleter
+{
+    void operator()(struct ctf_fs_ds_group_medops_data *data) noexcept;
+};
+
+using ctf_fs_ds_group_medops_data_up =
+    std::unique_ptr<ctf_fs_ds_group_medops_data, ctf_fs_ds_group_medops_data_deleter>;
 
-BT_HIDDEN
-void ctf_fs_ds_group_medops_data_reset(struct ctf_fs_ds_group_medops_data *data);
+enum ctf_msg_iter_medium_status
+ctf_fs_ds_group_medops_data_create(struct ctf_fs_ds_file_group *ds_file_group,
+                                   bt_self_message_iterator *self_msg_iter,
+                                   const bt2c::Logger& logger, ctf_fs_ds_group_medops_data_up& out);
 
-BT_HIDDEN
-void ctf_fs_ds_group_medops_data_destroy(struct ctf_fs_ds_group_medops_data *data);
+void ctf_fs_ds_group_medops_data_reset(struct ctf_fs_ds_group_medops_data *data);
 
 #endif /* CTF_FS_DS_FILE_H */
index 6361123cd6e5144491bdb36e78ce34f7759108df..13e279cd38b1fb085318f36c2bbb31555ea3995d 100644 (file)
  * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
  */
 
-#define BT_COMP_LOG_SELF_COMP (file->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (file->log_level)
-#define BT_LOG_TAG            "PLUGIN/SRC.CTF.FS/FILE"
-#include "logging/comp-logging.h"
-
+#include <glib.h>
 #include <stdio.h>
-#include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
-#include <glib.h>
-#include "file.hpp"
-
-BT_HIDDEN
-void ctf_fs_file_destroy(struct ctf_fs_file *file)
-{
-    if (!file) {
-        return;
-    }
-
-    if (file->fp) {
-        BT_COMP_LOGD("Closing file \"%s\" (%p)", file->path ? file->path->str : NULL, file->fp);
-
-        if (fclose(file->fp)) {
-            BT_COMP_LOGE("Cannot close file \"%s\": %s", file->path ? file->path->str : "NULL",
-                         strerror(errno));
-        }
-    }
-
-    if (file->path) {
-        g_string_free(file->path, TRUE);
-    }
-
-    g_free(file);
-}
-
-BT_HIDDEN
-struct ctf_fs_file *ctf_fs_file_create(bt_logging_level log_level, bt_self_component *self_comp)
-{
-    struct ctf_fs_file *file = g_new0(struct ctf_fs_file, 1);
-
-    if (!file) {
-        goto error;
-    }
 
-    file->log_level = log_level;
-    file->self_comp = self_comp;
-    file->path = g_string_new(NULL);
-    if (!file->path) {
-        goto error;
-    }
-
-    goto end;
+#include "cpp-common/vendor/fmt/format.h"
 
-error:
-    ctf_fs_file_destroy(file);
-    file = NULL;
-
-end:
-    return file;
-}
+#include "file.hpp"
 
-BT_HIDDEN
 int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode)
 {
     int ret = 0;
     struct stat stat;
 
-    BT_COMP_LOGI("Opening file \"%s\" with mode \"%s\"", file->path->str, mode);
-    file->fp = fopen(file->path->str, mode);
+    BT_CPPLOGI_SPEC(file->logger, "Opening file \"{}\" with mode \"{}\"", file->path, mode);
+    file->fp.reset(fopen(file->path.c_str(), mode));
     if (!file->fp) {
-        BT_COMP_LOGE_APPEND_CAUSE_ERRNO(file->self_comp, "Cannot open file", ": path=%s, mode=%s",
-                                        file->path->str, mode);
+        BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(file->logger, "Cannot open file", ": path={}, mode={}",
+                                           file->path, mode);
         goto error;
     }
 
-    BT_COMP_LOGI("Opened file: %p", file->fp);
+    BT_CPPLOGI_SPEC(file->logger, "Opened file: {}", fmt::ptr(file->fp));
 
-    if (fstat(fileno(file->fp), &stat)) {
-        BT_COMP_LOGE_APPEND_CAUSE_ERRNO(file->self_comp, "Cannot get file information", ": path=%s",
-                                        file->path->str);
+    if (fstat(fileno(file->fp.get()), &stat)) {
+        BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(file->logger, "Cannot get file information", ": path={}",
+                                           file->path);
         goto error;
     }
 
     file->size = stat.st_size;
-    BT_COMP_LOGI("File is %jd bytes", (intmax_t) file->size);
+    BT_CPPLOGI_SPEC(file->logger, "File is {} bytes", (intmax_t) file->size);
     goto end;
 
 error:
     ret = -1;
 
-    if (file->fp) {
-        if (fclose(file->fp)) {
-            BT_COMP_LOGE("Cannot close file \"%s\": %s", file->path->str, strerror(errno));
-        }
-    }
-
 end:
     return ret;
 }
index 59fdd48792711f462fea2f5678ddda5ebe2d3923..ee6441cbe6bdaa9c8887d49ec7e30e5baea6d1aa 100644 (file)
@@ -7,18 +7,32 @@
 #ifndef CTF_FS_FILE_H
 #define CTF_FS_FILE_H
 
-#include <stdio.h>
-#include <glib.h>
-#include "common/macros.h"
-#include "fs.hpp"
+#include <memory>
+#include <string>
 
-BT_HIDDEN
-void ctf_fs_file_destroy(struct ctf_fs_file *file);
+#include <babeltrace2/babeltrace.h>
 
-BT_HIDDEN
-struct ctf_fs_file *ctf_fs_file_create(bt_logging_level log_level, bt_self_component *self_comp);
+#include "cpp-common/bt2c/libc-up.hpp"
+#include "cpp-common/bt2c/logging.hpp"
+
+struct ctf_fs_file
+{
+    using UP = std::unique_ptr<ctf_fs_file>;
+
+    explicit ctf_fs_file(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.FS/FILE"}
+    {
+    }
+
+    bt2c::Logger logger;
+
+    std::string path;
+
+    bt2c::FileUP fp;
+
+    off_t size = 0;
+};
 
-BT_HIDDEN
 int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode);
 
 #endif /* CTF_FS_FILE_H */
index d26bbdfbfb1d6ed1ab45c88eb9174094f466e70a..e0f734405948b1ba92050598f4e864f18417a337 100644 (file)
@@ -7,27 +7,27 @@
  * Babeltrace CTF file system Reader Component
  */
 
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL   log_level
-#define BT_LOG_TAG            "PLUGIN/SRC.CTF.FS"
-#include "logging/comp-logging.h"
+#include <sstream>
 
-#include "common/common.h"
-#include <babeltrace2/babeltrace.h>
-#include "common/uuid.h"
 #include <glib.h>
+
+#include <babeltrace2/babeltrace.h>
+
 #include "common/assert.h"
-#include <inttypes.h>
-#include <stdbool.h>
-#include "fs.hpp"
-#include "metadata.hpp"
+#include "common/common.h"
+#include "common/uuid.h"
+#include "cpp-common/bt2c/glib-up.hpp"
+#include "cpp-common/bt2s/make-unique.hpp"
+
+#include "plugins/common/param-validation/param-validation.h"
+
+#include "../common/src/metadata/tsdl/ctf-meta-configure-ir-trace.hpp"
+#include "../common/src/msg-iter/msg-iter.hpp"
 #include "data-stream-file.hpp"
 #include "file.hpp"
-#include "../common/metadata/decoder.hpp"
-#include "../common/metadata/ctf-meta-configure-ir-trace.hpp"
-#include "../common/msg-iter/msg-iter.hpp"
+#include "fs.hpp"
+#include "metadata.hpp"
 #include "query.hpp"
-#include "plugins/common/param-validation/param-validation.h"
 
 struct tracer_info
 {
@@ -37,31 +37,12 @@ struct tracer_info
     int64_t patch;
 };
 
-static void ctf_fs_msg_iter_data_destroy(struct ctf_fs_msg_iter_data *msg_iter_data)
-{
-    if (!msg_iter_data) {
-        return;
-    }
-
-    if (msg_iter_data->msg_iter) {
-        ctf_msg_iter_destroy(msg_iter_data->msg_iter);
-    }
-
-    if (msg_iter_data->msg_iter_medops_data) {
-        ctf_fs_ds_group_medops_data_destroy(msg_iter_data->msg_iter_medops_data);
-    }
-
-    g_free(msg_iter_data);
-}
-
 static bt_message_iterator_class_next_method_status
 ctf_fs_iterator_next_one(struct ctf_fs_msg_iter_data *msg_iter_data, const bt_message **out_msg)
 {
+    const auto msg_iter_status =
+        ctf_msg_iter_get_next_message(msg_iter_data->msg_iter.get(), out_msg);
     bt_message_iterator_class_next_method_status status;
-    enum ctf_msg_iter_status msg_iter_status;
-    bt_logging_level log_level = msg_iter_data->log_level;
-
-    msg_iter_status = ctf_msg_iter_get_next_message(msg_iter_data->msg_iter, out_msg);
 
     switch (msg_iter_status) {
     case CTF_MSG_ITER_STATUS_OK:
@@ -82,14 +63,14 @@ ctf_fs_iterator_next_one(struct ctf_fs_msg_iter_data *msg_iter_data, const bt_me
         bt_common_abort();
 
     case CTF_MSG_ITER_STATUS_ERROR:
-        BT_MSG_ITER_LOGE_APPEND_CAUSE(msg_iter_data->self_msg_iter,
-                                      "Failed to get next message from CTF message iterator.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_iter_data->logger,
+                                     "Failed to get next message from CTF message iterator.");
         status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
         break;
 
     case CTF_MSG_ITER_STATUS_MEMORY_ERROR:
-        BT_MSG_ITER_LOGE_APPEND_CAUSE(msg_iter_data->self_msg_iter,
-                                      "Failed to get next message from CTF message iterator.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_iter_data->logger,
+                                     "Failed to get next message from CTF message iterator.");
         status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
         break;
 
@@ -100,86 +81,95 @@ ctf_fs_iterator_next_one(struct ctf_fs_msg_iter_data *msg_iter_data, const bt_me
     return status;
 }
 
-BT_HIDDEN
 bt_message_iterator_class_next_method_status
 ctf_fs_iterator_next(bt_self_message_iterator *iterator, bt_message_array_const msgs,
                      uint64_t capacity, uint64_t *count)
 {
-    bt_message_iterator_class_next_method_status status;
-    struct ctf_fs_msg_iter_data *msg_iter_data =
-        (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(iterator);
-    uint64_t i = 0;
+    try {
+        struct ctf_fs_msg_iter_data *msg_iter_data =
+            (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(iterator);
 
-    if (G_UNLIKELY(msg_iter_data->next_saved_error)) {
-        /*
+        if (G_UNLIKELY(msg_iter_data->next_saved_error)) {
+            /*
          * Last time we were called, we hit an error but had some
          * messages to deliver, so we stashed the error here.  Return
          * it now.
          */
-        BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(msg_iter_data->next_saved_error);
-        status = msg_iter_data->next_saved_status;
-        goto end;
-    }
-
-    do {
-        status = ctf_fs_iterator_next_one(msg_iter_data, &msgs[i]);
-        if (status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK) {
-            i++;
+            BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(msg_iter_data->next_saved_error);
+            return msg_iter_data->next_saved_status;
         }
-    } while (i < capacity && status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK);
 
-    if (i > 0) {
-        /*
+        bt_message_iterator_class_next_method_status status;
+        uint64_t i = 0;
+
+        do {
+            status = ctf_fs_iterator_next_one(msg_iter_data, &msgs[i]);
+            if (status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK) {
+                i++;
+            }
+        } while (i < capacity && status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK);
+
+        if (i > 0) {
+            /*
          * Even if ctf_fs_iterator_next_one() returned something
          * else than BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK, we
          * accumulated message objects in the output
          * message array, so we need to return
          * BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK so that they are
-         * transfered to downstream. This other status occurs
+         * transferred to downstream. This other status occurs
          * again the next time muxer_msg_iter_do_next() is
          * called, possibly without any accumulated
          * message, in which case we'll return it.
          */
-        if (status < 0) {
-            /*
+            if (status < 0) {
+                /*
              * Save this error for the next _next call.  Assume that
              * this component always appends error causes when
              * returning an error status code, which will cause the
              * current thread error to be non-NULL.
              */
-            msg_iter_data->next_saved_error = bt_current_thread_take_error();
-            BT_ASSERT(msg_iter_data->next_saved_error);
-            msg_iter_data->next_saved_status = status;
+                msg_iter_data->next_saved_error = bt_current_thread_take_error();
+                BT_ASSERT(msg_iter_data->next_saved_error);
+                msg_iter_data->next_saved_status = status;
+            }
+
+            *count = i;
+            status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
         }
 
-        *count = i;
-        status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+        return status;
+        return status;
+    } catch (const std::bad_alloc&) {
+        return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
     }
-
-end:
-    return status;
 }
 
-BT_HIDDEN
 bt_message_iterator_class_seek_beginning_method_status
 ctf_fs_iterator_seek_beginning(bt_self_message_iterator *it)
 {
-    struct ctf_fs_msg_iter_data *msg_iter_data =
-        (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(it);
+    try {
+        struct ctf_fs_msg_iter_data *msg_iter_data =
+            (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(it);
 
-    BT_ASSERT(msg_iter_data);
+        BT_ASSERT(msg_iter_data);
 
-    ctf_msg_iter_reset(msg_iter_data->msg_iter);
-    ctf_fs_ds_group_medops_data_reset(msg_iter_data->msg_iter_medops_data);
+        ctf_msg_iter_reset(msg_iter_data->msg_iter.get());
+        ctf_fs_ds_group_medops_data_reset(msg_iter_data->msg_iter_medops_data.get());
 
-    return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_OK;
+        return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_OK;
+    } catch (const std::bad_alloc&) {
+        return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_ERROR;
+    }
 }
 
-BT_HIDDEN
 void ctf_fs_iterator_finalize(bt_self_message_iterator *it)
 {
-    ctf_fs_msg_iter_data_destroy(
-        (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(it));
+    ctf_fs_msg_iter_data::UP {
+        (static_cast<ctf_fs_msg_iter_data *>(bt_self_message_iterator_get_data(it)))};
 }
 
 static bt_message_iterator_class_initialize_method_status
@@ -199,173 +189,69 @@ ctf_msg_iter_medium_status_to_msg_iter_initialize_status(enum ctf_msg_iter_mediu
     bt_common_abort();
 }
 
-BT_HIDDEN
 bt_message_iterator_class_initialize_method_status
 ctf_fs_iterator_init(bt_self_message_iterator *self_msg_iter,
                      bt_self_message_iterator_configuration *config,
                      bt_self_component_port_output *self_port)
 {
-    struct ctf_fs_port_data *port_data;
-    struct ctf_fs_msg_iter_data *msg_iter_data = NULL;
-    bt_message_iterator_class_initialize_method_status status;
-    bt_logging_level log_level;
-    enum ctf_msg_iter_medium_status medium_status;
-    bt_self_component *self_comp = bt_self_message_iterator_borrow_component(self_msg_iter);
-
-    port_data = (struct ctf_fs_port_data *) bt_self_component_port_get_data(
-        bt_self_component_port_output_as_self_component_port(self_port));
-    BT_ASSERT(port_data);
-    log_level = port_data->ctf_fs->log_level;
-    msg_iter_data = g_new0(struct ctf_fs_msg_iter_data, 1);
-    if (!msg_iter_data) {
-        status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
-    }
-
-    msg_iter_data->log_level = log_level;
-    msg_iter_data->self_comp = self_comp;
-    msg_iter_data->self_msg_iter = self_msg_iter;
-    msg_iter_data->ds_file_group = port_data->ds_file_group;
-
-    medium_status =
-        ctf_fs_ds_group_medops_data_create(msg_iter_data->ds_file_group, self_msg_iter, log_level,
-                                           &msg_iter_data->msg_iter_medops_data);
-    BT_ASSERT(medium_status == CTF_MSG_ITER_MEDIUM_STATUS_OK ||
-              medium_status == CTF_MSG_ITER_MEDIUM_STATUS_ERROR ||
-              medium_status == CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR);
-    if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-        BT_MSG_ITER_LOGE_APPEND_CAUSE(self_msg_iter, "Failed to create ctf_fs_ds_group_medops");
-        status = ctf_msg_iter_medium_status_to_msg_iter_initialize_status(medium_status);
-        goto error;
-    }
-
-    msg_iter_data->msg_iter = ctf_msg_iter_create(
-        msg_iter_data->ds_file_group->ctf_fs_trace->metadata->tc,
-        bt_common_get_page_size(msg_iter_data->log_level) * 8, ctf_fs_ds_group_medops,
-        msg_iter_data->msg_iter_medops_data, msg_iter_data->log_level, self_comp, self_msg_iter);
-    if (!msg_iter_data->msg_iter) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot create a CTF message iterator.");
-        status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
-    }
+    try {
+        ctf_fs_port_data *port_data = (struct ctf_fs_port_data *) bt_self_component_port_get_data(
+            bt_self_component_port_output_as_self_component_port(self_port));
+        BT_ASSERT(port_data);
+
+        auto msg_iter_data = bt2s::make_unique<ctf_fs_msg_iter_data>(self_msg_iter);
+        msg_iter_data->ds_file_group = port_data->ds_file_group;
+
+        ctf_msg_iter_medium_status medium_status = ctf_fs_ds_group_medops_data_create(
+            msg_iter_data->ds_file_group, self_msg_iter, msg_iter_data->logger,
+            msg_iter_data->msg_iter_medops_data);
+        BT_ASSERT(medium_status == CTF_MSG_ITER_MEDIUM_STATUS_OK ||
+                  medium_status == CTF_MSG_ITER_MEDIUM_STATUS_ERROR ||
+                  medium_status == CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR);
+        if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_iter_data->logger,
+                                         "Failed to create ctf_fs_ds_group_medops");
+            return ctf_msg_iter_medium_status_to_msg_iter_initialize_status(medium_status);
+        }
 
-    /*
+        msg_iter_data->msg_iter = ctf_msg_iter_create(
+            msg_iter_data->ds_file_group->ctf_fs_trace->metadata->tc,
+            bt_common_get_page_size(static_cast<int>(msg_iter_data->logger.level())) * 8,
+            ctf_fs_ds_group_medops, msg_iter_data->msg_iter_medops_data.get(), self_msg_iter,
+            msg_iter_data->logger);
+        if (!msg_iter_data->msg_iter) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(msg_iter_data->logger,
+                                         "Cannot create a CTF message iterator.");
+            return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+        }
+
+        /*
      * This iterator can seek forward if its stream class has a default
      * clock class.
      */
-    if (msg_iter_data->ds_file_group->sc->default_clock_class) {
-        bt_self_message_iterator_configuration_set_can_seek_forward(config, true);
-    }
-
-    bt_self_message_iterator_set_data(self_msg_iter, msg_iter_data);
-    msg_iter_data = NULL;
-
-    status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
-    goto end;
-
-error:
-    bt_self_message_iterator_set_data(self_msg_iter, NULL);
-
-end:
-    ctf_fs_msg_iter_data_destroy(msg_iter_data);
-    return status;
-}
-
-static void ctf_fs_trace_destroy(struct ctf_fs_trace *ctf_fs_trace)
-{
-    if (!ctf_fs_trace) {
-        return;
-    }
-
-    if (ctf_fs_trace->ds_file_groups) {
-        g_ptr_array_free(ctf_fs_trace->ds_file_groups, TRUE);
-    }
-
-    BT_TRACE_PUT_REF_AND_RESET(ctf_fs_trace->trace);
-
-    if (ctf_fs_trace->path) {
-        g_string_free(ctf_fs_trace->path, TRUE);
-    }
-
-    if (ctf_fs_trace->metadata) {
-        ctf_fs_metadata_fini(ctf_fs_trace->metadata);
-        g_free(ctf_fs_trace->metadata);
-    }
-
-    g_free(ctf_fs_trace);
-}
-
-BT_HIDDEN
-void ctf_fs_destroy(struct ctf_fs_component *ctf_fs)
-{
-    if (!ctf_fs) {
-        return;
-    }
-
-    ctf_fs_trace_destroy(ctf_fs->trace);
-
-    if (ctf_fs->port_data) {
-        g_ptr_array_free(ctf_fs->port_data, TRUE);
-    }
-
-    g_free(ctf_fs);
-}
-
-static void port_data_destroy(struct ctf_fs_port_data *port_data)
-{
-    if (!port_data) {
-        return;
-    }
-
-    g_free(port_data);
-}
-
-static void port_data_destroy_notifier(void *data)
-{
-    port_data_destroy((struct ctf_fs_port_data *) data);
-}
-
-static void ctf_fs_trace_destroy_notifier(void *data)
-{
-    struct ctf_fs_trace *trace = (struct ctf_fs_trace *) data;
-    ctf_fs_trace_destroy(trace);
-}
+        if (msg_iter_data->ds_file_group->sc->default_clock_class) {
+            bt_self_message_iterator_configuration_set_can_seek_forward(config, true);
+        }
 
-struct ctf_fs_component *ctf_fs_component_create(bt_logging_level log_level,
-                                                 bt_self_component *self_comp)
-{
-    struct ctf_fs_component *ctf_fs;
+        bt_self_message_iterator_set_data(self_msg_iter, msg_iter_data.release());
 
-    ctf_fs = g_new0(struct ctf_fs_component, 1);
-    if (!ctf_fs) {
-        goto error;
-    }
-
-    ctf_fs->log_level = log_level;
-    ctf_fs->port_data = g_ptr_array_new_with_free_func(port_data_destroy_notifier);
-    if (!ctf_fs->port_data) {
-        goto error;
+        return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
+    } catch (const std::bad_alloc&) {
+        return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
     }
-
-    goto end;
-
-error:
-    ctf_fs_destroy(ctf_fs);
-    ctf_fs = NULL;
-
-end:
-    return ctf_fs;
 }
 
 void ctf_fs_finalize(bt_self_component_source *component)
 {
-    ctf_fs_destroy((struct ctf_fs_component *) bt_self_component_get_data(
-        bt_self_component_source_as_self_component(component)));
+    ctf_fs_component::UP {static_cast<ctf_fs_component *>(
+        bt_self_component_get_data(bt_self_component_source_as_self_component(component)))};
 }
 
-gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group)
+std::string ctf_fs_make_port_name(ctf_fs_ds_file_group *ds_file_group)
 {
-    GString *name = g_string_new(NULL);
+    std::stringstream name;
 
     /*
      * The unique port name is generated by concatenating unique identifiers
@@ -381,9 +267,9 @@ gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group)
         char uuid_str[BT_UUID_STR_LEN + 1];
 
         bt_uuid_to_str(ds_file_group->ctf_fs_trace->metadata->tc->uuid, uuid_str);
-        g_string_assign(name, uuid_str);
+        name << uuid_str;
     } else {
-        g_string_assign(name, ds_file_group->ctf_fs_trace->path->str);
+        name << ds_file_group->ctf_fs_trace->path;
     }
 
     /*
@@ -391,235 +277,75 @@ gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group)
      * otherwise, as there will only be a single stream class.
      */
     if (ds_file_group->sc->id != UINT64_C(-1)) {
-        g_string_append_printf(name, " | %" PRIu64, ds_file_group->sc->id);
+        name << " | " << ds_file_group->sc->id;
     }
 
     /* For the stream, use the id if present, else, use the path. */
     if (ds_file_group->stream_id != UINT64_C(-1)) {
-        g_string_append_printf(name, " | %" PRIu64, ds_file_group->stream_id);
+        name << " | " << ds_file_group->stream_id;
     } else {
-        BT_ASSERT(ds_file_group->ds_file_infos->len == 1);
-        struct ctf_fs_ds_file_info *ds_file_info =
-            (struct ctf_fs_ds_file_info *) g_ptr_array_index(ds_file_group->ds_file_infos, 0);
-        g_string_append_printf(name, " | %s", ds_file_info->path->str);
+        BT_ASSERT(ds_file_group->ds_file_infos.size() == 1);
+        const auto& ds_file_info = *ds_file_group->ds_file_infos[0];
+        name << " | " << ds_file_info.path;
     }
 
-    return g_string_free(name, FALSE);
+    return name.str();
 }
 
 static int create_one_port_for_trace(struct ctf_fs_component *ctf_fs,
-                                     struct ctf_fs_trace *ctf_fs_trace,
                                      struct ctf_fs_ds_file_group *ds_file_group,
                                      bt_self_component_source *self_comp_src)
 {
-    int ret = 0;
-    struct ctf_fs_port_data *port_data = NULL;
-    gchar *port_name;
-    bt_logging_level log_level = ctf_fs->log_level;
-    bt_self_component *self_comp = bt_self_component_source_as_self_component(self_comp_src);
+    const auto port_name = ctf_fs_make_port_name(ds_file_group);
+    auto port_data = bt2s::make_unique<ctf_fs_port_data>();
 
-    port_name = ctf_fs_make_port_name(ds_file_group);
-    if (!port_name) {
-        goto error;
-    }
-
-    BT_COMP_LOGI("Creating one port named `%s`", port_name);
-
-    /* Create output port for this file */
-    port_data = g_new0(struct ctf_fs_port_data, 1);
-    if (!port_data) {
-        goto error;
-    }
+    BT_CPPLOGI_SPEC(ctf_fs->logger, "Creating one port named `{}`", port_name);
 
     port_data->ctf_fs = ctf_fs;
     port_data->ds_file_group = ds_file_group;
-    ret = bt_self_component_source_add_output_port(self_comp_src, port_name, port_data, NULL);
+
+    int ret = bt_self_component_source_add_output_port(self_comp_src, port_name.c_str(),
+                                                       port_data.get(), NULL);
     if (ret) {
-        goto error;
+        return ret;
     }
 
-    g_ptr_array_add(ctf_fs->port_data, port_data);
-    port_data = NULL;
-    goto end;
-
-error:
-    ret = -1;
-
-end:
-    g_free(port_name);
-
-    port_data_destroy(port_data);
-    return ret;
+    ctf_fs->port_data.emplace_back(std::move(port_data));
+    return 0;
 }
 
 static int create_ports_for_trace(struct ctf_fs_component *ctf_fs,
                                   struct ctf_fs_trace *ctf_fs_trace,
                                   bt_self_component_source *self_comp_src)
 {
-    int ret = 0;
-    size_t i;
-    bt_logging_level log_level = ctf_fs_trace->log_level;
-    bt_self_component *self_comp = bt_self_component_source_as_self_component(self_comp_src);
-
     /* Create one output port for each stream file group */
-    for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
-        struct ctf_fs_ds_file_group *ds_file_group =
-            (struct ctf_fs_ds_file_group *) g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
-
-        ret = create_one_port_for_trace(ctf_fs, ctf_fs_trace, ds_file_group, self_comp_src);
+    for (const auto& ds_file_group : ctf_fs_trace->ds_file_groups) {
+        int ret = create_one_port_for_trace(ctf_fs, ds_file_group.get(), self_comp_src);
         if (ret) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot create output port.");
-            goto end;
-        }
-    }
-
-end:
-    return ret;
-}
-
-static void ctf_fs_ds_file_info_destroy(struct ctf_fs_ds_file_info *ds_file_info)
-{
-    if (!ds_file_info) {
-        return;
-    }
-
-    if (ds_file_info->path) {
-        g_string_free(ds_file_info->path, TRUE);
-    }
-
-    g_free(ds_file_info);
-}
-
-static struct ctf_fs_ds_file_info *ctf_fs_ds_file_info_create(const char *path, int64_t begin_ns)
-{
-    struct ctf_fs_ds_file_info *ds_file_info;
-
-    ds_file_info = g_new0(struct ctf_fs_ds_file_info, 1);
-    if (!ds_file_info) {
-        goto end;
-    }
-
-    ds_file_info->path = g_string_new(path);
-    if (!ds_file_info->path) {
-        ctf_fs_ds_file_info_destroy(ds_file_info);
-        ds_file_info = NULL;
-        goto end;
-    }
-
-    ds_file_info->begin_ns = begin_ns;
-
-end:
-    return ds_file_info;
-}
-
-static void ctf_fs_ds_file_group_destroy(struct ctf_fs_ds_file_group *ds_file_group)
-{
-    if (!ds_file_group) {
-        return;
-    }
-
-    if (ds_file_group->ds_file_infos) {
-        g_ptr_array_free(ds_file_group->ds_file_infos, TRUE);
-    }
-
-    ctf_fs_ds_index_destroy(ds_file_group->index);
-
-    bt_stream_put_ref(ds_file_group->stream);
-    g_free(ds_file_group);
-}
-
-static struct ctf_fs_ds_file_group *ctf_fs_ds_file_group_create(struct ctf_fs_trace *ctf_fs_trace,
-                                                                struct ctf_stream_class *sc,
-                                                                uint64_t stream_instance_id,
-                                                                struct ctf_fs_ds_index *index)
-{
-    struct ctf_fs_ds_file_group *ds_file_group;
-
-    ds_file_group = g_new0(struct ctf_fs_ds_file_group, 1);
-    if (!ds_file_group) {
-        goto error;
-    }
-
-    ds_file_group->ds_file_infos =
-        g_ptr_array_new_with_free_func((GDestroyNotify) ctf_fs_ds_file_info_destroy);
-    if (!ds_file_group->ds_file_infos) {
-        goto error;
-    }
-
-    ds_file_group->index = index;
-
-    ds_file_group->stream_id = stream_instance_id;
-    BT_ASSERT(sc);
-    ds_file_group->sc = sc;
-    ds_file_group->ctf_fs_trace = ctf_fs_trace;
-    goto end;
-
-error:
-    ctf_fs_ds_file_group_destroy(ds_file_group);
-    ctf_fs_ds_index_destroy(index);
-    ds_file_group = NULL;
-
-end:
-    return ds_file_group;
-}
-
-/* Replace by g_ptr_array_insert when we depend on glib >= 2.40. */
-static void array_insert(GPtrArray *array, gpointer element, size_t pos)
-{
-    size_t original_array_len = array->len;
-
-    /* Allocate an unused element at the end of the array. */
-    g_ptr_array_add(array, NULL);
-
-    /* If we are not inserting at the end, move the elements by one. */
-    if (pos < original_array_len) {
-        memmove(&(array->pdata[pos + 1]), &(array->pdata[pos]),
-                (original_array_len - pos) * sizeof(gpointer));
-    }
-
-    /* Insert the value. */
-    array->pdata[pos] = element;
-}
-
-/*
- * Insert ds_file_info in ds_file_group's list of ds_file_infos at the right
- * place to keep it sorted.
- */
-
-static void ds_file_group_insert_ds_file_info_sorted(struct ctf_fs_ds_file_group *ds_file_group,
-                                                     struct ctf_fs_ds_file_info *ds_file_info)
-{
-    guint i;
-
-    /* Find the spot where to insert this ds_file_info. */
-    for (i = 0; i < ds_file_group->ds_file_infos->len; i++) {
-        struct ctf_fs_ds_file_info *other_ds_file_info =
-            (struct ctf_fs_ds_file_info *) g_ptr_array_index(ds_file_group->ds_file_infos, i);
-
-        if (ds_file_info->begin_ns < other_ds_file_info->begin_ns) {
-            break;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Cannot create output port.");
+            return ret;
         }
     }
 
-    array_insert(ds_file_group->ds_file_infos, ds_file_info, i);
+    return 0;
 }
 
-static bool ds_index_entries_equal(const struct ctf_fs_ds_index_entry *left,
-                                   const struct ctf_fs_ds_index_entry *right)
+static bool ds_index_entries_equal(const ctf_fs_ds_index_entry& left,
+                                   const ctf_fs_ds_index_entry& right)
 {
-    if (left->packet_size != right->packet_size) {
+    if (left.packetSize != right.packetSize) {
         return false;
     }
 
-    if (left->timestamp_begin != right->timestamp_begin) {
+    if (left.timestamp_begin != right.timestamp_begin) {
         return false;
     }
 
-    if (left->timestamp_end != right->timestamp_end) {
+    if (left.timestamp_end != right.timestamp_end) {
         return false;
     }
 
-    if (left->packet_seq_num != right->packet_seq_num) {
+    if (left.packet_seq_num != right.packet_seq_num) {
         return false;
     }
 
@@ -630,22 +356,15 @@ static bool ds_index_entries_equal(const struct ctf_fs_ds_index_entry *left,
  * Insert `entry` into `index`, without duplication.
  *
  * The entry is inserted only if there isn't an identical entry already.
- *
- * In any case, the ownership of `entry` is transferred to this function.  So if
- * the entry is not inserted, it is freed.
  */
 
-static void ds_index_insert_ds_index_entry_sorted(struct ctf_fs_ds_index *index,
-                                                  struct ctf_fs_ds_index_entry *entry)
+static void ds_index_insert_ds_index_entry_sorted(ctf_fs_ds_index& index,
+                                                  const ctf_fs_ds_index_entry& entry)
 {
-    guint i;
-    struct ctf_fs_ds_index_entry *other_entry = NULL;
-
     /* Find the spot where to insert this index entry. */
-    for (i = 0; i < index->entries->len; i++) {
-        other_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, i);
-
-        if (entry->timestamp_begin_ns <= other_entry->timestamp_begin_ns) {
+    auto otherEntry = index.entries.begin();
+    for (; otherEntry != index.entries.end(); ++otherEntry) {
+        if (entry.timestamp_begin_ns <= otherEntry->timestamp_begin_ns) {
             break;
         }
     }
@@ -657,79 +376,56 @@ static void ds_index_insert_ds_index_entry_sorted(struct ctf_fs_ds_index *index,
      * snapshots of the same trace.  We then want the index to contain
      * a reference to only one copy of that packet.
      */
-    if (i == index->entries->len || !ds_index_entries_equal(entry, other_entry)) {
-        array_insert(index->entries, entry, i);
-    } else {
-        g_free(entry);
+    if (otherEntry == index.entries.end() || !ds_index_entries_equal(entry, *otherEntry)) {
+        index.entries.emplace(otherEntry, entry);
     }
 }
 
-static void merge_ctf_fs_ds_indexes(struct ctf_fs_ds_index *dest, struct ctf_fs_ds_index *src)
+static void merge_ctf_fs_ds_indexes(ctf_fs_ds_index& dest, const ctf_fs_ds_index& src)
 {
-    guint i;
-
-    for (i = 0; i < src->entries->len; i++) {
-        struct ctf_fs_ds_index_entry *entry =
-            (struct ctf_fs_ds_index_entry *) g_ptr_array_index(src->entries, i);
-
-        /*
-               * Ownership of the ctf_fs_ds_index_entry is transferred to
-               * ds_index_insert_ds_index_entry_sorted.
-               */
-        g_ptr_array_index(src->entries, i) = NULL;
+    for (const auto& entry : src.entries) {
         ds_index_insert_ds_index_entry_sorted(dest, entry);
     }
 }
 
 static int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, const char *path)
 {
-    int64_t stream_instance_id = -1;
-    int64_t begin_ns = -1;
-    struct ctf_fs_ds_file_group *ds_file_group = NULL;
-    bool add_group = false;
-    int ret;
-    size_t i;
-    struct ctf_fs_ds_file *ds_file = NULL;
-    struct ctf_fs_ds_file_info *ds_file_info = NULL;
-    struct ctf_fs_ds_index *index = NULL;
-    struct ctf_msg_iter *msg_iter = NULL;
-    struct ctf_stream_class *sc = NULL;
-    struct ctf_msg_iter_packet_properties props;
-    bt_logging_level log_level = ctf_fs_trace->log_level;
-    bt_self_component *self_comp = ctf_fs_trace->self_comp;
-    bt_self_component_class *self_comp_class = ctf_fs_trace->self_comp_class;
-
     /*
      * Create a temporary ds_file to read some properties about the data
      * stream file.
      */
-    ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, path, log_level);
+    const auto ds_file =
+        ctf_fs_ds_file_create(ctf_fs_trace, bt2::Stream::Shared {}, path, ctf_fs_trace->logger);
     if (!ds_file) {
-        goto error;
+        return -1;
     }
 
     /* Create a temporary iterator to read the ds_file. */
-    msg_iter =
-        ctf_msg_iter_create(ctf_fs_trace->metadata->tc, bt_common_get_page_size(log_level) * 8,
-                            ctf_fs_ds_file_medops, ds_file, log_level, self_comp, NULL);
+    ctf_msg_iter_up msg_iter = ctf_msg_iter_create(
+        ctf_fs_trace->metadata->tc,
+        bt_common_get_page_size(static_cast<int>(ctf_fs_trace->logger.level())) * 8,
+        ctf_fs_ds_file_medops, ds_file.get(), nullptr, ctf_fs_trace->logger);
     if (!msg_iter) {
-        BT_COMP_LOGE_STR("Cannot create a CTF message iterator.");
-        goto error;
+        BT_CPPLOGE_STR_SPEC(ctf_fs_trace->logger, "Cannot create a CTF message iterator.");
+        return -1;
     }
 
-    ctf_msg_iter_set_dry_run(msg_iter, true);
+    ctf_msg_iter_set_dry_run(msg_iter.get(), true);
 
-    ret = ctf_msg_iter_get_packet_properties(msg_iter, &props);
+    ctf_msg_iter_packet_properties props;
+    int ret = ctf_msg_iter_get_packet_properties(msg_iter.get(), &props);
     if (ret) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            self_comp, self_comp_class,
-            "Cannot get stream file's first packet's header and context fields (`%s`).", path);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            ctf_fs_trace->logger,
+            "Cannot get stream file's first packet's header and context fields (`{}`).", path);
+        return ret;
     }
 
-    sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, props.stream_class_id);
+    ctf_stream_class *sc =
+        ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, props.stream_class_id);
     BT_ASSERT(sc);
-    stream_instance_id = props.data_stream_id;
+    int64_t stream_instance_id = props.data_stream_id;
+    int64_t begin_ns = -1;
 
     if (props.snapshots.beginning_clock != UINT64_C(-1)) {
         BT_ASSERT(sc->default_clock_class);
@@ -738,24 +434,23 @@ static int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, const
             sc->default_clock_class->offset_seconds, sc->default_clock_class->offset_cycles,
             &begin_ns);
         if (ret) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                self_comp, self_comp_class,
-                "Cannot convert clock cycles to nanoseconds from origin (`%s`).", path);
-            goto error;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                ctf_fs_trace->logger,
+                "Cannot convert clock cycles to nanoseconds from origin (`{}`).", path);
+            return ret;
         }
     }
 
-    ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns);
+    ctf_fs_ds_file_info::UP ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns);
     if (!ds_file_info) {
-        goto error;
+        return -1;
     }
 
-    index = ctf_fs_ds_file_build_index(ds_file, ds_file_info, msg_iter);
+    auto index = ctf_fs_ds_file_build_index(ds_file.get(), ds_file_info.get(), msg_iter.get());
     if (!index) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Failed to index CTF stream file \'%s\'",
-                                                ds_file->file->path->str);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs_trace->logger, "Failed to index CTF stream file \'{}\'",
+                                     ds_file->file->path);
+        return -1;
     }
 
     if (begin_ns == -1) {
@@ -775,380 +470,214 @@ static int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, const
          * there's no timestamp to order the file within its
          * group.
          */
-        ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace, sc, UINT64_C(-1), index);
-        /* Ownership of index is transferred. */
-        index = NULL;
-
-        if (!ds_file_group) {
-            goto error;
-        }
-
-        ds_file_group_insert_ds_file_info_sorted(ds_file_group, BT_MOVE_REF(ds_file_info));
-
-        add_group = true;
-        goto end;
+        ctf_fs_trace->ds_file_groups.emplace_back(bt2s::make_unique<ctf_fs_ds_file_group>(
+            ctf_fs_trace, sc, UINT64_C(-1), std::move(*index)));
+        ctf_fs_trace->ds_file_groups.back()->insert_ds_file_info_sorted(std::move(ds_file_info));
+        return 0;
     }
 
     BT_ASSERT(stream_instance_id != -1);
     BT_ASSERT(begin_ns != -1);
 
     /* Find an existing stream file group with this ID */
-    for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
-        ds_file_group =
-            (struct ctf_fs_ds_file_group *) g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
-
-        if (ds_file_group->sc == sc && ds_file_group->stream_id == stream_instance_id) {
+    ctf_fs_ds_file_group *ds_file_group = NULL;
+    for (const auto& candidate : ctf_fs_trace->ds_file_groups) {
+        if (candidate->sc == sc && candidate->stream_id == stream_instance_id) {
+            ds_file_group = candidate.get();
             break;
         }
-
-        ds_file_group = NULL;
     }
 
     if (!ds_file_group) {
-        ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace, sc, stream_instance_id, index);
-        /* Ownership of index is transferred. */
-        index = NULL;
-        if (!ds_file_group) {
-            goto error;
-        }
-
-        add_group = true;
+        ctf_fs_trace->ds_file_groups.emplace_back(bt2s::make_unique<ctf_fs_ds_file_group>(
+            ctf_fs_trace, sc, static_cast<std::uint64_t>(stream_instance_id), std::move(*index)));
+        ds_file_group = ctf_fs_trace->ds_file_groups.back().get();
     } else {
-        merge_ctf_fs_ds_indexes(ds_file_group->index, index);
+        merge_ctf_fs_ds_indexes(ds_file_group->index, *index);
     }
 
-    ds_file_group_insert_ds_file_info_sorted(ds_file_group, BT_MOVE_REF(ds_file_info));
+    ds_file_group->insert_ds_file_info_sorted(std::move(ds_file_info));
 
-    goto end;
-
-error:
-    ctf_fs_ds_file_group_destroy(ds_file_group);
-    ds_file_group = NULL;
-    ret = -1;
-
-end:
-    if (add_group && ds_file_group) {
-        g_ptr_array_add(ctf_fs_trace->ds_file_groups, ds_file_group);
-    }
-
-    ctf_fs_ds_file_destroy(ds_file);
-    ctf_fs_ds_file_info_destroy(ds_file_info);
-
-    if (msg_iter) {
-        ctf_msg_iter_destroy(msg_iter);
-    }
-
-    ctf_fs_ds_index_destroy(index);
-    return ret;
+    return 0;
 }
 
 static int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace)
 {
-    int ret = 0;
-    const char *basename;
-    GError *error = NULL;
-    GDir *dir = NULL;
-    bt_logging_level log_level = ctf_fs_trace->log_level;
-    bt_self_component *self_comp = ctf_fs_trace->self_comp;
-    bt_self_component_class *self_comp_class = ctf_fs_trace->self_comp_class;
-
     /* Check each file in the path directory, except specific ones */
-    dir = g_dir_open(ctf_fs_trace->path->str, 0, &error);
+    GError *error = NULL;
+    const bt2c::GDirUP dir {g_dir_open(ctf_fs_trace->path.c_str(), 0, &error)};
     if (!dir) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            self_comp, self_comp_class, "Cannot open directory `%s`: %s (code %d)",
-            ctf_fs_trace->path->str, error->message, error->code);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs_trace->logger,
+                                     "Cannot open directory `{}`: {} (code {})", ctf_fs_trace->path,
+                                     error->message, error->code);
+        if (error) {
+            g_error_free(error);
+        }
+        return -1;
     }
 
-    while ((basename = g_dir_read_name(dir))) {
-        struct ctf_fs_file *file;
-
+    while (const char *basename = g_dir_read_name(dir.get())) {
         if (strcmp(basename, CTF_FS_METADATA_FILENAME) == 0) {
             /* Ignore the metadata stream. */
-            BT_COMP_LOGI("Ignoring metadata file `%s" G_DIR_SEPARATOR_S "%s`",
-                         ctf_fs_trace->path->str, basename);
+            BT_CPPLOGI_SPEC(ctf_fs_trace->logger,
+                            "Ignoring metadata file `{}" G_DIR_SEPARATOR_S "{}`",
+                            ctf_fs_trace->path, basename);
             continue;
         }
 
         if (basename[0] == '.') {
-            BT_COMP_LOGI("Ignoring hidden file `%s" G_DIR_SEPARATOR_S "%s`",
-                         ctf_fs_trace->path->str, basename);
+            BT_CPPLOGI_SPEC(ctf_fs_trace->logger,
+                            "Ignoring hidden file `{}" G_DIR_SEPARATOR_S "{}`", ctf_fs_trace->path,
+                            basename);
             continue;
         }
 
         /* Create the file. */
-        file = ctf_fs_file_create(log_level, self_comp);
-        if (!file) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                self_comp, self_comp_class,
-                "Cannot create stream file object for file `%s" G_DIR_SEPARATOR_S "%s`",
-                ctf_fs_trace->path->str, basename);
-            goto error;
-        }
+        ctf_fs_file file {ctf_fs_trace->logger};
 
         /* Create full path string. */
-        g_string_append_printf(file->path, "%s" G_DIR_SEPARATOR_S "%s", ctf_fs_trace->path->str,
-                               basename);
-        if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) {
-            BT_COMP_LOGI("Ignoring non-regular file `%s`", file->path->str);
-            ctf_fs_file_destroy(file);
-            file = NULL;
+        file.path = fmt::format("{}" G_DIR_SEPARATOR_S "{}", ctf_fs_trace->path, basename);
+
+        if (!g_file_test(file.path.c_str(), G_FILE_TEST_IS_REGULAR)) {
+            BT_CPPLOGI_SPEC(ctf_fs_trace->logger, "Ignoring non-regular file `{}`", file.path);
             continue;
         }
 
-        ret = ctf_fs_file_open(file, "rb");
+        int ret = ctf_fs_file_open(&file, "rb");
         if (ret) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                self_comp, self_comp_class, "Cannot open stream file `%s`", file->path->str);
-            goto error;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs_trace->logger, "Cannot open stream file `{}`",
+                                         file.path);
+            return ret;
         }
 
-        if (file->size == 0) {
+        if (file.size == 0) {
             /* Skip empty stream. */
-            BT_COMP_LOGI("Ignoring empty file `%s`", file->path->str);
-            ctf_fs_file_destroy(file);
+            BT_CPPLOGI_SPEC(ctf_fs_trace->logger, "Ignoring empty file `{}`", file.path);
             continue;
         }
 
-        ret = add_ds_file_to_ds_file_group(ctf_fs_trace, file->path->str);
+        ret = add_ds_file_to_ds_file_group(ctf_fs_trace, file.path.c_str());
         if (ret) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                self_comp, self_comp_class, "Cannot add stream file `%s` to stream file group",
-                file->path->str);
-            ctf_fs_file_destroy(file);
-            goto error;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs_trace->logger,
+                                         "Cannot add stream file `{}` to stream file group",
+                                         file.path);
+            return ret;
         }
-
-        ctf_fs_file_destroy(file);
     }
 
-    goto end;
-
-error:
-    ret = -1;
-
-end:
-    if (dir) {
-        g_dir_close(dir);
-        dir = NULL;
-    }
-
-    if (error) {
-        g_error_free(error);
-    }
-
-    return ret;
+    return 0;
 }
 
-static int set_trace_name(bt_trace *trace, const char *name_suffix, bt_logging_level log_level,
-                          bt_self_component *self_comp)
+static void set_trace_name(const bt2::Trace trace, const char *name_suffix)
 {
-    int ret = 0;
-    const bt_value *val;
-    GString *name;
-
-    name = g_string_new(NULL);
-    if (!name) {
-        BT_COMP_LOGE_STR("Failed to allocate a GString.");
-        ret = -1;
-        goto end;
-    }
+    std::string name;
 
     /*
      * Check if we have a trace environment string value named `hostname`.
      * If so, use it as the trace name's prefix.
      */
-    val = bt_trace_borrow_environment_entry_value_by_name_const(trace, "hostname");
-    if (val && bt_value_is_string(val)) {
-        g_string_append(name, bt_value_string_get(val));
+    const auto val = trace.environmentEntry("hostname");
+    if (val && val->isString()) {
+        name += val->asString().value();
 
         if (name_suffix) {
-            g_string_append_c(name, G_DIR_SEPARATOR);
+            name += G_DIR_SEPARATOR;
         }
     }
 
     if (name_suffix) {
-        g_string_append(name, name_suffix);
-    }
-
-    ret = bt_trace_set_name(trace, name->str);
-    if (ret) {
-        goto end;
-    }
-
-    goto end;
-
-end:
-    if (name) {
-        g_string_free(name, TRUE);
+        name += name_suffix;
     }
 
-    return ret;
+    trace.name(name);
 }
 
-static struct ctf_fs_trace *ctf_fs_trace_create(bt_self_component *self_comp,
-                                                bt_self_component_class *self_comp_class,
-                                                const char *path, const char *name,
-                                                struct ctf_fs_metadata_config *metadata_config,
-                                                bt_logging_level log_level)
+static ctf_fs_trace::UP ctf_fs_trace_create(const char *path, const char *name,
+                                            const ctf::src::ClkClsCfg& clkClsCfg,
+                                            bt_self_component *selfComp,
+                                            const bt2c::Logger& parentLogger)
 {
-    struct ctf_fs_trace *ctf_fs_trace;
-    int ret;
-
-    /* Only one of them must be set. */
-    BT_ASSERT(!self_comp != !self_comp_class);
-
-    ctf_fs_trace = g_new0(struct ctf_fs_trace, 1);
-    if (!ctf_fs_trace) {
-        goto end;
-    }
-
-    ctf_fs_trace->log_level = log_level;
-    ctf_fs_trace->self_comp = self_comp;
-    ctf_fs_trace->self_comp_class = self_comp_class;
-    ctf_fs_trace->path = g_string_new(path);
-    if (!ctf_fs_trace->path) {
-        goto error;
-    }
-
-    ctf_fs_trace->metadata = g_new0(struct ctf_fs_metadata, 1);
-    if (!ctf_fs_trace->metadata) {
-        goto error;
-    }
-
-    ctf_fs_metadata_init(ctf_fs_trace->metadata);
-    ctf_fs_trace->ds_file_groups =
-        g_ptr_array_new_with_free_func((GDestroyNotify) ctf_fs_ds_file_group_destroy);
-    if (!ctf_fs_trace->ds_file_groups) {
-        goto error;
-    }
+    ctf_fs_trace::UP ctf_fs_trace = bt2s::make_unique<struct ctf_fs_trace>(parentLogger);
+    ctf_fs_trace->path = path;
+    ctf_fs_trace->metadata = bt2s::make_unique<ctf_fs_metadata>();
 
-    ret = ctf_fs_metadata_set_trace_class(self_comp, ctf_fs_trace, metadata_config);
+    int ret = ctf_fs_metadata_set_trace_class(selfComp, ctf_fs_trace.get(), clkClsCfg);
     if (ret) {
-        goto error;
+        return nullptr;
     }
 
     if (ctf_fs_trace->metadata->trace_class) {
-        ctf_fs_trace->trace = bt_trace_create(ctf_fs_trace->metadata->trace_class);
-        if (!ctf_fs_trace->trace) {
-            goto error;
+        bt_trace *trace = bt_trace_create(ctf_fs_trace->metadata->trace_class->libObjPtr());
+        if (!trace) {
+            return nullptr;
         }
+
+        ctf_fs_trace->trace = bt2::Trace::Shared::createWithoutRef(trace);
     }
 
     if (ctf_fs_trace->trace) {
-        ret = ctf_trace_class_configure_ir_trace(ctf_fs_trace->metadata->tc, ctf_fs_trace->trace);
-        if (ret) {
-            goto error;
-        }
+        ctf_trace_class_configure_ir_trace(ctf_fs_trace->metadata->tc, *ctf_fs_trace->trace);
 
-        ret = set_trace_name(ctf_fs_trace->trace, name, log_level, self_comp);
-        if (ret) {
-            goto error;
-        }
+        set_trace_name(*ctf_fs_trace->trace, name);
     }
 
-    ret = create_ds_file_groups(ctf_fs_trace);
+    ret = create_ds_file_groups(ctf_fs_trace.get());
     if (ret) {
-        goto error;
+        return nullptr;
     }
 
-    goto end;
-
-error:
-    ctf_fs_trace_destroy(ctf_fs_trace);
-    ctf_fs_trace = NULL;
-
-end:
     return ctf_fs_trace;
 }
 
 static int path_is_ctf_trace(const char *path)
 {
-    GString *metadata_path = g_string_new(NULL);
-    int ret = 0;
-
-    if (!metadata_path) {
-        ret = -1;
-        goto end;
-    }
-
-    g_string_printf(metadata_path, "%s" G_DIR_SEPARATOR_S "%s", path, CTF_FS_METADATA_FILENAME);
-
-    if (g_file_test(metadata_path->str, G_FILE_TEST_IS_REGULAR)) {
-        ret = 1;
-        goto end;
-    }
-
-end:
-    g_string_free(metadata_path, TRUE);
-    return ret;
+    return g_file_test(fmt::format("{}" G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME, path).c_str(),
+                       G_FILE_TEST_IS_REGULAR);
 }
 
 /* Helper for ctf_fs_component_create_ctf_fs_trace, to handle a single path. */
 
 static int ctf_fs_component_create_ctf_fs_trace_one_path(struct ctf_fs_component *ctf_fs,
                                                          const char *path_param,
-                                                         const char *trace_name, GPtrArray *traces,
-                                                         bt_self_component *self_comp,
-                                                         bt_self_component_class *self_comp_class)
+                                                         const char *trace_name,
+                                                         std::vector<ctf_fs_trace::UP>& traces,
+                                                         bt_self_component *selfComp)
 {
-    struct ctf_fs_trace *ctf_fs_trace;
-    int ret;
-    GString *norm_path;
-    bt_logging_level log_level = ctf_fs->log_level;
-
-    norm_path = bt_common_normalize_path(path_param, NULL);
+    bt2c::GStringUP norm_path {bt_common_normalize_path(path_param, NULL)};
     if (!norm_path) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Failed to normalize path: `%s`.", path_param);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Failed to normalize path: `{}`.", path_param);
+        return -1;
     }
 
-    ret = path_is_ctf_trace(norm_path->str);
+    int ret = path_is_ctf_trace(norm_path->str);
     if (ret < 0) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Failed to check if path is a CTF trace: path=%s",
-                                                norm_path->str);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            ctf_fs->logger, "Failed to check if path is a CTF trace: path={}", norm_path->str);
+        return ret;
     } else if (ret == 0) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-            self_comp, self_comp_class,
-            "Path is not a CTF trace (does not contain a metadata file): `%s`.", norm_path->str);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            ctf_fs->logger, "Path is not a CTF trace (does not contain a metadata file): `{}`.",
+            norm_path->str);
+        return -1;
     }
 
     // FIXME: Remove or ifdef for __MINGW32__
     if (strcmp(norm_path->str, "/") == 0) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Opening a trace in `/` is not supported.");
-        ret = -1;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Opening a trace in `/` is not supported.");
+        return -1;
     }
 
-    ctf_fs_trace = ctf_fs_trace_create(self_comp, self_comp_class, norm_path->str, trace_name,
-                                       &ctf_fs->metadata_config, log_level);
+    ctf_fs_trace::UP ctf_fs_trace = ctf_fs_trace_create(
+        norm_path->str, trace_name, ctf_fs->clkClsCfg, selfComp, ctf_fs->logger);
     if (!ctf_fs_trace) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Cannot create trace for `%s`.", norm_path->str);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Cannot create trace for `{}`.",
+                                     norm_path->str);
+        return -1;
     }
 
-    g_ptr_array_add(traces, ctf_fs_trace);
-    ctf_fs_trace = NULL;
-
-    ret = 0;
-    goto end;
+    traces.emplace_back(std::move(ctf_fs_trace));
 
-error:
-    ret = -1;
-
-end:
-    if (norm_path) {
-        g_string_free(norm_path, TRUE);
-    }
-
-    return ret;
+    return 0;
 }
 
 /*
@@ -1163,9 +692,8 @@ end:
 static unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace *trace)
 {
     unsigned int num = trace->metadata->tc->stream_classes->len;
-    guint i;
 
-    for (i = 0; i < trace->metadata->tc->stream_classes->len; i++) {
+    for (guint i = 0; i < trace->metadata->tc->stream_classes->len; i++) {
         struct ctf_stream_class *sc =
             (struct ctf_stream_class *) trace->metadata->tc->stream_classes->pdata[i];
         num += sc->event_classes->len;
@@ -1180,52 +708,38 @@ static unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace
  */
 
 static void merge_ctf_fs_ds_file_groups(struct ctf_fs_ds_file_group *dest,
-                                        struct ctf_fs_ds_file_group *src)
+                                        ctf_fs_ds_file_group::UP src)
 {
-    guint i;
-
-    for (i = 0; i < src->ds_file_infos->len; i++) {
-        struct ctf_fs_ds_file_info *ds_file_info =
-            (struct ctf_fs_ds_file_info *) g_ptr_array_index(src->ds_file_infos, i);
-
-        /* Ownership of the ds_file_info is transferred to dest. */
-        g_ptr_array_index(src->ds_file_infos, i) = NULL;
-
-        ds_file_group_insert_ds_file_info_sorted(dest, ds_file_info);
+    for (auto& ds_file_info : src->ds_file_infos) {
+        dest->insert_ds_file_info_sorted(std::move(ds_file_info));
     }
 
     /* Merge both indexes. */
     merge_ctf_fs_ds_indexes(dest->index, src->index);
 }
+
 /* Merge src_trace's data stream file groups into dest_trace's. */
 
 static int merge_matching_ctf_fs_ds_file_groups(struct ctf_fs_trace *dest_trace,
-                                                struct ctf_fs_trace *src_trace)
+                                                ctf_fs_trace::UP src_trace)
 {
-    GPtrArray *dest = dest_trace->ds_file_groups;
-    GPtrArray *src = src_trace->ds_file_groups;
-    guint s_i;
-    int ret = 0;
+    std::vector<ctf_fs_ds_file_group::UP>& dest = dest_trace->ds_file_groups;
+    std::vector<ctf_fs_ds_file_group::UP>& src = src_trace->ds_file_groups;
 
     /*
      * Save the initial length of dest: we only want to check against the
      * original elements in the inner loop.
      */
-    const guint dest_len = dest->len;
+    size_t dest_len = dest.size();
 
-    for (s_i = 0; s_i < src->len; s_i++) {
-        struct ctf_fs_ds_file_group *src_group =
-            (struct ctf_fs_ds_file_group *) g_ptr_array_index(src, s_i);
+    for (auto& src_group : src) {
         struct ctf_fs_ds_file_group *dest_group = NULL;
 
         /* A stream instance without ID can't match a stream in the other trace.  */
         if (src_group->stream_id != -1) {
-            guint d_i;
-
             /* Let's search for a matching ds_file_group in the destination.  */
-            for (d_i = 0; d_i < dest_len; d_i++) {
-                struct ctf_fs_ds_file_group *candidate_dest =
-                    (struct ctf_fs_ds_file_group *) g_ptr_array_index(dest, d_i);
+            for (size_t d_i = 0; d_i < dest_len; ++d_i) {
+                ctf_fs_ds_file_group *candidate_dest = dest[d_i].get();
 
                 /* Can't match a stream instance without ID.  */
                 if (candidate_dest->stream_id == -1) {
@@ -1255,36 +769,20 @@ static int merge_matching_ctf_fs_ds_file_groups(struct ctf_fs_trace *dest_trace,
          * trace chunk.
          */
         if (!dest_group) {
-            struct ctf_stream_class *sc;
-            struct ctf_fs_ds_index *index;
-
-            sc = ctf_trace_class_borrow_stream_class_by_id(dest_trace->metadata->tc,
-                                                           src_group->sc->id);
+            ctf_stream_class *sc = ctf_trace_class_borrow_stream_class_by_id(
+                dest_trace->metadata->tc, src_group->sc->id);
             BT_ASSERT(sc);
 
-            index = ctf_fs_ds_index_create(dest_trace->log_level, dest_trace->self_comp);
-            if (!index) {
-                ret = -1;
-                goto end;
-            }
-
-            dest_group = ctf_fs_ds_file_group_create(dest_trace, sc, src_group->stream_id, index);
-            /* Ownership of index is transferred. */
-            index = NULL;
-            if (!dest_group) {
-                ret = -1;
-                goto end;
-            }
-
-            g_ptr_array_add(dest_trace->ds_file_groups, dest_group);
+            dest_trace->ds_file_groups.emplace_back(bt2s::make_unique<ctf_fs_ds_file_group>(
+                dest_trace, sc, src_group->stream_id, ctf_fs_ds_index {}));
+            dest_group = dest_trace->ds_file_groups.back().get();
         }
 
         BT_ASSERT(dest_group);
-        merge_ctf_fs_ds_file_groups(dest_group, src_group);
+        merge_ctf_fs_ds_file_groups(dest_group, std::move(src_group));
     }
 
-end:
-    return ret;
+    return 0;
 }
 
 /*
@@ -1292,32 +790,23 @@ end:
  * one.
  *
  * The trace with the most expansive metadata is chosen and all other traces
- * are merged into that one.  The array slots of all the traces that get merged
- * in the chosen one are set to NULL, so only the slot of the chosen trace
- * remains non-NULL.
+ * are merged into that one.  On return, the elements of `traces` are nullptr
+ * and the merged trace is placed in `out_trace`.
  */
 
-static int merge_ctf_fs_traces(struct ctf_fs_trace **traces, unsigned int num_traces,
-                               struct ctf_fs_trace **out_trace)
+static int merge_ctf_fs_traces(std::vector<ctf_fs_trace::UP> traces, ctf_fs_trace::UP& out_trace)
 {
-    unsigned int winner_count;
-    struct ctf_fs_trace *winner;
-    guint i, winner_i;
-    int ret = 0;
-
-    BT_ASSERT(num_traces >= 2);
+    BT_ASSERT(traces.size() >= 2);
 
-    winner_count = metadata_count_stream_and_event_classes(traces[0]);
-    winner = traces[0];
-    winner_i = 0;
+    unsigned int winner_count = metadata_count_stream_and_event_classes(traces[0].get());
+    ctf_fs_trace *winner = traces[0].get();
+    guint winner_i = 0;
 
     /* Find the trace with the largest metadata. */
-    for (i = 1; i < num_traces; i++) {
-        struct ctf_fs_trace *candidate;
+    for (guint i = 1; i < traces.size(); i++) {
+        ctf_fs_trace *candidate = traces[i].get();
         unsigned int candidate_count;
 
-        candidate = traces[i];
-
         /* A bit of sanity check. */
         BT_ASSERT(bt_uuid_compare(winner->metadata->tc->uuid, candidate->metadata->tc->uuid) == 0);
 
@@ -1331,29 +820,25 @@ static int merge_ctf_fs_traces(struct ctf_fs_trace **traces, unsigned int num_tr
     }
 
     /* Merge all the other traces in the winning trace. */
-    for (i = 0; i < num_traces; i++) {
-        struct ctf_fs_trace *trace = traces[i];
-
+    for (ctf_fs_trace::UP& trace : traces) {
         /* Don't merge the winner into itself. */
-        if (trace == winner) {
+        if (trace.get() == winner) {
             continue;
         }
 
         /* Merge trace's data stream file groups into winner's. */
-        ret = merge_matching_ctf_fs_ds_file_groups(winner, trace);
+        int ret = merge_matching_ctf_fs_ds_file_groups(winner, std::move(trace));
         if (ret) {
-            goto end;
+            return ret;
         }
     }
 
     /*
      * Move the winner out of the array, into `*out_trace`.
      */
-    *out_trace = winner;
-    traces[winner_i] = NULL;
+    out_trace = std::move(traces[winner_i]);
 
-end:
-    return ret;
+    return 0;
 }
 
 enum target_event
@@ -1364,52 +849,45 @@ enum target_event
 
 static int decode_clock_snapshot_after_event(struct ctf_fs_trace *ctf_fs_trace,
                                              struct ctf_clock_class *default_cc,
-                                             struct ctf_fs_ds_index_entry *index_entry,
+                                             const ctf_fs_ds_index_entry& index_entry,
                                              enum target_event target_event, uint64_t *cs,
                                              int64_t *ts_ns)
 {
-    enum ctf_msg_iter_status iter_status = CTF_MSG_ITER_STATUS_OK;
-    struct ctf_fs_ds_file *ds_file = NULL;
-    struct ctf_msg_iter *msg_iter = NULL;
-    bt_logging_level log_level = ctf_fs_trace->log_level;
-    bt_self_component *self_comp = ctf_fs_trace->self_comp;
-    int ret = 0;
-
     BT_ASSERT(ctf_fs_trace);
-    BT_ASSERT(index_entry);
-    BT_ASSERT(index_entry->path);
+    BT_ASSERT(index_entry.path);
 
-    ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, index_entry->path, log_level);
+    const auto ds_file = ctf_fs_ds_file_create(ctf_fs_trace, bt2::Stream::Shared {},
+                                               index_entry.path, ctf_fs_trace->logger);
     if (!ds_file) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create a ctf_fs_ds_file");
-        ret = -1;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs_trace->logger, "Failed to create a ctf_fs_ds_file");
+        return -1;
     }
 
     BT_ASSERT(ctf_fs_trace->metadata);
     BT_ASSERT(ctf_fs_trace->metadata->tc);
 
-    msg_iter =
-        ctf_msg_iter_create(ctf_fs_trace->metadata->tc, bt_common_get_page_size(log_level) * 8,
-                            ctf_fs_ds_file_medops, ds_file, log_level, self_comp, NULL);
+    ctf_msg_iter_up msg_iter = ctf_msg_iter_create(
+        ctf_fs_trace->metadata->tc,
+        bt_common_get_page_size(static_cast<int>(ctf_fs_trace->logger.level())) * 8,
+
+        ctf_fs_ds_file_medops, ds_file.get(), NULL, ctf_fs_trace->logger);
     if (!msg_iter) {
         /* ctf_msg_iter_create() logs errors. */
-        ret = -1;
-        goto end;
+        return -1;
     }
 
     /*
      * Turn on dry run mode to prevent the creation and usage of Babeltrace
      * library objects (bt_field, bt_message_*, etc.).
      */
-    ctf_msg_iter_set_dry_run(msg_iter, true);
+    ctf_msg_iter_set_dry_run(msg_iter.get(), true);
 
     /* Seek to the beginning of the target packet. */
-    iter_status = ctf_msg_iter_seek(msg_iter, index_entry->offset);
+    enum ctf_msg_iter_status iter_status =
+        ctf_msg_iter_seek(msg_iter.get(), index_entry.offset.bytes());
     if (iter_status) {
         /* ctf_msg_iter_seek() logs errors. */
-        ret = -1;
-        goto end;
+        return -1;
     }
 
     switch (target_event) {
@@ -1419,42 +897,34 @@ static int decode_clock_snapshot_after_event(struct ctf_fs_trace *ctf_fs_trace,
          * the first event. To extract the first event's clock
          * snapshot.
          */
-        iter_status = ctf_msg_iter_curr_packet_first_event_clock_snapshot(msg_iter, cs);
+        iter_status = ctf_msg_iter_curr_packet_first_event_clock_snapshot(msg_iter.get(), cs);
         break;
     case LAST_EVENT:
         /* Decode the packet to extract the last event's clock snapshot. */
-        iter_status = ctf_msg_iter_curr_packet_last_event_clock_snapshot(msg_iter, cs);
+        iter_status = ctf_msg_iter_curr_packet_last_event_clock_snapshot(msg_iter.get(), cs);
         break;
     default:
         bt_common_abort();
     }
     if (iter_status) {
-        ret = -1;
-        goto end;
+        return -1;
     }
 
     /* Convert clock snapshot to timestamp. */
-    ret = bt_util_clock_cycles_to_ns_from_origin(
+    int ret = bt_util_clock_cycles_to_ns_from_origin(
         *cs, default_cc->frequency, default_cc->offset_seconds, default_cc->offset_cycles, ts_ns);
     if (ret) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to convert clock snapshot to timestamp");
-        goto end;
-    }
-
-end:
-    if (ds_file) {
-        ctf_fs_ds_file_destroy(ds_file);
-    }
-    if (msg_iter) {
-        ctf_msg_iter_destroy(msg_iter);
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs_trace->logger,
+                                     "Failed to convert clock snapshot to timestamp");
+        return ret;
     }
 
-    return ret;
+    return 0;
 }
 
 static int decode_packet_first_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
                                                struct ctf_clock_class *default_cc,
-                                               struct ctf_fs_ds_index_entry *index_entry,
+                                               const ctf_fs_ds_index_entry& index_entry,
                                                uint64_t *cs, int64_t *ts_ns)
 {
     return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc, index_entry, FIRST_EVENT, cs,
@@ -1463,7 +933,7 @@ static int decode_packet_first_event_timestamp(struct ctf_fs_trace *ctf_fs_trace
 
 static int decode_packet_last_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
                                               struct ctf_clock_class *default_cc,
-                                              struct ctf_fs_ds_index_entry *index_entry,
+                                              const ctf_fs_ds_index_entry& index_entry,
                                               uint64_t *cs, int64_t *ts_ns)
 {
     return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc, index_entry, LAST_EVENT, cs,
@@ -1477,10 +947,10 @@ static int decode_packet_last_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
  *
  * To fix up this erroneous data we do the following:
  *  1. If it's not the stream file's last packet: set the packet index entry's
- *     end time to the next packet's beginning time.
+ *     end time to the next packet's beginning time.
  *  2. If it's the stream file's last packet, set the packet index entry's end
- *     time to the packet's last event's time, if any, or to the packet's
- *     beginning time otherwise.
+ *     time to the packet's last event's time, if any, or to the packet's
+ *     beginning time otherwise.
  *
  * Known buggy tracer versions:
  *  - before lttng-ust 2.11.0
@@ -1490,73 +960,52 @@ static int decode_packet_last_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
  */
 static int fix_index_lttng_event_after_packet_bug(struct ctf_fs_trace *trace)
 {
-    int ret = 0;
-    guint ds_file_group_i;
-    GPtrArray *ds_file_groups = trace->ds_file_groups;
-    bt_logging_level log_level = trace->log_level;
-
-    for (ds_file_group_i = 0; ds_file_group_i < ds_file_groups->len; ds_file_group_i++) {
-        guint entry_i;
-        struct ctf_clock_class *default_cc;
-        struct ctf_fs_ds_index_entry *last_entry;
-        struct ctf_fs_ds_index *index;
-
-        struct ctf_fs_ds_file_group *ds_file_group =
-            (struct ctf_fs_ds_file_group *) g_ptr_array_index(ds_file_groups, ds_file_group_i);
-
+    for (const auto& ds_file_group : trace->ds_file_groups) {
         BT_ASSERT(ds_file_group);
-        index = ds_file_group->index;
+        auto& index = ds_file_group->index;
 
-        BT_ASSERT(index);
-        BT_ASSERT(index->entries);
-        BT_ASSERT(index->entries->len > 0);
+        BT_ASSERT(!index.entries.empty());
 
         /*
          * Iterate over all entries but the last one. The last one is
          * fixed differently after.
          */
-        for (entry_i = 0; entry_i < index->entries->len - 1; entry_i++) {
-            struct ctf_fs_ds_index_entry *curr_entry, *next_entry;
-
-            curr_entry = (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_i);
-            next_entry = (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_i + 1);
+        for (size_t entry_i = 0; entry_i < index.entries.size() - 1; ++entry_i) {
+            auto& curr_entry = index.entries[entry_i];
+            const auto& next_entry = index.entries[entry_i + 1];
 
             /*
              * 1. Set the current index entry `end` timestamp to
              * the next index entry `begin` timestamp.
              */
-            curr_entry->timestamp_end = next_entry->timestamp_begin;
-            curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
+            curr_entry.timestamp_end = next_entry.timestamp_begin;
+            curr_entry.timestamp_end_ns = next_entry.timestamp_begin_ns;
         }
 
         /*
          * 2. Fix the last entry by decoding the last event of the last
          * packet.
          */
-        last_entry =
-            (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, index->entries->len - 1);
-        BT_ASSERT(last_entry);
+        auto& last_entry = index.entries.back();
 
         BT_ASSERT(ds_file_group->sc->default_clock_class);
-        default_cc = ds_file_group->sc->default_clock_class;
+        ctf_clock_class *default_cc = ds_file_group->sc->default_clock_class;
 
         /*
          * Decode packet to read the timestamp of the last event of the
          * entry.
          */
-        ret = decode_packet_last_event_timestamp(trace, default_cc, last_entry,
-                                                 &last_entry->timestamp_end,
-                                                 &last_entry->timestamp_end_ns);
+        int ret = decode_packet_last_event_timestamp(
+            trace, default_cc, last_entry, &last_entry.timestamp_end, &last_entry.timestamp_end_ns);
         if (ret) {
-            BT_COMP_LOGE_APPEND_CAUSE(
-                trace->self_comp,
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                trace->logger,
                 "Failed to decode stream's last packet to get its last event's clock snapshot.");
-            goto end;
+            return ret;
         }
     }
 
-end:
-    return ret;
+    return 0;
 }
 
 /*
@@ -1575,58 +1024,44 @@ end:
  */
 static int fix_index_barectf_event_before_packet_bug(struct ctf_fs_trace *trace)
 {
-    int ret = 0;
-    guint ds_file_group_i;
-    GPtrArray *ds_file_groups = trace->ds_file_groups;
-    bt_logging_level log_level = trace->log_level;
-
-    for (ds_file_group_i = 0; ds_file_group_i < ds_file_groups->len; ds_file_group_i++) {
-        guint entry_i;
-        struct ctf_clock_class *default_cc;
-        ctf_fs_ds_file_group *ds_file_group =
-            (ctf_fs_ds_file_group *) g_ptr_array_index(ds_file_groups, ds_file_group_i);
+    for (const auto& ds_file_group : trace->ds_file_groups) {
+        auto& index = ds_file_group->index;
 
-        struct ctf_fs_ds_index *index = ds_file_group->index;
-
-        BT_ASSERT(index);
-        BT_ASSERT(index->entries);
-        BT_ASSERT(index->entries->len > 0);
+        BT_ASSERT(!index.entries.empty());
 
         BT_ASSERT(ds_file_group->sc->default_clock_class);
-        default_cc = ds_file_group->sc->default_clock_class;
+        ctf_clock_class *default_cc = ds_file_group->sc->default_clock_class;
 
         /*
          * 1. Iterate over the index, starting from the second entry
          * (index = 1).
          */
-        for (entry_i = 1; entry_i < index->entries->len; entry_i++) {
-            ctf_fs_ds_index_entry *prev_entry =
-                (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_i - 1);
-            ctf_fs_ds_index_entry *curr_entry =
-                (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_i);
+        for (size_t entry_i = 1; entry_i < index.entries.size(); ++entry_i) {
+            auto& prev_entry = index.entries[entry_i - 1];
+            auto& curr_entry = index.entries[entry_i];
             /*
              * 2. Set the current entry `begin` timestamp to the
              * timestamp of the first event of the current packet.
              */
-            ret = decode_packet_first_event_timestamp(trace, default_cc, curr_entry,
-                                                      &curr_entry->timestamp_begin,
-                                                      &curr_entry->timestamp_begin_ns);
+            int ret = decode_packet_first_event_timestamp(trace, default_cc, curr_entry,
+                                                          &curr_entry.timestamp_begin,
+                                                          &curr_entry.timestamp_begin_ns);
             if (ret) {
-                BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
-                                          "Failed to decode first event's clock snapshot");
-                goto end;
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(trace->logger,
+                                             "Failed to decode first event's clock snapshot");
+                return ret;
             }
 
             /*
              * 3. Set the previous entry `end` timestamp to the
              * timestamp of the first event of the current packet.
              */
-            prev_entry->timestamp_end = curr_entry->timestamp_begin;
-            prev_entry->timestamp_end_ns = curr_entry->timestamp_begin_ns;
+            prev_entry.timestamp_end = curr_entry.timestamp_begin;
+            prev_entry.timestamp_end_ns = curr_entry.timestamp_begin_ns;
         }
     }
-end:
-    return ret;
+
+    return 0;
 }
 
 /*
@@ -1648,69 +1083,52 @@ end:
  */
 static int fix_index_lttng_crash_quirk(struct ctf_fs_trace *trace)
 {
-    int ret = 0;
-    guint ds_file_group_idx;
-    GPtrArray *ds_file_groups = trace->ds_file_groups;
-    bt_logging_level log_level = trace->log_level;
-
-    for (ds_file_group_idx = 0; ds_file_group_idx < ds_file_groups->len; ds_file_group_idx++) {
-        guint entry_idx;
+    for (const auto& ds_file_group : trace->ds_file_groups) {
         struct ctf_clock_class *default_cc;
-        struct ctf_fs_ds_index *index;
-
-        ctf_fs_ds_file_group *ds_file_group =
-            (ctf_fs_ds_file_group *) g_ptr_array_index(ds_file_groups, ds_file_group_idx);
 
         BT_ASSERT(ds_file_group);
-        index = ds_file_group->index;
+        auto& index = ds_file_group->index;
 
         BT_ASSERT(ds_file_group->sc->default_clock_class);
         default_cc = ds_file_group->sc->default_clock_class;
 
-        BT_ASSERT(index);
-        BT_ASSERT(index->entries);
-        BT_ASSERT(index->entries->len > 0);
+        BT_ASSERT(!index.entries.empty());
 
-        ctf_fs_ds_index_entry *last_entry =
-            (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, index->entries->len - 1);
-        BT_ASSERT(last_entry);
+        auto& last_entry = index.entries.back();
 
         /* 1. Fix the last entry first. */
-        if (last_entry->timestamp_end == 0 && last_entry->timestamp_begin != 0) {
+        if (last_entry.timestamp_end == 0 && last_entry.timestamp_begin != 0) {
             /*
              * Decode packet to read the timestamp of the
              * last event of the stream file.
              */
-            ret = decode_packet_last_event_timestamp(trace, default_cc, last_entry,
-                                                     &last_entry->timestamp_end,
-                                                     &last_entry->timestamp_end_ns);
+            int ret = decode_packet_last_event_timestamp(trace, default_cc, last_entry,
+                                                         &last_entry.timestamp_end,
+                                                         &last_entry.timestamp_end_ns);
             if (ret) {
-                BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
-                                          "Failed to decode last event's clock snapshot");
-                goto end;
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(trace->logger,
+                                             "Failed to decode last event's clock snapshot");
+                return ret;
             }
         }
 
         /* Iterate over all entries but the last one. */
-        for (entry_idx = 0; entry_idx < index->entries->len - 1; entry_idx++) {
-            ctf_fs_ds_index_entry *curr_entry =
-                (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_idx);
-            ctf_fs_ds_index_entry *next_entry =
-                (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_idx + 1);
+        for (size_t entry_idx = 0; entry_idx < index.entries.size() - 1; ++entry_idx) {
+            auto& curr_entry = index.entries[entry_idx];
+            const auto& next_entry = index.entries[entry_idx + 1];
 
-            if (curr_entry->timestamp_end == 0 && curr_entry->timestamp_begin != 0) {
+            if (curr_entry.timestamp_end == 0 && curr_entry.timestamp_begin != 0) {
                 /*
                  * 2. Set the current index entry `end` timestamp to
                  * the next index entry `begin` timestamp.
                  */
-                curr_entry->timestamp_end = next_entry->timestamp_begin;
-                curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
+                curr_entry.timestamp_end = next_entry.timestamp_begin;
+                curr_entry.timestamp_end_ns = next_entry.timestamp_begin_ns;
             }
         }
     }
 
-end:
-    return ret;
+    return 0;
 }
 
 /*
@@ -1720,9 +1138,6 @@ end:
  */
 static int extract_tracer_info(struct ctf_fs_trace *trace, struct tracer_info *current_tracer_info)
 {
-    int ret = 0;
-    struct ctf_trace_class_env_entry *entry;
-
     /* Clear the current_tracer_info struct */
     memset(current_tracer_info, 0, sizeof(*current_tracer_info));
 
@@ -1731,9 +1146,10 @@ static int extract_tracer_info(struct ctf_fs_trace *trace, struct tracer_info *c
      * major version are needed. If one of these is missing, consider it an
      * extraction failure.
      */
-    entry = ctf_trace_class_borrow_env_entry_by_name(trace->metadata->tc, "tracer_name");
+    ctf_trace_class_env_entry *entry =
+        ctf_trace_class_borrow_env_entry_by_name(trace->metadata->tc, "tracer_name");
     if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR) {
-        goto missing_bare_minimum;
+        return -1;
     }
 
     /* Set tracer name. */
@@ -1741,7 +1157,7 @@ static int extract_tracer_info(struct ctf_fs_trace *trace, struct tracer_info *c
 
     entry = ctf_trace_class_borrow_env_entry_by_name(trace->metadata->tc, "tracer_major");
     if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
-        goto missing_bare_minimum;
+        return -1;
     }
 
     /* Set major version number. */
@@ -1749,7 +1165,7 @@ static int extract_tracer_info(struct ctf_fs_trace *trace, struct tracer_info *c
 
     entry = ctf_trace_class_borrow_env_entry_by_name(trace->metadata->tc, "tracer_minor");
     if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
-        goto end;
+        return 0;
     }
 
     /* Set minor version number. */
@@ -1766,18 +1182,13 @@ static int extract_tracer_info(struct ctf_fs_trace *trace, struct tracer_info *c
     }
 
     if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
-        goto end;
+        return 0;
     }
 
     /* Set patch version number. */
     current_tracer_info->patch = entry->value.i;
 
-    goto end;
-
-missing_bare_minimum:
-    ret = -1;
-end:
-    return ret;
+    return 0;
 }
 
 static bool is_tracer_affected_by_lttng_event_after_packet_bug(struct tracer_info *curr_tracer_info)
@@ -1858,15 +1269,11 @@ static bool is_tracer_affected_by_lttng_crash_quirk(struct tracer_info *curr_tra
  * Looks for trace produced by known buggy tracers and fix up the index
  * produced earlier.
  */
-static int fix_packet_index_tracer_bugs(struct ctf_fs_component *ctf_fs,
-                                        bt_self_component *self_comp,
-                                        bt_self_component_class *self_comp_class)
+static int fix_packet_index_tracer_bugs(ctf_fs_trace *trace)
 {
-    int ret = 0;
     struct tracer_info current_tracer_info;
-    bt_logging_level log_level = ctf_fs->log_level;
 
-    ret = extract_tracer_info(ctf_fs->trace, &current_tracer_info);
+    int ret = extract_tracer_info(trace, &current_tracer_info);
     if (ret) {
         /*
          * A trace may not have all the necessary environment
@@ -1875,156 +1282,111 @@ static int fix_packet_index_tracer_bugs(struct ctf_fs_component *ctf_fs,
          * are needed. Failing to extract these entries is not
          * an error.
          */
-        ret = 0;
-        BT_LOGI_STR("Cannot extract tracer information necessary to compare with buggy versions.");
-        goto end;
-        ;
+        BT_CPPLOGI_STR_SPEC(
+            trace->logger,
+            "Cannot extract tracer information necessary to compare with buggy versions.");
+        return 0;
     }
 
     /* Check if the trace may be affected by old tracer bugs. */
     if (is_tracer_affected_by_lttng_event_after_packet_bug(&current_tracer_info)) {
-        BT_LOGI_STR("Trace may be affected by LTTng tracer packet timestamp bug. Fixing up.");
-        ret = fix_index_lttng_event_after_packet_bug(ctf_fs->trace);
+        BT_CPPLOGI_STR_SPEC(
+            trace->logger,
+            "Trace may be affected by LTTng tracer packet timestamp bug. Fixing up.");
+        ret = fix_index_lttng_event_after_packet_bug(trace);
         if (ret) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                    "Failed to fix LTTng event-after-packet bug.");
-            goto end;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(trace->logger,
+                                         "Failed to fix LTTng event-after-packet bug.");
+            return ret;
         }
-        ctf_fs->trace->metadata->tc->quirks.lttng_event_after_packet = true;
+        trace->metadata->tc->quirks.lttng_event_after_packet = true;
     }
 
     if (is_tracer_affected_by_barectf_event_before_packet_bug(&current_tracer_info)) {
-        BT_LOGI_STR("Trace may be affected by barectf tracer packet timestamp bug. Fixing up.");
-        ret = fix_index_barectf_event_before_packet_bug(ctf_fs->trace);
+        BT_CPPLOGI_STR_SPEC(
+            trace->logger,
+            "Trace may be affected by barectf tracer packet timestamp bug. Fixing up.");
+        ret = fix_index_barectf_event_before_packet_bug(trace);
         if (ret) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                self_comp, self_comp_class, "Failed to fix barectf event-before-packet bug.");
-            goto end;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(trace->logger,
+                                         "Failed to fix barectf event-before-packet bug.");
+            return ret;
         }
-        ctf_fs->trace->metadata->tc->quirks.barectf_event_before_packet = true;
+        trace->metadata->tc->quirks.barectf_event_before_packet = true;
     }
 
     if (is_tracer_affected_by_lttng_crash_quirk(&current_tracer_info)) {
-        ret = fix_index_lttng_crash_quirk(ctf_fs->trace);
+        ret = fix_index_lttng_crash_quirk(trace);
         if (ret) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                    "Failed to fix lttng-crash timestamp quirks.");
-            goto end;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(trace->logger,
+                                         "Failed to fix lttng-crash timestamp quirks.");
+            return ret;
         }
-        ctf_fs->trace->metadata->tc->quirks.lttng_crash = true;
+        trace->metadata->tc->quirks.lttng_crash = true;
     }
 
-end:
-    return ret;
+    return 0;
 }
 
-static gint compare_ds_file_groups_by_first_path(gconstpointer a, gconstpointer b)
+static bool compare_ds_file_groups_by_first_path(const ctf_fs_ds_file_group::UP& ds_file_group_a,
+                                                 const ctf_fs_ds_file_group::UP& ds_file_group_b)
 {
-    ctf_fs_ds_file_group * const *ds_file_group_a = (ctf_fs_ds_file_group **) a;
-    ctf_fs_ds_file_group * const *ds_file_group_b = (ctf_fs_ds_file_group **) b;
+    BT_ASSERT(!ds_file_group_a->ds_file_infos.empty());
+    BT_ASSERT(!ds_file_group_b->ds_file_infos.empty());
 
-    BT_ASSERT((*ds_file_group_a)->ds_file_infos->len > 0);
-    BT_ASSERT((*ds_file_group_b)->ds_file_infos->len > 0);
+    const auto& first_ds_file_info_a = *ds_file_group_a->ds_file_infos[0];
+    const auto& first_ds_file_info_b = *ds_file_group_b->ds_file_infos[0];
 
-    const ctf_fs_ds_file_info *first_ds_file_info_a =
-        (const ctf_fs_ds_file_info *) (*ds_file_group_a)->ds_file_infos->pdata[0];
-    const ctf_fs_ds_file_info *first_ds_file_info_b =
-        (const ctf_fs_ds_file_info *) (*ds_file_group_b)->ds_file_infos->pdata[0];
-
-    return strcmp(first_ds_file_info_a->path->str, first_ds_file_info_b->path->str);
-}
-
-static gint compare_strings(gconstpointer p_a, gconstpointer p_b)
-{
-    const char *a = *((const char **) p_a);
-    const char *b = *((const char **) p_b);
-
-    return strcmp(a, b);
+    return first_ds_file_info_a.path < first_ds_file_info_b.path;
 }
 
 int ctf_fs_component_create_ctf_fs_trace(struct ctf_fs_component *ctf_fs,
-                                         const bt_value *paths_value,
-                                         const bt_value *trace_name_value,
-                                         bt_self_component *self_comp,
-                                         bt_self_component_class *self_comp_class)
+                                         const bt2::ConstArrayValue pathsValue,
+                                         const char *traceName, bt_self_component *selfComp)
 {
-    int ret = 0;
-    uint64_t i;
-    bt_logging_level log_level = ctf_fs->log_level;
-    GPtrArray *paths = NULL;
-    GPtrArray *traces;
-    const char *trace_name;
-
-    BT_ASSERT(bt_value_get_type(paths_value) == BT_VALUE_TYPE_ARRAY);
-    BT_ASSERT(!bt_value_array_is_empty(paths_value));
-
-    traces = g_ptr_array_new_with_free_func(ctf_fs_trace_destroy_notifier);
-    if (!traces) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Failed to allocate a GPtrArray.");
-        goto error;
-    }
-
-    paths = g_ptr_array_new_with_free_func(g_free);
-    if (!paths) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Failed to allocate a GPtrArray.");
-        goto error;
-    }
+    std::vector<std::string> paths;
 
-    trace_name = trace_name_value ? bt_value_string_get(trace_name_value) : NULL;
+    BT_ASSERT(!pathsValue.isEmpty());
 
     /*
      * Create a sorted array of the paths, to make the execution of this
      * component deterministic.
      */
-    for (i = 0; i < bt_value_array_get_length(paths_value); i++) {
-        const bt_value *path_value = bt_value_array_borrow_element_by_index_const(paths_value, i);
-        const char *input = bt_value_string_get(path_value);
-        gchar *input_copy;
-
-        input_copy = g_strdup(input);
-        if (!input_copy) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                    "Failed to copy a string.");
-            goto error;
-        }
-
-        g_ptr_array_add(paths, input_copy);
+    for (const auto pathValue : pathsValue) {
+        BT_ASSERT(pathValue.isString());
+        paths.emplace_back(pathValue.asString().value().str());
     }
 
-    g_ptr_array_sort(paths, compare_strings);
+    std::sort(paths.begin(), paths.end());
 
     /* Create a separate ctf_fs_trace object for each path. */
-    for (i = 0; i < paths->len; i++) {
-        const char *path = (const char *) g_ptr_array_index(paths, i);
-
-        ret = ctf_fs_component_create_ctf_fs_trace_one_path(ctf_fs, path, trace_name, traces,
-                                                            self_comp, self_comp_class);
+    std::vector<ctf_fs_trace::UP> traces;
+    for (const auto& path : paths) {
+        int ret = ctf_fs_component_create_ctf_fs_trace_one_path(ctf_fs, path.c_str(), traceName,
+                                                                traces, selfComp);
         if (ret) {
-            goto end;
+            return ret;
         }
     }
 
-    if (traces->len > 1) {
-        struct ctf_fs_trace *first_trace = (struct ctf_fs_trace *) traces->pdata[0];
+    if (traces.size() > 1) {
+        ctf_fs_trace *first_trace = traces[0].get();
         const uint8_t *first_trace_uuid = first_trace->metadata->tc->uuid;
-        struct ctf_fs_trace *trace;
 
         /*
          * We have more than one trace, they must all share the same
          * UUID, verify that.
          */
-        for (i = 0; i < traces->len; i++) {
-            struct ctf_fs_trace *this_trace = (struct ctf_fs_trace *) traces->pdata[i];
+        for (size_t i = 0; i < traces.size(); i++) {
+            ctf_fs_trace *this_trace = traces[i].get();
             const uint8_t *this_trace_uuid = this_trace->metadata->tc->uuid;
 
             if (!this_trace->metadata->tc->is_uuid_set) {
-                BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    self_comp, self_comp_class,
-                    "Multiple traces given, but a trace does not have a UUID: path=%s",
-                    this_trace->path->str);
-                goto error;
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    ctf_fs->logger,
+                    "Multiple traces given, but a trace does not have a UUID: path={}",
+                    this_trace->path);
+                return -1;
             }
 
             if (bt_uuid_compare(first_trace_uuid, this_trace_uuid) != 0) {
@@ -2034,35 +1396,31 @@ int ctf_fs_component_create_ctf_fs_trace(struct ctf_fs_component *ctf_fs,
                 bt_uuid_to_str(first_trace_uuid, first_trace_uuid_str);
                 bt_uuid_to_str(this_trace_uuid, this_trace_uuid_str);
 
-                BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                    self_comp, self_comp_class,
-                    "Multiple traces given, but UUIDs don't match: "
-                    "first-trace-uuid=%s, first-trace-path=%s, "
-                    "trace-uuid=%s, trace-path=%s",
-                    first_trace_uuid_str, first_trace->path->str, this_trace_uuid_str,
-                    this_trace->path->str);
-                goto error;
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger,
+                                             "Multiple traces given, but UUIDs don't match: "
+                                             "first-trace-uuid={}, first-trace-path={}, "
+                                             "trace-uuid={}, trace-path={}",
+                                             first_trace_uuid_str, first_trace->path,
+                                             this_trace_uuid_str, this_trace->path);
+                return -1;
             }
         }
 
-        ret = merge_ctf_fs_traces((struct ctf_fs_trace **) traces->pdata, traces->len, &trace);
+        int ret = merge_ctf_fs_traces(std::move(traces), ctf_fs->trace);
         if (ret) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                    "Failed to merge traces with the same UUID.");
-            goto error;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger,
+                                         "Failed to merge traces with the same UUID.");
+            return ret;
         }
-
-        ctf_fs->trace = trace;
     } else {
         /* Just one trace, it may or may not have a UUID, both are fine. */
-        ctf_fs->trace = (ctf_fs_trace *) traces->pdata[0];
-        traces->pdata[0] = NULL;
+        ctf_fs->trace = std::move(traces[0]);
     }
 
-    ret = fix_packet_index_tracer_bugs(ctf_fs, self_comp, self_comp_class);
+    int ret = fix_packet_index_tracer_bugs(ctf_fs->trace.get());
     if (ret) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Failed to fix packet index tracer bugs.");
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Failed to fix packet index tracer bugs.");
+        return ret;
     }
 
     /*
@@ -2077,116 +1435,56 @@ int ctf_fs_component_create_ctf_fs_trace(struct ctf_fs_component *ctf_fs,
      * Having a deterministic order here can help debugging and
      * testing.
      */
-    g_ptr_array_sort(ctf_fs->trace->ds_file_groups, compare_ds_file_groups_by_first_path);
-    goto end;
-error:
-    ret = -1;
-
-end:
-    if (traces) {
-        g_ptr_array_free(traces, TRUE);
-    }
+    std::sort(ctf_fs->trace->ds_file_groups.begin(), ctf_fs->trace->ds_file_groups.end(),
+              compare_ds_file_groups_by_first_path);
 
-    if (paths) {
-        g_ptr_array_free(paths, TRUE);
-    }
-
-    return ret;
+    return 0;
 }
 
-static GString *get_stream_instance_unique_name(struct ctf_fs_ds_file_group *ds_file_group)
+static const std::string&
+get_stream_instance_unique_name(struct ctf_fs_ds_file_group *ds_file_group)
 {
-    GString *name;
-    struct ctf_fs_ds_file_info *ds_file_info;
-
-    name = g_string_new(NULL);
-    if (!name) {
-        goto end;
-    }
-
     /*
-     * If there's more than one stream file in the stream file
-     * group, the first (earliest) stream file's path is used as
-     * the stream's unique name.
+     * The first (earliest) stream file's path is used as the stream's unique
+     * name.
      */
-    BT_ASSERT(ds_file_group->ds_file_infos->len > 0);
-    ds_file_info = (ctf_fs_ds_file_info *) g_ptr_array_index(ds_file_group->ds_file_infos, 0);
-    g_string_assign(name, ds_file_info->path->str);
-
-end:
-    return name;
+    BT_ASSERT(!ds_file_group->ds_file_infos.empty());
+    return ds_file_group->ds_file_infos[0]->path;
 }
 
 /* Create the IR stream objects for ctf_fs_trace. */
 
 static int create_streams_for_trace(struct ctf_fs_trace *ctf_fs_trace)
 {
-    int ret;
-    GString *name = NULL;
-    guint i;
-    bt_logging_level log_level = ctf_fs_trace->log_level;
-    bt_self_component *self_comp = ctf_fs_trace->self_comp;
-
-    for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
-        ctf_fs_ds_file_group *ds_file_group =
-            (ctf_fs_ds_file_group *) g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
-        name = get_stream_instance_unique_name(ds_file_group);
-
-        if (!name) {
-            goto error;
-        }
+    for (const auto& ds_file_group : ctf_fs_trace->ds_file_groups) {
+        const std::string& name = get_stream_instance_unique_name(ds_file_group.get());
 
-        if (ds_file_group->sc->ir_sc) {
-            BT_ASSERT(ctf_fs_trace->trace);
-
-            if (ds_file_group->stream_id == UINT64_C(-1)) {
-                /* No stream ID: use 0 */
-                ds_file_group->stream = bt_stream_create_with_id(
-                    ds_file_group->sc->ir_sc, ctf_fs_trace->trace, ctf_fs_trace->next_stream_id);
-                ctf_fs_trace->next_stream_id++;
-            } else {
-                /* Specific stream ID */
-                ds_file_group->stream =
-                    bt_stream_create_with_id(ds_file_group->sc->ir_sc, ctf_fs_trace->trace,
-                                             (uint64_t) ds_file_group->stream_id);
-            }
-        } else {
-            ds_file_group->stream = NULL;
-        }
+        BT_ASSERT(ds_file_group->sc->ir_sc);
+        BT_ASSERT(ctf_fs_trace->trace);
 
-        if (!ds_file_group->stream) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Cannot create stream for DS file group: "
-                                      "addr=%p, stream-name=\"%s\"",
-                                      ds_file_group, name->str);
-            goto error;
+        const bt2::StreamClass sc {ds_file_group->sc->ir_sc};
+
+        if (ds_file_group->stream_id == UINT64_C(-1)) {
+            /* No stream ID: use 0 */
+            ds_file_group->stream =
+                sc.instantiate(*ctf_fs_trace->trace, ctf_fs_trace->next_stream_id);
+            ctf_fs_trace->next_stream_id++;
+        } else {
+            /* Specific stream ID */
+            ds_file_group->stream = sc.instantiate(*ctf_fs_trace->trace, ds_file_group->stream_id);
         }
 
-        ret = bt_stream_set_name(ds_file_group->stream, name->str);
+        int ret = bt_stream_set_name(ds_file_group->stream->libObjPtr(), name.c_str());
         if (ret) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Cannot set stream's name: "
-                                      "addr=%p, stream-name=\"%s\"",
-                                      ds_file_group->stream, name->str);
-            goto error;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs_trace->logger,
+                                         "Cannot set stream's name: "
+                                         "addr={}, stream-name=\"{}\"",
+                                         fmt::ptr(ds_file_group->stream->libObjPtr()), name);
+            return ret;
         }
-
-        g_string_free(name, TRUE);
-        name = NULL;
     }
 
-    ret = 0;
-    goto end;
-
-error:
-    ret = -1;
-
-end:
-
-    if (name) {
-        g_string_free(name, TRUE);
-    }
-    return ret;
+    return 0;
 }
 
 static const bt_param_validation_value_descr inputs_elem_descr =
@@ -2206,138 +1504,122 @@ static bt_param_validation_map_value_entry_descr fs_params_entries_descr[] = {
      bt_param_validation_value_descr::makeBool()},
     BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END};
 
-bool read_src_fs_parameters(const bt_value *params, const bt_value **inputs,
-                            const bt_value **trace_name, struct ctf_fs_component *ctf_fs,
-                            bt_self_component *self_comp, bt_self_component_class *self_comp_class)
+ctf::src::fs::Parameters read_src_fs_parameters(const bt2::ConstMapValue params,
+                                                const bt2c::Logger& logger)
 {
-    bool ret;
-    const bt_value *value;
-    bt_logging_level log_level = ctf_fs->log_level;
-    enum bt_param_validation_status validate_value_status;
     gchar *error = NULL;
+    bt_param_validation_status validate_value_status =
+        bt_param_validation_validate(params.libObjPtr(), fs_params_entries_descr, &error);
 
-    validate_value_status = bt_param_validation_validate(params, fs_params_entries_descr, &error);
     if (validate_value_status != BT_PARAM_VALIDATION_STATUS_OK) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class, "%s", error);
-        ret = false;
-        goto end;
+        bt2c::GCharUP errorFreer {error};
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2c::Error, "{}", error);
     }
 
-    /* inputs parameter */
-    *inputs = bt_value_map_borrow_entry_value_const(params, "inputs");
+    ctf::src::fs::Parameters parameters {params["inputs"]->asArray()};
 
     /* clock-class-offset-s parameter */
-    value = bt_value_map_borrow_entry_value_const(params, "clock-class-offset-s");
-    if (value) {
-        ctf_fs->metadata_config.clock_class_offset_s = bt_value_integer_signed_get(value);
+    if (const auto clockClassOffsetS = params["clock-class-offset-s"]) {
+        parameters.clkClsCfg.offsetSec = clockClassOffsetS->asSignedInteger().value();
     }
 
     /* clock-class-offset-ns parameter */
-    value = bt_value_map_borrow_entry_value_const(params, "clock-class-offset-ns");
-    if (value) {
-        ctf_fs->metadata_config.clock_class_offset_ns = bt_value_integer_signed_get(value);
+    if (const auto clockClassOffsetNs = params["clock-class-offset-ns"]) {
+        parameters.clkClsCfg.offsetNanoSec = clockClassOffsetNs->asSignedInteger().value();
     }
 
     /* force-clock-class-origin-unix-epoch parameter */
-    value = bt_value_map_borrow_entry_value_const(params, "force-clock-class-origin-unix-epoch");
-    if (value) {
-        ctf_fs->metadata_config.force_clock_class_origin_unix_epoch = bt_value_bool_get(value);
+    if (const auto forceClockClassOriginUnixEpoch = params["force-clock-class-origin-unix-epoch"]) {
+        parameters.clkClsCfg.forceOriginIsUnixEpoch =
+            forceClockClassOriginUnixEpoch->asBool().value();
     }
 
     /* trace-name parameter */
-    *trace_name = bt_value_map_borrow_entry_value_const(params, "trace-name");
-
-    ret = true;
+    if (const auto traceName = params["trace-name"]) {
+        parameters.traceName = traceName->asString().value().str();
+    }
 
-end:
-    g_free(error);
-    return ret;
+    return parameters;
 }
 
-static struct ctf_fs_component *ctf_fs_create(const bt_value *params,
-                                              bt_self_component_source *self_comp_src)
+static ctf_fs_component::UP ctf_fs_create(const bt2::ConstMapValue params,
+                                          bt_self_component_source *self_comp_src)
 {
-    struct ctf_fs_component *ctf_fs = NULL;
-    const bt_value *inputs_value;
-    const bt_value *trace_name_value;
     bt_self_component *self_comp = bt_self_component_source_as_self_component(self_comp_src);
+    const bt2c::Logger logger {bt2::SelfSourceComponent {self_comp_src}, "PLUGIN/SRC.CTF.FS/COMP"};
+    const auto parameters = read_src_fs_parameters(params, logger);
+    auto ctf_fs = bt2s::make_unique<ctf_fs_component>(parameters.clkClsCfg, logger);
 
-    ctf_fs = ctf_fs_component_create(
-        bt_component_get_logging_level(bt_self_component_as_component(self_comp)), self_comp);
-    if (!ctf_fs) {
-        goto error;
-    }
-
-    if (!read_src_fs_parameters(params, &inputs_value, &trace_name_value, ctf_fs, self_comp,
-                                NULL)) {
-        goto error;
+    if (ctf_fs_component_create_ctf_fs_trace(
+            ctf_fs.get(), parameters.inputs,
+            parameters.traceName ? parameters.traceName->c_str() : nullptr, self_comp)) {
+        return nullptr;
     }
 
-    bt_self_component_set_data(self_comp, ctf_fs);
-
-    if (ctf_fs_component_create_ctf_fs_trace(ctf_fs, inputs_value, trace_name_value, self_comp,
-                                             NULL)) {
-        goto error;
+    if (create_streams_for_trace(ctf_fs->trace.get())) {
+        return nullptr;
     }
 
-    if (create_streams_for_trace(ctf_fs->trace)) {
-        goto error;
+    if (create_ports_for_trace(ctf_fs.get(), ctf_fs->trace.get(), self_comp_src)) {
+        return nullptr;
     }
 
-    if (create_ports_for_trace(ctf_fs, ctf_fs->trace, self_comp_src)) {
-        goto error;
-    }
-
-    goto end;
-
-error:
-    ctf_fs_destroy(ctf_fs);
-    ctf_fs = NULL;
-    bt_self_component_set_data(self_comp, NULL);
-
-end:
     return ctf_fs;
 }
 
-BT_HIDDEN
-bt_component_class_initialize_method_status
-ctf_fs_init(bt_self_component_source *self_comp_src, bt_self_component_source_configuration *config,
-            const bt_value *params, __attribute__((unused)) void *init_method_data)
+bt_component_class_initialize_method_status ctf_fs_init(bt_self_component_source *self_comp_src,
+                                                        bt_self_component_source_configuration *,
+                                                        const bt_value *params, void *)
 {
-    struct ctf_fs_component *ctf_fs;
-    bt_component_class_initialize_method_status ret =
-        BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+    try {
+        bt_component_class_initialize_method_status ret =
+            BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
 
-    ctf_fs = ctf_fs_create(params, self_comp_src);
-    if (!ctf_fs) {
-        ret = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-    }
+        ctf_fs_component::UP ctf_fs = ctf_fs_create(bt2::ConstMapValue {params}, self_comp_src);
+        if (!ctf_fs) {
+            ret = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+        }
 
-    return ret;
+        bt_self_component_set_data(bt_self_component_source_as_self_component(self_comp_src),
+                                   ctf_fs.release());
+        return ret;
+    } catch (const std::bad_alloc&) {
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+    }
 }
 
-BT_HIDDEN
-bt_component_class_query_method_status ctf_fs_query(bt_self_component_class_source *comp_class,
+bt_component_class_query_method_status ctf_fs_query(bt_self_component_class_source *comp_class_src,
                                                     bt_private_query_executor *priv_query_exec,
                                                     const char *object, const bt_value *params,
                                                     __attribute__((unused)) void *method_data,
                                                     const bt_value **result)
 {
-    bt_component_class_query_method_status status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-    bt_logging_level log_level = bt_query_executor_get_logging_level(
-        bt_private_query_executor_as_query_executor_const(priv_query_exec));
-
-    if (strcmp(object, "metadata-info") == 0) {
-        status = metadata_info_query(comp_class, params, log_level, result);
-    } else if (strcmp(object, "babeltrace.trace-infos") == 0) {
-        status = trace_infos_query(comp_class, params, log_level, result);
-    } else if (!strcmp(object, "babeltrace.support-info")) {
-        status = support_info_query(comp_class, params, log_level, result);
-    } else {
-        BT_LOGE("Unknown query object `%s`", object);
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
-        goto end;
+    try {
+        bt2c::Logger logger {bt2::SelfComponentClass {comp_class_src},
+                             bt2::PrivateQueryExecutor {priv_query_exec},
+                             "PLUGIN/SRC.CTF.FS/QUERY"};
+        bt2::ConstMapValue paramsObj(params);
+        bt2::Value::Shared resultObj;
+
+        if (strcmp(object, "metadata-info") == 0) {
+            resultObj = metadata_info_query(paramsObj, logger);
+        } else if (strcmp(object, "babeltrace.trace-infos") == 0) {
+            resultObj = trace_infos_query(paramsObj, logger);
+        } else if (!strcmp(object, "babeltrace.support-info")) {
+            resultObj = support_info_query(paramsObj, logger);
+        } else {
+            BT_CPPLOGE_SPEC(logger, "Unknown query object `{}`", object);
+            return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
+        }
+
+        *result = resultObj.release().libObjPtr();
+
+        return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+    } catch (const std::bad_alloc&) {
+        return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
     }
-end:
-    return status;
 }
index fe8f0507d24651b1dadab4d13ebfcd05b204d5cf..c9ca2a90be0dd093086a89c6ced1f38d3eaedfa7 100644 (file)
 #ifndef BABELTRACE_PLUGIN_CTF_FS_H
 #define BABELTRACE_PLUGIN_CTF_FS_H
 
-#include <stdbool.h>
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-#include "data-stream-file.hpp"
-#include "metadata.hpp"
-#include "../common/metadata/decoder.hpp"
-
-BT_HIDDEN
-extern bool ctf_fs_debug;
-
-struct ctf_fs_file
-{
-    bt_logging_level log_level;
+#include <glib.h>
 
-    /* Weak */
-    bt_self_component *self_comp;
+#include <babeltrace2/babeltrace.h>
 
-    /* Owned by this */
-    GString *path;
+#include "cpp-common/bt2c/logging.hpp"
 
-    /* Owned by this */
-    FILE *fp;
+#include "data-stream-file.hpp"
+#include "plugins/ctf/common/src/metadata/tsdl/decoder.hpp"
 
-    off_t size;
-};
+extern bool ctf_fs_debug;
 
 struct ctf_fs_metadata
 {
-    /* Owned by this */
-    struct ctf_metadata_decoder *decoder;
+    using UP = std::unique_ptr<ctf_fs_metadata>;
 
     /* Owned by this */
-    bt_trace_class *trace_class;
-
-    /* Weak (owned by `decoder` above) */
-    struct ctf_trace_class *tc;
-
-    /* Owned by this */
-    char *text;
-
-    int bo;
-};
+    ctf_metadata_decoder_up decoder;
 
-struct ctf_fs_component
-{
-    bt_logging_level log_level;
+    bt2::TraceClass::Shared trace_class;
 
-    /* Array of struct ctf_fs_port_data *, owned by this */
-    GPtrArray *port_data;
-
-    /* Owned by this */
-    struct ctf_fs_trace *trace;
+    /* Weak (owned by `decoder` above) */
+    struct ctf_trace_class *tc = nullptr;
 
-    struct ctf_fs_metadata_config metadata_config;
+    int bo = 0;
 };
 
 struct ctf_fs_trace
 {
-    bt_logging_level log_level;
+    using UP = std::unique_ptr<ctf_fs_trace>;
 
-    /*
-     * Weak. These are mostly used to generate log messages or to append
-     * error causes. They are mutually exclusive, only one of them must be
-     * set.
-     */
-    bt_self_component *self_comp;
-    bt_self_component_class *self_comp_class;
+    explicit ctf_fs_trace(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.FS/TRACE"}
+    {
+    }
 
-    /* Owned by this */
-    struct ctf_fs_metadata *metadata;
+    bt2c::Logger logger;
 
-    /* Owned by this */
-    bt_trace *trace;
+    ctf_fs_metadata::UP metadata;
 
-    /* Array of struct ctf_fs_ds_file_group *, owned by this */
-    GPtrArray *ds_file_groups;
+    bt2::Trace::Shared trace;
 
-    /* Owned by this */
-    GString *path;
+    std::vector<ctf_fs_ds_file_group::UP> ds_file_groups;
+
+    std::string path;
 
     /* Next automatic stream ID when not provided by packet header */
-    uint64_t next_stream_id;
+    uint64_t next_stream_id = 0;
 };
 
-struct ctf_fs_ds_index_entry
+struct ctf_fs_port_data
 {
-    /* Weak, belongs to ctf_fs_ds_file_info. */
-    const char *path;
-
-    /* Position, in bytes, of the packet from the beginning of the file. */
-    uint64_t offset;
-
-    /* Size of the packet, in bytes. */
-    uint64_t packet_size;
+    using UP = std::unique_ptr<ctf_fs_port_data>;
 
-    /*
-     * Extracted from the packet context, relative to the respective fields'
-     * mapped clock classes (in cycles).
-     */
-    uint64_t timestamp_begin, timestamp_end;
-
-    /*
-     * Converted from the packet context, relative to the trace's EPOCH
-     * (in ns since EPOCH).
-     */
-    int64_t timestamp_begin_ns, timestamp_end_ns;
+    /* Weak, belongs to ctf_fs_trace */
+    struct ctf_fs_ds_file_group *ds_file_group = nullptr;
 
-    /*
-     * Packet sequence number, or UINT64_MAX if not present in the index.
-     */
-    uint64_t packet_seq_num;
+    /* Weak */
+    struct ctf_fs_component *ctf_fs = nullptr;
 };
 
-struct ctf_fs_ds_index
+struct ctf_fs_component
 {
-    /* Array of pointer to struct ctf_fs_ds_index_entry. */
-    GPtrArray *entries;
-};
+    using UP = std::unique_ptr<ctf_fs_component>;
 
-struct ctf_fs_ds_file_group
-{
-    /*
-     * Array of struct ctf_fs_ds_file_info, owned by this.
-     *
-     * This is an _ordered_ array of data stream file infos which
-     * belong to this group (a single stream instance).
-     *
-     * You can call ctf_fs_ds_file_create() with one of those paths
-     * and the trace IR stream below.
-     */
-    GPtrArray *ds_file_infos;
+    explicit ctf_fs_component(const ctf::src::ClkClsCfg& clkClsCfgParam,
+                              const bt2c::Logger& parentLogger) noexcept :
+        logger {parentLogger, "PLUGIN/SRC.CTF.FS/COMP"},
+        clkClsCfg {clkClsCfgParam}
+    {
+    }
 
-    /* Owned by this */
-    struct ctf_stream_class *sc;
-
-    /* Owned by this */
-    bt_stream *stream;
+    bt2c::Logger logger;
 
-    /* Stream (instance) ID; -1ULL means none */
-    uint64_t stream_id;
+    std::vector<ctf_fs_port_data::UP> port_data;
 
-    /* Weak, belongs to component */
-    struct ctf_fs_trace *ctf_fs_trace;
+    ctf_fs_trace::UP trace;
 
-    /*
-     * Owned by this.
-     */
-    struct ctf_fs_ds_index *index;
-};
-
-struct ctf_fs_port_data
-{
-    /* Weak, belongs to ctf_fs_trace */
-    struct ctf_fs_ds_file_group *ds_file_group;
-
-    /* Weak */
-    struct ctf_fs_component *ctf_fs;
+    ctf::src::ClkClsCfg clkClsCfg;
 };
 
 struct ctf_fs_msg_iter_data
 {
-    bt_logging_level log_level;
+    using UP = std::unique_ptr<ctf_fs_msg_iter_data>;
 
-    /* Weak */
-    bt_self_component *self_comp;
+    explicit ctf_fs_msg_iter_data(bt_self_message_iterator *selfMsgIter) :
+        self_msg_iter {selfMsgIter}, logger {bt2::SelfMessageIterator {self_msg_iter},
+                                             "PLUGIN/SRC.CTF.FS/MSG-ITER"}
+    {
+    }
 
     /* Weak */
-    bt_self_message_iterator *self_msg_iter;
+    bt_self_message_iterator *self_msg_iter = nullptr;
+
+    bt2c::Logger logger;
 
     /* Weak, belongs to ctf_fs_trace */
-    struct ctf_fs_ds_file_group *ds_file_group;
+    struct ctf_fs_ds_file_group *ds_file_group = nullptr;
 
-    /* Owned by this */
-    struct ctf_msg_iter *msg_iter;
+    ctf_msg_iter_up msg_iter;
 
     /*
      * Saved error.  If we hit an error in the _next method, but have some
      * messages ready to return, we save the error here and return it on
      * the next _next call.
      */
-    bt_message_iterator_class_next_method_status next_saved_status;
-    const struct bt_error *next_saved_error;
+    bt_message_iterator_class_next_method_status next_saved_status =
+        BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+    const struct bt_error *next_saved_error = nullptr;
 
-    struct ctf_fs_ds_group_medops_data *msg_iter_medops_data;
+    ctf_fs_ds_group_medops_data_up msg_iter_medops_data;
 };
 
-BT_HIDDEN
 bt_component_class_initialize_method_status
 ctf_fs_init(bt_self_component_source *source, bt_self_component_source_configuration *config,
             const bt_value *params, void *init_method_data);
 
-BT_HIDDEN
 void ctf_fs_finalize(bt_self_component_source *component);
 
-BT_HIDDEN
 bt_component_class_query_method_status ctf_fs_query(bt_self_component_class_source *comp_class,
                                                     bt_private_query_executor *priv_query_exec,
                                                     const char *object, const bt_value *params,
                                                     void *method_data, const bt_value **result);
 
-BT_HIDDEN
 bt_message_iterator_class_initialize_method_status
 ctf_fs_iterator_init(bt_self_message_iterator *self_msg_iter,
                      bt_self_message_iterator_configuration *config,
                      bt_self_component_port_output *self_port);
 
-BT_HIDDEN
 void ctf_fs_iterator_finalize(bt_self_message_iterator *it);
 
-BT_HIDDEN
 bt_message_iterator_class_next_method_status
 ctf_fs_iterator_next(bt_self_message_iterator *iterator, bt_message_array_const msgs,
                      uint64_t capacity, uint64_t *count);
 
-BT_HIDDEN
 bt_message_iterator_class_seek_beginning_method_status
 ctf_fs_iterator_seek_beginning(bt_self_message_iterator *message_iterator);
 
-/* Create and initialize a new, empty ctf_fs_component. */
-
-BT_HIDDEN
-struct ctf_fs_component *ctf_fs_component_create(bt_logging_level log_level,
-                                                 bt_self_component *self_comp);
-
 /*
  * Create one `struct ctf_fs_trace` from one trace, or multiple traces sharing
  * the same UUID.
@@ -246,45 +159,44 @@ struct ctf_fs_component *ctf_fs_component_create(bt_logging_level log_level,
  * should be set.
  */
 
-BT_HIDDEN
 int ctf_fs_component_create_ctf_fs_trace(struct ctf_fs_component *ctf_fs,
-                                         const bt_value *paths_value,
-                                         const bt_value *trace_name_value,
-                                         bt_self_component *self_comp,
-                                         bt_self_component_class *self_comp_class);
+                                         bt2::ConstArrayValue pathsValue, const char *traceName,
+                                         bt_self_component *selfComp);
+
+namespace ctf {
+namespace src {
+namespace fs {
 
-/* Free `ctf_fs` and everything it owns. */
+/* `src.ctf.fs` parameters */
 
-BT_HIDDEN
-void ctf_fs_destroy(struct ctf_fs_component *ctf_fs);
+struct Parameters
+{
+    explicit Parameters(const bt2::ConstArrayValue inputsParam) noexcept : inputs {inputsParam}
+    {
+    }
+
+    bt2::ConstArrayValue inputs;
+    bt2s::optional<std::string> traceName;
+    ClkClsCfg clkClsCfg;
+};
+
+} /* namespace fs */
+} /* namespace src */
+} /* namespace ctf */
 
 /*
  * Read and validate parameters taken by the src.ctf.fs plugin.
  *
- *  - The mandatory `paths` parameter is returned in `*paths`.
- *  - The optional `clock-class-offset-s` and `clock-class-offset-ns`, if
- *    present, are recorded in the `ctf_fs` structure.
- *  - The optional `trace-name` parameter is returned in `*trace_name` if
- *    present, else `*trace_name` is set to NULL.
- *
- * `self_comp` and `self_comp_class` are used for logging, only one of them
- * should be set.
- *
- * Return true on success, false if any parameter didn't pass validation.
+ * Throw if any parameter doesn't pass validation.
  */
 
-BT_HIDDEN
-bool read_src_fs_parameters(const bt_value *params, const bt_value **paths,
-                            const bt_value **trace_name, struct ctf_fs_component *ctf_fs,
-                            bt_self_component *self_comp, bt_self_component_class *self_comp_class);
+ctf::src::fs::Parameters read_src_fs_parameters(bt2::ConstMapValue params,
+                                                const bt2c::Logger& logger);
 
 /*
  * Generate the port name to be used for a given data stream file group.
- *
- * The result must be freed using g_free by the caller.
  */
 
-BT_HIDDEN
-gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group);
+std::string ctf_fs_make_port_name(ctf_fs_ds_file_group *ds_file_group);
 
 #endif /* BABELTRACE_PLUGIN_CTF_FS_H */
index 644f57dec17c5c34f7610f9213fbedf458570973..36dc7cc934d46366aa12ebf7b13f239e8699eacb 100644 (file)
@@ -9,8 +9,9 @@
 #ifndef LTTNG_INDEX_H
 #define LTTNG_INDEX_H
 
-#include <stddef.h>
-#include "compat/limits.h"
+#include <cstdint>
+
+#include "compat/limits.h" /* IWYU pragma: keep  */
 
 #define CTF_INDEX_MAGIC    0xC1F1DCC1
 #define CTF_INDEX_MAJOR    1
index d7c89e5e7b3fa19655a998723d23f40a93cfd363..bf905d3ae0975c3c88b29cdb30ca03424996eee9 100644 (file)
@@ -5,27 +5,15 @@
  * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
  */
 
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL   log_level
-#define BT_LOG_TAG            "PLUGIN/SRC.CTF.FS/META"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
 #include "common/assert.h"
-#include <glib.h>
-#include "common/uuid.h"
-#include "compat/memstream.h"
-#include <babeltrace2/babeltrace.h>
+#include "cpp-common/bt2s/make-unique.hpp"
 
-#include "fs.hpp"
+#include "../common/src/metadata/tsdl/decoder.hpp"
 #include "file.hpp"
+#include "fs.hpp"
 #include "metadata.hpp"
-#include "../common/metadata/decoder.hpp"
 
-BT_HIDDEN
-FILE *ctf_fs_metadata_open_file(const char *trace_path)
+FILE *ctf_fs_metadata_open_file(const char *trace_path, const bt2c::Logger& logger)
 {
     GString *metadata_path;
     FILE *fp = NULL;
@@ -37,104 +25,79 @@ FILE *ctf_fs_metadata_open_file(const char *trace_path)
 
     g_string_append(metadata_path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME);
     fp = fopen(metadata_path->str, "rb");
+    if (!fp) {
+        BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(logger, "Failed to open metadata file", ": path=\"{}\"",
+                                           metadata_path->str);
+    }
+
     g_string_free(metadata_path, TRUE);
+
 end:
     return fp;
 }
 
-static struct ctf_fs_file *get_file(const char *trace_path, bt_logging_level log_level,
-                                    bt_self_component *self_comp)
+static ctf_fs_file::UP get_file(const bt2c::CStringView trace_path, const bt2c::Logger& logger)
 {
-    struct ctf_fs_file *file = ctf_fs_file_create(log_level, self_comp);
+    auto file = bt2s::make_unique<ctf_fs_file>(logger);
 
     if (!file) {
         goto error;
     }
 
-    g_string_append(file->path, trace_path);
-    g_string_append(file->path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME);
+    file->path = fmt::format("{}" G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME, trace_path);
 
-    if (ctf_fs_file_open(file, "rb")) {
+    if (ctf_fs_file_open(file.get(), "rb")) {
         goto error;
     }
 
     goto end;
 
 error:
-    if (file) {
-        ctf_fs_file_destroy(file);
-        file = NULL;
-    }
+    file.reset();
 
 end:
     return file;
 }
 
-BT_HIDDEN
 int ctf_fs_metadata_set_trace_class(bt_self_component *self_comp, struct ctf_fs_trace *ctf_fs_trace,
-                                    struct ctf_fs_metadata_config *config)
+                                    const ctf::src::ClkClsCfg& clkClsCfg)
 {
     int ret = 0;
-    struct ctf_fs_file *file = NULL;
-    bt_logging_level log_level = ctf_fs_trace->log_level;
-
-    ctf_metadata_decoder_config decoder_config {};
-    decoder_config.log_level = ctf_fs_trace->log_level, decoder_config.self_comp = self_comp,
-    decoder_config.clock_class_offset_s = config ? config->clock_class_offset_s : 0,
-    decoder_config.clock_class_offset_ns = config ? config->clock_class_offset_ns : 0,
-    decoder_config.force_clock_class_origin_unix_epoch =
-        config ? config->force_clock_class_origin_unix_epoch : false,
-    decoder_config.create_trace_class = true,
-
-    file = get_file(ctf_fs_trace->path->str, log_level, self_comp);
+    ctf_metadata_decoder_config decoder_config {ctf_fs_trace->logger};
+
+    decoder_config.self_comp = self_comp;
+    decoder_config.clkClsCfg = clkClsCfg;
+    decoder_config.create_trace_class = true;
+
+    const auto file = get_file(ctf_fs_trace->path, ctf_fs_trace->logger);
     if (!file) {
-        BT_COMP_LOGE("Cannot create metadata file object.");
+        BT_CPPLOGE_SPEC(ctf_fs_trace->logger, "Cannot create metadata file object.");
         ret = -1;
         goto end;
     }
 
     ctf_fs_trace->metadata->decoder = ctf_metadata_decoder_create(&decoder_config);
     if (!ctf_fs_trace->metadata->decoder) {
-        BT_COMP_LOGE("Cannot create metadata decoder object.");
+        BT_CPPLOGE_SPEC(ctf_fs_trace->logger, "Cannot create metadata decoder object.");
         ret = -1;
         goto end;
     }
 
-    ret = ctf_metadata_decoder_append_content(ctf_fs_trace->metadata->decoder, file->fp);
+    ret =
+        ctf_metadata_decoder_append_content(ctf_fs_trace->metadata->decoder.get(), file->fp.get());
     if (ret) {
-        BT_COMP_LOGE("Cannot update metadata decoder's content.");
+        BT_CPPLOGE_SPEC(ctf_fs_trace->logger, "Cannot update metadata decoder's content.");
         goto end;
     }
 
     ctf_fs_trace->metadata->trace_class =
-        ctf_metadata_decoder_get_ir_trace_class(ctf_fs_trace->metadata->decoder);
+        ctf_metadata_decoder_get_ir_trace_class(ctf_fs_trace->metadata->decoder.get());
     BT_ASSERT(!self_comp || ctf_fs_trace->metadata->trace_class);
+
     ctf_fs_trace->metadata->tc =
-        ctf_metadata_decoder_borrow_ctf_trace_class(ctf_fs_trace->metadata->decoder);
+        ctf_metadata_decoder_borrow_ctf_trace_class(ctf_fs_trace->metadata->decoder.get());
     BT_ASSERT(ctf_fs_trace->metadata->tc);
 
 end:
-    ctf_fs_file_destroy(file);
     return ret;
 }
-
-BT_HIDDEN
-int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata)
-{
-    /* Nothing to initialize for the moment. */
-    return 0;
-}
-
-BT_HIDDEN
-void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata)
-{
-    free(metadata->text);
-
-    if (metadata->trace_class) {
-        BT_TRACE_CLASS_PUT_REF_AND_RESET(metadata->trace_class);
-    }
-
-    if (metadata->decoder) {
-        ctf_metadata_decoder_destroy(metadata->decoder);
-    }
-}
index c04628f0de89006c2fa3798261a1f02663f0c08a..14d7cc191f19f152df8f0e25921cbb786612957f 100644 (file)
@@ -7,38 +7,25 @@
 #ifndef CTF_FS_METADATA_H
 #define CTF_FS_METADATA_H
 
-#include <stdbool.h>
 #include <stdio.h>
-#include <glib.h>
-#include "common/macros.h"
+
 #include <babeltrace2/babeltrace.h>
 
-#define CTF_FS_METADATA_FILENAME "metadata"
+namespace bt2c {
 
-struct ctf_fs_trace;
-struct ctf_fs_metadata;
+class Logger;
 
-struct ctf_fs_metadata_config
-{
-    bool force_clock_class_origin_unix_epoch;
-    int64_t clock_class_offset_s;
-    int64_t clock_class_offset_ns;
-};
+} /* namespace bt2c */
 
-BT_HIDDEN
-int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata);
+#include "../common/src/clk-cls-cfg.hpp"
 
-BT_HIDDEN
-void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata);
+#define CTF_FS_METADATA_FILENAME "metadata"
 
-BT_HIDDEN
 int ctf_fs_metadata_set_trace_class(bt_self_component *self_comp, struct ctf_fs_trace *ctf_fs_trace,
-                                    struct ctf_fs_metadata_config *config);
+                                    const ctf::src::ClkClsCfg& clkClsCfg);
 
-BT_HIDDEN
-FILE *ctf_fs_metadata_open_file(const char *trace_path);
+FILE *ctf_fs_metadata_open_file(const char *trace_path, const bt2c::Logger& logger);
 
-BT_HIDDEN
 bool ctf_metadata_is_packetized(FILE *fp, int *byte_order);
 
 #endif /* CTF_FS_METADATA_H */
index 846dde6be4f59b23ffe75ac95c04c03bc8e74df2..0b700e91250458ba0df0c8d05522b5747356d753 100644 (file)
@@ -6,25 +6,23 @@
  * Babeltrace CTF file system Reader Component queries
  */
 
-#define BT_LOG_OUTPUT_LEVEL log_level
-#define BT_LOG_TAG          "PLUGIN/SRC.CTF.FS/QUERY"
-#include "logging/log.h"
-
-#include "query.hpp"
-#include <stdbool.h>
 #include <glib.h>
 #include <glib/gstdio.h>
-#include <fcntl.h>
 #include <sys/types.h>
-#include <sys/stat.h>
-#include "common/assert.h"
-#include "metadata.hpp"
-#include "../common/metadata/decoder.hpp"
-#include "common/common.h"
-#include "common/macros.h"
+
 #include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2/exc.hpp"
+#include "cpp-common/bt2c/glib-up.hpp"
+#include "cpp-common/bt2c/libc-up.hpp"
+
+#include "plugins/common/param-validation/param-validation.h"
+
+#include "../common/src/metadata/tsdl/decoder.hpp"
+#include "data-stream-file.hpp"
 #include "fs.hpp"
-#include "logging/comp-logging.h"
+#include "metadata.hpp"
+#include "query.hpp"
 
 #define METADATA_TEXT_SIG "/* CTF 1.8"
 
@@ -35,404 +33,218 @@ struct range
     bool set = false;
 };
 
-BT_HIDDEN
-bt_component_class_query_method_status
-metadata_info_query(bt_self_component_class_source *self_comp_class_src, const bt_value *params,
-                    bt_logging_level log_level, const bt_value **user_result)
-{
-    bt_component_class_query_method_status status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-    bt_self_component_class *self_comp_class =
-        bt_self_component_class_source_as_self_component_class(self_comp_class_src);
-    bt_value *result = NULL;
-    const bt_value *path_value = NULL;
-    FILE *metadata_fp = NULL;
-    int ret;
-    int bo;
-    const char *path;
-    bool is_packetized;
-    struct ctf_metadata_decoder *decoder = NULL;
-    ctf_metadata_decoder_config decoder_cfg {};
-    enum ctf_metadata_decoder_status decoder_status;
-
-    result = bt_value_map_create();
-    if (!result) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
-    }
+static bt_param_validation_map_value_entry_descr metadataInfoQueryParamsDesc[] = {
+    {"path", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY,
+     bt_param_validation_value_descr::makeString()},
+    BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END};
 
-    BT_ASSERT(params);
+bt2::Value::Shared metadata_info_query(const bt2::ConstMapValue params, const bt2c::Logger& logger)
+{
+    gchar *validateError = nullptr;
+    const auto validationStatus = bt_param_validation_validate(
+        params.libObjPtr(), metadataInfoQueryParamsDesc, &validateError);
 
-    if (!bt_value_is_map(params)) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                                        "Query parameters is not a map value object.");
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-        goto error;
-    }
+    if (validationStatus == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
+        throw bt2::MemoryError {};
+    } else if (validationStatus == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
+        const bt2c::GCharUP deleter {validateError};
 
-    path_value = bt_value_map_borrow_entry_value_const(params, "path");
-    if (!path_value) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Mandatory `path` parameter missing");
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error, "{}", validateError);
     }
 
-    if (!bt_value_is_string(path_value)) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                                        "`path` parameter is required to be a string value");
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-        goto error;
+    const auto path = params["path"]->asString().value();
+    bt2c::FileUP metadataFp {ctf_fs_metadata_open_file(path, logger)};
+    if (!metadataFp) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error,
+                                               "Cannot open trace metadata: path=\"{}\".", path);
     }
 
-    path = bt_value_string_get(path_value);
-
-    BT_ASSERT(path);
-    metadata_fp = ctf_fs_metadata_open_file(path);
-    if (!metadata_fp) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Cannot open trace metadata: path=\"%s\".",
-                                        path);
-        goto error;
-    }
+    bool is_packetized;
+    int bo;
+    int ret = ctf_metadata_decoder_is_packetized(metadataFp.get(), &is_packetized, &bo, logger);
 
-    ret = ctf_metadata_decoder_is_packetized(metadata_fp, &is_packetized, &bo, log_level, NULL);
     if (ret) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(
-            self_comp_class,
-            "Cannot check whether or not the metadata stream is packetized: path=\"%s\".", path);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(
+            logger, bt2::Error,
+            "Cannot check whether or not the metadata stream is packetized: path=\"{}\".", path);
     }
 
-    decoder_cfg.log_level = log_level;
-    decoder_cfg.self_comp_class = self_comp_class;
+    ctf_metadata_decoder_config decoder_cfg {logger};
     decoder_cfg.keep_plain_text = true;
-    decoder = ctf_metadata_decoder_create(&decoder_cfg);
+    ctf_metadata_decoder_up decoder = ctf_metadata_decoder_create(&decoder_cfg);
     if (!decoder) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                                        "Cannot create metadata decoder: path=\"%s\".", path);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(
+            logger, bt2::Error, "Cannot create metadata decoder: path=\"{}}\".", path);
     }
 
-    rewind(metadata_fp);
-    decoder_status = ctf_metadata_decoder_append_content(decoder, metadata_fp);
+    rewind(metadataFp.get());
+    ctf_metadata_decoder_status decoder_status =
+        ctf_metadata_decoder_append_content(decoder.get(), metadataFp.get());
     if (decoder_status) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(
-            self_comp_class, "Cannot update metadata decoder's content: path=\"%s\".", path);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(
+            logger, bt2::Error, "Cannot update metadata decoder's content: path=\"{}\".", path);
     }
 
-    ret = bt_value_map_insert_string_entry(result, "text", ctf_metadata_decoder_get_text(decoder));
-    if (ret) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                                        "Cannot insert metadata text into query result.");
-        goto error;
-    }
-
-    ret = bt_value_map_insert_bool_entry(result, "is-packetized", is_packetized);
-    if (ret) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(
-            self_comp_class, "Cannot insert \"is-packetized\" attribute into query result.");
-        goto error;
-    }
-
-    goto end;
+    const char *plain_text = ctf_metadata_decoder_get_text(decoder.get());
+    std::string metadata_text;
 
-error:
-    BT_VALUE_PUT_REF_AND_RESET(result);
-    result = NULL;
-
-    if (status >= 0) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+    if (strncmp(plain_text, METADATA_TEXT_SIG, sizeof(METADATA_TEXT_SIG) - 1) != 0) {
+        metadata_text = METADATA_TEXT_SIG;
+        metadata_text += " */\n\n";
     }
 
-end:
-    ctf_metadata_decoder_destroy(decoder);
+    metadata_text += plain_text;
 
-    if (metadata_fp) {
-        ret = fclose(metadata_fp);
-        if (ret) {
-            BT_LOGE_ERRNO("Cannot close metatada file stream", ": path=\"%s\"", path);
-        }
-    }
+    const auto result = bt2::MapValue::create();
+    result->insert("text", metadata_text);
+    result->insert("is-packetized", is_packetized);
 
-    *user_result = result;
-    return status;
+    return result;
 }
 
-static int add_range(bt_value *info, struct range *range, const char *range_name)
+static void add_range(const bt2::MapValue info, const range& range, const char *range_name)
 {
-    int ret = 0;
-    bt_value_map_insert_entry_status status;
-    bt_value *range_map;
-
-    if (!range->set) {
+    if (!range.set) {
         /* Not an error. */
-        goto end;
+        return;
     }
 
-    status = bt_value_map_insert_empty_map_entry(info, range_name, &range_map);
-    if (status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-        ret = -1;
-        goto end;
-    }
-
-    status = bt_value_map_insert_signed_integer_entry(range_map, "begin", range->begin_ns);
-    if (status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-        ret = -1;
-        goto end;
-    }
-
-    status = bt_value_map_insert_signed_integer_entry(range_map, "end", range->end_ns);
-    if (status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-        ret = -1;
-        goto end;
-    }
-
-end:
-    return ret;
+    const auto rangeMap = info.insertEmptyMap(range_name);
+    rangeMap.insert("begin", range.begin_ns);
+    rangeMap.insert("end", range.end_ns);
 }
 
-static int populate_stream_info(struct ctf_fs_ds_file_group *group, bt_value *group_info,
-                                struct range *stream_range)
+static void populate_stream_info(struct ctf_fs_ds_file_group *group, const bt2::MapValue groupInfo)
 {
-    int ret = 0;
-    bt_value_map_insert_entry_status insert_status;
-    struct ctf_fs_ds_index_entry *first_ds_index_entry, *last_ds_index_entry;
-    gchar *port_name = NULL;
-
     /*
      * Since each `struct ctf_fs_ds_file_group` has a sorted array of
      * `struct ctf_fs_ds_index_entry`, we can compute the stream range from
      * the timestamp_begin of the first index entry and the timestamp_end
      * of the last index entry.
      */
-    BT_ASSERT(group->index);
-    BT_ASSERT(group->index->entries);
-    BT_ASSERT(group->index->entries->len > 0);
+    BT_ASSERT(!group->index.entries.empty());
 
     /* First entry. */
-    first_ds_index_entry =
-        (struct ctf_fs_ds_index_entry *) g_ptr_array_index(group->index->entries, 0);
+    const auto& first_ds_index_entry = group->index.entries.front();
 
     /* Last entry. */
-    last_ds_index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index(
-        group->index->entries, group->index->entries->len - 1);
+    const auto& last_ds_index_entry = group->index.entries.back();
 
-    stream_range->begin_ns = first_ds_index_entry->timestamp_begin_ns;
-    stream_range->end_ns = last_ds_index_entry->timestamp_end_ns;
+    range stream_range;
+    stream_range.begin_ns = first_ds_index_entry.timestamp_begin_ns;
+    stream_range.end_ns = last_ds_index_entry.timestamp_end_ns;
 
     /*
      * If any of the begin and end timestamps is not set it means that
      * packets don't include `timestamp_begin` _and_ `timestamp_end` fields
      * in their packet context so we can't set the range.
      */
-    stream_range->set =
-        stream_range->begin_ns != UINT64_C(-1) && stream_range->end_ns != UINT64_C(-1);
-
-    ret = add_range(group_info, stream_range, "range-ns");
-    if (ret) {
-        goto end;
-    }
+    stream_range.set = stream_range.begin_ns != UINT64_C(-1) && stream_range.end_ns != UINT64_C(-1);
 
-    port_name = ctf_fs_make_port_name(group);
-    if (!port_name) {
-        ret = -1;
-        goto end;
-    }
-
-    insert_status = bt_value_map_insert_string_entry(group_info, "port-name", port_name);
-    if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-        ret = -1;
-        goto end;
-    }
-
-end:
-    g_free(port_name);
-    return ret;
+    add_range(groupInfo, stream_range, "range-ns");
+    groupInfo.insert("port-name", ctf_fs_make_port_name(group));
 }
 
-static int populate_trace_info(const struct ctf_fs_trace *trace, bt_value *trace_info,
-                               bt_logging_level log_level, bt_self_component_class *self_comp_class)
+static void populate_trace_info(const struct ctf_fs_trace *trace, const bt2::MapValue traceInfo,
+                                const bt2c::Logger& logger)
 {
-    int ret = 0;
-    size_t group_idx;
-    bt_value_map_insert_entry_status insert_status;
-    bt_value_array_append_element_status append_status;
-    bt_value *file_groups = NULL;
-
-    BT_ASSERT(trace->ds_file_groups);
     /* Add trace range info only if it contains streams. */
-    if (trace->ds_file_groups->len == 0) {
-        ret = -1;
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Trace has no streams: trace-path=%s",
-                                        trace->path->str);
-        goto end;
+    if (trace->ds_file_groups.empty()) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error,
+                                               "Trace has no streams: trace-path={}", trace->path);
     }
 
-    insert_status = bt_value_map_insert_empty_array_entry(trace_info, "stream-infos", &file_groups);
-    if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-        ret = -1;
-        goto end;
-    }
+    const auto fileGroups = traceInfo.insertEmptyArray("stream-infos");
 
     /* Find range of all stream groups, and of the trace. */
-    for (group_idx = 0; group_idx < trace->ds_file_groups->len; group_idx++) {
-        bt_value *group_info;
-        range group_range;
-        ctf_fs_ds_file_group *group =
-            (ctf_fs_ds_file_group *) g_ptr_array_index(trace->ds_file_groups, group_idx);
-
-        append_status = bt_value_array_append_empty_map_element(file_groups, &group_info);
-        if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
-            ret = -1;
-            goto end;
-        }
-
-        ret = populate_stream_info(group, group_info, &group_range);
-        if (ret) {
-            goto end;
-        }
+    for (const auto& group : trace->ds_file_groups) {
+        const auto groupInfo = fileGroups.appendEmptyMap();
+        populate_stream_info(group.get(), groupInfo);
     }
-
-end:
-    return ret;
 }
 
-BT_HIDDEN
-bt_component_class_query_method_status
-trace_infos_query(bt_self_component_class_source *self_comp_class_src, const bt_value *params,
-                  bt_logging_level log_level, const bt_value **user_result)
+bt2::Value::Shared trace_infos_query(const bt2::ConstMapValue params, const bt2c::Logger& logger)
 {
-    struct ctf_fs_component *ctf_fs = NULL;
-    bt_component_class_query_method_status status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-    bt_self_component_class *self_comp_class =
-        bt_self_component_class_source_as_self_component_class(self_comp_class_src);
-    bt_value *result = NULL;
-    const bt_value *inputs_value = NULL;
-    const bt_value *trace_name_value;
-    int ret = 0;
-    bt_value *trace_info = NULL;
-    bt_value_array_append_element_status append_status;
-
-    BT_ASSERT(params);
-
-    if (!bt_value_is_map(params)) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                                        "Query parameters is not a map value object.");
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-        goto error;
-    }
+    const auto parameters = read_src_fs_parameters(params, logger);
+    ctf_fs_component ctf_fs {parameters.clkClsCfg, logger};
 
-    ctf_fs = ctf_fs_component_create(log_level, NULL);
-    if (!ctf_fs) {
-        goto error;
+    if (ctf_fs_component_create_ctf_fs_trace(
+            &ctf_fs, parameters.inputs,
+            parameters.traceName ? parameters.traceName->c_str() : nullptr, nullptr)) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error, "Failed to create trace");
     }
 
-    if (!read_src_fs_parameters(params, &inputs_value, &trace_name_value, ctf_fs, NULL,
-                                self_comp_class)) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-        goto error;
-    }
+    const auto result = bt2::ArrayValue::create();
+    const auto traceInfo = result->appendEmptyMap();
+    populate_trace_info(ctf_fs.trace.get(), traceInfo, logger);
 
-    if (ctf_fs_component_create_ctf_fs_trace(ctf_fs, inputs_value, trace_name_value, NULL,
-                                             self_comp_class)) {
-        goto error;
-    }
+    return result;
+}
 
-    result = bt_value_array_create();
-    if (!result) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
-    }
+static bt_param_validation_map_value_entry_descr supportInfoQueryParamsDesc[] = {
+    {"type", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY,
+     bt_param_validation_value_descr::makeString()},
+    {"input", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY,
+     bt_param_validation_value_descr::makeString()},
+    BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END};
 
-    append_status = bt_value_array_append_empty_map_element(result, &trace_info);
-    if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Failed to create trace info map.");
-        goto error;
-    }
+bt2::Value::Shared support_info_query(const bt2::ConstMapValue params, const bt2c::Logger& logger)
+{
+    gchar *validateError = NULL;
+    const auto validationStatus = bt_param_validation_validate(
+        params.libObjPtr(), supportInfoQueryParamsDesc, &validateError);
 
-    ret = populate_trace_info(ctf_fs->trace, trace_info, log_level, self_comp_class);
-    if (ret) {
-        goto error;
-    }
+    if (validationStatus == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
+        throw bt2::MemoryError {};
+    } else if (validationStatus == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
+        const bt2c::GCharUP deleter {validateError};
 
-    goto end;
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error, "{}", validateError);
+    }
 
-error:
-    BT_VALUE_PUT_REF_AND_RESET(result);
+    const auto type = params["type"]->asString().value();
 
-    if (status >= 0) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+    if (strcmp(type, "directory") != 0) {
+        /*
+         * The input type is not a directory so we are 100% sure it's not a CTF
+         * 1.8 trace as it would need a directory with at least 1 metadata file
+         * and 1 data stream file.
+         */
+        const auto result = bt2::MapValue::create();
+        result->insert("weight", 0.0f);
+        return result;
     }
 
-end:
-    if (ctf_fs) {
-        ctf_fs_destroy(ctf_fs);
-        ctf_fs = NULL;
-    }
+    const auto input = params["input"]->asString().value();
 
-    *user_result = result;
-    return status;
-}
+    bt2c::GCharUP metadataPath {g_build_filename(input, CTF_FS_METADATA_FILENAME, NULL)};
+    if (!metadataPath) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error, "Failed to read parameters");
+    }
 
-BT_HIDDEN
-bt_component_class_query_method_status
-support_info_query(bt_self_component_class_source *comp_class, const bt_value *params,
-                   bt_logging_level log_level, const bt_value **user_result)
-{
-    const bt_value *input_type_value;
-    const char *input_type;
-    bt_component_class_query_method_status status;
-    bt_value_map_insert_entry_status insert_entry_status;
     double weight = 0;
-    gchar *metadata_path = NULL;
-    bt_value *result = NULL;
-    struct ctf_metadata_decoder *metadata_decoder = NULL;
-    FILE *metadata_file = NULL;
     char uuid_str[BT_UUID_STR_LEN + 1];
     bool has_uuid = false;
-    const bt_value *input_value;
-    const char *input;
-
-    input_type_value = bt_value_map_borrow_entry_value_const(params, "type");
-    BT_ASSERT(input_type_value);
-    BT_ASSERT(bt_value_get_type(input_type_value) == BT_VALUE_TYPE_STRING);
-    input_type = bt_value_string_get(input_type_value);
-
-    if (strcmp(input_type, "directory") != 0) {
-        goto create_result;
-    }
-
-    input_value = bt_value_map_borrow_entry_value_const(params, "input");
-    BT_ASSERT(input_value);
-    BT_ASSERT(bt_value_get_type(input_value) == BT_VALUE_TYPE_STRING);
-    input = bt_value_string_get(input_value);
-
-    metadata_path = g_build_filename(input, CTF_FS_METADATA_FILENAME, NULL);
-    if (!metadata_path) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-        goto end;
-    }
-
-    metadata_file = g_fopen(metadata_path, "rb");
-    if (metadata_file) {
-        ctf_metadata_decoder_config metadata_decoder_config {};
+    bt2c::FileUP metadataFile {g_fopen(metadataPath.get(), "rb")};
+    if (metadataFile) {
         enum ctf_metadata_decoder_status decoder_status;
         bt_uuid_t uuid;
 
-        metadata_decoder_config.log_level = log_level;
-        metadata_decoder_config.self_comp_class =
-            bt_self_component_class_source_as_self_component_class(comp_class);
+        ctf_metadata_decoder_config metadata_decoder_config {logger};
 
-        metadata_decoder = ctf_metadata_decoder_create(&metadata_decoder_config);
+        ctf_metadata_decoder_up metadata_decoder =
+            ctf_metadata_decoder_create(&metadata_decoder_config);
         if (!metadata_decoder) {
-            status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-            goto end;
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error,
+                                                   "Failed to create metadata decoder");
         }
 
-        decoder_status = ctf_metadata_decoder_append_content(metadata_decoder, metadata_file);
+        decoder_status =
+            ctf_metadata_decoder_append_content(metadata_decoder.get(), metadataFile.get());
         if (decoder_status != CTF_METADATA_DECODER_STATUS_OK) {
-            BT_LOGW("cannot append metadata content: metadata-decoder-status=%d", decoder_status);
-            status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-            goto end;
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(
+                logger, bt2::Error, "Failed to append metadata content: metadata-decoder-status={}",
+                decoder_status);
         }
 
         /*
@@ -442,44 +254,21 @@ support_info_query(bt_self_component_class_source *comp_class, const bt_value *p
         weight = 0.75;
 
         /* If the trace has a UUID, return the stringified UUID as the group. */
-        if (ctf_metadata_decoder_get_trace_class_uuid(metadata_decoder, uuid) == 0) {
+        if (ctf_metadata_decoder_get_trace_class_uuid(metadata_decoder.get(), uuid) == 0) {
             bt_uuid_to_str(uuid, uuid_str);
             has_uuid = true;
         }
     }
 
-create_result:
-    result = bt_value_map_create();
-    if (!result) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-        goto end;
-    }
-
-    insert_entry_status = bt_value_map_insert_real_entry(result, "weight", weight);
-    if (insert_entry_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-        status = (bt_component_class_query_method_status) insert_entry_status;
-        goto end;
-    }
+    const auto result = bt2::MapValue::create();
+    result->insert("weight", weight);
 
     /* We are not supposed to have weight == 0 and a UUID. */
     BT_ASSERT(weight > 0 || !has_uuid);
 
     if (weight > 0 && has_uuid) {
-        insert_entry_status = bt_value_map_insert_string_entry(result, "group", uuid_str);
-        if (insert_entry_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-            status = (bt_component_class_query_method_status) insert_entry_status;
-            goto end;
-        }
+        result->insert("group", uuid_str);
     }
 
-    *user_result = result;
-    result = NULL;
-    status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-
-end:
-    g_free(metadata_path);
-    bt_value_put_ref(result);
-    ctf_metadata_decoder_destroy(metadata_decoder);
-
-    return status;
+    return result;
 }
index 7bdca13667ce5d57e7437f62f0a3beedd6c36663..2c7aca09ecc5d07f207e434c70d9645ee4d07467 100644 (file)
@@ -9,23 +9,18 @@
 #ifndef BABELTRACE_PLUGIN_CTF_FS_QUERY_H
 #define BABELTRACE_PLUGIN_CTF_FS_QUERY_H
 
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-
-BT_HIDDEN
-bt_component_class_query_method_status
-metadata_info_query(bt_self_component_class_source *comp_class, const bt_value *params,
-                    bt_logging_level log_level, const bt_value **result);
-
-BT_HIDDEN
-bt_component_class_query_method_status trace_infos_query(bt_self_component_class_source *comp_class,
-                                                         const bt_value *params,
-                                                         bt_logging_level log_level,
-                                                         const bt_value **result);
-
-BT_HIDDEN
-bt_component_class_query_method_status
-support_info_query(bt_self_component_class_source *comp_class, const bt_value *params,
-                   bt_logging_level log_level, const bt_value **result);
+#include "cpp-common/bt2/value.hpp"
+
+namespace bt2c {
+
+class Logger;
+
+} /* namespace bt2c */
+
+bt2::Value::Shared metadata_info_query(bt2::ConstMapValue params, const bt2c::Logger& logger);
+
+bt2::Value::Shared trace_infos_query(bt2::ConstMapValue params, const bt2c::Logger& logger);
+
+bt2::Value::Shared support_info_query(bt2::ConstMapValue params, const bt2c::Logger& logger);
 
 #endif /* BABELTRACE_PLUGIN_CTF_FS_QUERY_H */
diff --git a/src/plugins/ctf/lttng-live/Makefile.am b/src/plugins/ctf/lttng-live/Makefile.am
deleted file mode 100644 (file)
index 53761a7..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-libbabeltrace2_plugin_ctf_lttng_live_la_SOURCES = \
-               lttng-live.cpp \
-               lttng-live.hpp \
-               data-stream.cpp \
-               data-stream.hpp \
-               metadata.cpp \
-               metadata.hpp \
-               viewer-connection.cpp \
-               viewer-connection.hpp \
-               lttng-viewer-abi.hpp
-
-libbabeltrace2_plugin_ctf_lttng_live_la_LIBADD =
-
-if !ENABLE_BUILT_IN_PLUGINS
-libbabeltrace2_plugin_ctf_lttng_live_la_LIBADD += \
-       $(top_builddir)/src/plugins/common/muxing/libbabeltrace2-plugins-common-muxing.la
-endif
-
-if BABELTRACE_BUILD_WITH_MINGW
-libbabeltrace2_plugin_ctf_lttng_live_la_LIBADD += -lws2_32
-endif
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-lttng-live.la
index 7dd29dfc261dc67cb9c780d3ec231aee4a2ebe89..8f4ae873d234d7415b8c737c081d88209898dc5b 100644 (file)
@@ -7,22 +7,20 @@
  * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
  */
 
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL   log_level
-#define BT_LOG_TAG            "PLUGIN/SRC.CTF.LTTNG-LIVE/DS"
-#include "logging/comp-logging.h"
+#include <sstream>
 
-#include <inttypes.h>
+#include <glib.h>
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <glib.h>
-
 #include <babeltrace2/babeltrace.h>
 
-#include "../common/msg-iter/msg-iter.hpp"
 #include "common/assert.h"
-#include "compat/mman.h"
+#include "compat/mman.h" /* IWYU pragma: keep  */
+#include "cpp-common/bt2s/make-unique.hpp"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "../common/src/msg-iter/msg-iter.hpp"
 #include "data-stream.hpp"
 
 #define STREAM_NAME_PREFIX "stream-"
@@ -30,7 +28,6 @@
 static enum ctf_msg_iter_medium_status medop_request_bytes(size_t request_sz, uint8_t **buffer_addr,
                                                            size_t *buffer_sz, void *data)
 {
-    enum ctf_msg_iter_medium_status status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
     lttng_live_stream_iterator *stream = (lttng_live_stream_iterator *) data;
     struct lttng_live_trace *trace = stream->trace;
     struct lttng_live_session *session = trace->session;
@@ -42,40 +39,44 @@ static enum ctf_msg_iter_medium_status medop_request_bytes(size_t request_sz, ui
     BT_ASSERT(request_sz);
 
     if (stream->has_stream_hung_up) {
-        status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-        goto end;
+        return CTF_MSG_ITER_MEDIUM_STATUS_EOF;
     }
 
     len_left = stream->base_offset + stream->len - stream->offset;
     if (!len_left) {
         lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
-        status = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
-        goto end;
+        return CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
     }
 
-    read_len = MIN(request_sz, stream->buflen);
+    read_len = MIN(request_sz, stream->buf.size());
     read_len = MIN(read_len, len_left);
-    status = lttng_live_get_stream_bytes(live_msg_iter, stream, stream->buf, stream->offset,
-                                         read_len, &recv_len);
-    *buffer_addr = stream->buf;
+
+    const auto status = lttng_live_get_stream_bytes(live_msg_iter, stream, stream->buf.data(),
+                                                    stream->offset, read_len, &recv_len);
+
+    if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+        return status;
+    }
+
+    *buffer_addr = stream->buf.data();
     *buffer_sz = recv_len;
     stream->offset += recv_len;
-end:
-    return status;
+
+    return CTF_MSG_ITER_MEDIUM_STATUS_OK;
 }
 
 static bt_stream *medop_borrow_stream(bt_stream_class *stream_class, int64_t stream_id, void *data)
 {
     lttng_live_stream_iterator *lttng_live_stream = (lttng_live_stream_iterator *) data;
-    bt_logging_level log_level = lttng_live_stream->log_level;
-    bt_self_component *self_comp = lttng_live_stream->self_comp;
 
     if (!lttng_live_stream->stream) {
         uint64_t stream_class_id = bt_stream_class_get_id(stream_class);
 
-        BT_COMP_LOGI("Creating stream %s (ID: %" PRIu64 ") out of stream "
-                     "class %" PRId64,
-                     lttng_live_stream->name->str, stream_id, stream_class_id);
+        BT_CPPLOGI_SPEC(lttng_live_stream->logger,
+                        "Creating stream {} (ID: {}) out of stream class {}",
+                        lttng_live_stream->name, stream_id, stream_class_id);
+
+        bt_stream *stream;
 
         if (stream_id < 0) {
             /*
@@ -84,26 +85,28 @@ static bt_stream *medop_borrow_stream(bt_stream_class *stream_class, int64_t str
              * LTTng. In these cases, use the viewer_stream_id that
              * is unique for a live viewer session.
              */
-            lttng_live_stream->stream = bt_stream_create_with_id(
-                stream_class, lttng_live_stream->trace->trace, lttng_live_stream->viewer_stream_id);
+            stream =
+                bt_stream_create_with_id(stream_class, lttng_live_stream->trace->trace->libObjPtr(),
+                                         lttng_live_stream->viewer_stream_id);
         } else {
-            lttng_live_stream->stream = bt_stream_create_with_id(
-                stream_class, lttng_live_stream->trace->trace, (uint64_t) stream_id);
+            stream = bt_stream_create_with_id(
+                stream_class, lttng_live_stream->trace->trace->libObjPtr(), (uint64_t) stream_id);
         }
 
-        if (!lttng_live_stream->stream) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Cannot create stream %s (stream class ID "
-                                      "%" PRId64 ", stream ID %" PRIu64 ")",
-                                      lttng_live_stream->name->str, stream_class_id, stream_id);
-            goto end;
+        if (!stream) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                lttng_live_stream->logger,
+                "Cannot create stream {} (stream class ID {}, stream ID {})",
+                lttng_live_stream->name, stream_class_id, stream_id);
+            return nullptr;
         }
 
-        bt_stream_set_name(lttng_live_stream->stream, lttng_live_stream->name->str);
+        lttng_live_stream->stream = bt2::Stream::Shared::createWithoutRef(stream);
+
+        lttng_live_stream->stream->name(lttng_live_stream->name);
     }
 
-end:
-    return lttng_live_stream->stream;
+    return lttng_live_stream->stream->libObjPtr();
 }
 
 static struct ctf_msg_iter_medium_ops medops = {
@@ -113,48 +116,41 @@ static struct ctf_msg_iter_medium_ops medops = {
     medop_borrow_stream,
 };
 
-BT_HIDDEN
 enum lttng_live_iterator_status lttng_live_lazy_msg_init(struct lttng_live_session *session,
                                                          bt_self_message_iterator *self_msg_iter)
 {
     struct lttng_live_component *lttng_live = session->lttng_live_msg_iter->lttng_live_comp;
-    uint64_t trace_idx, stream_iter_idx;
-    bt_logging_level log_level = session->log_level;
-    bt_self_component *self_comp = session->self_comp;
 
     if (!session->lazy_stream_msg_init) {
         return LTTNG_LIVE_ITERATOR_STATUS_OK;
     }
 
-    BT_COMP_LOGD("Lazily initializing self message iterator for live session: "
-                 "session-id=%" PRIu64 ", self-msg-iter-addr=%p",
-                 session->id, self_msg_iter);
-
-    for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) {
-        struct lttng_live_trace *trace =
-            (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+    BT_CPPLOGD_SPEC(session->logger,
+                    "Lazily initializing self message iterator for live session: "
+                    "session-id={}, self-msg-iter-addr={}",
+                    session->id, fmt::ptr(self_msg_iter));
 
-        for (stream_iter_idx = 0; stream_iter_idx < trace->stream_iterators->len;
-             stream_iter_idx++) {
+    for (lttng_live_trace::UP& trace : session->traces) {
+        for (lttng_live_stream_iterator::UP& stream_iter : trace->stream_iterators) {
             struct ctf_trace_class *ctf_tc;
-            struct lttng_live_stream_iterator *stream_iter =
-                (lttng_live_stream_iterator *) g_ptr_array_index(trace->stream_iterators,
-                                                                 stream_iter_idx);
 
             if (stream_iter->msg_iter) {
                 continue;
             }
-            ctf_tc = ctf_metadata_decoder_borrow_ctf_trace_class(trace->metadata->decoder);
-            BT_COMP_LOGD("Creating CTF message iterator: "
-                         "session-id=%" PRIu64 ", ctf-tc-addr=%p, "
-                         "stream-iter-name=%s, self-msg-iter-addr=%p",
-                         session->id, ctf_tc, stream_iter->name->str, self_msg_iter);
+
+            ctf_tc = ctf_metadata_decoder_borrow_ctf_trace_class(trace->metadata->decoder.get());
+            BT_CPPLOGD_SPEC(stream_iter->logger,
+                            "Creating CTF message iterator: session-id={}, ctf-tc-addr={}, "
+                            "stream-iter-name={}, self-msg-iter-addr={}",
+                            session->id, fmt::ptr(ctf_tc), stream_iter->name,
+                            fmt::ptr(self_msg_iter));
             stream_iter->msg_iter =
-                ctf_msg_iter_create(ctf_tc, lttng_live->max_query_size, medops, stream_iter,
-                                    log_level, self_comp, self_msg_iter);
+                ctf_msg_iter_create(ctf_tc, lttng_live->max_query_size, medops, stream_iter.get(),
+                                    self_msg_iter, stream_iter->logger);
             if (!stream_iter->msg_iter) {
-                BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create CTF message iterator");
-                goto error;
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(stream_iter->logger,
+                                             "Failed to create CTF message iterator");
+                return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
             }
         }
     }
@@ -162,45 +158,28 @@ enum lttng_live_iterator_status lttng_live_lazy_msg_init(struct lttng_live_sessi
     session->lazy_stream_msg_init = false;
 
     return LTTNG_LIVE_ITERATOR_STATUS_OK;
-
-error:
-    return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
 }
 
-BT_HIDDEN
 struct lttng_live_stream_iterator *
 lttng_live_stream_iterator_create(struct lttng_live_session *session, uint64_t ctf_trace_id,
                                   uint64_t stream_id, bt_self_message_iterator *self_msg_iter)
 {
-    struct lttng_live_stream_iterator *stream_iter;
-    struct lttng_live_component *lttng_live;
-    struct lttng_live_trace *trace;
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
+    std::stringstream nameSs;
 
     BT_ASSERT(session);
     BT_ASSERT(session->lttng_live_msg_iter);
     BT_ASSERT(session->lttng_live_msg_iter->lttng_live_comp);
-    log_level = session->log_level;
-    self_comp = session->self_comp;
-
-    lttng_live = session->lttng_live_msg_iter->lttng_live_comp;
 
-    stream_iter = g_new0(struct lttng_live_stream_iterator, 1);
-    if (!stream_iter) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Failed to allocate struct lttng_live_stream_iterator");
-        goto error;
-    }
-
-    stream_iter->log_level = log_level;
-    stream_iter->self_comp = self_comp;
-    trace = lttng_live_session_borrow_or_create_trace_by_id(session, ctf_trace_id);
+    lttng_live_component *lttng_live = session->lttng_live_msg_iter->lttng_live_comp;
+    lttng_live_trace *trace =
+        lttng_live_session_borrow_or_create_trace_by_id(session, ctf_trace_id);
     if (!trace) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to borrow CTF trace.");
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(session->logger, "Failed to borrow CTF trace.");
+        return nullptr;
     }
 
+    auto stream_iter = bt2s::make_unique<lttng_live_stream_iterator>(session->logger);
+
     stream_iter->trace = trace;
     stream_iter->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA;
     stream_iter->viewer_stream_id = stream_id;
@@ -213,67 +192,33 @@ lttng_live_stream_iterator_create(struct lttng_live_session *session, uint64_t c
 
     if (trace->trace) {
         struct ctf_trace_class *ctf_tc =
-            ctf_metadata_decoder_borrow_ctf_trace_class(trace->metadata->decoder);
+            ctf_metadata_decoder_borrow_ctf_trace_class(trace->metadata->decoder.get());
         BT_ASSERT(!stream_iter->msg_iter);
         stream_iter->msg_iter =
-            ctf_msg_iter_create(ctf_tc, lttng_live->max_query_size, medops, stream_iter, log_level,
-                                self_comp, self_msg_iter);
+            ctf_msg_iter_create(ctf_tc, lttng_live->max_query_size, medops, stream_iter.get(),
+                                self_msg_iter, stream_iter->logger);
         if (!stream_iter->msg_iter) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create CTF message iterator");
-            goto error;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(stream_iter->logger,
+                                         "Failed to create CTF message iterator");
+            return nullptr;
         }
     }
-    stream_iter->buf = g_new0(uint8_t, lttng_live->max_query_size);
-    if (!stream_iter->buf) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate live stream iterator buffer");
-        goto error;
-    }
+    stream_iter->buf.resize(lttng_live->max_query_size);
 
-    stream_iter->buflen = lttng_live->max_query_size;
-    stream_iter->name = g_string_new(NULL);
-    if (!stream_iter->name) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate live stream iterator name buffer");
-        goto error;
-    }
+    nameSs << STREAM_NAME_PREFIX << stream_iter->viewer_stream_id;
+    stream_iter->name = nameSs.str();
 
-    g_string_printf(stream_iter->name, STREAM_NAME_PREFIX "%" PRIu64,
-                    stream_iter->viewer_stream_id);
-    g_ptr_array_add(trace->stream_iterators, stream_iter);
+    const auto ret = stream_iter.get();
+    trace->stream_iterators.emplace_back(std::move(stream_iter));
 
     /* Track the number of active stream iterator. */
     session->lttng_live_msg_iter->active_stream_iter++;
 
-    goto end;
-error:
-    lttng_live_stream_iterator_destroy(stream_iter);
-    stream_iter = NULL;
-end:
-    return stream_iter;
+    return ret;
 }
 
-BT_HIDDEN
-void lttng_live_stream_iterator_destroy(struct lttng_live_stream_iterator *stream_iter)
+lttng_live_stream_iterator::~lttng_live_stream_iterator()
 {
-    if (!stream_iter) {
-        return;
-    }
-
-    if (stream_iter->stream) {
-        BT_STREAM_PUT_REF_AND_RESET(stream_iter->stream);
-    }
-
-    if (stream_iter->msg_iter) {
-        ctf_msg_iter_destroy(stream_iter->msg_iter);
-    }
-    g_free(stream_iter->buf);
-    if (stream_iter->name) {
-        g_string_free(stream_iter->name, TRUE);
-    }
-
-    bt_message_put_ref(stream_iter->current_msg);
-
     /* Track the number of active stream iterator. */
-    stream_iter->trace->session->lttng_live_msg_iter->active_stream_iter--;
-
-    g_free(stream_iter);
+    this->trace->session->lttng_live_msg_iter->active_stream_iter--;
 }
index c16eae87fe83f604a80667f02763a1f8b5832377..675bbdbbaae51e7cf36c535999a7d6744d60878e 100644 (file)
@@ -7,12 +7,8 @@
 #ifndef LTTNG_LIVE_DATA_STREAM_H
 #define LTTNG_LIVE_DATA_STREAM_H
 
-#include <stdio.h>
 #include <stdint.h>
 
-#include <glib.h>
-
-#include "../common/msg-iter/msg-iter.hpp"
 #include "lttng-live.hpp"
 
 enum lttng_live_iterator_status lttng_live_lazy_msg_init(struct lttng_live_session *session,
@@ -22,6 +18,4 @@ struct lttng_live_stream_iterator *
 lttng_live_stream_iterator_create(struct lttng_live_session *session, uint64_t ctf_trace_id,
                                   uint64_t stream_id, bt_self_message_iterator *self_msg_iter);
 
-void lttng_live_stream_iterator_destroy(struct lttng_live_stream_iterator *stream);
-
 #endif /* LTTNG_LIVE_DATA_STREAM_H */
index b3e19d9c73dba6d4946f0117b8f4dc78cef99b0c..79fbe55052a4abb1ae9643669b9d45cbbc051844 100644 (file)
@@ -8,28 +8,22 @@
  * Babeltrace CTF LTTng-live Client Component
  */
 
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL   log_level
-#define BT_LOG_TAG            "PLUGIN/SRC.CTF.LTTNG-LIVE"
-#include "logging/comp-logging.h"
-
-#include <inttypes.h>
-#include <stdbool.h>
-#include <unistd.h>
-
 #include <glib.h>
+#include <unistd.h>
 
 #include "common/assert.h"
-#include <babeltrace2/babeltrace.h>
-#include "compat/compiler.h"
-#include <babeltrace2/types.h>
+#include "cpp-common/bt2c/fmt.hpp"
+#include "cpp-common/bt2c/glib-up.hpp"
+#include "cpp-common/bt2c/vector.hpp"
+#include "cpp-common/bt2s/make-unique.hpp"
+#include "cpp-common/vendor/fmt/format.h"
 
 #include "plugins/common/muxing/muxing.h"
 #include "plugins/common/param-validation/param-validation.h"
 
 #include "data-stream.hpp"
-#include "metadata.hpp"
 #include "lttng-live.hpp"
+#include "metadata.hpp"
 
 #define MAX_QUERY_SIZE                     (256 * 1024)
 #define URL_PARAM                          "url"
 #define SESS_NOT_FOUND_ACTION_FAIL_STR     "fail"
 #define SESS_NOT_FOUND_ACTION_END_STR      "end"
 
-#define print_dbg(fmt, ...) BT_COMP_LOGD(fmt, ##__VA_ARGS__)
-
-static const char *lttng_live_iterator_status_string(enum lttng_live_iterator_status status)
-{
-    switch (status) {
-    case LTTNG_LIVE_ITERATOR_STATUS_CONTINUE:
-        return "LTTNG_LIVE_ITERATOR_STATUS_CONTINUE";
-    case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
-        return "LTTNG_LIVE_ITERATOR_STATUS_AGAIN";
-    case LTTNG_LIVE_ITERATOR_STATUS_END:
-        return "LTTNG_LIVE_ITERATOR_STATUS_END";
-    case LTTNG_LIVE_ITERATOR_STATUS_OK:
-        return "LTTNG_LIVE_ITERATOR_STATUS_OK";
-    case LTTNG_LIVE_ITERATOR_STATUS_INVAL:
-        return "LTTNG_LIVE_ITERATOR_STATUS_INVAL";
-    case LTTNG_LIVE_ITERATOR_STATUS_ERROR:
-        return "LTTNG_LIVE_ITERATOR_STATUS_ERROR";
-    case LTTNG_LIVE_ITERATOR_STATUS_NOMEM:
-        return "LTTNG_LIVE_ITERATOR_STATUS_NOMEM";
-    case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED:
-        return "LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED";
-    default:
-        bt_common_abort();
-    }
-}
-
-static const char *lttng_live_stream_state_string(enum lttng_live_stream_state state)
-{
-    switch (state) {
-    case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA:
-        return "ACTIVE_NO_DATA";
-    case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA:
-        return "QUIESCENT_NO_DATA";
-    case LTTNG_LIVE_STREAM_QUIESCENT:
-        return "QUIESCENT";
-    case LTTNG_LIVE_STREAM_ACTIVE_DATA:
-        return "ACTIVE_DATA";
-    case LTTNG_LIVE_STREAM_EOF:
-        return "EOF";
-    default:
-        return "ERROR";
-    }
-}
-
 void lttng_live_stream_iterator_set_state(struct lttng_live_stream_iterator *stream_iter,
                                           enum lttng_live_stream_state new_state)
 {
-    bt_self_component *self_comp = stream_iter->self_comp;
-    bt_logging_level log_level = stream_iter->log_level;
-
-    BT_COMP_LOGD("Setting live stream iterator state: viewer-stream-id=%" PRIu64
-                 ", old-state=%s, new-state=%s",
-                 stream_iter->viewer_stream_id, lttng_live_stream_state_string(stream_iter->state),
-                 lttng_live_stream_state_string(new_state));
+    BT_CPPLOGD_SPEC(stream_iter->logger,
+                    "Setting live stream iterator state: viewer-stream-id={}, "
+                    "old-state={}, new-state={}",
+                    stream_iter->viewer_stream_id, stream_iter->state, new_state);
 
     stream_iter->state = new_state;
 }
 
 #define LTTNG_LIVE_LOGD_STREAM_ITER(live_stream_iter)                                              \
     do {                                                                                           \
-        BT_COMP_LOGD("Live stream iterator state=%s, "                                             \
-                     "last-inact-ts-is-set=%d, last-inact-ts-value=%" PRId64 ", "                  \
-                     "curr-inact-ts=%" PRId64,                                                     \
-                     lttng_live_stream_state_string(live_stream_iter->state),                      \
-                     live_stream_iter->last_inactivity_ts.is_set,                                  \
-                     live_stream_iter->last_inactivity_ts.value,                                   \
-                     live_stream_iter->current_inactivity_ts);                                     \
+        BT_CPPLOGD_SPEC((live_stream_iter)->logger,                                                \
+                        "Live stream iterator state={}, "                                          \
+                        "last-inact-ts-is-set={}, last-inact-ts-value={}, "                        \
+                        "curr-inact-ts={}",                                                        \
+                        (live_stream_iter)->state, (live_stream_iter)->last_inactivity_ts.is_set,  \
+                        (live_stream_iter)->last_inactivity_ts.value,                              \
+                        (live_stream_iter)->current_inactivity_ts);                                \
     } while (0);
 
-BT_HIDDEN
 bool lttng_live_graph_is_canceled(struct lttng_live_msg_iter *msg_iter)
 {
-    bool ret;
-
     if (!msg_iter) {
-        ret = false;
-        goto end;
+        return false;
     }
 
-    ret = bt_self_message_iterator_is_interrupted(msg_iter->self_msg_iter);
-
-end:
-    return ret;
+    return bt_self_message_iterator_is_interrupted(msg_iter->self_msg_iter);
 }
 
 static struct lttng_live_trace *
 lttng_live_session_borrow_trace_by_id(struct lttng_live_session *session, uint64_t trace_id)
 {
-    uint64_t trace_idx;
-    struct lttng_live_trace *ret_trace = NULL;
-
-    for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) {
-        struct lttng_live_trace *trace =
-            (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+    for (lttng_live_trace::UP& trace : session->traces) {
         if (trace->id == trace_id) {
-            ret_trace = trace;
-            goto end;
+            return trace.get();
         }
     }
 
-end:
-    return ret_trace;
-}
-
-static void lttng_live_destroy_trace(struct lttng_live_trace *trace)
-{
-    bt_logging_level log_level = trace->log_level;
-    bt_self_component *self_comp = trace->self_comp;
-
-    BT_COMP_LOGD("Destroying live trace: trace-id=%" PRIu64, trace->id);
-
-    BT_ASSERT(trace->stream_iterators);
-    g_ptr_array_free(trace->stream_iterators, TRUE);
-
-    BT_TRACE_PUT_REF_AND_RESET(trace->trace);
-    BT_TRACE_CLASS_PUT_REF_AND_RESET(trace->trace_class);
-
-    lttng_live_metadata_fini(trace);
-    g_free(trace);
+    return nullptr;
 }
 
 static struct lttng_live_trace *lttng_live_create_trace(struct lttng_live_session *session,
                                                         uint64_t trace_id)
 {
-    struct lttng_live_trace *trace = NULL;
-    bt_logging_level log_level = session->log_level;
-    bt_self_component *self_comp = session->self_comp;
-
-    BT_COMP_LOGD("Creating live trace: "
-                 "session-id=%" PRIu64 ", trace-id=%" PRIu64,
-                 session->id, trace_id);
-    trace = g_new0(struct lttng_live_trace, 1);
-    if (!trace) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate live trace");
-        goto error;
-    }
-    trace->log_level = session->log_level;
-    trace->self_comp = session->self_comp;
+    BT_CPPLOGD_SPEC(session->logger, "Creating live trace: session-id={}, trace-id={}", session->id,
+                    trace_id);
+
+    auto trace = bt2s::make_unique<lttng_live_trace>(session->logger);
+
     trace->session = session;
     trace->id = trace_id;
-    trace->trace_class = NULL;
-    trace->trace = NULL;
-    trace->stream_iterators =
-        g_ptr_array_new_with_free_func((GDestroyNotify) lttng_live_stream_iterator_destroy);
-    BT_ASSERT(trace->stream_iterators);
     trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
-    g_ptr_array_add(session->traces, trace);
 
-    goto end;
-error:
-    g_free(trace);
-    trace = NULL;
-end:
-    return trace;
+    const auto ret = trace.get();
+    session->traces.emplace_back(std::move(trace));
+    return ret;
 }
 
-BT_HIDDEN
 struct lttng_live_trace *
 lttng_live_session_borrow_or_create_trace_by_id(struct lttng_live_session *session,
                                                 uint64_t trace_id)
 {
-    struct lttng_live_trace *trace;
-
-    trace = lttng_live_session_borrow_trace_by_id(session, trace_id);
-    if (trace) {
-        goto end;
+    if (lttng_live_trace *trace = lttng_live_session_borrow_trace_by_id(session, trace_id)) {
+        return trace;
     }
 
     /* The session is the owner of the newly created trace. */
-    trace = lttng_live_create_trace(session, trace_id);
-
-end:
-    return trace;
+    return lttng_live_create_trace(session, trace_id);
 }
 
-BT_HIDDEN
 int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter, uint64_t session_id,
                            const char *hostname, const char *session_name)
 {
-    int ret = 0;
-    struct lttng_live_session *session;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-
-    BT_COMP_LOGD("Adding live session: "
-                 "session-id=%" PRIu64 ", hostname=\"%s\" session-name=\"%s\"",
-                 session_id, hostname, session_name);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Adding live session: "
+                    "session-id={}, hostname=\"{}\", session-name=\"{}\"",
+                    session_id, hostname, session_name);
 
-    session = g_new0(struct lttng_live_session, 1);
-    if (!session) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate live session");
-        goto error;
-    }
+    auto session = bt2s::make_unique<lttng_live_session>(lttng_live_msg_iter->logger);
 
-    session->log_level = lttng_live_msg_iter->log_level;
     session->self_comp = lttng_live_msg_iter->self_comp;
     session->id = session_id;
-    session->traces = g_ptr_array_new_with_free_func((GDestroyNotify) lttng_live_destroy_trace);
-    BT_ASSERT(session->traces);
     session->lttng_live_msg_iter = lttng_live_msg_iter;
     session->new_streams_needed = true;
-    session->hostname = g_string_new(hostname);
-    BT_ASSERT(session->hostname);
+    session->hostname = hostname;
+    session->session_name = session_name;
 
-    session->session_name = g_string_new(session_name);
-    BT_ASSERT(session->session_name);
+    lttng_live_msg_iter->sessions.emplace_back(std::move(session));
 
-    g_ptr_array_add(lttng_live_msg_iter->sessions, session);
-    goto end;
-error:
-    g_free(session);
-    ret = -1;
-end:
-    return ret;
+    return 0;
 }
 
-static void lttng_live_destroy_session(struct lttng_live_session *session)
+lttng_live_session::~lttng_live_session()
 {
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
-
-    if (!session) {
-        goto end;
-    }
+    BT_CPPLOGD_SPEC(this->logger, "Destroying live session: session-id={}, session-name=\"{}\"",
+                    this->id, this->session_name);
 
-    log_level = session->log_level;
-    self_comp = session->self_comp;
-    BT_COMP_LOGD("Destroying live session: "
-                 "session-id=%" PRIu64 ", session-name=\"%s\"",
-                 session->id, session->session_name->str);
-    if (session->id != -1ULL) {
-        if (lttng_live_session_detach(session)) {
-            if (!lttng_live_graph_is_canceled(session->lttng_live_msg_iter)) {
+    if (this->id != -1ULL) {
+        if (lttng_live_session_detach(this)) {
+            if (!lttng_live_graph_is_canceled(this->lttng_live_msg_iter)) {
                 /* Old relayd cannot detach sessions. */
-                BT_COMP_LOGD("Unable to detach lttng live session %" PRIu64, session->id);
+                BT_CPPLOGD_SPEC(this->logger, "Unable to detach lttng live session {}", this->id);
             }
         }
-        session->id = -1ULL;
-    }
-
-    if (session->traces) {
-        g_ptr_array_free(session->traces, TRUE);
-    }
 
-    if (session->hostname) {
-        g_string_free(session->hostname, TRUE);
+        this->id = -1ULL;
     }
-    if (session->session_name) {
-        g_string_free(session->session_name, TRUE);
-    }
-    g_free(session);
-
-end:
-    return;
 }
 
-static void lttng_live_msg_iter_destroy(struct lttng_live_msg_iter *lttng_live_msg_iter)
+lttng_live_msg_iter::~lttng_live_msg_iter()
 {
-    if (!lttng_live_msg_iter) {
-        goto end;
-    }
-
-    if (lttng_live_msg_iter->sessions) {
-        g_ptr_array_free(lttng_live_msg_iter->sessions, TRUE);
-    }
-
-    if (lttng_live_msg_iter->viewer_connection) {
-        live_viewer_connection_destroy(lttng_live_msg_iter->viewer_connection);
-    }
-    BT_ASSERT(lttng_live_msg_iter->lttng_live_comp);
-    BT_ASSERT(lttng_live_msg_iter->lttng_live_comp->has_msg_iter);
+    BT_ASSERT(this->lttng_live_comp);
+    BT_ASSERT(this->lttng_live_comp->has_msg_iter);
 
     /* All stream iterators must be destroyed at this point. */
-    BT_ASSERT(lttng_live_msg_iter->active_stream_iter == 0);
-    lttng_live_msg_iter->lttng_live_comp->has_msg_iter = false;
-
-    g_free(lttng_live_msg_iter);
-
-end:
-    return;
+    BT_ASSERT(this->active_stream_iter == 0);
+    this->lttng_live_comp->has_msg_iter = false;
 }
 
-BT_HIDDEN
 void lttng_live_msg_iter_finalize(bt_self_message_iterator *self_msg_iter)
 {
-    struct lttng_live_msg_iter *lttng_live_msg_iter;
-
-    BT_ASSERT(self_msg_iter);
-
-    lttng_live_msg_iter =
-        (struct lttng_live_msg_iter *) bt_self_message_iterator_get_data(self_msg_iter);
-    BT_ASSERT(lttng_live_msg_iter);
-    lttng_live_msg_iter_destroy(lttng_live_msg_iter);
+    lttng_live_msg_iter::UP {
+        static_cast<lttng_live_msg_iter *>(bt_self_message_iterator_get_data(self_msg_iter))};
 }
 
 static enum lttng_live_iterator_status
 lttng_live_iterator_next_check_stream_state(struct lttng_live_stream_iterator *lttng_live_stream)
 {
-    bt_logging_level log_level = lttng_live_stream->log_level;
-    bt_self_component *self_comp = lttng_live_stream->self_comp;
-
     switch (lttng_live_stream->state) {
     case LTTNG_LIVE_STREAM_QUIESCENT:
     case LTTNG_LIVE_STREAM_ACTIVE_DATA:
         break;
     case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA:
         /* Invalid state. */
-        BT_COMP_LOGF("Unexpected stream state \"ACTIVE_NO_DATA\"");
+        BT_CPPLOGF_SPEC(lttng_live_stream->logger, "Unexpected stream state \"ACTIVE_NO_DATA\"");
         bt_common_abort();
     case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA:
         /* Invalid state. */
-        BT_COMP_LOGF("Unexpected stream state \"QUIESCENT_NO_DATA\"");
+        BT_CPPLOGF_SPEC(lttng_live_stream->logger, "Unexpected stream state \"QUIESCENT_NO_DATA\"");
         bt_common_abort();
     case LTTNG_LIVE_STREAM_EOF:
         break;
@@ -370,27 +193,25 @@ static enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_no_da
     struct lttng_live_msg_iter *lttng_live_msg_iter,
     struct lttng_live_stream_iterator *lttng_live_stream)
 {
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
     enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
     enum lttng_live_stream_state orig_state = lttng_live_stream->state;
     struct packet_index index;
 
     if (lttng_live_stream->trace->metadata_stream_state ==
         LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED) {
-        BT_COMP_LOGD(
-            "Need to get an update for the metadata stream before proceeding further with this stream: "
-            "stream-name=\"%s\"",
-            lttng_live_stream->name->str);
+        BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                        "Need to get an update for the metadata stream before proceeding "
+                        "further with this stream: stream-name=\"{}\"",
+                        lttng_live_stream->name);
         ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
         goto end;
     }
 
     if (lttng_live_stream->trace->session->new_streams_needed) {
-        BT_COMP_LOGD(
-            "Need to get an update of all streams before proceeding further with this stream: "
-            "stream-name=\"%s\"",
-            lttng_live_stream->name->str);
+        BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                        "Need to get an update of all streams before proceeding further "
+                        "with this stream: stream-name=\"{}\"",
+                        lttng_live_stream->name);
         ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
         goto end;
     }
@@ -430,11 +251,12 @@ static enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_no_da
     lttng_live_stream->offset = index.offset;
     lttng_live_stream->len = index.packet_size / CHAR_BIT;
 
-    BT_COMP_LOGD("Setting live stream reading info: stream-name=\"%s\", "
-                 "viewer-stream-id=%" PRIu64 ", stream-base-offset=%" PRIu64
-                 ", stream-offset=%" PRIu64 ", stream-len=%" PRIu64,
-                 lttng_live_stream->name->str, lttng_live_stream->viewer_stream_id,
-                 lttng_live_stream->base_offset, lttng_live_stream->offset, lttng_live_stream->len);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Setting live stream reading info: stream-name=\"{}\", "
+                    "viewer-stream-id={}, stream-base-offset={}, stream-offset={}, stream-len={}",
+                    lttng_live_stream->name, lttng_live_stream->viewer_stream_id,
+                    lttng_live_stream->base_offset, lttng_live_stream->offset,
+                    lttng_live_stream->len);
 
 end:
     if (ret == LTTNG_LIVE_ITERATOR_STATUS_OK) {
@@ -454,13 +276,11 @@ static enum lttng_live_iterator_status
 lttng_live_get_session(struct lttng_live_msg_iter *lttng_live_msg_iter,
                        struct lttng_live_session *session)
 {
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
     enum lttng_live_iterator_status status;
-    uint64_t trace_idx;
 
     if (!session->attached) {
-        BT_COMP_LOGD("Attach to session: session-id=%" PRIu64, session->id);
+        BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger, "Attach to session: session-id={}",
+                        session->id);
         enum lttng_live_viewer_status attach_status =
             lttng_live_session_attach(session, lttng_live_msg_iter->self_msg_iter);
         if (attach_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
@@ -472,18 +292,18 @@ lttng_live_get_session(struct lttng_live_msg_iter *lttng_live_msg_iter,
                  * cancelled.
                  */
                 bt_current_thread_clear_error();
-                status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+                return LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
             } else {
-                status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-                BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Error attaching to LTTng live session");
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                             "Error attaching to LTTng live session");
+                return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
             }
-            goto end;
         }
     }
 
-    BT_COMP_LOGD("Updating all data streams: "
-                 "session-id=%" PRIu64 ", session-name=\"%s\"",
-                 session->id, session->session_name->str);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Updating all data streams: session-id={}, session-name=\"{}\"", session->id,
+                    session->session_name);
 
     status = lttng_live_session_get_new_streams(session, lttng_live_msg_iter->self_msg_iter);
     switch (status) {
@@ -491,52 +311,47 @@ lttng_live_get_session(struct lttng_live_msg_iter *lttng_live_msg_iter,
         break;
     case LTTNG_LIVE_ITERATOR_STATUS_END:
         /*
-                * We received a `_END` from the `_get_new_streams()` function,
-                * which means no more data will ever be received from the data
-                * streams of this session. But it's possible that the metadata
-                * is incomplete.
-                * The live protocol guarantees that we receive all the
-                * metadata needed before we receive data streams needing it.
-                * But it's possible to receive metadata NOT needed by
-                * data streams after the session was closed. For example, this
-                * could happen if a new event is registered and the session is
-                * stopped before any tracepoint for that event is actually
-                * fired.
-                */
-        BT_COMP_LOGD(
+         * We received a `_END` from the `_get_new_streams()` function,
+         * which means no more data will ever be received from the data
+         * streams of this session. But it's possible that the metadata
+         * is incomplete.
+         * The live protocol guarantees that we receive all the
+         * metadata needed before we receive data streams needing it.
+         * But it's possible to receive metadata NOT needed by
+         * data streams after the session was closed. For example, this
+         * could happen if a new event is registered and the session is
+         * stopped before any tracepoint for that event is actually
+         * fired.
+         */
+        BT_CPPLOGD_SPEC(
+            lttng_live_msg_iter->logger,
             "Updating streams returned _END status. Override status to _OK in order fetch any remaining metadata:"
-            "session-id=%" PRIu64 ", session-name=\"%s\"",
-            session->id, session->session_name->str);
-        status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-        break;
+            "session-id={}, session-name=\"{}\"",
+            session->id, session->session_name);
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     default:
-        goto end;
+        return status;
     }
 
-    BT_COMP_LOGD("Updating metadata stream for session: "
-                 "session-id=%" PRIu64 ", session-name=\"%s\"",
-                 session->id, session->session_name->str);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Updating metadata stream for session: session-id={}, session-name=\"{}\"",
+                    session->id, session->session_name);
 
-    trace_idx = 0;
-    while (trace_idx < session->traces->len) {
-        struct lttng_live_trace *trace =
-            (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
-
-        status = lttng_live_metadata_update(trace);
+    for (lttng_live_trace::UP& trace : session->traces) {
+        status = lttng_live_metadata_update(trace.get());
         switch (status) {
         case LTTNG_LIVE_ITERATOR_STATUS_END:
         case LTTNG_LIVE_ITERATOR_STATUS_OK:
-            trace_idx++;
             break;
         case LTTNG_LIVE_ITERATOR_STATUS_CONTINUE:
         case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
-            goto end;
+            return status;
         default:
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Error updating trace metadata: "
-                                      "stream-iter-status=%s, trace-id=%" PRIu64,
-                                      lttng_live_iterator_status_string(status), trace->id);
-            goto end;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                lttng_live_msg_iter->logger,
+                "Error updating trace metadata: stream-iter-status={}, trace-id={}", status,
+                trace->id);
+            return status;
         }
     }
 
@@ -544,32 +359,23 @@ lttng_live_get_session(struct lttng_live_msg_iter *lttng_live_msg_iter,
      * Now that we have the metadata we can initialize the downstream
      * iterator.
      */
-    status = lttng_live_lazy_msg_init(session, lttng_live_msg_iter->self_msg_iter);
-
-end:
-    return status;
+    return lttng_live_lazy_msg_init(session, lttng_live_msg_iter->self_msg_iter);
 }
 
 static void
 lttng_live_force_new_streams_and_metadata(struct lttng_live_msg_iter *lttng_live_msg_iter)
 {
-    uint64_t session_idx, trace_idx;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-
-    for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; session_idx++) {
-        struct lttng_live_session *session =
-            (lttng_live_session *) g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
-        BT_COMP_LOGD("Force marking session as needing new streams: "
-                     "session-id=%" PRIu64,
-                     session->id);
+    for (const auto& session : lttng_live_msg_iter->sessions) {
+        BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                        "Force marking session as needing new streams: "
+                        "session-id={}",
+                        session->id);
         session->new_streams_needed = true;
-        for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) {
-            struct lttng_live_trace *trace =
-                (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
-            BT_COMP_LOGD("Force marking trace metadata state as needing an update: "
-                         "session-id=%" PRIu64 ", trace-id=%" PRIu64,
-                         session->id, trace->id);
+        for (lttng_live_trace::UP& trace : session->traces) {
+            BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                            "Force marking trace metadata state as needing an update: "
+                            "session-id={}, trace-id={}",
+                            session->id, trace->id);
 
             BT_ASSERT(trace->metadata_stream_state != LTTNG_LIVE_METADATA_STREAM_STATE_CLOSED);
 
@@ -581,32 +387,30 @@ lttng_live_force_new_streams_and_metadata(struct lttng_live_msg_iter *lttng_live
 static enum lttng_live_iterator_status
 lttng_live_iterator_handle_new_streams_and_metadata(struct lttng_live_msg_iter *lttng_live_msg_iter)
 {
-    enum lttng_live_iterator_status status;
     enum lttng_live_viewer_status viewer_status;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-    uint64_t session_idx = 0, nr_sessions_opened = 0;
-    struct lttng_live_session *session;
+    uint64_t nr_sessions_opened = 0;
     enum session_not_found_action sess_not_found_act =
         lttng_live_msg_iter->lttng_live_comp->params.sess_not_found_act;
 
-    BT_COMP_LOGD("Update data and metadata of all sessions: "
-                 "live-msg-iter-addr=%p",
-                 lttng_live_msg_iter);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Update data and metadata of all sessions: "
+                    "live-msg-iter-addr={}",
+                    fmt::ptr(lttng_live_msg_iter));
     /*
      * In a remotely distant future, we could add a "new
      * session" flag to the protocol, which would tell us that we
      * need to query for new sessions even though we have sessions
      * currently ongoing.
      */
-    if (lttng_live_msg_iter->sessions->len == 0) {
+    if (lttng_live_msg_iter->sessions.empty()) {
         if (sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE) {
-            BT_COMP_LOGD(
+            BT_CPPLOGD_SPEC(
+                lttng_live_msg_iter->logger,
                 "No session found. Exiting in accordance with the `session-not-found-action` parameter");
-            status = LTTNG_LIVE_ITERATOR_STATUS_END;
-            goto end;
+            return LTTNG_LIVE_ITERATOR_STATUS_END;
         } else {
-            BT_COMP_LOGD(
+            BT_CPPLOGD_SPEC(
+                lttng_live_msg_iter->logger,
                 "No session found. Try creating a new one in accordance with the `session-not-found-action` parameter");
             /*
              * Retry to create a viewer session for the requested
@@ -615,23 +419,20 @@ lttng_live_iterator_handle_new_streams_and_metadata(struct lttng_live_msg_iter *
             viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
             if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
                 if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-                    status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-                    BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                              "Error creating LTTng live viewer session");
+                    BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                                 "Error creating LTTng live viewer session");
+                    return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
                 } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-                    status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+                    return LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
                 } else {
                     bt_common_abort();
                 }
-                goto end;
             }
         }
     }
 
-    for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; session_idx++) {
-        session =
-            (lttng_live_session *) g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
-        status = lttng_live_get_session(lttng_live_msg_iter, session);
+    for (const auto& session : lttng_live_msg_iter->sessions) {
+        const auto status = lttng_live_get_session(lttng_live_msg_iter, session.get());
         switch (status) {
         case LTTNG_LIVE_ITERATOR_STATUS_OK:
         case LTTNG_LIVE_ITERATOR_STATUS_END:
@@ -642,7 +443,7 @@ lttng_live_iterator_handle_new_streams_and_metadata(struct lttng_live_msg_iter *
              */
             break;
         default:
-            goto end;
+            return status;
         }
         if (!session->closed) {
             nr_sessions_opened++;
@@ -650,54 +451,41 @@ lttng_live_iterator_handle_new_streams_and_metadata(struct lttng_live_msg_iter *
     }
 
     if (sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE && nr_sessions_opened == 0) {
-        status = LTTNG_LIVE_ITERATOR_STATUS_END;
+        return LTTNG_LIVE_ITERATOR_STATUS_END;
     } else {
-        status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     }
-
-end:
-    return status;
 }
 
 static enum lttng_live_iterator_status
 emit_inactivity_message(struct lttng_live_msg_iter *lttng_live_msg_iter,
-                        struct lttng_live_stream_iterator *stream_iter, const bt_message **message,
-                        uint64_t timestamp)
+                        struct lttng_live_stream_iterator *stream_iter,
+                        bt2::ConstMessage::Shared& message, uint64_t timestamp)
 {
-    enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-    bt_message *msg = NULL;
-
     BT_ASSERT(stream_iter->trace->clock_class);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Emitting inactivity message for stream: ctf-stream-id={}, "
+                    "viewer-stream-id={}, timestamp={}",
+                    stream_iter->ctf_stream_class_id.value, stream_iter->viewer_stream_id,
+                    timestamp);
 
-    BT_COMP_LOGD("Emitting inactivity message for stream: ctf-stream-id=%" PRIu64
-                 ", viewer-stream-id=%" PRIu64 ", timestamp=%" PRIu64,
-                 stream_iter->ctf_stream_class_id.value, stream_iter->viewer_stream_id, timestamp);
+    const auto msg = bt_message_message_iterator_inactivity_create(
+        lttng_live_msg_iter->self_msg_iter, stream_iter->trace->clock_class, timestamp);
 
-    msg = bt_message_message_iterator_inactivity_create(lttng_live_msg_iter->self_msg_iter,
-                                                        stream_iter->trace->clock_class, timestamp);
     if (!msg) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Error emitting message iterator inactivity message");
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                     "Error emitting message iterator inactivity message");
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
 
-    *message = msg;
-end:
-    return ret;
-
-error:
-    ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-    bt_message_put_ref(msg);
-    goto end;
+    message = bt2::ConstMessage::Shared::createWithoutRef(msg);
+    return LTTNG_LIVE_ITERATOR_STATUS_OK;
 }
 
 static enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_quiescent_stream(
     struct lttng_live_msg_iter *lttng_live_msg_iter,
-    struct lttng_live_stream_iterator *lttng_live_stream, const bt_message **message)
+    struct lttng_live_stream_iterator *lttng_live_stream, bt2::ConstMessage::Shared& message)
 {
-    enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
-
     if (lttng_live_stream->state != LTTNG_LIVE_STREAM_QUIESCENT) {
         return LTTNG_LIVE_ITERATOR_STATUS_OK;
     }
@@ -711,34 +499,35 @@ static enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_quies
         lttng_live_stream_iterator_set_state(lttng_live_stream,
                                              LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA);
 
-        ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-        goto end;
+        return LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
     }
 
-    ret = emit_inactivity_message(lttng_live_msg_iter, lttng_live_stream, message,
-                                  lttng_live_stream->current_inactivity_ts);
+    const auto status = emit_inactivity_message(lttng_live_msg_iter, lttng_live_stream, message,
+                                                lttng_live_stream->current_inactivity_ts);
+
+    if (status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+        return status;
+    }
 
     lttng_live_stream->last_inactivity_ts.value = lttng_live_stream->current_inactivity_ts;
     lttng_live_stream->last_inactivity_ts.is_set = true;
-end:
-    return ret;
+
+    return LTTNG_LIVE_ITERATOR_STATUS_OK;
 }
 
-static int live_get_msg_ts_ns(struct lttng_live_stream_iterator *stream_iter,
-                              struct lttng_live_msg_iter *lttng_live_msg_iter,
+static int live_get_msg_ts_ns(struct lttng_live_msg_iter *lttng_live_msg_iter,
                               const bt_message *msg, int64_t last_msg_ts_ns, int64_t *ts_ns)
 {
     const bt_clock_snapshot *clock_snapshot = NULL;
     int ret = 0;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
 
     BT_ASSERT_DBG(msg);
     BT_ASSERT_DBG(ts_ns);
 
-    BT_COMP_LOGD("Getting message's timestamp: iter-data-addr=%p, msg-addr=%p, "
-                 "last-msg-ts=%" PRId64,
-                 lttng_live_msg_iter, msg, last_msg_ts_ns);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Getting message's timestamp: iter-data-addr={}, msg-addr={}, "
+                    "last-msg-ts={}",
+                    fmt::ptr(lttng_live_msg_iter), fmt::ptr(msg), last_msg_ts_ns);
 
     switch (bt_message_get_type(msg)) {
     case BT_MESSAGE_TYPE_EVENT:
@@ -763,145 +552,119 @@ static int live_get_msg_ts_ns(struct lttng_live_stream_iterator *stream_iter,
         break;
     default:
         /* All the other messages have a higher priority */
-        BT_COMP_LOGD_STR("Message has no timestamp: using the last message timestamp.");
+        BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                        "Message has no timestamp, using the last message timestamp: "
+                        "iter-data-addr={}, msg-addr={}, last-msg-ts={}, ts={}",
+                        fmt::ptr(lttng_live_msg_iter), fmt::ptr(msg), last_msg_ts_ns, *ts_ns);
         *ts_ns = last_msg_ts_ns;
-        goto end;
+        return 0;
     }
 
     ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, ts_ns);
     if (ret) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Cannot get nanoseconds from Epoch of clock snapshot: "
-                                  "clock-snapshot-addr=%p",
-                                  clock_snapshot);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                     "Cannot get nanoseconds from Epoch of clock snapshot: "
+                                     "clock-snapshot-addr={}",
+                                     fmt::ptr(clock_snapshot));
+        return -1;
     }
 
-    goto end;
-
-error:
-    ret = -1;
-
-end:
-    if (ret == 0) {
-        BT_COMP_LOGD("Found message's timestamp: "
-                     "iter-data-addr=%p, msg-addr=%p, "
-                     "last-msg-ts=%" PRId64 ", ts=%" PRId64,
-                     lttng_live_msg_iter, msg, last_msg_ts_ns, *ts_ns);
-    }
+    BT_CPPLOGD_SPEC(
+        lttng_live_msg_iter->logger,
+        "Found message's timestamp: iter-data-addr={}, msg-addr={}, last-msg-ts={}, ts={}",
+        fmt::ptr(lttng_live_msg_iter), fmt::ptr(msg), last_msg_ts_ns, *ts_ns);
 
-    return ret;
+    return 0;
 }
 
 static enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_active_data_stream(
     struct lttng_live_msg_iter *lttng_live_msg_iter,
-    struct lttng_live_stream_iterator *lttng_live_stream, const bt_message **message)
+    struct lttng_live_stream_iterator *lttng_live_stream, bt2::ConstMessage::Shared& message)
 {
-    enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
     enum ctf_msg_iter_status status;
-    uint64_t session_idx, trace_idx;
-
-    for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; session_idx++) {
-        struct lttng_live_session *session =
-            (lttng_live_session *) g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
 
+    for (const auto& session : lttng_live_msg_iter->sessions) {
         if (session->new_streams_needed) {
-            BT_COMP_LOGD("Need an update for streams: "
-                         "session-id=%" PRIu64,
-                         session->id);
-            ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-            goto end;
+            BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                            "Need an update for streams: session-id={}", session->id);
+            return LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
         }
-        for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) {
-            struct lttng_live_trace *trace =
-                (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+        for (lttng_live_trace::UP& trace : session->traces) {
             if (trace->metadata_stream_state == LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED) {
-                BT_COMP_LOGD("Need an update for metadata stream: "
-                             "session-id=%" PRIu64 ", trace-id=%" PRIu64,
-                             session->id, trace->id);
-                ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-                goto end;
+                BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                                "Need an update for metadata stream: session-id={}, trace-id={}",
+                                session->id, trace->id);
+                return LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
             }
         }
     }
 
     if (lttng_live_stream->state != LTTNG_LIVE_STREAM_ACTIVE_DATA) {
-        ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Invalid state of live stream iterator"
-                                  "stream-iter-status=%s",
-                                  lttng_live_stream_state_string(lttng_live_stream->state));
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                     "Invalid state of live stream iterator: stream-iter-status={}",
+                                     lttng_live_stream->state);
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
 
-    status = ctf_msg_iter_get_next_message(lttng_live_stream->msg_iter, message);
+    const bt_message *msg;
+    status = ctf_msg_iter_get_next_message(lttng_live_stream->msg_iter.get(), &msg);
     switch (status) {
     case CTF_MSG_ITER_STATUS_EOF:
-        ret = LTTNG_LIVE_ITERATOR_STATUS_END;
-        break;
+        return LTTNG_LIVE_ITERATOR_STATUS_END;
     case CTF_MSG_ITER_STATUS_OK:
-        ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
-        break;
+        message = bt2::ConstMessage::Shared::createWithoutRef(msg);
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     case CTF_MSG_ITER_STATUS_AGAIN:
         /*
          * Continue immediately (end of packet). The next
          * get_index may return AGAIN to delay the following
          * attempt.
          */
-        ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-        break;
+        return LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
     case CTF_MSG_ITER_STATUS_ERROR:
     default:
-        ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "CTF message iterator failed to get next message: "
-                                  "msg-iter=%p, msg-iter-status=%s",
-                                  lttng_live_stream->msg_iter, ctf_msg_iter_status_string(status));
-        break;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(
+            lttng_live_msg_iter->logger,
+            "CTF message iterator failed to get next message: msg-iter={}, msg-iter-status={}",
+            fmt::ptr(lttng_live_stream->msg_iter), status);
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
-
-end:
-    return ret;
 }
 
 static enum lttng_live_iterator_status
 lttng_live_iterator_close_stream(struct lttng_live_msg_iter *lttng_live_msg_iter,
                                  struct lttng_live_stream_iterator *stream_iter,
-                                 const bt_message **curr_msg)
+                                 bt2::ConstMessage::Shared& curr_msg)
 {
-    enum lttng_live_iterator_status live_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-
-    BT_COMP_LOGD("Closing live stream iterator: stream-name=\"%s\", "
-                 "viewer-stream-id=%" PRIu64,
-                 stream_iter->name->str, stream_iter->viewer_stream_id);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Closing live stream iterator: stream-name=\"{}\", "
+                    "viewer-stream-id={}",
+                    stream_iter->name, stream_iter->viewer_stream_id);
 
     /*
      * The viewer has hung up on us so we are closing the stream. The
      * `ctf_msg_iter` should simply realize that it needs to close the
      * stream properly by emitting the necessary stream end message.
      */
+    const bt_message *msg;
     enum ctf_msg_iter_status status =
-        ctf_msg_iter_get_next_message(stream_iter->msg_iter, curr_msg);
+        ctf_msg_iter_get_next_message(stream_iter->msg_iter.get(), &msg);
 
     if (status == CTF_MSG_ITER_STATUS_ERROR) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Error getting the next message from CTF message iterator");
-        live_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                     "Error getting the next message from CTF message iterator");
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     } else if (status == CTF_MSG_ITER_STATUS_EOF) {
-        BT_COMP_LOGI("Reached the end of the live stream iterator.");
-        live_status = LTTNG_LIVE_ITERATOR_STATUS_END;
-        goto end;
+        BT_CPPLOGI_SPEC(lttng_live_msg_iter->logger,
+                        "Reached the end of the live stream iterator.");
+        return LTTNG_LIVE_ITERATOR_STATUS_END;
     }
 
     BT_ASSERT(status == CTF_MSG_ITER_STATUS_OK);
 
-end:
-    return live_status;
+    curr_msg = bt2::ConstMessage::Shared::createWithoutRef(msg);
+
+    return LTTNG_LIVE_ITERATOR_STATUS_OK;
 }
 
 /*
@@ -955,15 +718,14 @@ end:
 static enum lttng_live_iterator_status
 lttng_live_iterator_next_msg_on_stream(struct lttng_live_msg_iter *lttng_live_msg_iter,
                                        struct lttng_live_stream_iterator *stream_iter,
-                                       const bt_message **curr_msg)
+                                       bt2::ConstMessage::Shared& curr_msg)
 {
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
     enum lttng_live_iterator_status live_status;
 
-    BT_COMP_LOGD("Advancing live stream iterator until next message if possible: "
-                 "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
-                 stream_iter->name->str, stream_iter->viewer_stream_id);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Advancing live stream iterator until next message if possible: "
+                    "stream-name=\"{}\", viewer-stream-id={}",
+                    stream_iter->name, stream_iter->viewer_stream_id);
 
     if (stream_iter->has_stream_hung_up) {
         /*
@@ -1005,46 +767,45 @@ retry:
     live_status = lttng_live_iterator_next_handle_one_quiescent_stream(lttng_live_msg_iter,
                                                                        stream_iter, curr_msg);
     if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-        BT_ASSERT(!*curr_msg);
+        BT_ASSERT(!curr_msg);
         goto end;
     }
-    if (*curr_msg) {
+    if (curr_msg) {
         goto end;
     }
     live_status = lttng_live_iterator_next_handle_one_active_data_stream(lttng_live_msg_iter,
                                                                          stream_iter, curr_msg);
     if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-        BT_ASSERT(!*curr_msg);
+        BT_ASSERT(!curr_msg);
     }
 
 end:
     if (live_status == LTTNG_LIVE_ITERATOR_STATUS_CONTINUE) {
-        BT_COMP_LOGD("Ask the relay daemon for an updated view of the data and metadata streams");
+        BT_CPPLOGD_SPEC(
+            lttng_live_msg_iter->logger,
+            "Ask the relay daemon for an updated view of the data and metadata streams");
         goto retry;
     }
 
-    BT_COMP_LOGD("Returning from advancing live stream iterator: status=%s, "
-                 "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
-                 lttng_live_iterator_status_string(live_status), stream_iter->name->str,
-                 stream_iter->viewer_stream_id);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Returning from advancing live stream iterator: status={}, "
+                    "stream-name=\"{}\", viewer-stream-id={}",
+                    live_status, stream_iter->name, stream_iter->viewer_stream_id);
 
     return live_status;
 }
 
-static bool is_discarded_packet_or_event_message(const bt_message *msg)
+static bool is_discarded_packet_or_event_message(const bt2::ConstMessage msg)
 {
-    const enum bt_message_type msg_type = bt_message_get_type(msg);
-
-    return msg_type == BT_MESSAGE_TYPE_DISCARDED_EVENTS ||
-           msg_type == BT_MESSAGE_TYPE_DISCARDED_PACKETS;
+    return msg.type() == bt2::MessageType::DiscardedEvents ||
+           msg.type() == bt2::MessageType::DiscardedPackets;
 }
 
 static enum lttng_live_iterator_status
 adjust_discarded_packets_message(bt_self_message_iterator *iter, const bt_stream *stream,
-                                 const bt_message *msg_in, bt_message **msg_out,
+                                 const bt_message *msg_in, bt2::ConstMessage::Shared& msg_out,
                                  uint64_t new_begin_ts)
 {
-    enum lttng_live_iterator_status status = LTTNG_LIVE_ITERATOR_STATUS_OK;
     enum bt_property_availability availability;
     const bt_clock_snapshot *clock_snapshot;
     uint64_t end_ts;
@@ -1056,24 +817,23 @@ adjust_discarded_packets_message(bt_self_message_iterator *iter, const bt_stream
     availability = bt_message_discarded_packets_get_count(msg_in, &count);
     BT_ASSERT_DBG(availability == BT_PROPERTY_AVAILABILITY_AVAILABLE);
 
-    *msg_out = bt_message_discarded_packets_create_with_default_clock_snapshots(
+    const auto msg = bt_message_discarded_packets_create_with_default_clock_snapshots(
         iter, stream, new_begin_ts, end_ts);
-    if (!*msg_out) {
-        status = LTTNG_LIVE_ITERATOR_STATUS_NOMEM;
-        goto end;
+
+    if (!msg) {
+        return LTTNG_LIVE_ITERATOR_STATUS_NOMEM;
     }
 
-    bt_message_discarded_packets_set_count(*msg_out, count);
-end:
-    return status;
+    bt_message_discarded_packets_set_count(msg, count);
+    msg_out = bt2::ConstMessage::Shared::createWithoutRef(msg);
+    return LTTNG_LIVE_ITERATOR_STATUS_OK;
 }
 
 static enum lttng_live_iterator_status
 adjust_discarded_events_message(bt_self_message_iterator *iter, const bt_stream *stream,
-                                const bt_message *msg_in, bt_message **msg_out,
+                                const bt_message *msg_in, bt2::ConstMessage::Shared& msg_out,
                                 uint64_t new_begin_ts)
 {
-    enum lttng_live_iterator_status status = LTTNG_LIVE_ITERATOR_STATUS_OK;
     enum bt_property_availability availability;
     const bt_clock_snapshot *clock_snapshot;
     uint64_t end_ts;
@@ -1085,39 +845,36 @@ adjust_discarded_events_message(bt_self_message_iterator *iter, const bt_stream
     availability = bt_message_discarded_events_get_count(msg_in, &count);
     BT_ASSERT_DBG(availability == BT_PROPERTY_AVAILABILITY_AVAILABLE);
 
-    *msg_out = bt_message_discarded_events_create_with_default_clock_snapshots(
+    const auto msg = bt_message_discarded_events_create_with_default_clock_snapshots(
         iter, stream, new_begin_ts, end_ts);
-    if (!*msg_out) {
-        status = LTTNG_LIVE_ITERATOR_STATUS_NOMEM;
-        goto end;
+
+    if (!msg) {
+        return LTTNG_LIVE_ITERATOR_STATUS_NOMEM;
     }
 
-    bt_message_discarded_events_set_count(*msg_out, count);
-end:
-    return status;
+    bt_message_discarded_events_set_count(msg, count);
+    msg_out = bt2::ConstMessage::Shared::createWithoutRef(msg);
+    return LTTNG_LIVE_ITERATOR_STATUS_OK;
 }
 
 static enum lttng_live_iterator_status
 handle_late_message(struct lttng_live_msg_iter *lttng_live_msg_iter,
                     struct lttng_live_stream_iterator *stream_iter, int64_t late_msg_ts_ns,
-                    const bt_message *late_msg)
+                    const bt2::ConstMessage& late_msg)
 {
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
     const bt_clock_class *clock_class;
     const bt_stream_class *stream_class;
     enum bt_clock_class_cycles_to_ns_from_origin_status ts_ns_status;
     int64_t last_inactivity_ts_ns;
-    enum lttng_live_iterator_status stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
     enum lttng_live_iterator_status adjust_status;
-    bt_message *adjusted_message;
+    bt2::ConstMessage::Shared adjusted_message;
 
     /*
      * The timestamp of the current message is before the last message sent
      * by this component. We CANNOT send it as is.
      *
      * The only expected scenario in which that could happen is the
-     * following, everything else is a bug in this component, relay deamon,
+     * following, everything else is a bug in this component, relay daemon,
      * or CTF parser.
      *
      * Expected scenario: The CTF message iterator emitted discarded
@@ -1135,92 +892,88 @@ handle_late_message(struct lttng_live_msg_iter *lttng_live_msg_iter,
      * In short, the only scenario in which it's okay and fixable to
      * received a late message is when:
      *  1. the late message is a discarded packets or discarded events
-     * message,
+     *     message,
      *  2. this stream produced an inactivity message downstream, and
      *  3. the timestamp of the late message is within the inactivity
-     * timespan we sent downstream through the inactivity message.
+     *     timespan we sent downstream through the inactivity message.
      */
 
-    BT_COMP_LOGD("Handling late message on live stream iterator: "
-                 "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
-                 stream_iter->name->str, stream_iter->viewer_stream_id);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Handling late message on live stream iterator: "
+                    "stream-name=\"{}\", viewer-stream-id={}",
+                    stream_iter->name, stream_iter->viewer_stream_id);
 
     if (!stream_iter->last_inactivity_ts.is_set) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Invalid live stream state: "
-                                             "have a late message when no inactivity message "
-                                             "was ever sent for that stream.");
-        stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                     "Invalid live stream state: "
+                                     "have a late message when no inactivity message "
+                                     "was ever sent for that stream.");
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
 
     if (!is_discarded_packet_or_event_message(late_msg)) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Invalid live stream state: "
-                                  "have a late message that is not a packet discarded or "
-                                  "event discarded message: late-msg-type=%s",
-                                  bt_common_message_type_string(bt_message_get_type(late_msg)));
-        stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                     "Invalid live stream state: "
+                                     "have a late message that is not a packet discarded or "
+                                     "event discarded message: late-msg-type={}",
+                                     late_msg.type());
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
 
-    stream_class = bt_stream_borrow_class_const(stream_iter->stream);
+    stream_class = bt_stream_borrow_class_const(stream_iter->stream->libObjPtr());
     clock_class = bt_stream_class_borrow_default_clock_class_const(stream_class);
 
     ts_ns_status = bt_clock_class_cycles_to_ns_from_origin(
         clock_class, stream_iter->last_inactivity_ts.value, &last_inactivity_ts_ns);
     if (ts_ns_status != BT_CLOCK_CLASS_CYCLES_TO_NS_FROM_ORIGIN_STATUS_OK) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Error converting last "
-                                             "inactivity message timestamp to nanoseconds");
-        stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                     "Error converting last "
+                                     "inactivity message timestamp to nanoseconds");
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
 
     if (last_inactivity_ts_ns <= late_msg_ts_ns) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Invalid live stream state: "
-                                  "have a late message that is none included in a stream "
-                                  "inactivity timespan: last-inactivity-ts-ns=%" PRIu64
-                                  "late-msg-ts-ns=%" PRIu64,
-                                  last_inactivity_ts_ns, late_msg_ts_ns);
-        stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                     "Invalid live stream state: "
+                                     "have a late message that is none included in a stream "
+                                     "inactivity timespan: last-inactivity-ts-ns={}, "
+                                     "late-msg-ts-ns={}",
+                                     last_inactivity_ts_ns, late_msg_ts_ns);
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
 
     /*
      * We now know that it's okay for this message to be late, we can now
      * adjust its timestamp to ensure monotonicity.
      */
-    BT_COMP_LOGD("Adjusting the timestamp of late message: late-msg-type=%s, "
-                 "msg-new-ts-ns=%" PRIu64,
-                 bt_common_message_type_string(bt_message_get_type(late_msg)),
-                 stream_iter->last_inactivity_ts.value);
-    switch (bt_message_get_type(late_msg)) {
-    case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Adjusting the timestamp of late message: late-msg-type={}, "
+                    "msg-new-ts-ns={}",
+                    late_msg.type(), stream_iter->last_inactivity_ts.value);
+    switch (late_msg.type()) {
+    case bt2::MessageType::DiscardedEvents:
         adjust_status = adjust_discarded_events_message(
-            lttng_live_msg_iter->self_msg_iter, stream_iter->stream, late_msg, &adjusted_message,
-            stream_iter->last_inactivity_ts.value);
+            lttng_live_msg_iter->self_msg_iter, stream_iter->stream->libObjPtr(),
+            late_msg.libObjPtr(), adjusted_message, stream_iter->last_inactivity_ts.value);
         break;
-    case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
+    case bt2::MessageType::DiscardedPackets:
         adjust_status = adjust_discarded_packets_message(
-            lttng_live_msg_iter->self_msg_iter, stream_iter->stream, late_msg, &adjusted_message,
-            stream_iter->last_inactivity_ts.value);
+            lttng_live_msg_iter->self_msg_iter, stream_iter->stream->libObjPtr(),
+            late_msg.libObjPtr(), adjusted_message, stream_iter->last_inactivity_ts.value);
         break;
     default:
         bt_common_abort();
     }
 
     if (adjust_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-        stream_iter_status = adjust_status;
-        goto end;
+        return adjust_status;
     }
 
     BT_ASSERT_DBG(adjusted_message);
     stream_iter->current_msg = adjusted_message;
     stream_iter->current_msg_ts_ns = last_inactivity_ts_ns;
-    bt_message_put_ref(late_msg);
 
-end:
-    return stream_iter_status;
+    return LTTNG_LIVE_ITERATOR_STATUS_OK;
 }
 
 static enum lttng_live_iterator_status
@@ -1229,19 +982,15 @@ next_stream_iterator_for_trace(struct lttng_live_msg_iter *lttng_live_msg_iter,
                                struct lttng_live_stream_iterator **youngest_trace_stream_iter)
 {
     struct lttng_live_stream_iterator *youngest_candidate_stream_iter = NULL;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-    enum lttng_live_iterator_status stream_iter_status;
-    ;
     int64_t youngest_candidate_msg_ts = INT64_MAX;
     uint64_t stream_iter_idx;
 
     BT_ASSERT_DBG(live_trace);
-    BT_ASSERT_DBG(live_trace->stream_iterators);
 
-    BT_COMP_LOGD("Finding the next stream iterator for trace: "
-                 "trace-id=%" PRIu64,
-                 live_trace->id);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Finding the next stream iterator for trace: "
+                    "trace-id={}",
+                    live_trace->id);
     /*
      * Update the current message of every stream iterators of this trace.
      * The current msg of every stream must have a timestamp equal or
@@ -1249,22 +998,21 @@ next_stream_iterator_for_trace(struct lttng_live_msg_iter *lttng_live_msg_iter,
      * ensure monotonicity.
      */
     stream_iter_idx = 0;
-    while (stream_iter_idx < live_trace->stream_iterators->len) {
+    while (stream_iter_idx < live_trace->stream_iterators.size()) {
         bool stream_iter_is_ended = false;
-        struct lttng_live_stream_iterator *stream_iter =
-            (lttng_live_stream_iterator *) g_ptr_array_index(live_trace->stream_iterators,
-                                                             stream_iter_idx);
+        lttng_live_stream_iterator *stream_iter =
+            live_trace->stream_iterators[stream_iter_idx].get();
 
         /*
          * If there is no current message for this stream, go fetch
          * one.
          */
         while (!stream_iter->current_msg) {
-            const bt_message *msg = NULL;
+            bt2::ConstMessage::Shared msg;
             int64_t curr_msg_ts_ns = INT64_MAX;
 
-            stream_iter_status =
-                lttng_live_iterator_next_msg_on_stream(lttng_live_msg_iter, stream_iter, &msg);
+            const auto stream_iter_status =
+                lttng_live_iterator_next_msg_on_stream(lttng_live_msg_iter, stream_iter, msg);
 
             if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
                 stream_iter_is_ended = true;
@@ -1272,21 +1020,21 @@ next_stream_iterator_for_trace(struct lttng_live_msg_iter *lttng_live_msg_iter,
             }
 
             if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-                goto end;
+                return stream_iter_status;
             }
 
             BT_ASSERT_DBG(msg);
 
-            BT_COMP_LOGD("Live stream iterator returned message: msg-type=%s, "
-                         "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
-                         bt_common_message_type_string(bt_message_get_type(msg)),
-                         stream_iter->name->str, stream_iter->viewer_stream_id);
+            BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                            "Live stream iterator returned message: msg-type={}, "
+                            "stream-name=\"{}\", viewer-stream-id={}",
+                            msg->type(), stream_iter->name, stream_iter->viewer_stream_id);
 
             /*
              * Get the timestamp in nanoseconds from origin of this
-             * messsage.
+             * message.
              */
-            live_get_msg_ts_ns(stream_iter, lttng_live_msg_iter, msg,
+            live_get_msg_ts_ns(lttng_live_msg_iter, msg->libObjPtr(),
                                lttng_live_msg_iter->last_msg_ts_ns, &curr_msg_ts_ns);
 
             /*
@@ -1296,25 +1044,24 @@ next_stream_iterator_for_trace(struct lttng_live_msg_iter *lttng_live_msg_iter,
              * iterator. If not, we need to handle it with care.
              */
             if (curr_msg_ts_ns >= lttng_live_msg_iter->last_msg_ts_ns) {
-                stream_iter->current_msg = msg;
+                stream_iter->current_msg = std::move(msg);
                 stream_iter->current_msg_ts_ns = curr_msg_ts_ns;
             } else {
                 /*
                  * We received a message from the past. This
                  * may be fixable but it can also be an error.
                  */
-                stream_iter_status =
-                    handle_late_message(lttng_live_msg_iter, stream_iter, curr_msg_ts_ns, msg);
-                if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-                    BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                              "Late message could not be handled correctly: "
-                                              "lttng-live-msg-iter-addr=%p, "
-                                              "stream-name=\"%s\", "
-                                              "curr-msg-ts=%" PRId64 ", last-msg-ts=%" PRId64,
-                                              lttng_live_msg_iter, stream_iter->name->str,
-                                              curr_msg_ts_ns, lttng_live_msg_iter->last_msg_ts_ns);
-                    stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-                    goto end;
+                if (handle_late_message(lttng_live_msg_iter, stream_iter, curr_msg_ts_ns, *msg) !=
+                    LTTNG_LIVE_ITERATOR_STATUS_OK) {
+                    BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                                 "Late message could not be handled correctly: "
+                                                 "lttng-live-msg-iter-addr={}, "
+                                                 "stream-name=\"{}\", "
+                                                 "curr-msg-ts={}, last-msg-ts={}",
+                                                 fmt::ptr(lttng_live_msg_iter), stream_iter->name,
+                                                 curr_msg_ts_ns,
+                                                 lttng_live_msg_iter->last_msg_ts_ns);
+                    return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
                 }
             }
         }
@@ -1338,7 +1085,8 @@ next_stream_iterator_for_trace(struct lttng_live_msg_iter *lttng_live_msg_iter,
                  */
                 BT_ASSERT_DBG(stream_iter != youngest_candidate_stream_iter);
                 int ret = common_muxing_compare_messages(
-                    stream_iter->current_msg, youngest_candidate_stream_iter->current_msg);
+                    stream_iter->current_msg->libObjPtr(),
+                    youngest_candidate_stream_iter->current_msg->libObjPtr());
                 if (ret < 0) {
                     /*
                      * The `youngest_candidate_stream_iter->current_msg`
@@ -1352,11 +1100,12 @@ next_stream_iterator_for_trace(struct lttng_live_msg_iter *lttng_live_msg_iter,
                      * Unable to pick which one should go
                      * first.
                      */
-                    BT_COMP_LOGW(
+                    BT_CPPLOGW_SPEC(
+                        lttng_live_msg_iter->logger,
                         "Cannot deterministically pick next live stream message iterator because they have identical next messages: "
-                        "stream-iter-addr=%p"
-                        "stream-iter-addr=%p",
-                        stream_iter, youngest_candidate_stream_iter);
+                        "stream-iter-addr={}"
+                        "stream-iter-addr={}",
+                        fmt::ptr(stream_iter), fmt::ptr(youngest_candidate_stream_iter));
                 }
             }
 
@@ -1371,24 +1120,21 @@ next_stream_iterator_for_trace(struct lttng_live_msg_iter *lttng_live_msg_iter,
              * removed element with the array's last
              * element.
              */
-            g_ptr_array_remove_index_fast(live_trace->stream_iterators, stream_iter_idx);
+            bt2c::vectorFastRemove(live_trace->stream_iterators, stream_iter_idx);
         }
     }
 
     if (youngest_candidate_stream_iter) {
         *youngest_trace_stream_iter = youngest_candidate_stream_iter;
-        stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     } else {
         /*
          * The only case where we don't have a candidate for this trace
          * is if we reached the end of all the iterators.
          */
-        BT_ASSERT(live_trace->stream_iterators->len == 0);
-        stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END;
+        BT_ASSERT(live_trace->stream_iterators.empty());
+        return LTTNG_LIVE_ITERATOR_STATUS_END;
     }
-
-end:
-    return stream_iter_status;
 }
 
 static enum lttng_live_iterator_status
@@ -1396,16 +1142,15 @@ next_stream_iterator_for_session(struct lttng_live_msg_iter *lttng_live_msg_iter
                                  struct lttng_live_session *session,
                                  struct lttng_live_stream_iterator **youngest_session_stream_iter)
 {
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
     enum lttng_live_iterator_status stream_iter_status;
     uint64_t trace_idx = 0;
     int64_t youngest_candidate_msg_ts = INT64_MAX;
     struct lttng_live_stream_iterator *youngest_candidate_stream_iter = NULL;
 
-    BT_COMP_LOGD("Finding the next stream iterator for session: "
-                 "session-id=%" PRIu64,
-                 session->id);
+    BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                    "Finding the next stream iterator for session: "
+                    "session-id={}",
+                    session->id);
     /*
      * Make sure we are attached to the session and look for new streams
      * and metadata.
@@ -1414,16 +1159,13 @@ next_stream_iterator_for_session(struct lttng_live_msg_iter *lttng_live_msg_iter
     if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK &&
         stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_CONTINUE &&
         stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_END) {
-        goto end;
+        return stream_iter_status;
     }
 
-    BT_ASSERT_DBG(session->traces);
-
-    while (trace_idx < session->traces->len) {
+    while (trace_idx < session->traces.size()) {
         bool trace_is_ended = false;
         struct lttng_live_stream_iterator *stream_iter;
-        struct lttng_live_trace *trace =
-            (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+        lttng_live_trace *trace = session->traces[trace_idx].get();
 
         stream_iter_status =
             next_stream_iterator_for_trace(lttng_live_msg_iter, trace, &stream_iter);
@@ -1434,7 +1176,7 @@ next_stream_iterator_for_session(struct lttng_live_msg_iter *lttng_live_msg_iter
              */
             trace_is_ended = true;
         } else if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-            goto end;
+            return stream_iter_status;
         }
 
         if (!trace_is_ended) {
@@ -1450,7 +1192,8 @@ next_stream_iterator_for_session(struct lttng_live_msg_iter *lttng_live_msg_iter
                  * deterministic way.
                  */
                 int ret = common_muxing_compare_messages(
-                    stream_iter->current_msg, youngest_candidate_stream_iter->current_msg);
+                    stream_iter->current_msg->libObjPtr(),
+                    youngest_candidate_stream_iter->current_msg->libObjPtr());
                 if (ret < 0) {
                     /*
                      * The `youngest_candidate_stream_iter->current_msg`
@@ -1461,26 +1204,27 @@ next_stream_iterator_for_session(struct lttng_live_msg_iter *lttng_live_msg_iter
                     youngest_candidate_stream_iter = stream_iter;
                 } else if (ret == 0) {
                     /* Unable to pick which one should go first. */
-                    BT_COMP_LOGW(
+                    BT_CPPLOGW_SPEC(
+                        lttng_live_msg_iter->logger,
                         "Cannot deterministically pick next live stream message iterator because they have identical next messages: "
-                        "stream-iter-addr=%p"
-                        "stream-iter-addr=%p",
-                        stream_iter, youngest_candidate_stream_iter);
+                        "stream-iter-addr={}"
+                        "youngest-candidate-stream-iter-addr={}",
+                        fmt::ptr(stream_iter), fmt::ptr(youngest_candidate_stream_iter));
                 }
             }
             trace_idx++;
         } else {
             /*
              * trace_idx is not incremented since
-             * g_ptr_array_remove_index_fast replaces the
+             * vectorFastRemove replaces the
              * element at trace_idx with the array's last element.
              */
-            g_ptr_array_remove_index_fast(session->traces, trace_idx);
+            bt2c::vectorFastRemove(session->traces, trace_idx);
         }
     }
     if (youngest_candidate_stream_iter) {
         *youngest_session_stream_iter = youngest_candidate_stream_iter;
-        stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     } else {
         /*
          * The only cases where we don't have a candidate for this
@@ -1491,11 +1235,9 @@ next_stream_iterator_for_session(struct lttng_live_msg_iter *lttng_live_msg_iter
          *
          * In either cases, we return END.
          */
-        BT_ASSERT(session->traces->len == 0);
-        stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END;
+        BT_ASSERT(session->traces.empty());
+        return LTTNG_LIVE_ITERATOR_STATUS_END;
     }
-end:
-    return stream_iter_status;
 }
 
 static inline void put_messages(bt_message_array_const msgs, uint64_t count)
@@ -1507,384 +1249,366 @@ static inline void put_messages(bt_message_array_const msgs, uint64_t count)
     }
 }
 
-BT_HIDDEN
 bt_message_iterator_class_next_method_status
 lttng_live_msg_iter_next(bt_self_message_iterator *self_msg_it, bt_message_array_const msgs,
                          uint64_t capacity, uint64_t *count)
 {
-    bt_message_iterator_class_next_method_status status;
-    enum lttng_live_viewer_status viewer_status;
-    struct lttng_live_msg_iter *lttng_live_msg_iter =
-        (struct lttng_live_msg_iter *) bt_self_message_iterator_get_data(self_msg_it);
-    struct lttng_live_component *lttng_live = lttng_live_msg_iter->lttng_live_comp;
-    bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-    bt_logging_level log_level = lttng_live_msg_iter->log_level;
-    enum lttng_live_iterator_status stream_iter_status;
-    uint64_t session_idx;
+    try {
+        bt_message_iterator_class_next_method_status status;
+        enum lttng_live_viewer_status viewer_status;
+        struct lttng_live_msg_iter *lttng_live_msg_iter =
+            (struct lttng_live_msg_iter *) bt_self_message_iterator_get_data(self_msg_it);
+        struct lttng_live_component *lttng_live = lttng_live_msg_iter->lttng_live_comp;
+        enum lttng_live_iterator_status stream_iter_status;
 
-    *count = 0;
+        *count = 0;
 
-    BT_ASSERT_DBG(lttng_live_msg_iter);
+        BT_ASSERT_DBG(lttng_live_msg_iter);
+
+        if (G_UNLIKELY(lttng_live_msg_iter->was_interrupted)) {
+            /*
+             * The iterator was interrupted in a previous call to the
+             * `_next()` method. We currently do not support generating
+             * messages after such event. The babeltrace2 CLI should never
+             * be running the graph after being interrupted. So this check
+             * is to prevent other graph users from using this live
+             * iterator in an messed up internal state.
+             */
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                lttng_live_msg_iter->logger,
+                "Message iterator was interrupted during a previous call to the `next()` and currently does not support continuing after such event.");
+            return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+        }
 
-    if (G_UNLIKELY(lttng_live_msg_iter->was_interrupted)) {
         /*
-         * The iterator was interrupted in a previous call to the
-         * `_next()` method. We currently do not support generating
-         * messages after such event. The babeltrace2 CLI should never
-         * be running the graph after being interrupted. So this check
-         * is to prevent other graph users from using this live
-         * iterator in an messed up internal state.
+         * Clear all the invalid message reference that might be left over in
+         * the output array.
          */
-        status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-        BT_COMP_LOGE_APPEND_CAUSE(
-            self_comp,
-            "Message iterator was interrupted during a previous call to the `next()` and currently does not support continuing after such event.");
-        goto end;
-    }
+        memset(msgs, 0, capacity * sizeof(*msgs));
 
-    /*
-     * Clear all the invalid message reference that might be left over in
-     * the output array.
-     */
-    memset(msgs, 0, capacity * sizeof(*msgs));
-
-    /*
-     * If no session are exposed on the relay found at the url provided by
-     * the user, session count will be 0. In this case, we return status
-     * end to return gracefully.
-     */
-    if (lttng_live_msg_iter->sessions->len == 0) {
-        if (lttng_live->params.sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE) {
-            status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
-            goto end;
-        } else {
-            /*
-             * The are no more active session for this session
-             * name. Retry to create a viewer session for the
-             * requested session name.
-             */
-            viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
-            if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-                if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-                    status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-                    BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                              "Error creating LTTng live viewer session");
-                } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-                    status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
-                } else {
-                    bt_common_abort();
+        /*
+         * If no session are exposed on the relay found at the url provided by
+         * the user, session count will be 0. In this case, we return status
+         * end to return gracefully.
+         */
+        if (lttng_live_msg_iter->sessions.empty()) {
+            if (lttng_live->params.sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE) {
+                return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
+            } else {
+                /*
+                 * The are no more active session for this session
+                 * name. Retry to create a viewer session for the
+                 * requested session name.
+                 */
+                viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
+                if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+                    if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+                        BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                                     "Error creating LTTng live viewer session");
+                        return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+                    } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+                        return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
+                    } else {
+                        bt_common_abort();
+                    }
                 }
-                goto end;
             }
         }
-    }
 
-    if (lttng_live_msg_iter->active_stream_iter == 0) {
-        lttng_live_force_new_streams_and_metadata(lttng_live_msg_iter);
-    }
+        if (lttng_live_msg_iter->active_stream_iter == 0) {
+            lttng_live_force_new_streams_and_metadata(lttng_live_msg_iter);
+        }
 
-    /*
-     * Here the muxing of message is done.
-     *
-     * We need to iterate over all the streams of all the traces of all the
-     * viewer sessions in order to get the message with the smallest
-     * timestamp. In this case, a session is a viewer session and there is
-     * one viewer session per consumer daemon. (UST 32bit, UST 64bit and/or
-     * kernel). Each viewer session can have multiple traces, for example,
-     * 64bit UST viewer sessions could have multiple per-pid traces.
-     *
-     * We iterate over the streams of each traces to update and see what is
-     * their next message's timestamp. From those timestamps, we select the
-     * message with the smallest timestamp as the best candidate message
-     * for that trace and do the same thing across all the sessions.
-     *
-     * We then compare the timestamp of best candidate message of all the
-     * sessions to pick the message with the smallest timestamp and we
-     * return it.
-     */
-    while (*count < capacity) {
-        struct lttng_live_stream_iterator *youngest_stream_iter = NULL,
-                                          *candidate_stream_iter = NULL;
-        int64_t youngest_msg_ts_ns = INT64_MAX;
-
-        BT_ASSERT_DBG(lttng_live_msg_iter->sessions);
-        session_idx = 0;
-        while (session_idx < lttng_live_msg_iter->sessions->len) {
-            struct lttng_live_session *session = (lttng_live_session *) g_ptr_array_index(
-                lttng_live_msg_iter->sessions, session_idx);
-
-            /* Find the best candidate message to send downstream. */
-            stream_iter_status = next_stream_iterator_for_session(lttng_live_msg_iter, session,
-                                                                  &candidate_stream_iter);
-
-            /* If we receive an END status, it means that either:
-             * - Those traces never had active streams (UST with no
-             *   data produced yet),
-             * - All live stream iterators have ENDed.*/
-            if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
-                if (session->closed && session->traces->len == 0) {
-                    /*
-                     * Remove the session from the list.
-                     * session_idx is not modified since
-                     * g_ptr_array_remove_index_fast
-                     * replaces the the removed element with
-                     * the array's last element.
-                     */
-                    g_ptr_array_remove_index_fast(lttng_live_msg_iter->sessions, session_idx);
-                } else {
-                    session_idx++;
+        /*
+         * Here the muxing of message is done.
+         *
+         * We need to iterate over all the streams of all the traces of all the
+         * viewer sessions in order to get the message with the smallest
+         * timestamp. In this case, a session is a viewer session and there is
+         * one viewer session per consumer daemon. (UST 32bit, UST 64bit and/or
+         * kernel). Each viewer session can have multiple traces, for example,
+         * 64bit UST viewer sessions could have multiple per-pid traces.
+         *
+         * We iterate over the streams of each traces to update and see what is
+         * their next message's timestamp. From those timestamps, we select the
+         * message with the smallest timestamp as the best candidate message
+         * for that trace and do the same thing across all the sessions.
+         *
+         * We then compare the timestamp of best candidate message of all the
+         * sessions to pick the message with the smallest timestamp and we
+         * return it.
+         */
+        while (*count < capacity) {
+            struct lttng_live_stream_iterator *youngest_stream_iter = NULL,
+                                              *candidate_stream_iter = NULL;
+            int64_t youngest_msg_ts_ns = INT64_MAX;
+
+            uint64_t session_idx = 0;
+            while (session_idx < lttng_live_msg_iter->sessions.size()) {
+                lttng_live_session *session = lttng_live_msg_iter->sessions[session_idx].get();
+
+                /* Find the best candidate message to send downstream. */
+                stream_iter_status = next_stream_iterator_for_session(lttng_live_msg_iter, session,
+                                                                      &candidate_stream_iter);
+
+                /* If we receive an END status, it means that either:
+                 * - Those traces never had active streams (UST with no
+                 *   data produced yet),
+                 * - All live stream iterators have ENDed.
+                 */
+                if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
+                    if (session->closed && session->traces.empty()) {
+                        /*
+                         * Remove the session from the list.
+                         * session_idx is not modified since
+                         * g_ptr_array_remove_index_fast
+                         * replaces the the removed element with
+                         * the array's last element.
+                         */
+                        bt2c::vectorFastRemove(lttng_live_msg_iter->sessions, session_idx);
+                    } else {
+                        session_idx++;
+                    }
+                    continue;
                 }
-                continue;
-            }
 
-            if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-                goto return_status;
-            }
+                if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+                    goto return_status;
+                }
 
-            if (G_UNLIKELY(youngest_stream_iter == NULL) ||
-                candidate_stream_iter->current_msg_ts_ns < youngest_msg_ts_ns) {
-                youngest_msg_ts_ns = candidate_stream_iter->current_msg_ts_ns;
-                youngest_stream_iter = candidate_stream_iter;
-            } else if (candidate_stream_iter->current_msg_ts_ns == youngest_msg_ts_ns) {
-                /*
-                 * The currently selected message to be sent
-                 * downstream next has the exact same timestamp
-                 * that of the current candidate message. We
-                 * must break the tie in a predictable manner.
-                 */
-                BT_COMP_LOGD_STR(
-                    "Two of the next message candidates have the same timestamps, pick one deterministically.");
-                /*
-                 * Order the messages in an arbitrary but
-                 * deterministic way.
-                 */
-                int ret = common_muxing_compare_messages(candidate_stream_iter->current_msg,
-                                                         youngest_stream_iter->current_msg);
-                if (ret < 0) {
-                    /*
-                     * The `candidate_stream_iter->current_msg`
-                     * should go first. Update the next
-                     * iterator and the current timestamp.
-                     */
+                if (G_UNLIKELY(youngest_stream_iter == NULL) ||
+                    candidate_stream_iter->current_msg_ts_ns < youngest_msg_ts_ns) {
                     youngest_msg_ts_ns = candidate_stream_iter->current_msg_ts_ns;
                     youngest_stream_iter = candidate_stream_iter;
-                } else if (ret == 0) {
-                    /* Unable to pick which one should go first. */
-                    BT_COMP_LOGW(
-                        "Cannot deterministically pick next live stream message iterator because they have identical next messages: "
-                        "next-stream-iter-addr=%p"
-                        "candidate-stream-iter-addr=%p",
-                        youngest_stream_iter, candidate_stream_iter);
+                } else if (candidate_stream_iter->current_msg_ts_ns == youngest_msg_ts_ns) {
+                    /*
+                     * The currently selected message to be sent
+                     * downstream next has the exact same timestamp
+                     * that of the current candidate message. We
+                     * must break the tie in a predictable manner.
+                     */
+                    BT_CPPLOGD_STR_SPEC(
+                        lttng_live_msg_iter->logger,
+                        "Two of the next message candidates have the same timestamps, pick one deterministically.");
+                    /*
+                     * Order the messages in an arbitrary but
+                     * deterministic way.
+                     */
+                    int ret = common_muxing_compare_messages(
+                        candidate_stream_iter->current_msg->libObjPtr(),
+                        youngest_stream_iter->current_msg->libObjPtr());
+                    if (ret < 0) {
+                        /*
+                         * The `candidate_stream_iter->current_msg`
+                         * should go first. Update the next
+                         * iterator and the current timestamp.
+                         */
+                        youngest_msg_ts_ns = candidate_stream_iter->current_msg_ts_ns;
+                        youngest_stream_iter = candidate_stream_iter;
+                    } else if (ret == 0) {
+                        /* Unable to pick which one should go first. */
+                        BT_CPPLOGW_SPEC(
+                            lttng_live_msg_iter->logger,
+                            "Cannot deterministically pick next live stream message iterator because they have identical next messages: "
+                            "next-stream-iter-addr={}"
+                            "candidate-stream-iter-addr={}",
+                            fmt::ptr(youngest_stream_iter), fmt::ptr(candidate_stream_iter));
+                    }
                 }
-            }
 
-            session_idx++;
-        }
+                session_idx++;
+            }
 
-        if (!youngest_stream_iter) {
-            stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-            goto return_status;
-        }
+            if (!youngest_stream_iter) {
+                stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+                goto return_status;
+            }
 
-        BT_ASSERT_DBG(youngest_stream_iter->current_msg);
-        /* Ensure monotonicity. */
-        BT_ASSERT_DBG(lttng_live_msg_iter->last_msg_ts_ns <=
-                      youngest_stream_iter->current_msg_ts_ns);
+            BT_ASSERT_DBG(youngest_stream_iter->current_msg);
+            /* Ensure monotonicity. */
+            BT_ASSERT_DBG(lttng_live_msg_iter->last_msg_ts_ns <=
+                          youngest_stream_iter->current_msg_ts_ns);
 
-        /*
-         * Insert the next message to the message batch. This will set
-         * stream iterator current messsage to NULL so that next time
-         * we fetch the next message of that stream iterator
-         */
-        BT_MESSAGE_MOVE_REF(msgs[*count], youngest_stream_iter->current_msg);
-        (*count)++;
+            /*
+             * Insert the next message to the message batch. This will set
+             * stream iterator current message to NULL so that next time
+             * we fetch the next message of that stream iterator
+             */
+            msgs[*count] = youngest_stream_iter->current_msg.release().libObjPtr();
+            (*count)++;
 
-        /* Update the last timestamp in nanoseconds sent downstream. */
-        lttng_live_msg_iter->last_msg_ts_ns = youngest_msg_ts_ns;
-        youngest_stream_iter->current_msg_ts_ns = INT64_MAX;
+            /* Update the last timestamp in nanoseconds sent downstream. */
+            lttng_live_msg_iter->last_msg_ts_ns = youngest_msg_ts_ns;
+            youngest_stream_iter->current_msg_ts_ns = INT64_MAX;
 
-        stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-    }
+            stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+        }
 
 return_status:
-    switch (stream_iter_status) {
-    case LTTNG_LIVE_ITERATOR_STATUS_OK:
-    case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
-        /*
-         * If we gathered messages, return _OK even if the graph was
-         * interrupted. This allows for the components downstream to at
-         * least get the thoses messages. If the graph was indeed
-         * interrupted there should not be another _next() call as the
-         * application will tear down the graph. This component class
-         * doesn't support restarting after an interruption.
-         */
-        if (*count > 0) {
-            status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-        } else {
-            status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
+        switch (stream_iter_status) {
+        case LTTNG_LIVE_ITERATOR_STATUS_OK:
+        case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
+            /*
+             * If we gathered messages, return _OK even if the graph was
+             * interrupted. This allows for the components downstream to at
+             * least get the those messages. If the graph was indeed
+             * interrupted there should not be another _next() call as the
+             * application will tear down the graph. This component class
+             * doesn't support restarting after an interruption.
+             */
+            if (*count > 0) {
+                status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+            } else {
+                status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
+            }
+            break;
+        case LTTNG_LIVE_ITERATOR_STATUS_END:
+            status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
+            break;
+        case LTTNG_LIVE_ITERATOR_STATUS_NOMEM:
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                         "Memory error preparing the next batch of messages: "
+                                         "live-iter-status={}",
+                                         stream_iter_status);
+            status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
+            break;
+        case LTTNG_LIVE_ITERATOR_STATUS_ERROR:
+        case LTTNG_LIVE_ITERATOR_STATUS_INVAL:
+        case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED:
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                         "Error preparing the next batch of messages: "
+                                         "live-iter-status={}",
+                                         stream_iter_status);
+
+            status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+            /* Put all existing messages on error. */
+            put_messages(msgs, *count);
+            break;
+        default:
+            bt_common_abort();
         }
-        break;
-    case LTTNG_LIVE_ITERATOR_STATUS_END:
-        status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
-        break;
-    case LTTNG_LIVE_ITERATOR_STATUS_NOMEM:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Memory error preparing the next batch of messages: "
-                                  "live-iter-status=%s",
-                                  lttng_live_iterator_status_string(stream_iter_status));
-        status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
-        break;
-    case LTTNG_LIVE_ITERATOR_STATUS_ERROR:
-    case LTTNG_LIVE_ITERATOR_STATUS_INVAL:
-    case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Error preparing the next batch of messages: "
-                                  "live-iter-status=%s",
-                                  lttng_live_iterator_status_string(stream_iter_status));
-
-        status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-        /* Put all existing messages on error. */
-        put_messages(msgs, *count);
-        break;
-    default:
-        bt_common_abort();
-    }
 
-end:
-    return status;
+        return status;
+    } catch (const std::bad_alloc&) {
+        return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+    }
 }
 
-static struct lttng_live_msg_iter *
+static lttng_live_msg_iter::UP
 lttng_live_msg_iter_create(struct lttng_live_component *lttng_live_comp,
                            bt_self_message_iterator *self_msg_it)
 {
-    bt_self_component *self_comp = lttng_live_comp->self_comp;
-    bt_logging_level log_level = lttng_live_comp->log_level;
-
-    struct lttng_live_msg_iter *lttng_live_msg_iter = g_new0(struct lttng_live_msg_iter, 1);
-    if (!lttng_live_msg_iter) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate lttng_live_msg_iter");
-        goto end;
-    }
-
-    lttng_live_msg_iter->log_level = lttng_live_comp->log_level;
-    lttng_live_msg_iter->self_comp = lttng_live_comp->self_comp;
-    lttng_live_msg_iter->lttng_live_comp = lttng_live_comp;
-    lttng_live_msg_iter->self_msg_iter = self_msg_it;
+    auto msg_iter = bt2s::make_unique<struct lttng_live_msg_iter>(lttng_live_comp->logger);
 
-    lttng_live_msg_iter->active_stream_iter = 0;
-    lttng_live_msg_iter->last_msg_ts_ns = INT64_MIN;
-    lttng_live_msg_iter->was_interrupted = false;
+    msg_iter->self_comp = lttng_live_comp->self_comp;
+    msg_iter->lttng_live_comp = lttng_live_comp;
+    msg_iter->self_msg_iter = self_msg_it;
+    msg_iter->active_stream_iter = 0;
+    msg_iter->last_msg_ts_ns = INT64_MIN;
+    msg_iter->was_interrupted = false;
 
-    lttng_live_msg_iter->sessions =
-        g_ptr_array_new_with_free_func((GDestroyNotify) lttng_live_destroy_session);
-    BT_ASSERT(lttng_live_msg_iter->sessions);
-
-end:
-    return lttng_live_msg_iter;
+    return msg_iter;
 }
 
-BT_HIDDEN
 bt_message_iterator_class_initialize_method_status
 lttng_live_msg_iter_init(bt_self_message_iterator *self_msg_it,
-                         bt_self_message_iterator_configuration *config,
-                         bt_self_component_port_output *self_port)
+                         bt_self_message_iterator_configuration *, bt_self_component_port_output *)
 {
-    bt_message_iterator_class_initialize_method_status status;
-    struct lttng_live_component *lttng_live;
-    struct lttng_live_msg_iter *lttng_live_msg_iter;
-    enum lttng_live_viewer_status viewer_status;
-    bt_logging_level log_level;
-    bt_self_component *self_comp = bt_self_message_iterator_borrow_component(self_msg_it);
-
-    lttng_live = (lttng_live_component *) bt_self_component_get_data(self_comp);
-    log_level = lttng_live->log_level;
-    self_comp = lttng_live->self_comp;
-
-    /* There can be only one downstream iterator at the same time. */
-    BT_ASSERT(!lttng_live->has_msg_iter);
-    lttng_live->has_msg_iter = true;
-
-    lttng_live_msg_iter = lttng_live_msg_iter_create(lttng_live, self_msg_it);
-    if (!lttng_live_msg_iter) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create lttng_live_msg_iter");
-        status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
-    }
-
-    viewer_status = live_viewer_connection_create(
-        self_comp, NULL, log_level, lttng_live->params.url->str, false, lttng_live_msg_iter,
-        &lttng_live_msg_iter->viewer_connection);
-    if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create viewer connection");
-        } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-            /*
-             * Interruption in the _iter_init() method is not
-             * supported. Return an error.
-             */
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Interrupted while creating viewer connection");
+    try {
+        struct lttng_live_component *lttng_live;
+        enum lttng_live_viewer_status viewer_status;
+        bt_self_component *self_comp = bt_self_message_iterator_borrow_component(self_msg_it);
+
+        lttng_live = (lttng_live_component *) bt_self_component_get_data(self_comp);
+
+        /* There can be only one downstream iterator at the same time. */
+        BT_ASSERT(!lttng_live->has_msg_iter);
+        lttng_live->has_msg_iter = true;
+
+        auto lttng_live_msg_iter = lttng_live_msg_iter_create(lttng_live, self_msg_it);
+        if (!lttng_live_msg_iter) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live->logger,
+                                         "Failed to create lttng_live_msg_iter");
+            return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
         }
 
-        status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-        goto error;
-    }
+        viewer_status = live_viewer_connection_create(
+            lttng_live->params.url.c_str(), false, lttng_live_msg_iter.get(),
+            lttng_live_msg_iter->logger, lttng_live_msg_iter->viewer_connection);
+        if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+            if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                             "Failed to create viewer connection");
+            } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+                /*
+                 * Interruption in the _iter_init() method is not
+                 * supported. Return an error.
+                 */
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                             "Interrupted while creating viewer connection");
+            }
 
-    viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
-    if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create viewer session");
-        } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-            /*
-             * Interruption in the _iter_init() method is not
-             * supported. Return an error.
-             */
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Interrupted when creating viewer session");
+            return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
         }
 
-        status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-        goto error;
-    }
+        viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter.get());
+        if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+            if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                             "Failed to create viewer session");
+            } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+                /*
+                 * Interruption in the _iter_init() method is not
+                 * supported. Return an error.
+                 */
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(lttng_live_msg_iter->logger,
+                                             "Interrupted when creating viewer session");
+            }
 
-    if (lttng_live_msg_iter->sessions->len == 0) {
-        switch (lttng_live->params.sess_not_found_act) {
-        case SESSION_NOT_FOUND_ACTION_CONTINUE:
-            BT_COMP_LOGI(
-                "Unable to connect to the requested live viewer session. Keep trying to connect because of "
-                "%s=\"%s\" component parameter: url=\"%s\"",
-                SESS_NOT_FOUND_ACTION_PARAM, SESS_NOT_FOUND_ACTION_CONTINUE_STR,
-                lttng_live->params.url->str);
-            break;
-        case SESSION_NOT_FOUND_ACTION_FAIL:
-            BT_COMP_LOGE_APPEND_CAUSE(
-                self_comp,
-                "Unable to connect to the requested live viewer session. Fail the message iterator initialization because of %s=\"%s\" "
-                "component parameter: url =\"%s\"",
-                SESS_NOT_FOUND_ACTION_PARAM, SESS_NOT_FOUND_ACTION_FAIL_STR,
-                lttng_live->params.url->str);
-            status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-            goto error;
-        case SESSION_NOT_FOUND_ACTION_END:
-            BT_COMP_LOGI(
-                "Unable to connect to the requested live viewer session. End gracefully at the first _next() "
-                "call because of %s=\"%s\" component parameter: "
-                "url=\"%s\"",
-                SESS_NOT_FOUND_ACTION_PARAM, SESS_NOT_FOUND_ACTION_END_STR,
-                lttng_live->params.url->str);
-            break;
-        default:
-            bt_common_abort();
+            return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
         }
-    }
 
-    bt_self_message_iterator_set_data(self_msg_it, lttng_live_msg_iter);
-    status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
-    goto end;
+        if (lttng_live_msg_iter->sessions.empty()) {
+            switch (lttng_live->params.sess_not_found_act) {
+            case SESSION_NOT_FOUND_ACTION_CONTINUE:
+                BT_CPPLOGI_SPEC(
+                    lttng_live_msg_iter->logger,
+                    "Unable to connect to the requested live viewer session. "
+                    "Keep trying to connect because of {}=\"{}\" component parameter: url=\"{}\"",
+                    SESS_NOT_FOUND_ACTION_PARAM, SESS_NOT_FOUND_ACTION_CONTINUE_STR,
+                    lttng_live->params.url);
+                break;
+            case SESSION_NOT_FOUND_ACTION_FAIL:
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(
+                    lttng_live_msg_iter->logger,
+                    "Unable to connect to the requested live viewer session. "
+                    "Fail the message iterator initialization because of {}=\"{}\" "
+                    "component parameter: url =\"{}\"",
+                    SESS_NOT_FOUND_ACTION_PARAM, SESS_NOT_FOUND_ACTION_FAIL_STR,
+                    lttng_live->params.url);
+                return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+            case SESSION_NOT_FOUND_ACTION_END:
+                BT_CPPLOGI_SPEC(lttng_live_msg_iter->logger,
+                                "Unable to connect to the requested live viewer session. "
+                                "End gracefully at the first _next() call because of {}=\"{}\""
+                                " component parameter: url=\"{}\"",
+                                SESS_NOT_FOUND_ACTION_PARAM, SESS_NOT_FOUND_ACTION_END_STR,
+                                lttng_live->params.url);
+                break;
+            default:
+                bt_common_abort();
+            }
+        }
 
-error:
-    lttng_live_msg_iter_destroy(lttng_live_msg_iter);
-end:
-    return status;
+        bt_self_message_iterator_set_data(self_msg_it, lttng_live_msg_iter.release());
+        return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
+    } catch (const std::bad_alloc&) {
+        return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+    }
 }
 
 static struct bt_param_validation_map_value_entry_descr list_sessions_params[] = {
@@ -1892,191 +1616,128 @@ static struct bt_param_validation_map_value_entry_descr list_sessions_params[] =
      bt_param_validation_value_descr::makeString()},
     BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END};
 
-static bt_component_class_query_method_status
-lttng_live_query_list_sessions(const bt_value *params, const bt_value **result,
-                               bt_self_component_class *self_comp_class, bt_logging_level log_level)
+static bt2::Value::Shared lttng_live_query_list_sessions(const bt2::ConstMapValue params,
+                                                         const bt2c::Logger& logger)
 {
-    bt_component_class_query_method_status status;
-    const bt_value *url_value = NULL;
     const char *url;
-    struct live_viewer_connection *viewer_connection = NULL;
+    live_viewer_connection::UP viewer_connection;
     enum lttng_live_viewer_status viewer_status;
     enum bt_param_validation_status validation_status;
     gchar *validate_error = NULL;
 
-    validation_status = bt_param_validation_validate(params, list_sessions_params, &validate_error);
+    validation_status =
+        bt_param_validation_validate(params.libObjPtr(), list_sessions_params, &validate_error);
     if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
+        throw bt2c::MemoryError {};
     } else if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "%s", validate_error);
-        goto error;
+        bt2c::GCharUP errorFreer {validate_error};
+
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error, "{}", validate_error);
     }
 
-    url_value = bt_value_map_borrow_entry_value_const(params, URL_PARAM);
-    url = bt_value_string_get(url_value);
+    url = params[URL_PARAM]->asString().value();
 
-    viewer_status = live_viewer_connection_create(NULL, self_comp_class, log_level, url, true, NULL,
-                                                  &viewer_connection);
+    viewer_status = live_viewer_connection_create(url, true, NULL, logger, viewer_connection);
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
         if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-            BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Failed to create viewer connection");
-            status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error,
+                                                   "Failed to create viewer connection");
         } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-            status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
+            throw bt2c::TryAgain {};
         } else {
             bt_common_abort();
         }
-        goto error;
     }
 
-    status = live_viewer_connection_list_sessions(viewer_connection, result);
-    if (status != BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Failed to list viewer sessions");
-        goto error;
-    }
-
-    goto end;
-
-error:
-    BT_VALUE_PUT_REF_AND_RESET(*result);
-
-    if (status >= 0) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-    }
-
-end:
-    if (viewer_connection) {
-        live_viewer_connection_destroy(viewer_connection);
-    }
-
-    g_free(validate_error);
-
-    return status;
+    return live_viewer_connection_list_sessions(viewer_connection.get());
 }
 
-static bt_component_class_query_method_status
-lttng_live_query_support_info(const bt_value *params, const bt_value **result,
-                              bt_self_component_class *self_comp_class, bt_logging_level log_level)
+static bt2::Value::Shared lttng_live_query_support_info(const bt2::ConstMapValue params,
+                                                        const bt2c::Logger& logger)
 {
-    bt_component_class_query_method_status status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-    const bt_value *input_type_value;
-    const bt_value *input_value;
-    double weight = 0;
-    struct bt_common_lttng_live_url_parts parts = {0};
+    struct bt_common_lttng_live_url_parts parts = {};
+    bt_common_lttng_live_url_parts_deleter partsDeleter {parts};
 
     /* Used by the logging macros */
     __attribute__((unused)) bt_self_component *self_comp = NULL;
 
-    *result = NULL;
-    input_type_value = bt_value_map_borrow_entry_value_const(params, "type");
-    if (!input_type_value) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Missing expected `type` parameter.");
-        goto error;
+    const auto typeValue = params["type"];
+    if (!typeValue) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error,
+                                               "Missing expected `type` parameter.");
     }
 
-    if (!bt_value_is_string(input_type_value)) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "`type` parameter is not a string value.");
-        goto error;
+    if (!typeValue->isString()) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error,
+                                               "`type` parameter is not a string value.");
     }
 
-    if (strcmp(bt_value_string_get(input_type_value), "string") != 0) {
+    if (strcmp(typeValue->asString().value(), "string") != 0) {
         /* We don't handle file system paths */
-        goto create_result;
+        return bt2::RealValue::create();
     }
 
-    input_value = bt_value_map_borrow_entry_value_const(params, "input");
-    if (!input_value) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Missing expected `input` parameter.");
-        goto error;
+    const auto inputValue = params["input"];
+    if (!inputValue) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error,
+                                               "Missing expected `input` parameter.");
     }
 
-    if (!bt_value_is_string(input_value)) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                                        "`input` parameter is not a string value.");
-        goto error;
+    if (!inputValue->isString()) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2::Error,
+                                               "`input` parameter is not a string value.");
     }
 
-    parts = bt_common_parse_lttng_live_url(bt_value_string_get(input_value), NULL, 0);
+    parts = bt_common_parse_lttng_live_url(inputValue->asString().value(), NULL, 0);
     if (parts.session_name) {
         /*
          * Looks pretty much like an LTTng live URL: we got the
          * session name part, which forms a complete URL.
          */
-        weight = .75;
-    }
-
-create_result:
-    *result = bt_value_real_create_init(weight);
-    if (!*result) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
+        return bt2::RealValue::create(.75);
     }
 
-    goto end;
-
-error:
-    if (status >= 0) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-    }
-
-    BT_ASSERT(!*result);
-
-end:
-    bt_common_destroy_lttng_live_url_parts(&parts);
-    return status;
+    return bt2::RealValue::create();
 }
 
-BT_HIDDEN
 bt_component_class_query_method_status lttng_live_query(bt_self_component_class_source *comp_class,
                                                         bt_private_query_executor *priv_query_exec,
                                                         const char *object, const bt_value *params,
                                                         __attribute__((unused)) void *method_data,
                                                         const bt_value **result)
 {
-    bt_component_class_query_method_status status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-    bt_self_component *self_comp = NULL;
-    bt_self_component_class *self_comp_class =
-        bt_self_component_class_source_as_self_component_class(comp_class);
-    bt_logging_level log_level = bt_query_executor_get_logging_level(
-        bt_private_query_executor_as_query_executor_const(priv_query_exec));
-
-    if (strcmp(object, "sessions") == 0) {
-        status = lttng_live_query_list_sessions(params, result, self_comp_class, log_level);
-    } else if (strcmp(object, "babeltrace.support-info") == 0) {
-        status = lttng_live_query_support_info(params, result, self_comp_class, log_level);
-    } else {
-        BT_COMP_LOGI("Unknown query object `%s`", object);
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
-        goto end;
-    }
+    try {
+        bt2c::Logger logger {bt2::SelfComponentClass {comp_class},
+                             bt2::PrivateQueryExecutor {priv_query_exec},
+                             "PLUGIN/SRC.CTF.LTTNG-LIVE/QUERY"};
+        const bt2::ConstMapValue paramsObj(params);
+        bt2::Value::Shared resultObj;
+
+        if (strcmp(object, "sessions") == 0) {
+            resultObj = lttng_live_query_list_sessions(paramsObj, logger);
+        } else if (strcmp(object, "babeltrace.support-info") == 0) {
+            resultObj = lttng_live_query_support_info(paramsObj, logger);
+        } else {
+            BT_CPPLOGI_SPEC(logger, "Unknown query object `{}`", object);
+            return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
+        }
 
-end:
-    return status;
-}
+        *result = resultObj.release().libObjPtr();
 
-static void lttng_live_component_destroy_data(struct lttng_live_component *lttng_live)
-{
-    if (!lttng_live) {
-        return;
-    }
-    if (lttng_live->params.url) {
-        g_string_free(lttng_live->params.url, TRUE);
+        return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+    } catch (const bt2c::TryAgain&) {
+        return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const std::bad_alloc&) {
+        return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
     }
-    g_free(lttng_live);
 }
 
-BT_HIDDEN
 void lttng_live_component_finalize(bt_self_component_source *component)
 {
-    lttng_live_component *data = (lttng_live_component *) bt_self_component_get_data(
-        bt_self_component_source_as_self_component(component));
-
-    if (!data) {
-        return;
-    }
-    lttng_live_component_destroy_data(data);
+    lttng_live_component::UP {static_cast<lttng_live_component *>(
+        bt_self_component_get_data(bt_self_component_source_as_self_component(component)))};
 }
 
 static enum session_not_found_action
@@ -2114,101 +1775,76 @@ static struct bt_param_validation_map_value_entry_descr params_descr[] = {
     BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END};
 
 static bt_component_class_initialize_method_status
-lttng_live_component_create(const bt_value *params, bt_logging_level log_level,
-                            bt_self_component *self_comp, struct lttng_live_component **component)
+lttng_live_component_create(const bt_value *params, bt_self_component_source *self_comp,
+                            lttng_live_component::UP& component)
 {
-    struct lttng_live_component *lttng_live = NULL;
     const bt_value *inputs_value;
     const bt_value *url_value;
     const bt_value *value;
-    const char *url;
     enum bt_param_validation_status validation_status;
     gchar *validation_error = NULL;
-    bt_component_class_initialize_method_status status;
+    bt2c::Logger logger {bt2::SelfSourceComponent {self_comp}, "PLUGIN/SRC.CTF.LTTNG-LIVE/COMP"};
 
     validation_status = bt_param_validation_validate(params, params_descr, &validation_error);
     if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
-        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
     } else if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "%s", validation_error);
-        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-        goto error;
+        bt2c::GCharUP errorFreer {validation_error};
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "{}", validation_error);
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
     }
 
-    lttng_live = g_new0(struct lttng_live_component, 1);
-    if (!lttng_live) {
-        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-        goto end;
-    }
-    lttng_live->log_level = log_level;
-    lttng_live->self_comp = self_comp;
+    auto lttng_live = bt2s::make_unique<lttng_live_component>(std::move(logger));
+    lttng_live->self_comp = bt_self_component_source_as_self_component(self_comp);
     lttng_live->max_query_size = MAX_QUERY_SIZE;
     lttng_live->has_msg_iter = false;
 
     inputs_value = bt_value_map_borrow_entry_value_const(params, INPUTS_PARAM);
     url_value = bt_value_array_borrow_element_by_index_const(inputs_value, 0);
-    url = bt_value_string_get(url_value);
-
-    lttng_live->params.url = g_string_new(url);
-    if (!lttng_live->params.url) {
-        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
-    }
+    lttng_live->params.url = bt_value_string_get(url_value);
 
     value = bt_value_map_borrow_entry_value_const(params, SESS_NOT_FOUND_ACTION_PARAM);
     if (value) {
         lttng_live->params.sess_not_found_act = parse_session_not_found_action_param(value);
     } else {
-        BT_COMP_LOGI("Optional `%s` parameter is missing: "
-                     "defaulting to `%s`.",
-                     SESS_NOT_FOUND_ACTION_PARAM, SESS_NOT_FOUND_ACTION_CONTINUE_STR);
+        BT_CPPLOGI_SPEC(lttng_live->logger,
+                        "Optional `{}` parameter is missing: defaulting to `{}`.",
+                        SESS_NOT_FOUND_ACTION_PARAM, SESS_NOT_FOUND_ACTION_CONTINUE_STR);
         lttng_live->params.sess_not_found_act = SESSION_NOT_FOUND_ACTION_CONTINUE;
     }
 
-    status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-    goto end;
-
-error:
-    lttng_live_component_destroy_data(lttng_live);
-    lttng_live = NULL;
-end:
-    g_free(validation_error);
-
-    *component = lttng_live;
-    return status;
+    component = std::move(lttng_live);
+    return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
 }
 
-BT_HIDDEN
 bt_component_class_initialize_method_status
 lttng_live_component_init(bt_self_component_source *self_comp_src,
-                          bt_self_component_source_configuration *config, const bt_value *params,
-                          __attribute__((unused)) void *init_method_data)
+                          bt_self_component_source_configuration *, const bt_value *params, void *)
 {
-    struct lttng_live_component *lttng_live;
-    bt_component_class_initialize_method_status ret;
-    bt_self_component *self_comp = bt_self_component_source_as_self_component(self_comp_src);
-    bt_logging_level log_level =
-        bt_component_get_logging_level(bt_self_component_as_component(self_comp));
-    bt_self_component_add_port_status add_port_status;
-
-    ret = lttng_live_component_create(params, log_level, self_comp, &lttng_live);
-    if (ret != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
-        goto error;
-    }
+    try {
+        lttng_live_component::UP lttng_live;
+        bt_component_class_initialize_method_status ret;
+        bt_self_component *self_comp = bt_self_component_source_as_self_component(self_comp_src);
+        bt_self_component_add_port_status add_port_status;
+
+        ret = lttng_live_component_create(params, self_comp_src, lttng_live);
+        if (ret != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
+            return ret;
+        }
 
-    add_port_status = bt_self_component_source_add_output_port(self_comp_src, "out", NULL, NULL);
-    if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
-        ret = (bt_component_class_initialize_method_status) add_port_status;
-        goto end;
-    }
+        add_port_status =
+            bt_self_component_source_add_output_port(self_comp_src, "out", NULL, NULL);
+        if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
+            ret = (bt_component_class_initialize_method_status) add_port_status;
+            return ret;
+        }
 
-    bt_self_component_set_data(self_comp, lttng_live);
-    goto end;
+        bt_self_component_set_data(self_comp, lttng_live.release());
 
-error:
-    lttng_live_component_destroy_data(lttng_live);
-    lttng_live = NULL;
-end:
-    return ret;
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+    } catch (const std::bad_alloc&) {
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+    } catch (const bt2::Error&) {
+        return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+    }
 }
index 4a4faf41d7adb7b36930be68ab86c3065ccd67a0..9dbc432abf3f527abc3ef64017b474aab2ddbc3d 100644 (file)
 #ifndef BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H
 #define BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H
 
-#include <stdbool.h>
-#include <stdint.h>
-
 #include <glib.h>
+#include <stdint.h>
 
 #include <babeltrace2/babeltrace.h>
 
-#include "common/macros.h"
-#include "../common/metadata/decoder.hpp"
-#include "../common/msg-iter/msg-iter.hpp"
+#include "cpp-common/bt2/message.hpp"
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+
+#include "../common/src/metadata/tsdl/decoder.hpp"
+#include "../common/src/msg-iter/msg-iter.hpp"
 #include "viewer-connection.hpp"
 
-struct lttng_live_component;
-struct lttng_live_session;
-struct lttng_live_msg_iter;
+/*
+ * bt_common_lttng_live_url_parts is defined in common code, and is also used
+ * by C code, so it can't be C++-ified yet.  Use this separate deleter object
+ * in the mean time.
+ */
+struct bt_common_lttng_live_url_parts_deleter
+{
+    explicit bt_common_lttng_live_url_parts_deleter(bt_common_lttng_live_url_parts& obj) noexcept :
+        _mObj {&obj}
+    {
+    }
+
+    bt_common_lttng_live_url_parts_deleter(const bt_common_lttng_live_url_parts_deleter&) = delete;
+    bt_common_lttng_live_url_parts&
+    operator=(const bt_common_lttng_live_url_parts_deleter&) = delete;
+
+    ~bt_common_lttng_live_url_parts_deleter()
+    {
+        bt_common_destroy_lttng_live_url_parts(_mObj);
+    }
+
+private:
+    bt_common_lttng_live_url_parts *_mObj;
+};
 
 enum lttng_live_stream_state
 {
@@ -47,38 +68,67 @@ enum lttng_live_stream_state
     LTTNG_LIVE_STREAM_EOF,
 };
 
+inline const char *format_as(const lttng_live_stream_state state) noexcept
+{
+    switch (state) {
+    case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA:
+        return "ACTIVE_NO_DATA";
+
+    case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA:
+        return "QUIESCENT_NO_DATA";
+
+    case LTTNG_LIVE_STREAM_QUIESCENT:
+        return "QUIESCENT";
+
+    case LTTNG_LIVE_STREAM_ACTIVE_DATA:
+        return "ACTIVE_DATA";
+
+    case LTTNG_LIVE_STREAM_EOF:
+        return "EOF";
+    }
+
+    bt_common_abort();
+}
+
 /* Iterator over a live stream. */
 struct lttng_live_stream_iterator
 {
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
+    using UP = std::unique_ptr<lttng_live_stream_iterator>;
+
+    explicit lttng_live_stream_iterator(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.LTTNG-LIVE/STREAM-ITER"}
+    {
+    }
 
-    /* Owned by this. */
-    bt_stream *stream;
+    ~lttng_live_stream_iterator();
+
+    bt2c::Logger logger;
+
+    bt2::Stream::Shared stream;
 
     /* Weak reference. */
-    struct lttng_live_trace *trace;
+    struct lttng_live_trace *trace = nullptr;
 
     /*
      * Since only a single iterator per viewer connection, we have
      * only a single message iterator per stream.
      */
-    struct ctf_msg_iter *msg_iter;
+    ctf_msg_iter_up msg_iter;
 
-    uint64_t viewer_stream_id;
+    uint64_t viewer_stream_id = 0;
 
     struct
     {
-        bool is_set;
-        uint64_t value;
+        bool is_set = false;
+        uint64_t value = 0;
     } ctf_stream_class_id;
 
     /* base offset in current index. */
-    uint64_t base_offset;
+    uint64_t base_offset = 0;
     /* len to read in current index. */
-    uint64_t len;
+    uint64_t len = 0;
     /* offset in current index. */
-    uint64_t offset;
+    uint64_t offset = 0;
 
     /*
      * Clock Snapshot value of the last message iterator inactivity message
@@ -86,45 +136,46 @@ struct lttng_live_stream_iterator
      */
     struct
     {
-        bool is_set;
-        uint64_t value;
+        bool is_set = false;
+        uint64_t value = 0;
     } last_inactivity_ts;
 
     /*
      * Clock Snapshot value of the current message iterator inactivity
      * message we might want to send downstream.
      */
-    uint64_t current_inactivity_ts;
+    uint64_t current_inactivity_ts = 0;
 
-    enum lttng_live_stream_state state;
+    enum lttng_live_stream_state state = LTTNG_LIVE_STREAM_QUIESCENT;
 
-    /*
-     * The current message produced by this live stream iterator. Owned by
-     * this.
-     */
-    const bt_message *current_msg;
+    /* The current message produced by this live stream iterator. */
+    bt2::ConstMessage::Shared current_msg;
 
     /* Timestamp in nanoseconds of the current message (current_msg). */
-    int64_t current_msg_ts_ns;
+    int64_t current_msg_ts_ns = 0;
 
-    /* Owned by this. */
-    uint8_t *buf;
-    size_t buflen;
+    std::vector<uint8_t> buf;
 
-    /* Owned by this. */
-    GString *name;
+    std::string name;
 
-    bool has_stream_hung_up;
+    bool has_stream_hung_up = false;
 };
 
 struct lttng_live_metadata
 {
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
+    using UP = std::unique_ptr<lttng_live_metadata>;
+
+    explicit lttng_live_metadata(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.LTTNG-LIVE/METADATA"}
+    {
+    }
+
+    bt2c::Logger logger;
+
+    uint64_t stream_id = 0;
 
-    uint64_t stream_id;
     /* Weak reference. */
-    struct ctf_metadata_decoder *decoder;
+    ctf_metadata_decoder_up decoder;
 };
 
 enum lttng_live_metadata_stream_state
@@ -151,55 +202,65 @@ enum lttng_live_metadata_stream_state
 
 struct lttng_live_trace
 {
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
+    using UP = std::unique_ptr<lttng_live_trace>;
+
+    explicit lttng_live_trace(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.LTTNG-LIVE/TRACE"}
+    {
+    }
+
+    bt2c::Logger logger;
 
     /* Back reference to session. */
-    struct lttng_live_session *session;
+    struct lttng_live_session *session = nullptr;
 
     /* ctf trace ID within the session. */
-    uint64_t id;
+    uint64_t id = 0;
 
-    /* Owned by this. */
-    bt_trace *trace;
+    bt2::Trace::Shared trace;
 
-    /* Weak reference. */
-    bt_trace_class *trace_class;
+    bt2::TraceClass::Shared trace_class;
 
-    struct lttng_live_metadata *metadata;
+    lttng_live_metadata::UP metadata;
 
-    const bt_clock_class *clock_class;
+    const bt_clock_class *clock_class = nullptr;
 
-    /* Array of pointers to struct lttng_live_stream_iterator. */
-    /* Owned by this. */
-    GPtrArray *stream_iterators;
+    std::vector<lttng_live_stream_iterator::UP> stream_iterators;
 
-    enum lttng_live_metadata_stream_state metadata_stream_state;
+    enum lttng_live_metadata_stream_state metadata_stream_state =
+        LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
 };
 
 struct lttng_live_session
 {
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
+    using UP = std::unique_ptr<lttng_live_session>;
+
+    explicit lttng_live_session(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.LTTNG-LIVE/SESSION"}
+    {
+    }
+
+    ~lttng_live_session();
+
+    bt2c::Logger logger;
+
+    bt_self_component *self_comp = nullptr;
 
     /* Weak reference. */
-    struct lttng_live_msg_iter *lttng_live_msg_iter;
+    struct lttng_live_msg_iter *lttng_live_msg_iter = nullptr;
 
-    /* Owned by this. */
-    GString *hostname;
+    std::string hostname;
 
-    /* Owned by this. */
-    GString *session_name;
+    std::string session_name;
 
-    uint64_t id;
+    uint64_t id = 0;
 
-    /* Array of pointers to struct lttng_live_trace. */
-    GPtrArray *traces;
+    std::vector<lttng_live_trace::UP> traces;
 
-    bool attached;
-    bool new_streams_needed;
-    bool lazy_stream_msg_init;
-    bool closed;
+    bool attached = false;
+    bool new_streams_needed = false;
+    bool lazy_stream_msg_init = false;
+    bool closed = false;
 };
 
 enum session_not_found_action
@@ -214,51 +275,66 @@ enum session_not_found_action
  */
 struct lttng_live_component
 {
-    bt_logging_level log_level;
+    using UP = std::unique_ptr<lttng_live_component>;
+
+    explicit lttng_live_component(bt2c::Logger loggerParam) noexcept :
+        logger {std::move(loggerParam)}
+    {
+    }
+
+    bt2c::Logger logger;
 
     /* Weak reference. */
-    bt_self_component *self_comp;
+    bt_self_component *self_comp = nullptr;
 
     struct
     {
-        GString *url;
-        enum session_not_found_action sess_not_found_act;
+        std::string url;
+        enum session_not_found_action sess_not_found_act = SESSION_NOT_FOUND_ACTION_CONTINUE;
     } params;
 
-    size_t max_query_size;
+    size_t max_query_size = 0;
 
     /*
      * Keeps track of whether the downstream component already has a
      * message iterator on this component.
      */
-    bool has_msg_iter;
+    bool has_msg_iter = false;
 };
 
 struct lttng_live_msg_iter
 {
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
+    using UP = std::unique_ptr<lttng_live_msg_iter>;
+
+    explicit lttng_live_msg_iter(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.LTTNG-LIVE/MSG-ITER"}
+    {
+    }
+
+    ~lttng_live_msg_iter();
+
+    bt2c::Logger logger;
+
+    bt_self_component *self_comp = nullptr;
 
     /* Weak reference. */
-    struct lttng_live_component *lttng_live_comp;
+    struct lttng_live_component *lttng_live_comp = nullptr;
 
     /* Weak reference. */
-    bt_self_message_iterator *self_msg_iter;
+    bt_self_message_iterator *self_msg_iter = nullptr;
 
-    /* Owned by this. */
-    struct live_viewer_connection *viewer_connection;
+    live_viewer_connection::UP viewer_connection;
 
-    /* Array of pointers to struct lttng_live_session. */
-    GPtrArray *sessions;
+    std::vector<lttng_live_session::UP> sessions;
 
     /* Number of live stream iterator this message iterator has.*/
-    uint64_t active_stream_iter;
+    uint64_t active_stream_iter = 0;
 
     /* Timestamp in nanosecond of the last message sent downstream. */
-    int64_t last_msg_ts_ns;
+    int64_t last_msg_ts_ns = 0;
 
     /* True if the iterator was interrupted. */
-    bool was_interrupted;
+    bool was_interrupted = false;
 };
 
 enum lttng_live_iterator_status
@@ -281,6 +357,37 @@ enum lttng_live_iterator_status
     LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED = -4,
 };
 
+inline const char *format_as(const lttng_live_iterator_status status) noexcept
+{
+    switch (status) {
+    case LTTNG_LIVE_ITERATOR_STATUS_CONTINUE:
+        return "LTTNG_LIVE_ITERATOR_STATUS_CONTINUE";
+
+    case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
+        return "LTTNG_LIVE_ITERATOR_STATUS_AGAIN";
+
+    case LTTNG_LIVE_ITERATOR_STATUS_END:
+        return "LTTNG_LIVE_ITERATOR_STATUS_END";
+
+    case LTTNG_LIVE_ITERATOR_STATUS_OK:
+        return "LTTNG_LIVE_ITERATOR_STATUS_OK";
+
+    case LTTNG_LIVE_ITERATOR_STATUS_INVAL:
+        return "LTTNG_LIVE_ITERATOR_STATUS_INVAL";
+
+    case LTTNG_LIVE_ITERATOR_STATUS_ERROR:
+        return "LTTNG_LIVE_ITERATOR_STATUS_ERROR";
+
+    case LTTNG_LIVE_ITERATOR_STATUS_NOMEM:
+        return "LTTNG_LIVE_ITERATOR_STATUS_NOMEM";
+
+    case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED:
+        return "LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED";
+    }
+
+    bt_common_abort();
+}
+
 bt_component_class_initialize_method_status
 lttng_live_component_init(bt_self_component_source *self_comp,
                           bt_self_component_source_configuration *config, const bt_value *params,
@@ -328,7 +435,7 @@ int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter, uint
  * written to the file.
  */
 enum lttng_live_get_one_metadata_status
-lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, FILE *fp, size_t *reply_len);
+lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, std::vector<char>& buf);
 
 enum lttng_live_iterator_status
 lttng_live_get_next_index(struct lttng_live_msg_iter *lttng_live_msg_iter,
@@ -341,7 +448,6 @@ lttng_live_get_stream_bytes(struct lttng_live_msg_iter *lttng_live_msg_iter,
 
 bool lttng_live_graph_is_canceled(struct lttng_live_msg_iter *msg_iter);
 
-BT_HIDDEN
 void lttng_live_stream_iterator_set_state(struct lttng_live_stream_iterator *stream_iter,
                                           enum lttng_live_stream_state new_state);
 
index 0d1911ad8883b9af0efcfccbefc160b4e021425b..926ace11ee35cc6ee7178c71b255c5804a99de86 100644 (file)
 #define LTTNG_VIEWER_ABI_H
 
 #include <stdint.h>
-#include "compat/limits.h"
+
+#include "common/common.h"
+#include "compat/limits.h"                /* IWYU pragma: keep  */
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
 
 #define LTTNG_VIEWER_PATH_MAX      4096
 #define LTTNG_VIEWER_NAME_MAX      255
@@ -38,6 +41,40 @@ enum lttng_viewer_command
     LTTNG_VIEWER_DETACH_SESSION = 9,
 };
 
+inline const char *format_as(const lttng_viewer_command cmd) noexcept
+{
+    switch (cmd) {
+    case LTTNG_VIEWER_CONNECT:
+        return "CONNECT";
+
+    case LTTNG_VIEWER_LIST_SESSIONS:
+        return "LIST_SESSIONS";
+
+    case LTTNG_VIEWER_ATTACH_SESSION:
+        return "ATTACH_SESSION";
+
+    case LTTNG_VIEWER_GET_NEXT_INDEX:
+        return "GET_NEXT_INDEX";
+
+    case LTTNG_VIEWER_GET_PACKET:
+        return "GET_PACKET";
+
+    case LTTNG_VIEWER_GET_METADATA:
+        return "GET_METADATA";
+
+    case LTTNG_VIEWER_GET_NEW_STREAMS:
+        return "GET_NEW_STREAMS";
+
+    case LTTNG_VIEWER_CREATE_SESSION:
+        return "CREATE_SESSION";
+
+    case LTTNG_VIEWER_DETACH_SESSION:
+        return "DETACH_SESSION";
+    }
+
+    bt_common_abort();
+}
+
 enum lttng_viewer_attach_return_code
 {
     LTTNG_VIEWER_ATTACH_OK = 1,         /* The attach command succeeded. */
@@ -65,6 +102,31 @@ enum lttng_viewer_next_index_return_code
     LTTNG_VIEWER_INDEX_EOF = 6,      /* End of index file. */
 };
 
+inline const char *format_as(const lttng_viewer_next_index_return_code code) noexcept
+{
+    switch (code) {
+    case LTTNG_VIEWER_INDEX_OK:
+        return "INDEX_OK";
+
+    case LTTNG_VIEWER_INDEX_RETRY:
+        return "INDEX_RETRY";
+
+    case LTTNG_VIEWER_INDEX_HUP:
+        return "INDEX_HUP";
+
+    case LTTNG_VIEWER_INDEX_ERR:
+        return "INDEX_ERR";
+
+    case LTTNG_VIEWER_INDEX_INACTIVE:
+        return "INDEX_INACTIVE";
+
+    case LTTNG_VIEWER_INDEX_EOF:
+        return "INDEX_EOF";
+    }
+
+    bt_common_abort();
+}
+
 enum lttng_viewer_get_packet_return_code
 {
     LTTNG_VIEWER_GET_PACKET_OK = 1,
@@ -73,6 +135,25 @@ enum lttng_viewer_get_packet_return_code
     LTTNG_VIEWER_GET_PACKET_EOF = 4,
 };
 
+inline const char *format_as(const lttng_viewer_get_packet_return_code code) noexcept
+{
+    switch (code) {
+    case LTTNG_VIEWER_GET_PACKET_OK:
+        return "GET_PACKET_OK";
+
+    case LTTNG_VIEWER_GET_PACKET_RETRY:
+        return "GET_PACKET_RETRY";
+
+    case LTTNG_VIEWER_GET_PACKET_ERR:
+        return "GET_PACKET_ERR";
+
+    case LTTNG_VIEWER_GET_PACKET_EOF:
+        return "GET_PACKET_EOF";
+    }
+
+    bt_common_abort();
+};
+
 enum lttng_viewer_get_metadata_return_code
 {
     LTTNG_VIEWER_METADATA_OK = 1,
@@ -94,6 +175,19 @@ enum lttng_viewer_seek
     LTTNG_VIEWER_SEEK_LAST = 2,
 };
 
+inline const char *format_as(const lttng_viewer_seek seek) noexcept
+{
+    switch (seek) {
+    case LTTNG_VIEWER_SEEK_BEGINNING:
+        return "SEEK_BEGINNING";
+
+    case LTTNG_VIEWER_SEEK_LAST:
+        return "SEEK_LAST";
+    }
+
+    bt_common_abort();
+}
+
 enum lttng_viewer_new_streams_return_code
 {
     LTTNG_VIEWER_NEW_STREAMS_OK = 1,     /* If new streams are being sent. */
index 775396ee7d9cd2266dc4863f1758101bd3047c1d..693042d7bf228be5acae70c22b82f85887c238f6 100644 (file)
@@ -6,22 +6,14 @@
  * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
  */
 
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL   log_level
-#define BT_LOG_TAG            "PLUGIN/SRC.CTF.LTTNG-LIVE/META"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <glib.h>
 #include "compat/memstream.h"
-#include <babeltrace2/babeltrace.h>
+#include "cpp-common/bt2c/libc-up.hpp"
+#include "cpp-common/bt2s/make-unique.hpp"
 
+#include "../common/src/metadata/tsdl/ctf-meta-configure-ir-trace.hpp"
+#include "../common/src/metadata/tsdl/decoder.hpp"
+#include "lttng-live.hpp"
 #include "metadata.hpp"
-#include "../common/metadata/decoder.hpp"
-#include "../common/metadata/ctf-meta-configure-ir-trace.hpp"
 
 #define TSDL_MAGIC 0x75d11d57
 
@@ -40,13 +32,11 @@ struct packet_header
 } __attribute__((__packed__));
 
 static bool stream_classes_all_have_default_clock_class(bt_trace_class *tc,
-                                                        bt_logging_level log_level,
-                                                        bt_self_component *self_comp)
+                                                        const bt2c::Logger& logger)
 {
     uint64_t i, sc_count;
     const bt_clock_class *cc = NULL;
     const bt_stream_class *sc;
-    bool ret = true;
 
     sc_count = bt_trace_class_get_stream_class_count(tc);
     for (i = 0; i < sc_count; i++) {
@@ -56,17 +46,15 @@ static bool stream_classes_all_have_default_clock_class(bt_trace_class *tc,
 
         cc = bt_stream_class_borrow_default_clock_class_const(sc);
         if (!cc) {
-            ret = false;
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Stream class doesn't have a default clock class: "
-                                      "sc-id=%" PRIu64 ", sc-name=\"%s\"",
-                                      bt_stream_class_get_id(sc), bt_stream_class_get_name(sc));
-            goto end;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
+                                         "Stream class doesn't have a default clock class: "
+                                         "sc-id={}, sc-name=\"{}\"",
+                                         bt_stream_class_get_id(sc), bt_stream_class_get_name(sc));
+            return false;
         }
     }
 
-end:
-    return ret;
+    return true;
 }
 /*
  * Iterate over the stream classes and returns the first clock class
@@ -86,31 +74,25 @@ static const bt_clock_class *borrow_any_clock_class(bt_trace_class *tc)
 
         cc = bt_stream_class_borrow_default_clock_class_const(sc);
         if (cc) {
-            goto end;
+            return cc;
         }
     }
-end:
-    BT_ASSERT_DBG(cc);
-    return cc;
+
+    bt_common_abort();
 }
 
-BT_HIDDEN
 enum lttng_live_iterator_status lttng_live_metadata_update(struct lttng_live_trace *trace)
 {
     struct lttng_live_session *session = trace->session;
-    struct lttng_live_metadata *metadata = trace->metadata;
-    size_t size, len_read = 0;
-    char *metadata_buf = NULL;
+    struct lttng_live_metadata *metadata = trace->metadata.get();
+    std::vector<char> metadataBuf;
     bool keep_receiving;
-    FILE *fp = NULL;
+    bt2c::FileUP fp;
     enum ctf_metadata_decoder_status decoder_status;
-    enum lttng_live_iterator_status status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-    bt_logging_level log_level = trace->log_level;
-    bt_self_component *self_comp = trace->self_comp;
     enum lttng_live_get_one_metadata_status metadata_status;
 
-    BT_COMP_LOGD("Updating metadata for trace: session-id=%" PRIu64 ", trace-id=%" PRIu64,
-                 session->id, trace->id);
+    BT_CPPLOGD_SPEC(metadata->logger, "Updating metadata for trace: session-id={}, trace-id={}",
+                    session->id, trace->id);
 
     /* No metadata stream yet. */
     if (!metadata) {
@@ -120,40 +102,22 @@ enum lttng_live_iterator_status lttng_live_metadata_update(struct lttng_live_tra
              * metadata this indicates that we will never receive
              * any metadata.
              */
-            status = LTTNG_LIVE_ITERATOR_STATUS_END;
+            return LTTNG_LIVE_ITERATOR_STATUS_END;
         } else if (session->new_streams_needed) {
-            status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+            return LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
         } else {
             session->new_streams_needed = true;
-            status = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
+            return LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
         }
-        goto end;
     }
 
     if (trace->metadata_stream_state != LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED) {
-        goto end;
-    }
-
-    /*
-     * Open a new write only file handle to populate the `metadata_buf`
-     * memory buffer so we can write in loop in it easily.
-     */
-    fp = bt_open_memstream(&metadata_buf, &size);
-    if (!fp) {
-        if (errno == EINTR && lttng_live_graph_is_canceled(session->lttng_live_msg_iter)) {
-            session->lttng_live_msg_iter->was_interrupted = true;
-            status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-        } else {
-            BT_COMP_LOGE_APPEND_CAUSE_ERRNO(self_comp, "Metadata open_memstream", ".");
-            status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        }
-        goto end;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     }
 
     keep_receiving = true;
     /* Grab all available metadata. */
     while (keep_receiving) {
-        size_t reply_len = 0;
         /*
          * lttng_live_get_one_metadata_packet() asks the Relay Daemon
          * for new metadata. If new metadata is received, the function
@@ -166,19 +130,20 @@ enum lttng_live_iterator_status lttng_live_metadata_update(struct lttng_live_tra
          * If we receive an _ERROR status, it means there was a
          * networking, allocating, or some other unrecoverable error.
          */
-        metadata_status = lttng_live_get_one_metadata_packet(trace, fp, &reply_len);
+        metadata_status = lttng_live_get_one_metadata_packet(trace, metadataBuf);
 
         switch (metadata_status) {
         case LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK:
-            len_read += reply_len;
             break;
         case LTTNG_LIVE_GET_ONE_METADATA_STATUS_END:
             keep_receiving = false;
             break;
         case LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED:
-            BT_COMP_LOGD("Metadata stream was closed by the Relay, the trace is no longer active: "
-                         "trace-id=%" PRIu64 ", metadata-stream-id=%" PRIu64,
-                         trace->id, metadata->stream_id);
+            BT_CPPLOGD_SPEC(
+                metadata->logger,
+                "Metadata stream was closed by the Relay, the trace is no longer active: "
+                "trace-id={}, metadata-stream-id={}",
+                trace->id, metadata->stream_id);
             /*
              * The stream was closed and we received everything
              * there was to receive for this metadata stream.
@@ -189,157 +154,103 @@ enum lttng_live_iterator_status lttng_live_metadata_update(struct lttng_live_tra
             trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_CLOSED;
             break;
         case LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR:
-            BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                      "Error getting one trace metadata packet: "
-                                      "trace-id=%" PRIu64,
-                                      trace->id);
-            goto error;
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(metadata->logger,
+                                         "Error getting one trace metadata packet: trace-id={}",
+                                         trace->id);
+            return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
         default:
             bt_common_abort();
         }
     }
 
-    /* The memory buffer `metadata_buf` contains all the metadata. */
-    if (bt_close_memstream(&metadata_buf, &size, fp)) {
-        BT_COMP_LOGW_ERRNO("Metadata bt_close_memstream", ".");
-    }
-
-    fp = NULL;
-
-    if (len_read == 0) {
+    if (metadataBuf.empty()) {
         if (!trace->trace) {
-            status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-            goto end;
+            return LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
         }
 
-        /* The relay sent zero bytes of metdata. */
+        /* The relay sent zero bytes of metadata. */
         trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NOT_NEEDED;
-        goto end;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     }
 
     /*
      * Open a new reading file handle on the `metadata_buf` and pass it to
      * the metadata decoder.
      */
-    fp = bt_fmemopen(metadata_buf, len_read, "rb");
+    fp.reset(bt_fmemopen(metadataBuf.data(), metadataBuf.size(), "rb"));
     if (!fp) {
         if (errno == EINTR && lttng_live_graph_is_canceled(session->lttng_live_msg_iter)) {
             session->lttng_live_msg_iter->was_interrupted = true;
-            status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+            return LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
         } else {
-            BT_COMP_LOGE_APPEND_CAUSE_ERRNO(self_comp, "Cannot memory-open metadata buffer", ".");
-            status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+            BT_CPPLOGE_ERRNO_APPEND_CAUSE_SPEC(metadata->logger,
+                                               "Cannot memory-open metadata buffer", ".");
+            return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
         }
-        goto end;
     }
 
     /*
      * The call to ctf_metadata_decoder_append_content() will append
      * new metadata to our current trace class.
      */
-    BT_COMP_LOGD("Appending new metadata to the ctf_trace class");
-    decoder_status = ctf_metadata_decoder_append_content(metadata->decoder, fp);
+    BT_CPPLOGD_SPEC(metadata->logger, "Appending new metadata to the ctf_trace class");
+    decoder_status = ctf_metadata_decoder_append_content(metadata->decoder.get(), fp.get());
     switch (decoder_status) {
     case CTF_METADATA_DECODER_STATUS_OK:
         if (!trace->trace_class) {
             struct ctf_trace_class *tc =
-                ctf_metadata_decoder_borrow_ctf_trace_class(metadata->decoder);
+                ctf_metadata_decoder_borrow_ctf_trace_class(metadata->decoder.get());
 
-            trace->trace_class = ctf_metadata_decoder_get_ir_trace_class(metadata->decoder);
-            trace->trace = bt_trace_create(trace->trace_class);
+            trace->trace_class = ctf_metadata_decoder_get_ir_trace_class(metadata->decoder.get());
+            trace->trace = trace->trace_class->instantiate();
             if (!trace->trace) {
-                BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create bt_trace");
-                goto error;
-            }
-            if (ctf_trace_class_configure_ir_trace(tc, trace->trace)) {
-                BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to configure ctf trace class");
-                goto error;
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(metadata->logger, "Failed to create bt_trace");
+                return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
             }
-            if (!stream_classes_all_have_default_clock_class(trace->trace_class, log_level,
-                                                             self_comp)) {
+
+            ctf_trace_class_configure_ir_trace(tc, *trace->trace);
+
+            if (!stream_classes_all_have_default_clock_class(trace->trace_class->libObjPtr(),
+                                                             metadata->logger)) {
                 /* Error logged in function. */
-                goto error;
+                return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
             }
-            trace->clock_class = borrow_any_clock_class(trace->trace_class);
+            trace->clock_class = borrow_any_clock_class(trace->trace_class->libObjPtr());
         }
 
-        /* The metadata was updated succesfully. */
+        /* The metadata was updated successfully. */
         trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NOT_NEEDED;
 
-        break;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     default:
-        goto error;
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
-
-    goto end;
-
-error:
-    status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-end:
-    if (fp) {
-        int closeret;
-
-        closeret = fclose(fp);
-        if (closeret) {
-            BT_COMP_LOGW_ERRNO("Error on fclose", ".");
-        }
-    }
-    free(metadata_buf);
-    return status;
 }
 
-BT_HIDDEN
 int lttng_live_metadata_create_stream(struct lttng_live_session *session, uint64_t ctf_trace_id,
-                                      uint64_t stream_id, const char *trace_name)
+                                      uint64_t stream_id)
 {
-    bt_self_component *self_comp = session->self_comp;
-    bt_logging_level log_level = session->log_level;
-    struct lttng_live_metadata *metadata = NULL;
     struct lttng_live_trace *trace;
 
-    ctf_metadata_decoder_config cfg {};
-    cfg.log_level = session->log_level;
+    ctf_metadata_decoder_config cfg {session->logger};
     cfg.self_comp = session->self_comp;
-    cfg.clock_class_offset_s = 0;
-    cfg.clock_class_offset_ns = 0;
     cfg.create_trace_class = true;
 
-    metadata = g_new0(struct lttng_live_metadata, 1);
-    if (!metadata) {
-        return -1;
-    }
-    metadata->log_level = session->log_level;
-    metadata->self_comp = session->self_comp;
+    auto metadata = bt2s::make_unique<lttng_live_metadata>(session->logger);
     metadata->stream_id = stream_id;
 
     metadata->decoder = ctf_metadata_decoder_create(&cfg);
     if (!metadata->decoder) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create CTF metadata decoder");
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(session->logger, "Failed to create CTF metadata decoder");
+        return -1;
     }
+
     trace = lttng_live_session_borrow_or_create_trace_by_id(session, ctf_trace_id);
     if (!trace) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to borrow trace");
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(session->logger, "Failed to borrow trace");
+        return -1;
     }
-    trace->metadata = metadata;
-    return 0;
-
-error:
-    ctf_metadata_decoder_destroy(metadata->decoder);
-    g_free(metadata);
-    return -1;
-}
-
-BT_HIDDEN
-void lttng_live_metadata_fini(struct lttng_live_trace *trace)
-{
-    struct lttng_live_metadata *metadata = trace->metadata;
 
-    if (!metadata) {
-        return;
-    }
-    ctf_metadata_decoder_destroy(metadata->decoder);
-    trace->metadata = NULL;
-    g_free(metadata);
+    trace->metadata = std::move(metadata);
+    return 0;
 }
index 486c91322e2b612bc988afc41cd452f5aebe70ed..7c2d7659d023724ee388e603cee1dc7feb78aabf 100644 (file)
@@ -8,15 +8,12 @@
 #define LTTNG_LIVE_METADATA_H
 
 #include <stdint.h>
-#include <stdio.h>
 
 #include "lttng-live.hpp"
 
 int lttng_live_metadata_create_stream(struct lttng_live_session *session, uint64_t ctf_trace_id,
-                                      uint64_t stream_id, const char *trace_name);
+                                      uint64_t stream_id);
 
 enum lttng_live_iterator_status lttng_live_metadata_update(struct lttng_live_trace *trace);
 
-void lttng_live_metadata_fini(struct lttng_live_trace *trace);
-
 #endif /* LTTNG_LIVE_METADATA_H */
index 7960b50e685299c9406bdab96652c44176f1c4ca..46cfd2c4378a21298f697e1eb695d1d93b5a1de7 100644 (file)
  * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  */
 
-#define BT_COMP_LOG_SELF_COMP (viewer_connection->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (viewer_connection->log_level)
-#define BT_LOG_TAG            "PLUGIN/SRC.CTF.LTTNG-LIVE/VIEWER"
-#include "logging/comp-logging.h"
-
-#include <fcntl.h>
-#include <stdbool.h>
+#include <glib.h>
 #include <stdint.h>
 #include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
 
-#include <glib.h>
+#include <babeltrace2/babeltrace.h>
 
-#include "compat/socket.h"
-#include "compat/endian.h"
-#include "compat/compiler.h"
 #include "common/common.h"
-#include <babeltrace2/babeltrace.h>
+#include "compat/endian.h" /* IWYU pragma: keep  */
+#include "cpp-common/bt2s/make-unique.hpp"
 
+#include "data-stream.hpp"
 #include "lttng-live.hpp"
-#include "viewer-connection.hpp"
 #include "lttng-viewer-abi.hpp"
-#include "data-stream.hpp"
 #include "metadata.hpp"
+#include "viewer-connection.hpp"
 
-#define viewer_handle_send_recv_status(_self_comp, _self_comp_class, _status, _action, _msg_str)   \
+#define viewer_handle_send_recv_status(_status, _action, _msg_str)                                 \
     do {                                                                                           \
         switch (_status) {                                                                         \
         case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:                                                 \
             break;                                                                                 \
         case LTTNG_LIVE_VIEWER_STATUS_ERROR:                                                       \
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(_self_comp, _self_comp_class,                  \
-                                                    "Error " _action " " _msg_str);                \
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,                                \
+                                         "Error " _action " " _msg_str);                           \
             break;                                                                                 \
         default:                                                                                   \
             bt_common_abort();                                                                     \
         }                                                                                          \
     } while (0)
 
-#define viewer_handle_send_status(_self_comp, _self_comp_class, _status, _msg_str)                 \
-    viewer_handle_send_recv_status(_self_comp, _self_comp_class, _status, "sending", _msg_str)
+#define viewer_handle_send_status(_status, _msg_str)                                               \
+    viewer_handle_send_recv_status(_status, "sending", _msg_str)
 
-#define viewer_handle_recv_status(_self_comp, _self_comp_class, _status, _msg_str)                 \
-    viewer_handle_send_recv_status(_self_comp, _self_comp_class, _status, "receiving", _msg_str)
+#define viewer_handle_recv_status(_status, _msg_str)                                               \
+    viewer_handle_send_recv_status(_status, "receiving", _msg_str)
 
-#define LTTNG_LIVE_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE_ERRNO(_self_comp, _self_comp_class, _msg,  \
-                                                              _fmt, ...)                           \
+#define LTTNG_LIVE_CPPLOGE_APPEND_CAUSE_ERRNO(_msg, _fmt, ...)                                     \
     do {                                                                                           \
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(_self_comp, _self_comp_class, _msg ": %s" _fmt,    \
-                                                bt_socket_errormsg(), ##__VA_ARGS__);              \
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, _msg ": {}" _fmt,                  \
+                                     bt_socket_errormsg(), ##__VA_ARGS__);                         \
     } while (0)
 
-static const char *lttng_viewer_command_string(enum lttng_viewer_command cmd)
-{
-    switch (cmd) {
-    case LTTNG_VIEWER_CONNECT:
-        return "CONNECT";
-    case LTTNG_VIEWER_LIST_SESSIONS:
-        return "LIST_SESSIONS";
-    case LTTNG_VIEWER_ATTACH_SESSION:
-        return "ATTACH_SESSION";
-    case LTTNG_VIEWER_GET_NEXT_INDEX:
-        return "GET_NEXT_INDEX";
-    case LTTNG_VIEWER_GET_PACKET:
-        return "GET_PACKET";
-    case LTTNG_VIEWER_GET_METADATA:
-        return "GET_METADATA";
-    case LTTNG_VIEWER_GET_NEW_STREAMS:
-        return "GET_NEW_STREAMS";
-    case LTTNG_VIEWER_CREATE_SESSION:
-        return "CREATE_SESSION";
-    case LTTNG_VIEWER_DETACH_SESSION:
-        return "DETACH_SESSION";
-    }
-
-    bt_common_abort();
-}
-
-static const char *
-lttng_viewer_next_index_return_code_string(enum lttng_viewer_next_index_return_code code)
-{
-    switch (code) {
-    case LTTNG_VIEWER_INDEX_OK:
-        return "INDEX_OK";
-    case LTTNG_VIEWER_INDEX_RETRY:
-        return "INDEX_RETRY";
-    case LTTNG_VIEWER_INDEX_HUP:
-        return "INDEX_HUP";
-    case LTTNG_VIEWER_INDEX_ERR:
-        return "INDEX_ERR";
-    case LTTNG_VIEWER_INDEX_INACTIVE:
-        return "INDEX_INACTIVE";
-    case LTTNG_VIEWER_INDEX_EOF:
-        return "INDEX_EOF";
-    }
-
-    bt_common_abort();
-}
-
-static const char *lttng_viewer_next_index_return_code_string(uint32_t code)
-{
-    return lttng_viewer_next_index_return_code_string((lttng_viewer_next_index_return_code) code);
-}
-
-static const char *
-lttng_viewer_get_packet_return_code_string(enum lttng_viewer_get_packet_return_code code)
-{
-    switch (code) {
-    case LTTNG_VIEWER_GET_PACKET_OK:
-        return "GET_PACKET_OK";
-    case LTTNG_VIEWER_GET_PACKET_RETRY:
-        return "GET_PACKET_RETRY";
-    case LTTNG_VIEWER_GET_PACKET_ERR:
-        return "GET_PACKET_ERR";
-    case LTTNG_VIEWER_GET_PACKET_EOF:
-        return "GET_PACKET_EOF";
-    }
-
-    bt_common_abort();
-};
-
-static const char *lttng_viewer_get_packet_return_code_string(uint32_t code)
-{
-    return lttng_viewer_get_packet_return_code_string((lttng_viewer_get_packet_return_code) code);
-}
-
-static const char *lttng_viewer_seek_string(enum lttng_viewer_seek seek)
-{
-    switch (seek) {
-    case LTTNG_VIEWER_SEEK_BEGINNING:
-        return "SEEK_BEGINNING";
-    case LTTNG_VIEWER_SEEK_LAST:
-        return "SEEK_LAST";
-    }
-
-    bt_common_abort();
-}
-
 static inline enum lttng_live_iterator_status
 viewer_status_to_live_iterator_status(enum lttng_live_viewer_status viewer_status)
 {
@@ -177,12 +79,14 @@ viewer_status_to_ctf_msg_iter_medium_status(enum lttng_live_viewer_status viewer
 
 static inline void viewer_connection_close_socket(struct live_viewer_connection *viewer_connection)
 {
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    bt_self_component *self_comp = viewer_connection->self_comp;
+    if (viewer_connection->control_sock == BT_INVALID_SOCKET) {
+        return;
+    }
+
     int ret = bt_socket_close(viewer_connection->control_sock);
     if (ret == -1) {
-        BT_COMP_OR_COMP_CLASS_LOGW_ERRNO(self_comp, self_comp_class,
-                                         "Error closing viewer connection socket: ", ".");
+        BT_CPPLOGW_ERRNO_SPEC(viewer_connection->logger,
+                              "Error closing viewer connection socket: ", ".");
     }
 
     viewer_connection->control_sock = BT_INVALID_SOCKET;
@@ -198,11 +102,8 @@ static enum lttng_live_viewer_status
 lttng_live_recv(struct live_viewer_connection *viewer_connection, void *buf, size_t len)
 {
     ssize_t received;
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    bt_self_component *self_comp = viewer_connection->self_comp;
     size_t total_received = 0, to_receive = len;
     struct lttng_live_msg_iter *lttng_live_msg_iter = viewer_connection->lttng_live_msg_iter;
-    enum lttng_live_viewer_status status;
     BT_SOCKET sock = viewer_connection->control_sock;
 
     /*
@@ -218,9 +119,8 @@ lttng_live_recv(struct live_viewer_connection *viewer_connection, void *buf, siz
                      * SIGINT and the graph is being torn
                      * down.
                      */
-                    status = LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
                     lttng_live_msg_iter->was_interrupted = true;
-                    goto end;
+                    return LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
                 } else {
                     /*
                      * A signal was received, but the graph
@@ -233,12 +133,10 @@ lttng_live_recv(struct live_viewer_connection *viewer_connection, void *buf, siz
                  * For any other types of socket error, close
                  * the socket and return an error.
                  */
-                LTTNG_LIVE_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE_ERRNO(
-                    self_comp, self_comp_class, "Error receiving from Relay", ".");
+                LTTNG_LIVE_CPPLOGE_APPEND_CAUSE_ERRNO("Error receiving from Relay", ".");
 
                 viewer_connection_close_socket(viewer_connection);
-                status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                goto end;
+                return LTTNG_LIVE_VIEWER_STATUS_ERROR;
             }
         } else if (received == 0) {
             /*
@@ -248,11 +146,10 @@ lttng_live_recv(struct live_viewer_connection *viewer_connection, void *buf, siz
              * a message from it, it means something when wrong.
              * Close the socket and return an error.
              */
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                    "Remote side has closed connection");
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                         "Remote side has closed connection");
             viewer_connection_close_socket(viewer_connection);
-            status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-            goto end;
+            return LTTNG_LIVE_VIEWER_STATUS_ERROR;
         }
 
         BT_ASSERT(received <= to_receive);
@@ -262,10 +159,7 @@ lttng_live_recv(struct live_viewer_connection *viewer_connection, void *buf, siz
     } while (to_receive > 0);
 
     BT_ASSERT(total_received == len);
-    status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-end:
-    return status;
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
 /*
@@ -277,9 +171,6 @@ end:
 static enum lttng_live_viewer_status
 lttng_live_send(struct live_viewer_connection *viewer_connection, const void *buf, size_t len)
 {
-    enum lttng_live_viewer_status status;
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    bt_self_component *self_comp = viewer_connection->self_comp;
     struct lttng_live_msg_iter *lttng_live_msg_iter = viewer_connection->lttng_live_msg_iter;
     BT_SOCKET sock = viewer_connection->control_sock;
     size_t to_send = len;
@@ -294,9 +185,8 @@ lttng_live_send(struct live_viewer_connection *viewer_connection, const void *bu
                      * This interruption was a SIGINT and
                      * the graph is being teared down.
                      */
-                    status = LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
                     lttng_live_msg_iter->was_interrupted = true;
-                    goto end;
+                    return LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
                 } else {
                     /*
                      * A signal was received, but the graph
@@ -309,12 +199,10 @@ lttng_live_send(struct live_viewer_connection *viewer_connection, const void *bu
                  * For any other types of socket error, close
                  * the socket and return an error.
                  */
-                LTTNG_LIVE_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE_ERRNO(
-                    self_comp, self_comp_class, "Error sending to Relay", ".");
+                LTTNG_LIVE_CPPLOGE_APPEND_CAUSE_ERRNO("Error sending to Relay", ".");
 
                 viewer_connection_close_socket(viewer_connection);
-                status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                goto end;
+                return LTTNG_LIVE_VIEWER_STATUS_ERROR;
             }
         }
 
@@ -325,35 +213,30 @@ lttng_live_send(struct live_viewer_connection *viewer_connection, const void *bu
     } while (to_send > 0);
 
     BT_ASSERT(total_sent == len);
-    status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-end:
-    return status;
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
 static int parse_url(struct live_viewer_connection *viewer_connection)
 {
     char error_buf[256] = {0};
-    bt_self_component *self_comp = viewer_connection->self_comp;
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    struct bt_common_lttng_live_url_parts lttng_live_url_parts = {0};
-    int ret = -1;
-    const char *path = viewer_connection->url->str;
+    struct bt_common_lttng_live_url_parts lttng_live_url_parts = {};
+    bt_common_lttng_live_url_parts_deleter partsDeleter {lttng_live_url_parts};
 
-    if (!path) {
-        goto end;
+    if (viewer_connection->url.empty()) {
+        return -1;
     }
 
-    lttng_live_url_parts = bt_common_parse_lttng_live_url(path, error_buf, sizeof(error_buf));
+    lttng_live_url_parts = bt_common_parse_lttng_live_url(viewer_connection->url.c_str(), error_buf,
+                                                          sizeof(error_buf));
     if (!lttng_live_url_parts.proto) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Invalid LTTng live URL format: %s", error_buf);
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Invalid LTTng live URL format: {}",
+                                     error_buf);
+        return -1;
     }
-    viewer_connection->proto = lttng_live_url_parts.proto;
+    viewer_connection->proto.reset(lttng_live_url_parts.proto);
     lttng_live_url_parts.proto = NULL;
 
-    viewer_connection->relay_hostname = lttng_live_url_parts.hostname;
+    viewer_connection->relay_hostname.reset(lttng_live_url_parts.hostname);
     lttng_live_url_parts.hostname = NULL;
 
     if (lttng_live_url_parts.port >= 0) {
@@ -362,19 +245,15 @@ static int parse_url(struct live_viewer_connection *viewer_connection)
         viewer_connection->port = LTTNG_DEFAULT_NETWORK_VIEWER_PORT;
     }
 
-    viewer_connection->target_hostname = lttng_live_url_parts.target_hostname;
+    viewer_connection->target_hostname.reset(lttng_live_url_parts.target_hostname);
     lttng_live_url_parts.target_hostname = NULL;
 
     if (lttng_live_url_parts.session_name) {
-        viewer_connection->session_name = lttng_live_url_parts.session_name;
+        viewer_connection->session_name.reset(lttng_live_url_parts.session_name);
         lttng_live_url_parts.session_name = NULL;
     }
 
-    ret = 0;
-
-end:
-    bt_common_destroy_lttng_live_url_parts(&lttng_live_url_parts);
-    return ret;
+    return 0;
 }
 
 static enum lttng_live_viewer_status
@@ -383,15 +262,12 @@ lttng_live_handshake(struct live_viewer_connection *viewer_connection)
     struct lttng_viewer_cmd cmd;
     struct lttng_viewer_connect connect;
     enum lttng_live_viewer_status status;
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    bt_self_component *self_comp = viewer_connection->self_comp;
     const size_t cmd_buf_len = sizeof(cmd) + sizeof(connect);
     char cmd_buf[cmd_buf_len];
 
-    BT_COMP_OR_COMP_CLASS_LOGD(
-        self_comp, self_comp_class,
-        "Handshaking with the relay daemon: cmd=%s, major-version=%u, minor-version=%u",
-        lttng_viewer_command_string(LTTNG_VIEWER_CONNECT), LTTNG_LIVE_MAJOR, LTTNG_LIVE_MINOR);
+    BT_CPPLOGD_SPEC(viewer_connection->logger,
+                    "Handshaking with the relay daemon: cmd={}, major-version={}, minor-version={}",
+                    LTTNG_VIEWER_CONNECT, LTTNG_LIVE_MAJOR, LTTNG_LIVE_MINOR);
 
     cmd.cmd = htobe32(LTTNG_VIEWER_CONNECT);
     cmd.data_size = htobe64((uint64_t) sizeof(connect));
@@ -412,26 +288,25 @@ lttng_live_handshake(struct live_viewer_connection *viewer_connection)
 
     status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_send_status(self_comp, self_comp_class, status, "viewer connect command");
-        goto end;
+        viewer_handle_send_status(status, "viewer connect command");
+        return status;
     }
 
     status = lttng_live_recv(viewer_connection, &connect, sizeof(connect));
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, self_comp_class, status, "viewer connect reply");
-        goto end;
+        viewer_handle_recv_status(status, "viewer connect reply");
+        return status;
     }
 
-    BT_COMP_OR_COMP_CLASS_LOGI(self_comp, self_comp_class, "Received viewer session ID : %" PRIu64,
-                               (uint64_t) be64toh(connect.viewer_session_id));
-    BT_COMP_OR_COMP_CLASS_LOGI(self_comp, self_comp_class, "Relayd version : %u.%u",
-                               be32toh(connect.major), be32toh(connect.minor));
+    BT_CPPLOGI_SPEC(viewer_connection->logger, "Received viewer session ID : {}",
+                    (uint64_t) be64toh(connect.viewer_session_id));
+    BT_CPPLOGI_SPEC(viewer_connection->logger, "Relayd version : {}.{}", be32toh(connect.major),
+                    be32toh(connect.minor));
 
     if (LTTNG_LIVE_MAJOR != be32toh(connect.major)) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Incompatible lttng-relayd protocol");
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                     "Incompatible lttng-relayd protocol");
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     }
     /* Use the smallest protocol version implemented. */
     if (LTTNG_LIVE_MINOR > be32toh(connect.minor)) {
@@ -441,12 +316,7 @@ lttng_live_handshake(struct live_viewer_connection *viewer_connection)
     }
     viewer_connection->major = LTTNG_LIVE_MAJOR;
 
-    status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-    goto end;
-
-end:
-    return status;
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
 static enum lttng_live_viewer_status
@@ -455,19 +325,15 @@ lttng_live_connect_viewer(struct live_viewer_connection *viewer_connection)
     struct hostent *host;
     struct sockaddr_in server_addr;
     enum lttng_live_viewer_status status;
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    bt_self_component *self_comp = viewer_connection->self_comp;
 
     if (parse_url(viewer_connection)) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class, "Failed to parse URL");
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Failed to parse URL");
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     }
 
-    BT_COMP_OR_COMP_CLASS_LOGD(
-        self_comp, self_comp_class,
-        "Connecting to hostname : %s, port : %d, "
-        "target hostname : %s, session name : %s, proto : %s",
+    BT_CPPLOGD_SPEC(
+        viewer_connection->logger,
+        "Connecting to hostname : {}, port : {}, target hostname : {}, session name : {}, proto : {}",
         viewer_connection->relay_hostname->str, viewer_connection->port,
         !viewer_connection->target_hostname ? "<none>" : viewer_connection->target_hostname->str,
         !viewer_connection->session_name ? "<none>" : viewer_connection->session_name->str,
@@ -475,18 +341,16 @@ lttng_live_connect_viewer(struct live_viewer_connection *viewer_connection)
 
     host = gethostbyname(viewer_connection->relay_hostname->str);
     if (!host) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Cannot lookup hostname: hostname=\"%s\"",
-                                                viewer_connection->relay_hostname->str);
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                     "Cannot lookup hostname: hostname=\"{}\"",
+                                     viewer_connection->relay_hostname->str);
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     }
 
     if ((viewer_connection->control_sock = socket(AF_INET, SOCK_STREAM, 0)) == BT_INVALID_SOCKET) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Socket creation failed: %s", bt_socket_errormsg());
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Socket creation failed: {}",
+                                     bt_socket_errormsg());
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     }
 
     server_addr.sin_family = AF_INET;
@@ -496,10 +360,10 @@ lttng_live_connect_viewer(struct live_viewer_connection *viewer_connection)
 
     if (connect(viewer_connection->control_sock, (struct sockaddr *) &server_addr,
                 sizeof(struct sockaddr)) == BT_SOCKET_ERROR) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class, "Connection failed: %s",
-                                                bt_socket_errormsg());
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Connection failed: {}",
+                                     bt_socket_errormsg());
+        viewer_connection_close_socket(viewer_connection);
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     }
 
     status = lttng_live_handshake(viewer_connection);
@@ -509,130 +373,93 @@ lttng_live_connect_viewer(struct live_viewer_connection *viewer_connection)
      * interruption.
      */
     if (status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Viewer handshake failed");
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Viewer handshake failed");
+        viewer_connection_close_socket(viewer_connection);
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     } else if (status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-        goto end;
+        return LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
     }
 
-    goto end;
-
-error:
-    if (viewer_connection->control_sock != BT_INVALID_SOCKET) {
-        if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) {
-            BT_COMP_OR_COMP_CLASS_LOGW(self_comp, self_comp_class, "Error closing socket: %s.",
-                                       bt_socket_errormsg());
-        }
-    }
-    viewer_connection->control_sock = BT_INVALID_SOCKET;
-end:
-    return status;
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
-static void lttng_live_disconnect_viewer(struct live_viewer_connection *viewer_connection)
-{
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    bt_self_component *self_comp = viewer_connection->self_comp;
-
-    if (viewer_connection->control_sock == BT_INVALID_SOCKET) {
-        return;
-    }
-    if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) {
-        BT_COMP_OR_COMP_CLASS_LOGW(self_comp, self_comp_class, "Error closing socket: %s",
-                                   bt_socket_errormsg());
-        viewer_connection->control_sock = BT_INVALID_SOCKET;
-    }
-}
-
-static int list_update_session(bt_value *results, const struct lttng_viewer_session *session,
-                               bool *_found, struct live_viewer_connection *viewer_connection)
+static int list_update_session(const bt2::ArrayValue results,
+                               const struct lttng_viewer_session *session, bool *_found,
+                               struct live_viewer_connection *viewer_connection)
 {
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    bt_self_component *self_comp = viewer_connection->self_comp;
-    int ret = 0;
-    uint64_t i, len;
-    bt_value *map = NULL;
-    bt_value *hostname = NULL;
-    bt_value *session_name = NULL;
-    bt_value *btval = NULL;
     bool found = false;
 
-    len = bt_value_array_get_length(results);
-    for (i = 0; i < len; i++) {
-        const char *hostname_str = NULL;
-        const char *session_name_str = NULL;
-
-        map = bt_value_array_borrow_element_by_index(results, i);
-        hostname = bt_value_map_borrow_entry_value(map, "target-hostname");
-        if (!hostname) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                    "Error borrowing \"target-hostname\" entry.");
-            ret = -1;
-            goto end;
+    for (const auto value : results) {
+        const auto map = value.asMap();
+        const auto hostnameVal = map["target-hostname"];
+
+        if (!hostnameVal) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                         "Error borrowing \"target-hostname\" entry.");
+            return -1;
         }
-        session_name = bt_value_map_borrow_entry_value(map, "session-name");
-        if (!session_name) {
-            BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                    "Error borrowing \"session-name\" entry.");
-            ret = -1;
-            goto end;
+
+        const auto sessionNameVal = map["session-name"];
+
+        if (!sessionNameVal) {
+            BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                         "Error borrowing \"session-name\" entry.");
+            return -1;
         }
-        hostname_str = bt_value_string_get(hostname);
-        session_name_str = bt_value_string_get(session_name);
+
+        const auto hostname_str = hostnameVal->asString().value();
+        const auto session_name_str = sessionNameVal->asString().value();
 
         if (strcmp(session->hostname, hostname_str) == 0 &&
             strcmp(session->session_name, session_name_str) == 0) {
-            int64_t val;
             uint32_t streams = be32toh(session->streams);
             uint32_t clients = be32toh(session->clients);
 
             found = true;
 
-            btval = bt_value_map_borrow_entry_value(map, "stream-count");
-            if (!btval) {
-                BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                        "Error borrowing \"stream-count\" entry.");
-                ret = -1;
-                goto end;
+            const auto streamCountVal = map["stream-count"];
+
+            if (!streamCountVal) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                             "Error borrowing \"stream-count\" entry.");
+                return -1;
             }
-            val = bt_value_integer_unsigned_get(btval);
+
+            auto val = streamCountVal->asUnsignedInteger().value();
+
             /* sum */
             val += streams;
-            bt_value_integer_unsigned_set(btval, val);
-
-            btval = bt_value_map_borrow_entry_value(map, "client-count");
-            if (!btval) {
-                BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                        "Error borrowing \"client-count\" entry.");
-                ret = -1;
-                goto end;
+            streamCountVal->asUnsignedInteger().value(val);
+
+            const auto clientCountVal = map["client-count"];
+
+            if (!clientCountVal) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                             "Error borrowing \"client-count\" entry.");
+                return -1;
             }
-            val = bt_value_integer_unsigned_get(btval);
+
+            val = clientCountVal->asUnsignedInteger().value();
+
             /* max */
-            val = bt_max_t(int64_t, clients, val);
-            bt_value_integer_unsigned_set(btval, val);
+            val = std::max<uint64_t>(clients, val);
+            clientCountVal->asUnsignedInteger().value(val);
         }
 
         if (found) {
             break;
         }
     }
-end:
+
     *_found = found;
-    return ret;
+    return 0;
 }
 
-static int list_append_session(bt_value *results, GString *base_url,
+static int list_append_session(const bt2::ArrayValue results, const std::string& base_url,
                                const struct lttng_viewer_session *session,
                                struct live_viewer_connection *viewer_connection)
 {
     int ret = 0;
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    bt_value_map_insert_entry_status insert_status;
-    bt_value_array_append_element_status append_status;
-    bt_value *map = NULL;
-    GString *url = NULL;
     bool found = false;
 
     /*
@@ -641,60 +468,34 @@ static int list_append_session(bt_value *results, GString *base_url,
      */
     ret = list_update_session(results, session, &found, viewer_connection);
     if (ret || found) {
-        goto end;
+        return ret;
     }
 
-    map = bt_value_map_create();
-    if (!map) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error creating map value.");
-        ret = -1;
-        goto end;
-    }
+    const auto map = bt2::MapValue::create();
 
-    if (base_url->len < 1) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error: base_url length smaller than 1.");
-        ret = -1;
-        goto end;
+    if (base_url.empty()) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Error: base_url empty.");
+        return -1;
     }
+
     /*
      * key = "url",
      * value = <string>,
      */
-    url = g_string_new(base_url->str);
-    g_string_append(url, "/host/");
-    g_string_append(url, session->hostname);
-    g_string_append_c(url, '/');
-    g_string_append(url, session->session_name);
-
-    insert_status = bt_value_map_insert_string_entry(map, "url", url->str);
-    if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error inserting \"url\" entry.");
-        ret = -1;
-        goto end;
-    }
+    map->insert("url",
+                fmt::format("{}/host/{}/{}", base_url, session->hostname, session->session_name));
 
     /*
      * key = "target-hostname",
      * value = <string>,
      */
-    insert_status = bt_value_map_insert_string_entry(map, "target-hostname", session->hostname);
-    if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                                        "Error inserting \"target-hostname\" entry.");
-        ret = -1;
-        goto end;
-    }
+    map->insert("target-hostname", session->hostname);
 
     /*
      * key = "session-name",
      * value = <string>,
      */
-    insert_status = bt_value_map_insert_string_entry(map, "session-name", session->session_name);
-    if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error inserting \"session-name\" entry.");
-        ret = -1;
-        goto end;
-    }
+    map->insert("session-name", session->session_name);
 
     /*
      * key = "timer-us",
@@ -703,12 +504,7 @@ static int list_append_session(bt_value *results, GString *base_url,
     {
         uint32_t live_timer = be32toh(session->live_timer);
 
-        insert_status = bt_value_map_insert_unsigned_integer_entry(map, "timer-us", live_timer);
-        if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-            BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error inserting \"timer-us\" entry.");
-            ret = -1;
-            goto end;
-        }
+        map->insert("timer-us", (uint64_t) live_timer);
     }
 
     /*
@@ -718,13 +514,7 @@ static int list_append_session(bt_value *results, GString *base_url,
     {
         uint32_t streams = be32toh(session->streams);
 
-        insert_status = bt_value_map_insert_unsigned_integer_entry(map, "stream-count", streams);
-        if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-            BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                                            "Error inserting \"stream-count\" entry.");
-            ret = -1;
-            goto end;
-        }
+        map->insert("stream-count", (uint64_t) streams);
     }
 
     /*
@@ -734,27 +524,11 @@ static int list_append_session(bt_value *results, GString *base_url,
     {
         uint32_t clients = be32toh(session->clients);
 
-        insert_status = bt_value_map_insert_unsigned_integer_entry(map, "client-count", clients);
-        if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-            BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                                            "Error inserting \"client-count\" entry.");
-            ret = -1;
-            goto end;
-        }
-    }
-
-    append_status = bt_value_array_append_element(results, map);
-    if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error appending map to results.");
-        ret = -1;
+        map->insert("client-count", (uint64_t) clients);
     }
 
-end:
-    if (url) {
-        g_string_free(url, true);
-    }
-    BT_VALUE_PUT_REF_AND_RESET(map);
-    return ret;
+    results.append(*map);
+    return 0;
 }
 
 /*
@@ -793,28 +567,17 @@ end:
  *   }
  */
 
-BT_HIDDEN
-bt_component_class_query_method_status
-live_viewer_connection_list_sessions(struct live_viewer_connection *viewer_connection,
-                                     const bt_value **user_result)
+bt2::Value::Shared
+live_viewer_connection_list_sessions(struct live_viewer_connection *viewer_connection)
 {
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-    bt_component_class_query_method_status status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-    bt_value *result = NULL;
     enum lttng_live_viewer_status viewer_status;
     struct lttng_viewer_cmd cmd;
     struct lttng_viewer_list_sessions list;
     uint32_t i, sessions_count;
+    auto result = bt2::ArrayValue::create();
 
-    result = bt_value_array_create();
-    if (!result) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error creating array");
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-        goto error;
-    }
-
-    BT_LOGD("Requesting list of sessions: cmd=%s",
-            lttng_viewer_command_string(LTTNG_VIEWER_LIST_SESSIONS));
+    BT_CPPLOGD_SPEC(viewer_connection->logger, "Requesting list of sessions: cmd={}",
+                    LTTNG_VIEWER_LIST_SESSIONS);
 
     cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS);
     cmd.data_size = htobe64((uint64_t) 0);
@@ -822,22 +585,18 @@ live_viewer_connection_list_sessions(struct live_viewer_connection *viewer_conne
 
     viewer_status = lttng_live_send(viewer_connection, &cmd, sizeof(cmd));
     if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error sending list sessions command");
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(viewer_connection->logger, bt2::Error,
+                                               "Error sending list sessions command");
     } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
-        goto error;
+        throw bt2c::TryAgain {};
     }
 
     viewer_status = lttng_live_recv(viewer_connection, &list, sizeof(list));
     if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-        BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error receiving session list");
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(viewer_connection->logger, bt2::Error,
+                                               "Error receiving session list");
     } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-        status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
-        goto error;
+        throw bt2c::TryAgain {};
     }
 
     sessions_count = be32toh(list.sessions_count);
@@ -846,29 +605,21 @@ live_viewer_connection_list_sessions(struct live_viewer_connection *viewer_conne
 
         viewer_status = lttng_live_recv(viewer_connection, &lsession, sizeof(lsession));
         if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-            BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error receiving session:");
-            status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-            goto error;
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(viewer_connection->logger, bt2::Error,
+                                                   "Error receiving session:");
         } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-            status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
-            goto error;
+            throw bt2c::TryAgain {};
         }
 
         lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0';
         lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0';
-        if (list_append_session(result, viewer_connection->url, &lsession, viewer_connection)) {
-            BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "Error appending session");
-            status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-            goto error;
+        if (list_append_session(*result, viewer_connection->url, &lsession, viewer_connection)) {
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(viewer_connection->logger, bt2::Error,
+                                                   "Error appending session");
         }
     }
 
-    *user_result = result;
-    goto end;
-error:
-    BT_VALUE_PUT_REF_AND_RESET(result);
-end:
-    return status;
+    return result;
 }
 
 static enum lttng_live_viewer_status
@@ -880,12 +631,11 @@ lttng_live_query_session_ids(struct lttng_live_msg_iter *lttng_live_msg_iter)
     uint32_t i, sessions_count;
     uint64_t session_id;
     enum lttng_live_viewer_status status;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
-    bt_self_component *self_comp = viewer_connection->self_comp;
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+    live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection.get();
 
-    BT_COMP_LOGD("Asking the relay daemon for the list of sessions: cmd=%s",
-                 lttng_viewer_command_string(LTTNG_VIEWER_LIST_SESSIONS));
+    BT_CPPLOGD_SPEC(viewer_connection->logger,
+                    "Asking the relay daemon for the list of sessions: cmd={}",
+                    LTTNG_VIEWER_LIST_SESSIONS);
 
     cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS);
     cmd.data_size = htobe64((uint64_t) 0);
@@ -893,30 +643,31 @@ lttng_live_query_session_ids(struct lttng_live_msg_iter *lttng_live_msg_iter)
 
     status = lttng_live_send(viewer_connection, &cmd, sizeof(cmd));
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_send_status(self_comp, self_comp_class, status, "list sessions command");
-        goto end;
+        viewer_handle_send_status(status, "list sessions command");
+        return status;
     }
 
     status = lttng_live_recv(viewer_connection, &list, sizeof(list));
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, self_comp_class, status, "session list reply");
-        goto end;
+        viewer_handle_recv_status(status, "session list reply");
+        return status;
     }
 
     sessions_count = be32toh(list.sessions_count);
     for (i = 0; i < sessions_count; i++) {
         status = lttng_live_recv(viewer_connection, &lsession, sizeof(lsession));
         if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-            viewer_handle_recv_status(self_comp, self_comp_class, status, "session reply");
-            goto end;
+            viewer_handle_recv_status(status, "session reply");
+            return status;
         }
         lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0';
         lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0';
         session_id = be64toh(lsession.id);
 
-        BT_COMP_LOGI("Adding session to internal list: "
-                     "session-id=%" PRIu64 ", hostname=\"%s\", session-name=\"%s\"",
-                     session_id, lsession.hostname, lsession.session_name);
+        BT_CPPLOGI_SPEC(viewer_connection->logger,
+                        "Adding session to internal list: "
+                        "session-id={}, hostname=\"{}\", session-name=\"{}\"",
+                        session_id, lsession.hostname, lsession.session_name);
 
         if ((strncmp(lsession.session_name, viewer_connection->session_name->str,
                      LTTNG_VIEWER_NAME_MAX) == 0) &&
@@ -924,32 +675,26 @@ lttng_live_query_session_ids(struct lttng_live_msg_iter *lttng_live_msg_iter)
                      LTTNG_VIEWER_HOST_NAME_MAX) == 0)) {
             if (lttng_live_add_session(lttng_live_msg_iter, session_id, lsession.hostname,
                                        lsession.session_name)) {
-                BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to add live session");
-                status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                goto end;
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                             "Failed to add live session");
+                return LTTNG_LIVE_VIEWER_STATUS_ERROR;
             }
         }
     }
 
-    status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-end:
-    return status;
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
-BT_HIDDEN
 enum lttng_live_viewer_status
 lttng_live_create_viewer_session(struct lttng_live_msg_iter *lttng_live_msg_iter)
 {
     struct lttng_viewer_cmd cmd;
     struct lttng_viewer_create_session_response resp;
     enum lttng_live_viewer_status status;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
-    bt_self_component *self_comp = viewer_connection->self_comp;
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+    live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection.get();
 
-    BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class, "Creating a viewer session: cmd=%s",
-                               lttng_viewer_command_string(LTTNG_VIEWER_CREATE_SESSION));
+    BT_CPPLOGD_SPEC(viewer_connection->logger, "Creating a viewer session: cmd={}",
+                    LTTNG_VIEWER_CREATE_SESSION);
 
     cmd.cmd = htobe32(LTTNG_VIEWER_CREATE_SESSION);
     cmd.data_size = htobe64((uint64_t) 0);
@@ -957,32 +702,31 @@ lttng_live_create_viewer_session(struct lttng_live_msg_iter *lttng_live_msg_iter
 
     status = lttng_live_send(viewer_connection, &cmd, sizeof(cmd));
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_send_status(self_comp, self_comp_class, status, "create session command");
-        goto end;
+        viewer_handle_send_status(status, "create session command");
+        return status;
     }
 
     status = lttng_live_recv(viewer_connection, &resp, sizeof(resp));
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, self_comp_class, status, "create session reply");
-        goto end;
+        viewer_handle_recv_status(status, "create session reply");
+        return status;
     }
 
     if (be32toh(resp.status) != LTTNG_VIEWER_CREATE_SESSION_OK) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Error creating viewer session");
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Error creating viewer session");
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     }
 
     status = lttng_live_query_session_ids(lttng_live_msg_iter);
     if (status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to query live viewer session ids");
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                     "Failed to query live viewer session ids");
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     } else if (status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-        goto end;
+        return LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
     }
 
-end:
-    return status;
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
 static enum lttng_live_viewer_status receive_streams(struct lttng_live_session *session,
@@ -992,10 +736,9 @@ static enum lttng_live_viewer_status receive_streams(struct lttng_live_session *
     uint32_t i;
     struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter;
     enum lttng_live_viewer_status status;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
-    bt_self_component *self_comp = viewer_connection->self_comp;
+    live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection.get();
 
-    BT_COMP_LOGI("Getting %" PRIu32 " new streams", stream_count);
+    BT_CPPLOGI_SPEC(viewer_connection->logger, "Getting {} new streams", stream_count);
     for (i = 0; i < stream_count; i++) {
         struct lttng_viewer_stream stream;
         struct lttng_live_stream_iterator *live_stream;
@@ -1004,8 +747,8 @@ static enum lttng_live_viewer_status receive_streams(struct lttng_live_session *
 
         status = lttng_live_recv(viewer_connection, &stream, sizeof(stream));
         if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-            viewer_handle_recv_status(self_comp, NULL, status, "stream reply");
-            goto end;
+            viewer_handle_recv_status(status, "stream reply");
+            return status;
         }
         stream.path_name[LTTNG_VIEWER_PATH_MAX - 1] = '\0';
         stream.channel_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0';
@@ -1013,34 +756,29 @@ static enum lttng_live_viewer_status receive_streams(struct lttng_live_session *
         ctf_trace_id = be64toh(stream.ctf_trace_id);
 
         if (stream.metadata_flag) {
-            BT_COMP_LOGI("    metadata stream %" PRIu64 " : %s/%s", stream_id, stream.path_name,
-                         stream.channel_name);
-            if (lttng_live_metadata_create_stream(session, ctf_trace_id, stream_id,
-                                                  stream.path_name)) {
-                BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Error creating metadata stream");
-                status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                goto end;
+            BT_CPPLOGI_SPEC(viewer_connection->logger, "    metadata stream {} : {}/{}", stream_id,
+                            stream.path_name, stream.channel_name);
+            if (lttng_live_metadata_create_stream(session, ctf_trace_id, stream_id)) {
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                             "Error creating metadata stream");
+                return LTTNG_LIVE_VIEWER_STATUS_ERROR;
             }
             session->lazy_stream_msg_init = true;
         } else {
-            BT_COMP_LOGI("    stream %" PRIu64 " : %s/%s", stream_id, stream.path_name,
-                         stream.channel_name);
+            BT_CPPLOGI_SPEC(viewer_connection->logger, "    stream {} : {}/{}", stream_id,
+                            stream.path_name, stream.channel_name);
             live_stream =
                 lttng_live_stream_iterator_create(session, ctf_trace_id, stream_id, self_msg_iter);
             if (!live_stream) {
-                BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Error creating stream");
-                status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                goto end;
+                BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Error creating stream");
+                return LTTNG_LIVE_VIEWER_STATUS_ERROR;
             }
         }
     }
-    status = LTTNG_LIVE_VIEWER_STATUS_OK;
 
-end:
-    return status;
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
-BT_HIDDEN
 enum lttng_live_viewer_status lttng_live_session_attach(struct lttng_live_session *session,
                                                         bt_self_message_iterator *self_msg_iter)
 {
@@ -1049,16 +787,15 @@ enum lttng_live_viewer_status lttng_live_session_attach(struct lttng_live_sessio
     struct lttng_viewer_attach_session_request rq;
     struct lttng_viewer_attach_session_response rp;
     struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
-    bt_self_component *self_comp = viewer_connection->self_comp;
+    live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection.get();
     uint64_t session_id = session->id;
     uint32_t streams_count;
     const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
     char cmd_buf[cmd_buf_len];
 
-    BT_COMP_LOGD("Attaching to session: cmd=%s, session-id=%" PRIu64 ", seek=%s",
-                 lttng_viewer_command_string(LTTNG_VIEWER_ATTACH_SESSION), session_id,
-                 lttng_viewer_seek_string(LTTNG_VIEWER_SEEK_LAST));
+    BT_CPPLOGD_SPEC(viewer_connection->logger,
+                    "Attaching to session: cmd={}, session-id={}, seek={}",
+                    LTTNG_VIEWER_ATTACH_SESSION, session_id, LTTNG_VIEWER_SEEK_LAST);
 
     cmd.cmd = htobe32(LTTNG_VIEWER_ATTACH_SESSION);
     cmd.data_size = htobe64((uint64_t) sizeof(rq));
@@ -1079,14 +816,14 @@ enum lttng_live_viewer_status lttng_live_session_attach(struct lttng_live_sessio
     memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
     status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_send_status(self_comp, NULL, status, "attach session command");
-        goto end;
+        viewer_handle_send_status(status, "attach session command");
+        return status;
     }
 
     status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, NULL, status, "attach session reply");
-        goto end;
+        viewer_handle_recv_status(status, "attach session reply");
+        return status;
     }
 
     streams_count = be32toh(rp.streams_count);
@@ -1094,25 +831,23 @@ enum lttng_live_viewer_status lttng_live_session_attach(struct lttng_live_sessio
     case LTTNG_VIEWER_ATTACH_OK:
         break;
     case LTTNG_VIEWER_ATTACH_UNK:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Session id %" PRIu64 " is unknown", session_id);
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Session id {} is unknown",
+                                     session_id);
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     case LTTNG_VIEWER_ATTACH_ALREADY:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "There is already a viewer attached to this session");
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                     "There is already a viewer attached to this session");
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     case LTTNG_VIEWER_ATTACH_NOT_LIVE:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Not a live session");
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Not a live session");
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     case LTTNG_VIEWER_ATTACH_SEEK_ERR:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Wrong seek parameter");
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Wrong seek parameter");
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     default:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Unknown attach return code %u", be32toh(rp.status));
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Unknown attach return code {}",
+                                     be32toh(rp.status));
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     }
 
     /* We receive the initial list of streams. */
@@ -1121,10 +856,10 @@ enum lttng_live_viewer_status lttng_live_session_attach(struct lttng_live_sessio
     case LTTNG_LIVE_VIEWER_STATUS_OK:
         break;
     case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:
-        goto end;
+        return status;
     case LTTNG_LIVE_VIEWER_STATUS_ERROR:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Error receiving streams");
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Error receiving streams");
+        return status;
     default:
         bt_common_abort();
     }
@@ -1132,11 +867,9 @@ enum lttng_live_viewer_status lttng_live_session_attach(struct lttng_live_sessio
     session->attached = true;
     session->new_streams_needed = false;
 
-end:
-    return status;
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
-BT_HIDDEN
 enum lttng_live_viewer_status lttng_live_session_detach(struct lttng_live_session *session)
 {
     struct lttng_viewer_cmd cmd;
@@ -1144,8 +877,7 @@ enum lttng_live_viewer_status lttng_live_session_detach(struct lttng_live_sessio
     struct lttng_viewer_detach_session_request rq;
     struct lttng_viewer_detach_session_response rp;
     struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter;
-    bt_self_component *self_comp = session->self_comp;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
+    live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection.get();
     uint64_t session_id = session->id;
     const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
     char cmd_buf[cmd_buf_len];
@@ -1159,8 +891,8 @@ enum lttng_live_viewer_status lttng_live_session_detach(struct lttng_live_sessio
         return LTTNG_LIVE_VIEWER_STATUS_OK;
     }
 
-    BT_COMP_LOGD("Detaching from session: cmd=%s, session-id=%" PRIu64,
-                 lttng_viewer_command_string(LTTNG_VIEWER_DETACH_SESSION), session_id);
+    BT_CPPLOGD_SPEC(viewer_connection->logger, "Detaching from session: cmd={}, session-id={}",
+                    LTTNG_VIEWER_DETACH_SESSION, session_id);
 
     cmd.cmd = htobe32(LTTNG_VIEWER_DETACH_SESSION);
     cmd.data_size = htobe64((uint64_t) sizeof(rq));
@@ -1178,65 +910,56 @@ enum lttng_live_viewer_status lttng_live_session_detach(struct lttng_live_sessio
     memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
     status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_send_status(self_comp, NULL, status, "detach session command");
-        goto end;
+        viewer_handle_send_status(status, "detach session command");
+        return status;
     }
 
     status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
     if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, NULL, status, "detach session reply");
-        goto end;
+        viewer_handle_recv_status(status, "detach session reply");
+        return status;
     }
 
     switch (be32toh(rp.status)) {
     case LTTNG_VIEWER_DETACH_SESSION_OK:
         break;
     case LTTNG_VIEWER_DETACH_SESSION_UNK:
-        BT_COMP_LOGW("Session id %" PRIu64 " is unknown", session_id);
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGW_SPEC(viewer_connection->logger, "Session id {} is unknown", session_id);
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     case LTTNG_VIEWER_DETACH_SESSION_ERR:
-        BT_COMP_LOGW("Error detaching session id %" PRIu64 "", session_id);
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGW_SPEC(viewer_connection->logger, "Error detaching session id {}", session_id);
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     default:
-        BT_COMP_LOGE("Unknown detach return code %u", be32toh(rp.status));
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_SPEC(viewer_connection->logger, "Unknown detach return code {}",
+                        be32toh(rp.status));
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     }
 
     session->attached = false;
 
-    status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-end:
-    return status;
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
-BT_HIDDEN
 enum lttng_live_get_one_metadata_status
-lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, FILE *fp, size_t *reply_len)
+lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, std::vector<char>& buf)
 {
     uint64_t len = 0;
-    enum lttng_live_get_one_metadata_status status;
     enum lttng_live_viewer_status viewer_status;
     struct lttng_viewer_cmd cmd;
     struct lttng_viewer_get_metadata rq;
     struct lttng_viewer_metadata_packet rp;
-    gchar *data = NULL;
-    ssize_t writelen;
+    std::vector<char> data;
     struct lttng_live_session *session = trace->session;
     struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter;
-    struct lttng_live_metadata *metadata = trace->metadata;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
-    bt_self_component *self_comp = viewer_connection->self_comp;
+    struct lttng_live_metadata *metadata = trace->metadata.get();
+    live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection.get();
     const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
     char cmd_buf[cmd_buf_len];
 
-    BT_COMP_LOGD("Requesting new metadata for trace:"
-                 "cmd=%s, trace-id=%" PRIu64 ", metadata-stream-id=%" PRIu64,
-                 lttng_viewer_command_string(LTTNG_VIEWER_GET_METADATA), trace->id,
-                 metadata->stream_id);
+    BT_CPPLOGD_SPEC(viewer_connection->logger,
+                    "Requesting new metadata for trace:"
+                    "cmd={}, trace-id={}, metadata-stream-id={}",
+                    LTTNG_VIEWER_GET_METADATA, trace->id, metadata->stream_id);
 
     rq.stream_id = htobe64(metadata->stream_id);
     cmd.cmd = htobe32(LTTNG_VIEWER_GET_METADATA);
@@ -1252,39 +975,35 @@ lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, FILE *fp, siz
     memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
     viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_send_status(self_comp, NULL, viewer_status, "get metadata command");
-        status = (enum lttng_live_get_one_metadata_status) viewer_status;
-        goto end;
+        viewer_handle_send_status(viewer_status, "get metadata command");
+        return (lttng_live_get_one_metadata_status) viewer_status;
     }
 
     viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, NULL, viewer_status, "get metadata reply");
-        status = (enum lttng_live_get_one_metadata_status) viewer_status;
-        goto end;
+        viewer_handle_recv_status(viewer_status, "get metadata reply");
+        return (lttng_live_get_one_metadata_status) viewer_status;
     }
 
     switch (be32toh(rp.status)) {
     case LTTNG_VIEWER_METADATA_OK:
-        BT_COMP_LOGD("Received get_metadata response: ok");
+        BT_CPPLOGD_SPEC(viewer_connection->logger, "Received get_metadata response: ok");
         break;
     case LTTNG_VIEWER_NO_NEW_METADATA:
-        BT_COMP_LOGD("Received get_metadata response: no new");
-        status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_END;
-        goto end;
+        BT_CPPLOGD_SPEC(viewer_connection->logger, "Received get_metadata response: no new");
+        return LTTNG_LIVE_GET_ONE_METADATA_STATUS_END;
     case LTTNG_VIEWER_METADATA_ERR:
         /*
              * The Relayd cannot find this stream id. Maybe its
              * gone already. This can happen in short lived UST app
              * in a per-pid session.
              */
-        BT_COMP_LOGD("Received get_metadata response: error");
-        status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED;
-        goto end;
+        BT_CPPLOGD_SPEC(viewer_connection->logger, "Received get_metadata response: error");
+        return LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED;
     default:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Received get_metadata response: unknown");
-        status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                     "Received get_metadata response: unknown");
+        return LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
     }
 
     len = be64toh(rp.len);
@@ -1294,49 +1013,32 @@ lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, FILE *fp, siz
          * length of 0. This means we must try again. This scenario
          * arises when a clear command is performed on an lttng session.
          */
-        BT_COMP_LOGD(
+        BT_CPPLOGD_SPEC(
+            viewer_connection->logger,
             "Expecting a metadata packet of size 0. Retry to get a packet from the relay.");
-        goto empty_metadata_packet_retry;
+        return LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK;
     }
 
-    BT_COMP_LOGD("Writing %" PRIu64 " bytes to metadata", len);
+    BT_CPPLOGD_SPEC(viewer_connection->logger, "Writing {} bytes to metadata", len);
     if (len <= 0) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Erroneous response length");
-        status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Erroneous response length");
+        return LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
     }
 
-    data = g_new0(gchar, len);
-    if (!data) {
-        BT_COMP_LOGE_APPEND_CAUSE_ERRNO(self_comp, "Failed to allocate data buffer", ".");
-        status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
-        goto end;
-    }
+    data.resize(len);
 
-    viewer_status = lttng_live_recv(viewer_connection, data, len);
+    viewer_status = lttng_live_recv(viewer_connection, data.data(), len);
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, NULL, viewer_status, "get metadata packet");
-        status = (enum lttng_live_get_one_metadata_status) viewer_status;
-        goto end;
+        viewer_handle_recv_status(viewer_status, "get metadata packet");
+        return (lttng_live_get_one_metadata_status) viewer_status;
     }
 
     /*
      * Write the metadata to the file handle.
      */
-    writelen = fwrite(data, sizeof(uint8_t), len, fp);
-    if (writelen != len) {
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Writing in the metadata file stream");
-        status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
-        goto end;
-    }
-
-empty_metadata_packet_retry:
-    *reply_len = len;
-    status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK;
+    buf.insert(buf.end(), data.begin(), data.end());
 
-end:
-    g_free(data);
-    return status;
+    return LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK;
 }
 
 /*
@@ -1358,20 +1060,15 @@ static void lttng_index_to_packet_index(struct lttng_viewer_index *lindex,
 
 static void lttng_live_need_new_streams(struct lttng_live_msg_iter *lttng_live_msg_iter)
 {
-    uint64_t session_idx;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
-
-    for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; session_idx++) {
-        struct lttng_live_session *session =
-            (lttng_live_session *) g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
-        BT_COMP_LOGD("Marking session as needing new streams: "
-                     "session-id=%" PRIu64,
-                     session->id);
+    for (const auto& session : lttng_live_msg_iter->sessions) {
+        BT_CPPLOGD_SPEC(lttng_live_msg_iter->logger,
+                        "Marking session as needing new streams: "
+                        "session-id={}",
+                        session->id);
         session->new_streams_needed = true;
     }
 }
 
-BT_HIDDEN
 enum lttng_live_iterator_status
 lttng_live_get_next_index(struct lttng_live_msg_iter *lttng_live_msg_iter,
                           struct lttng_live_stream_iterator *stream, struct packet_index *index)
@@ -1380,18 +1077,16 @@ lttng_live_get_next_index(struct lttng_live_msg_iter *lttng_live_msg_iter,
     struct lttng_viewer_get_next_index rq;
     enum lttng_live_viewer_status viewer_status;
     struct lttng_viewer_index rp;
-    enum lttng_live_iterator_status status;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
-    bt_self_component *self_comp = viewer_connection->self_comp;
+    live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection.get();
     struct lttng_live_trace *trace = stream->trace;
     const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
     char cmd_buf[cmd_buf_len];
     uint32_t flags, rp_status;
 
-    BT_COMP_LOGD("Requesting next index for stream: cmd=%s, "
-                 "viewer-stream-id=%" PRIu64,
-                 lttng_viewer_command_string(LTTNG_VIEWER_GET_NEXT_INDEX),
-                 stream->viewer_stream_id);
+    BT_CPPLOGD_SPEC(viewer_connection->logger,
+                    "Requesting next index for stream: cmd={}, "
+                    "viewer-stream-id={}",
+                    LTTNG_VIEWER_GET_NEXT_INDEX, stream->viewer_stream_id);
     cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEXT_INDEX);
     cmd.data_size = htobe64((uint64_t) sizeof(rq));
     cmd.cmd_version = htobe32(0);
@@ -1409,22 +1104,31 @@ lttng_live_get_next_index(struct lttng_live_msg_iter *lttng_live_msg_iter,
 
     viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_send_status(self_comp, NULL, viewer_status, "get next index command");
-        goto error;
+        viewer_handle_send_status(viewer_status, "get next index command");
+        return viewer_status_to_live_iterator_status(viewer_status);
     }
 
     viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, NULL, viewer_status, "get next index reply");
-        goto error;
+        viewer_handle_recv_status(viewer_status, "get next index reply");
+        return viewer_status_to_live_iterator_status(viewer_status);
     }
 
     flags = be32toh(rp.flags);
     rp_status = be32toh(rp.status);
 
-    BT_COMP_LOGD("Received response from relay daemon: cmd=%s, response=%s",
-                 lttng_viewer_command_string(LTTNG_VIEWER_GET_NEXT_INDEX),
-                 lttng_viewer_next_index_return_code_string(rp_status));
+    BT_CPPLOGD_SPEC(
+        viewer_connection->logger, "Received response from relay daemon: cmd=%s, response={}",
+        LTTNG_VIEWER_GET_NEXT_INDEX, static_cast<lttng_viewer_next_index_return_code>(rp_status));
+
+    if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) {
+        BT_CPPLOGD_SPEC(viewer_connection->logger,
+                        "Marking all sessions as possibly needing new streams: "
+                        "response={}, response-flag=NEW_STREAM",
+                        static_cast<lttng_viewer_next_index_return_code>(rp_status));
+        lttng_live_need_new_streams(lttng_live_msg_iter);
+    }
+
     switch (rp_status) {
     case LTTNG_VIEWER_INDEX_INACTIVE:
     {
@@ -1441,8 +1145,7 @@ lttng_live_get_next_index(struct lttng_live_msg_iter *lttng_live_msg_iter,
             stream->ctf_stream_class_id.is_set = true;
         }
         lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_QUIESCENT);
-        status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-        break;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     }
     case LTTNG_VIEWER_INDEX_OK:
     {
@@ -1456,77 +1159,60 @@ lttng_live_get_next_index(struct lttng_live_msg_iter *lttng_live_msg_iter,
             stream->ctf_stream_class_id.value = ctf_stream_class_id;
             stream->ctf_stream_class_id.is_set = true;
         }
-
         lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_DATA);
 
         if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) {
-            BT_COMP_LOGD("Marking trace as needing new metadata: "
-                         "response=%s, response-flag=NEW_METADATA, trace-id=%" PRIu64,
-                         lttng_viewer_next_index_return_code_string(rp_status), trace->id);
+            BT_CPPLOGD_SPEC(viewer_connection->logger,
+                            "Marking trace as needing new metadata: "
+                            "response={}, response-flag=NEW_METADATA, trace-id={}",
+                            static_cast<lttng_viewer_next_index_return_code>(rp_status), trace->id);
             trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
         }
-        if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) {
-            BT_COMP_LOGD("Marking all sessions as possibly needing new streams: "
-                         "response=%s, response-flag=NEW_STREAM",
-                         lttng_viewer_next_index_return_code_string(rp_status));
-            lttng_live_need_new_streams(lttng_live_msg_iter);
-        }
-        status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
         break;
     }
     case LTTNG_VIEWER_INDEX_RETRY:
         memset(index, 0, sizeof(struct packet_index));
         lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
-        status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-        goto end;
+        return LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
     case LTTNG_VIEWER_INDEX_HUP:
         memset(index, 0, sizeof(struct packet_index));
         index->offset = EOF;
         lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_EOF);
         stream->has_stream_hung_up = true;
-        status = LTTNG_LIVE_ITERATOR_STATUS_END;
-        break;
+        return LTTNG_LIVE_ITERATOR_STATUS_END;
     case LTTNG_VIEWER_INDEX_ERR:
         memset(index, 0, sizeof(struct packet_index));
         lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
-        status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        goto end;
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     default:
-        BT_COMP_LOGD("Received get_next_index response: unknown value");
+        BT_CPPLOGD_SPEC(viewer_connection->logger,
+                        "Received get_next_index response: unknown value");
         memset(index, 0, sizeof(struct packet_index));
         lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
-        status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        goto end;
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
-    goto end;
-
-error:
-    status = viewer_status_to_live_iterator_status(viewer_status);
-end:
-    return status;
 }
 
-BT_HIDDEN
 enum ctf_msg_iter_medium_status
 lttng_live_get_stream_bytes(struct lttng_live_msg_iter *lttng_live_msg_iter,
                             struct lttng_live_stream_iterator *stream, uint8_t *buf,
                             uint64_t offset, uint64_t req_len, uint64_t *recv_len)
 {
-    enum ctf_msg_iter_medium_status status;
     enum lttng_live_viewer_status viewer_status;
     struct lttng_viewer_trace_packet rp;
     struct lttng_viewer_cmd cmd;
     struct lttng_viewer_get_packet rq;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
-    bt_self_component *self_comp = viewer_connection->self_comp;
+    live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection.get();
     struct lttng_live_trace *trace = stream->trace;
     const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
     char cmd_buf[cmd_buf_len];
     uint32_t flags, rp_status;
 
-    BT_COMP_LOGD("Requesting data from stream: cmd=%s, "
-                 "offset=%" PRIu64 ", request-len=%" PRIu64,
-                 lttng_viewer_command_string(LTTNG_VIEWER_GET_PACKET), offset, req_len);
+    BT_CPPLOGD_SPEC(viewer_connection->logger,
+                    "Requesting data from stream: cmd={}, "
+                    "offset={}, request-len={}",
+                    LTTNG_VIEWER_GET_PACKET, offset, req_len);
 
     cmd.cmd = htobe32(LTTNG_VIEWER_GET_PACKET);
     cmd.data_size = htobe64((uint64_t) sizeof(rq));
@@ -1547,113 +1233,102 @@ lttng_live_get_stream_bytes(struct lttng_live_msg_iter *lttng_live_msg_iter,
 
     viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_send_status(self_comp, NULL, viewer_status, "get data packet command");
-        goto error_convert_status;
+        viewer_handle_send_status(viewer_status, "get data packet command");
+        return viewer_status_to_ctf_msg_iter_medium_status(viewer_status);
     }
 
     viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, NULL, viewer_status, "get data packet reply");
-        goto error_convert_status;
+        viewer_handle_recv_status(viewer_status, "get data packet reply");
+        return viewer_status_to_ctf_msg_iter_medium_status(viewer_status);
     }
 
     flags = be32toh(rp.flags);
     rp_status = be32toh(rp.status);
 
-    BT_COMP_LOGD("Received response from relay daemon: cmd=%s, response=%s",
-                 lttng_viewer_command_string(LTTNG_VIEWER_GET_PACKET),
-                 lttng_viewer_get_packet_return_code_string(rp_status));
+    BT_CPPLOGD_SPEC(
+        viewer_connection->logger, "Received response from relay daemon: cmd={}, response={}",
+        LTTNG_VIEWER_GET_PACKET, static_cast<lttng_viewer_get_packet_return_code>(rp_status));
     switch (rp_status) {
     case LTTNG_VIEWER_GET_PACKET_OK:
         req_len = be32toh(rp.len);
-        BT_COMP_LOGD("Got packet from relay daemon: response=%s, packet-len=%" PRIu64 "",
-                     lttng_viewer_get_packet_return_code_string(rp_status), req_len);
+        BT_CPPLOGD_SPEC(viewer_connection->logger,
+                        "Got packet from relay daemon: response={}, packet-len={}",
+                        static_cast<lttng_viewer_get_packet_return_code>(rp_status), req_len);
         break;
     case LTTNG_VIEWER_GET_PACKET_RETRY:
         /* Unimplemented by relay daemon */
-        status = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
-        goto end;
+        return CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
     case LTTNG_VIEWER_GET_PACKET_ERR:
         if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) {
-            BT_COMP_LOGD("Marking trace as needing new metadata: "
-                         "response=%s, response-flag=NEW_METADATA, trace-id=%" PRIu64,
-                         lttng_viewer_next_index_return_code_string(rp_status), trace->id);
+            BT_CPPLOGD_SPEC(viewer_connection->logger,
+                            "Marking trace as needing new metadata: "
+                            "response={}, response-flag=NEW_METADATA, trace-id={}",
+                            static_cast<lttng_viewer_get_packet_return_code>(rp_status), trace->id);
             trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
         }
         if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) {
-            BT_COMP_LOGD("Marking all sessions as possibly needing new streams: "
-                         "response=%s, response-flag=NEW_STREAM",
-                         lttng_viewer_next_index_return_code_string(rp_status));
+            BT_CPPLOGD_SPEC(viewer_connection->logger,
+                            "Marking all sessions as possibly needing new streams: "
+                            "response={}, response-flag=NEW_STREAM",
+                            static_cast<lttng_viewer_get_packet_return_code>(rp_status));
             lttng_live_need_new_streams(lttng_live_msg_iter);
         }
         if (flags & (LTTNG_VIEWER_FLAG_NEW_METADATA | LTTNG_VIEWER_FLAG_NEW_STREAM)) {
-            status = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
-            BT_COMP_LOGD("Reply with any one flags set means we should retry: response=%s",
-                         lttng_viewer_get_packet_return_code_string(rp_status));
-            goto end;
+            BT_CPPLOGD_SPEC(viewer_connection->logger,
+                            "Reply with any one flags set means we should retry: response={}",
+                            static_cast<lttng_viewer_get_packet_return_code>(rp_status));
+            return CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
         }
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Received get_data_packet response: error");
-        status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                     "Received get_data_packet response: error");
+        return CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
     case LTTNG_VIEWER_GET_PACKET_EOF:
-        status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-        goto end;
+        return CTF_MSG_ITER_MEDIUM_STATUS_EOF;
     default:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Received get_data_packet response: unknown (%d)",
-                                  rp_status);
-        status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                     "Received get_data_packet response: unknown ({})", rp_status);
+        return CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
     }
 
     if (req_len == 0) {
-        status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-        goto end;
+        return CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
     }
 
     viewer_status = lttng_live_recv(viewer_connection, buf, req_len);
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, NULL, viewer_status, "get data packet");
-        goto error_convert_status;
+        viewer_handle_recv_status(viewer_status, "get data packet");
+        return viewer_status_to_ctf_msg_iter_medium_status(viewer_status);
     }
     *recv_len = req_len;
 
-    status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-    goto end;
-
-error_convert_status:
-    status = viewer_status_to_ctf_msg_iter_medium_status(viewer_status);
-end:
-    return status;
+    return CTF_MSG_ITER_MEDIUM_STATUS_OK;
 }
 
 /*
  * Request new streams for a session.
  */
-BT_HIDDEN
 enum lttng_live_iterator_status
 lttng_live_session_get_new_streams(struct lttng_live_session *session,
                                    bt_self_message_iterator *self_msg_iter)
 {
-    enum lttng_live_iterator_status status = LTTNG_LIVE_ITERATOR_STATUS_OK;
     struct lttng_viewer_cmd cmd;
     struct lttng_viewer_new_streams_request rq;
     struct lttng_viewer_new_streams_response rp;
     struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter;
     enum lttng_live_viewer_status viewer_status;
-    struct live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection;
-    bt_self_component *self_comp = viewer_connection->self_comp;
+    live_viewer_connection *viewer_connection = lttng_live_msg_iter->viewer_connection.get();
     uint32_t streams_count;
     const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
     char cmd_buf[cmd_buf_len];
 
     if (!session->new_streams_needed) {
-        status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-        goto end;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     }
 
-    BT_COMP_LOGD("Requesting new streams for session: cmd=%s, "
-                 "session-id=%" PRIu64,
-                 lttng_viewer_command_string(LTTNG_VIEWER_GET_NEW_STREAMS), session->id);
+    BT_CPPLOGD_SPEC(viewer_connection->logger,
+                    "Requesting new streams for session: cmd={}, session-id={}",
+                    LTTNG_VIEWER_GET_NEW_STREAMS, session->id);
 
     cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEW_STREAMS);
     cmd.data_size = htobe64((uint64_t) sizeof(rq));
@@ -1672,16 +1347,14 @@ lttng_live_session_get_new_streams(struct lttng_live_session *session,
 
     viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_send_status(self_comp, NULL, viewer_status, "get new streams command");
-        status = viewer_status_to_live_iterator_status(viewer_status);
-        goto end;
+        viewer_handle_send_status(viewer_status, "get new streams command");
+        return viewer_status_to_live_iterator_status(viewer_status);
     }
 
     viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, NULL, viewer_status, "get new streams reply");
-        status = viewer_status_to_live_iterator_status(viewer_status);
-        goto end;
+        viewer_handle_recv_status(viewer_status, "get new streams reply");
+        return viewer_status_to_live_iterator_status(viewer_status);
     }
 
     streams_count = be32toh(rp.streams_count);
@@ -1692,144 +1365,75 @@ lttng_live_session_get_new_streams(struct lttng_live_session *session,
         break;
     case LTTNG_VIEWER_NEW_STREAMS_NO_NEW:
         session->new_streams_needed = false;
-        goto end;
+        return LTTNG_LIVE_ITERATOR_STATUS_OK;
     case LTTNG_VIEWER_NEW_STREAMS_HUP:
         session->new_streams_needed = false;
         session->closed = true;
-        status = LTTNG_LIVE_ITERATOR_STATUS_END;
-        goto end;
+        return LTTNG_LIVE_ITERATOR_STATUS_END;
     case LTTNG_VIEWER_NEW_STREAMS_ERR:
-        BT_COMP_LOGD("Received get_new_streams response: error");
-        status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGD_SPEC(viewer_connection->logger, "Received get_new_streams response: error");
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     default:
-        BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                  "Received get_new_streams response: Unknown:"
-                                  "return code %u",
-                                  be32toh(rp.status));
-        status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-        goto end;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                     "Received get_new_streams response: Unknown return code {}",
+                                     be32toh(rp.status));
+        return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
     }
 
     viewer_status = receive_streams(session, streams_count, self_msg_iter);
     if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-        viewer_handle_recv_status(self_comp, NULL, viewer_status, "new streams");
-        status = viewer_status_to_live_iterator_status(viewer_status);
-        goto end;
+        viewer_handle_recv_status(viewer_status, "new streams");
+        return viewer_status_to_live_iterator_status(viewer_status);
     }
 
-    status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-end:
-    return status;
+    return LTTNG_LIVE_ITERATOR_STATUS_OK;
 }
 
-BT_HIDDEN
-enum lttng_live_viewer_status live_viewer_connection_create(
-    bt_self_component *self_comp, bt_self_component_class *self_comp_class,
-    bt_logging_level log_level, const char *url, bool in_query,
-    struct lttng_live_msg_iter *lttng_live_msg_iter, struct live_viewer_connection **viewer)
+enum lttng_live_viewer_status
+live_viewer_connection_create(const char *url, bool in_query,
+                              struct lttng_live_msg_iter *lttng_live_msg_iter,
+                              const bt2c::Logger& parentLogger, live_viewer_connection::UP& viewer)
 {
-    struct live_viewer_connection *viewer_connection;
-    enum lttng_live_viewer_status status;
+    auto viewer_connection = bt2s::make_unique<live_viewer_connection>(parentLogger);
 
-    viewer_connection = g_new0(struct live_viewer_connection, 1);
-
-    if (bt_socket_init(log_level) != 0) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Failed to init socket");
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto error;
+    if (bt_socket_init(viewer_connection->logger) != 0) {
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger, "Failed to init socket");
+        return LTTNG_LIVE_VIEWER_STATUS_ERROR;
     }
 
-    viewer_connection->log_level = log_level;
-
-    viewer_connection->self_comp = self_comp;
-    viewer_connection->self_comp_class = self_comp_class;
-
     viewer_connection->control_sock = BT_INVALID_SOCKET;
     viewer_connection->port = -1;
     viewer_connection->in_query = in_query;
     viewer_connection->lttng_live_msg_iter = lttng_live_msg_iter;
-    viewer_connection->url = g_string_new(url);
-    if (!viewer_connection->url) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Failed to allocate URL buffer");
-        status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-        goto error;
-    }
+    viewer_connection->url = url;
+
+    BT_CPPLOGD_SPEC(viewer_connection->logger, "Establishing connection to url \"{}\"...", url);
+    const auto status = lttng_live_connect_viewer(viewer_connection.get());
 
-    BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
-                               "Establishing connection to url \"%s\"...", url);
-    status = lttng_live_connect_viewer(viewer_connection);
     /*
      * Only print error and append cause in case of error. not in case of
      * interruption.
      */
     if (status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-        BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                                "Failed to establish connection: "
-                                                "url=\"%s\"",
-                                                url);
-        goto error;
+        BT_CPPLOGE_APPEND_CAUSE_SPEC(viewer_connection->logger,
+                                     "Failed to establish connection: "
+                                     "url=\"{}\"",
+                                     url);
+        return status;
     } else if (status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-        goto error;
+        return status;
     }
-    BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
-                               "Connection to url \"%s\" is established", url);
-
-    *viewer = viewer_connection;
-    status = LTTNG_LIVE_VIEWER_STATUS_OK;
-    goto end;
+    BT_CPPLOGD_SPEC(viewer_connection->logger, "Connection to url \"{}\" is established", url);
 
-error:
-    if (viewer_connection) {
-        live_viewer_connection_destroy(viewer_connection);
-    }
-end:
-    return status;
+    viewer = std::move(viewer_connection);
+    return LTTNG_LIVE_VIEWER_STATUS_OK;
 }
 
-BT_HIDDEN
-void live_viewer_connection_destroy(struct live_viewer_connection *viewer_connection)
+live_viewer_connection::~live_viewer_connection()
 {
-    bt_self_component *self_comp = viewer_connection->self_comp;
-    bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-
-    if (!viewer_connection) {
-        goto end;
-    }
-
-    BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
-                               "Closing connection to relay:"
-                               "relay-url=\"%s\"",
-                               viewer_connection->url->str);
-
-    lttng_live_disconnect_viewer(viewer_connection);
-
-    if (viewer_connection->url) {
-        g_string_free(viewer_connection->url, true);
-    }
+    BT_CPPLOGD_SPEC(this->logger, "Closing connection to relay: relay-url=\"{}\"", this->url);
 
-    if (viewer_connection->relay_hostname) {
-        g_string_free(viewer_connection->relay_hostname, true);
-    }
-
-    if (viewer_connection->target_hostname) {
-        g_string_free(viewer_connection->target_hostname, true);
-    }
-
-    if (viewer_connection->session_name) {
-        g_string_free(viewer_connection->session_name, true);
-    }
-
-    if (viewer_connection->proto) {
-        g_string_free(viewer_connection->proto, true);
-    }
-
-    g_free(viewer_connection);
+    viewer_connection_close_socket(this);
 
     bt_socket_fini();
-
-end:
-    return;
 }
index 2943e1099c1d73c0e31e1f6e78628a17b2ad57f4..0f284581e1de33328f631544906d4c89be823adc 100644 (file)
@@ -7,16 +7,17 @@
 #ifndef LTTNG_LIVE_VIEWER_CONNECTION_H
 #define LTTNG_LIVE_VIEWER_CONNECTION_H
 
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
+#include <string>
 
 #include <glib.h>
+#include <stdint.h>
 
 #include <babeltrace2/babeltrace.h>
 
-#include "common/macros.h"
-#include "compat/socket.h"
+#include "compat/socket.hpp"
+#include "cpp-common/bt2/value.hpp"
+#include "cpp-common/bt2c/glib-up.hpp"
+#include "cpp-common/bt2c/logging.hpp"
 
 #define LTTNG_DEFAULT_NETWORK_VIEWER_PORT 5344
 
@@ -48,29 +49,34 @@ enum lttng_live_get_one_metadata_status
     LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED = -3,
 };
 
-struct lttng_live_component;
-
 struct live_viewer_connection
 {
-    bt_logging_level log_level;
-    bt_self_component *self_comp;
-    bt_self_component_class *self_comp_class;
+    using UP = std::unique_ptr<live_viewer_connection>;
+
+    explicit live_viewer_connection(const bt2c::Logger& parentLogger) :
+        logger {parentLogger, "PLUGIN/SRC.CTF.LTTNG-LIVE/VIEWER"}
+    {
+    }
 
-    GString *url;
+    ~live_viewer_connection();
 
-    GString *relay_hostname;
-    GString *target_hostname;
-    GString *session_name;
-    GString *proto;
+    bt2c::Logger logger;
 
-    BT_SOCKET control_sock;
-    int port;
+    std::string url;
 
-    int32_t major;
-    int32_t minor;
+    bt2c::GStringUP relay_hostname;
+    bt2c::GStringUP target_hostname;
+    bt2c::GStringUP session_name;
+    bt2c::GStringUP proto;
 
-    bool in_query;
-    struct lttng_live_msg_iter *lttng_live_msg_iter;
+    BT_SOCKET control_sock {};
+    int port = 0;
+
+    int32_t major = 0;
+    int32_t minor = 0;
+
+    bool in_query = false;
+    struct lttng_live_msg_iter *lttng_live_msg_iter = nullptr;
 };
 
 struct packet_index_time
@@ -95,19 +101,14 @@ struct packet_index
 };
 
 enum lttng_live_viewer_status
-live_viewer_connection_create(bt_self_component *self_comp,
-                              bt_self_component_class *self_comp_class, bt_logging_level log_level,
-                              const char *url, bool in_query,
+live_viewer_connection_create(const char *url, bool in_query,
                               struct lttng_live_msg_iter *lttng_live_msg_iter,
-                              struct live_viewer_connection **viewer_connection);
-
-void live_viewer_connection_destroy(struct live_viewer_connection *conn);
+                              const bt2c::Logger& parentLogger, live_viewer_connection::UP& viewer);
 
 enum lttng_live_viewer_status
 lttng_live_create_viewer_session(struct lttng_live_msg_iter *lttng_live_msg_iter);
 
-bt_component_class_query_method_status
-live_viewer_connection_list_sessions(struct live_viewer_connection *viewer_connection,
-                                     const bt_value **user_result);
+bt2::Value::Shared
+live_viewer_connection_list_sessions(struct live_viewer_connection *viewer_connection);
 
 #endif /* LTTNG_LIVE_VIEWER_CONNECTION_H */
index 835d16c32d6ee1ab82135b1dc6bc241e703d64d1..e51266197f140cd87a3ddc93bbcecc3bb462e0c5 100644 (file)
@@ -8,8 +8,8 @@
 
 #include <babeltrace2/babeltrace.h>
 
-#include "fs-src/fs.hpp"
 #include "fs-sink/fs-sink.hpp"
+#include "fs-src/fs.hpp"
 #include "lttng-live/lttng-live.hpp"
 
 #ifndef BT_BUILT_IN_PLUGINS
diff --git a/src/plugins/lttng-utils/Makefile.am b/src/plugins/lttng-utils/Makefile.am
deleted file mode 100644 (file)
index 0beeadf..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS =
-
-babeltrace_plugin_lttng_utils_la_LIBADD =
-
-if ENABLE_DEBUG_INFO
-SUBDIRS += debug-info
-babeltrace_plugin_lttng_utils_la_LIBADD += \
-       debug-info/libdebug-info.la
-endif
-
-plugindir = "$(BABELTRACE_PLUGINS_DIR)"
-plugin_LTLIBRARIES = babeltrace-plugin-lttng-utils.la
-
-babeltrace_plugin_lttng_utils_la_SOURCES = \
-       plugin.c
-
-babeltrace_plugin_lttng_utils_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LT_NO_UNDEFINED) \
-       -avoid-version -module $(LD_NOTEXT) \
-       $(ELFUTILS_LIBS)
-
-if !ENABLE_BUILT_IN_PLUGINS
-babeltrace_plugin_lttng_utils_la_LIBADD += \
-       $(top_builddir)/src/lib/libbabeltrace2.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/plugins/common/param-validation/libbabeltrace2-param-validation.la
-endif
diff --git a/src/plugins/lttng-utils/debug-info/Makefile.am b/src/plugins/lttng-utils/debug-info/Makefile.am
deleted file mode 100644 (file)
index 9eb500a..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libdebug-info.la
-
-libdebug_info_la_LIBADD = \
-       $(top_builddir)/src/fd-cache/libbabeltrace2-fd-cache.la
-
-libdebug_info_la_SOURCES = \
-       bin-info.c \
-       bin-info.h \
-       crc32.c \
-       crc32.h \
-       debug-info.c \
-       debug-info.h \
-       dwarf.c \
-       dwarf.h \
-       trace-ir-data-copy.c \
-       trace-ir-data-copy.h \
-       trace-ir-mapping.c \
-       trace-ir-mapping.h \
-       trace-ir-metadata-copy.c \
-       trace-ir-metadata-copy.h \
-       trace-ir-metadata-field-class-copy.c \
-       trace-ir-metadata-field-class-copy.h \
-       utils.c \
-       utils.h
index b5afb0ff8672a5ccf8276dfdde123a4aba924dad..99127291591d75888c7b843dd84a7e24aaf3bd29 100644 (file)
@@ -11,7 +11,6 @@
 #define BT_LOG_TAG "PLUGIN/FLT.LTTNG-UTILS.DEBUG-INFO/BIN-INFO"
 #include "logging/comp-logging.h"
 
-#include <babeltrace2/logging.h>
 #include <dwarf.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -41,7 +40,6 @@
 #define ADDR_STR_LEN 20
 #define BUILD_ID_NOTE_NAME "GNU"
 
-BT_HIDDEN
 int bin_info_init(bt_logging_level log_level, bt_self_component *self_comp)
 {
        int ret = 0;
@@ -56,7 +54,6 @@ int bin_info_init(bt_logging_level log_level, bt_self_component *self_comp)
        return ret;
 }
 
-BT_HIDDEN
 struct bin_info *bin_info_create(struct bt_fd_cache *fdc, const char *path,
                uint64_t low_addr, uint64_t memsz, bool is_pic,
                const char *debug_info_dir, const char *target_prefix,
@@ -110,7 +107,6 @@ error:
        return NULL;
 }
 
-BT_HIDDEN
 void bin_info_destroy(struct bin_info *bin)
 {
        if (!bin) {
@@ -349,7 +345,6 @@ error:
        return is_matching;
 }
 
-BT_HIDDEN
 int bin_info_set_build_id(struct bin_info *bin, uint8_t *build_id,
                size_t build_id_len)
 {
@@ -393,7 +388,6 @@ error:
        return -1;
 }
 
-BT_HIDDEN
 int bin_info_set_debug_link(struct bin_info *bin, const char *filename,
                uint32_t crc)
 {
@@ -719,7 +713,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 void source_location_destroy(struct source_location *src_loc)
 {
        if (!src_loc) {
@@ -1091,7 +1084,6 @@ error:
        return -1;
 }
 
-BT_HIDDEN
 int bin_info_lookup_function_name(struct bin_info *bin,
                uint64_t addr, char **func_name)
 {
@@ -1156,7 +1148,6 @@ error:
        return -1;
 }
 
-BT_HIDDEN
 int bin_info_get_bin_loc(struct bin_info *bin, uint64_t addr, char **bin_loc)
 {
        gchar *_bin_loc = NULL;
@@ -1506,7 +1497,6 @@ error:
        return -1;
 }
 
-BT_HIDDEN
 int bin_info_lookup_source_location(struct bin_info *bin, uint64_t addr,
                struct source_location **src_loc)
 {
index 37d441670b2b8f26ada0c310ae76f71e173e28c6..cd9b9d61c658625291c4fbcaebed0f2a0eee3bbb 100644 (file)
@@ -77,7 +77,6 @@ struct source_location {
  *
  * @returns            0 on success, -1 on failure
  */
-BT_HIDDEN
 int bin_info_init(bt_logging_level log_level,
                bt_self_component *self_comp);
 
@@ -96,7 +95,6 @@ int bin_info_init(bt_logging_level log_level,
  * @returns            Pointer to the new bin_info on success,
  *                     NULL on failure.
  */
-BT_HIDDEN
 struct bin_info *bin_info_create(struct bt_fd_cache *fdc, const char *path,
                uint64_t low_addr, uint64_t memsz, bool is_pic,
                const char *debug_info_dir, const char *target_prefix,
@@ -107,7 +105,6 @@ struct bin_info *bin_info_create(struct bt_fd_cache *fdc, const char *path,
  *
  * @param bin  bin_info instance to destroy
  */
-BT_HIDDEN
 void bin_info_destroy(struct bin_info *bin);
 
 /**
@@ -119,7 +116,6 @@ void bin_info_destroy(struct bin_info *bin);
  * @param build_id_len Length in bytes of the build_id
  * @returns            0 on success, -1 on failure
  */
-BT_HIDDEN
 int bin_info_set_build_id(struct bin_info *bin, uint8_t *build_id,
                size_t build_id_len);
 
@@ -132,7 +128,6 @@ int bin_info_set_build_id(struct bin_info *bin, uint8_t *build_id,
  * @param crc          Checksum for the debug info file
  * @returns            0 on success, -1 on failure
  */
-BT_HIDDEN
 int bin_info_set_debug_link(struct bin_info *bin, const char *filename,
                uint32_t crc);
 
@@ -174,7 +169,6 @@ int bin_info_has_address(struct bin_info *bin, uint64_t addr)
  * @param func_name    Out parameter, the function name.
  * @returns            0 on success, -1 on failure
  */
-BT_HIDDEN
 int bin_info_lookup_function_name(struct bin_info *bin, uint64_t addr,
                char **func_name);
 
@@ -196,7 +190,6 @@ int bin_info_lookup_function_name(struct bin_info *bin, uint64_t addr,
  * @param src_loc      Out parameter, the source location
  * @returns            0 on success, -1 on failure
  */
-BT_HIDDEN
 int bin_info_lookup_source_location(struct bin_info *bin, uint64_t addr,
                struct source_location **src_loc);
 /**
@@ -216,7 +209,6 @@ int bin_info_lookup_source_location(struct bin_info *bin, uint64_t addr,
  * @param bin_loc      Out parameter, the binary location
  * @returns            0 on success, -1 on failure
  */
-BT_HIDDEN
 int bin_info_get_bin_loc(struct bin_info *bin, uint64_t addr, char **bin_loc);
 
 /**
@@ -224,7 +216,6 @@ int bin_info_get_bin_loc(struct bin_info *bin, uint64_t addr, char **bin_loc);
  *
  * @param src_loc      source_location instance to destroy
  */
-BT_HIDDEN
 void source_location_destroy(struct source_location *src_loc);
 
 #endif /* _BABELTRACE_BIN_INFO_H */
index e193caeb803abd146b097d30c5ceb28253d42b4c..7e1e254ca992d3c882667b099ca5862cb0705548 100644 (file)
@@ -22,7 +22,6 @@
  * @param crc  Out parameter, the computed checksum
  * @returns    0 on success, -1 on failure.
  */
-BT_HIDDEN
 int crc32(int fd, uint32_t *crc);
 
 #endif /* _BABELTRACE_CRC32_H */
index aa791ddd2a21d532e34550ddc433424b55af286d..f8ef8182942cc161d0dc2b353c72db2390a9380d 100644 (file)
@@ -1510,8 +1510,7 @@ end:
 }
 
 static
-bt_message *handle_msg_iterator_inactivity(struct debug_info_msg_iter *debug_it,
-               const bt_message *in_message)
+bt_message *handle_msg_iterator_inactivity(const bt_message *in_message)
 {
        /*
         * This message type can be forwarded directly because it does
@@ -1664,7 +1663,7 @@ const bt_message *handle_message(struct debug_info_msg_iter *debug_it,
                out_message = handle_stream_end_message(debug_it, in_message);
                break;
        case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
-               out_message = handle_msg_iterator_inactivity(debug_it, in_message);
+               out_message = handle_msg_iterator_inactivity(in_message);
                break;
        case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
                out_message = handle_discarded_events_message(debug_it, in_message);
@@ -1756,11 +1755,10 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_component_class_initialize_method_status debug_info_comp_init(
                bt_self_component_filter *self_comp_flt,
-               bt_self_component_filter_configuration *config,
-               const bt_value *params, __attribute__((unused)) void *init_method_data)
+               bt_self_component_filter_configuration *config __attribute__((unused)),
+               const bt_value *params, void *init_method_data __attribute__((unused)))
 {
        struct debug_info_component *debug_info_comp;
        bt_component_class_initialize_method_status status =
@@ -1822,7 +1820,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 void debug_info_comp_finalize(bt_self_component_filter *self_comp_flt)
 {
        struct debug_info_component *debug_info =
@@ -1838,7 +1835,6 @@ void debug_info_comp_finalize(bt_self_component_filter *self_comp_flt)
        destroy_debug_info_comp(debug_info);
 }
 
-BT_HIDDEN
 bt_message_iterator_class_next_method_status debug_info_msg_iter_next(
                bt_self_message_iterator *self_msg_iter,
                const bt_message_array_const msgs, uint64_t capacity,
@@ -1955,11 +1951,10 @@ end:
        return;
 }
 
-BT_HIDDEN
 bt_message_iterator_class_initialize_method_status debug_info_msg_iter_init(
                bt_self_message_iterator *self_msg_iter,
                bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *self_port)
+               bt_self_component_port_output *self_port __attribute__((unused)))
 {
        bt_message_iterator_class_initialize_method_status status;
        bt_message_iterator_create_from_message_iterator_status
@@ -2048,7 +2043,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_message_iterator_class_can_seek_beginning_method_status
 debug_info_msg_iter_can_seek_beginning(bt_self_message_iterator *self_msg_iter,
                bt_bool *can_seek)
@@ -2061,7 +2055,6 @@ debug_info_msg_iter_can_seek_beginning(bt_self_message_iterator *self_msg_iter,
                debug_info_msg_iter->msg_iter, can_seek);
 }
 
-BT_HIDDEN
 bt_message_iterator_class_seek_beginning_method_status
 debug_info_msg_iter_seek_beginning(bt_self_message_iterator *self_msg_iter)
 {
@@ -2089,7 +2082,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 void debug_info_msg_iter_finalize(bt_self_message_iterator *it)
 {
        struct debug_info_msg_iter *debug_info_msg_iter;
index 5d544ecf5d29b91669b0b74bf9fb68ba5e0c63e7..8a7e8ea0e464cd856380432e3760fcf74b576449 100644 (file)
 #define MEMSZ_FIELD_NAME               "memsz"
 #define PATH_FIELD_NAME                        "path"
 
-BT_HIDDEN
 bt_component_class_initialize_method_status debug_info_comp_init(
                bt_self_component_filter *self_comp,
                bt_self_component_filter_configuration *config,
                const bt_value *params, void *init_method_data);
 
-BT_HIDDEN
 void debug_info_comp_finalize(bt_self_component_filter *self_comp);
 
-BT_HIDDEN
 bt_message_iterator_class_initialize_method_status debug_info_msg_iter_init(
                bt_self_message_iterator *self_msg_iter,
                bt_self_message_iterator_configuration *config,
                bt_self_component_port_output *self_port);
 
-BT_HIDDEN
 bt_message_iterator_class_next_method_status debug_info_msg_iter_next(
                bt_self_message_iterator *self_msg_iter,
                const bt_message_array_const msgs, uint64_t capacity,
                uint64_t *count);
 
-BT_HIDDEN
 bt_message_iterator_class_can_seek_beginning_method_status
 debug_info_msg_iter_can_seek_beginning(
                bt_self_message_iterator *message_iterator,
                bt_bool *can_seek);
 
-BT_HIDDEN
 bt_message_iterator_class_seek_beginning_method_status debug_info_msg_iter_seek_beginning(
                bt_self_message_iterator *message_iterator);
 
-BT_HIDDEN
 void debug_info_msg_iter_finalize(bt_self_message_iterator *it);
 
 #endif /* BABELTRACE_PLUGIN_DEBUG_INFO_H */
index d4777ab72bf74291f44d798f66c005ab1227359d..20c605c89939980330e36e43affb73185ec5d457 100644 (file)
@@ -12,7 +12,6 @@
 
 #include "dwarf.h"
 
-BT_HIDDEN
 struct bt_dwarf_cu *bt_dwarf_cu_create(Dwarf *dwarf_info)
 {
        struct bt_dwarf_cu *cu;
@@ -32,13 +31,11 @@ error:
        return NULL;
 }
 
-BT_HIDDEN
 void bt_dwarf_cu_destroy(struct bt_dwarf_cu *cu)
 {
        g_free(cu);
 }
 
-BT_HIDDEN
 int bt_dwarf_cu_next(struct bt_dwarf_cu *cu)
 {
        int ret;
@@ -65,7 +62,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu)
 {
        Dwarf_Die *dwarf_die = NULL;
@@ -103,7 +99,6 @@ error:
        return NULL;
 }
 
-BT_HIDDEN
 void bt_dwarf_die_destroy(struct bt_dwarf_die *die)
 {
        if (!die) {
@@ -114,13 +109,11 @@ void bt_dwarf_die_destroy(struct bt_dwarf_die *die)
        g_free(die);
 }
 
-BT_HIDDEN
 int bt_dwarf_die_has_children(struct bt_dwarf_die *die)
 {
        return dwarf_haschildren(die->dwarf_die);
 }
 
-BT_HIDDEN
 int bt_dwarf_die_child(struct bt_dwarf_die *die)
 {
        int ret;
@@ -153,7 +146,6 @@ error:
        return ret;
 }
 
-BT_HIDDEN
 int bt_dwarf_die_next(struct bt_dwarf_die *die)
 {
        int ret;
@@ -196,7 +188,6 @@ error:
        return ret;
 }
 
-BT_HIDDEN
 int bt_dwarf_die_get_tag(struct bt_dwarf_die *die, int *tag)
 {
        int _tag;
@@ -217,7 +208,6 @@ error:
        return -1;
 }
 
-BT_HIDDEN
 int bt_dwarf_die_get_name(struct bt_dwarf_die *die, char **name)
 {
        const char *_name;
@@ -242,7 +232,6 @@ error:
        return -1;
 }
 
-BT_HIDDEN
 int bt_dwarf_die_get_call_file(struct bt_dwarf_die *die, char **filename)
 {
        int ret;
@@ -300,7 +289,6 @@ error:
        return -1;
 }
 
-BT_HIDDEN
 int bt_dwarf_die_get_call_line(struct bt_dwarf_die *die,
                uint64_t *line_no)
 {
@@ -338,7 +326,6 @@ error:
        return -1;
 }
 
-BT_HIDDEN
 int bt_dwarf_die_contains_addr(struct bt_dwarf_die *die, uint64_t addr,
                bool *contains)
 {
index eb2adc1acdefea7d0ad25a8eb8a4abb54cdf774d..2a446b194c22a06af8ae0f3b79f40242ee8ec621 100644 (file)
@@ -58,7 +58,6 @@ struct bt_dwarf_die {
  * @returns            Pointer to the new bt_dwarf_cu on success,
  *                     NULL on failure.
  */
-BT_HIDDEN
 struct bt_dwarf_cu *bt_dwarf_cu_create(Dwarf *dwarf_info);
 
 /**
@@ -66,7 +65,6 @@ struct bt_dwarf_cu *bt_dwarf_cu_create(Dwarf *dwarf_info);
  *
  * @param cu   bt_dwarf_cu instance
  */
-BT_HIDDEN
 void bt_dwarf_cu_destroy(struct bt_dwarf_cu *cu);
 
 /**
@@ -79,7 +77,6 @@ void bt_dwarf_cu_destroy(struct bt_dwarf_cu *cu);
  * @returns    0 on success, 1 if no next CU is available,
  *             -1 on failure
  */
-BT_HIDDEN
 int bt_dwarf_cu_next(struct bt_dwarf_cu *cu);
 
 /**
@@ -90,7 +87,6 @@ int bt_dwarf_cu_next(struct bt_dwarf_cu *cu);
  * @returns    Pointer to the new bt_dwarf_die on success,
  *             NULL on failure.
  */
-BT_HIDDEN
 struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu);
 
 /**
@@ -98,7 +94,6 @@ struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu);
  *
  * @param die  bt_dwarf_die instance
  */
-BT_HIDDEN
 void bt_dwarf_die_destroy(struct bt_dwarf_die *die);
 
 /**
@@ -107,7 +102,6 @@ void bt_dwarf_die_destroy(struct bt_dwarf_die *die);
  * @param die  bt_dwarf_die instance
  * @returns    0 if the die no child, 1 otherwise
  */
-BT_HIDDEN
 int bt_dwarf_die_has_children(struct bt_dwarf_die *die);
 
 /**
@@ -118,7 +112,6 @@ int bt_dwarf_die_has_children(struct bt_dwarf_die *die);
  * @returns    0 on success, 1 if no child DIE is available,
  *             -1 on failure
  */
-BT_HIDDEN
 int bt_dwarf_die_child(struct bt_dwarf_die *die);
 
 /**
@@ -138,7 +131,6 @@ int bt_dwarf_die_child(struct bt_dwarf_die *die);
  * @returns    0 on success, 1 if no other siblings are available, -1 on
  *             failure
  */
-BT_HIDDEN
 int bt_dwarf_die_next(struct bt_dwarf_die *die);
 
 /**
@@ -151,7 +143,6 @@ int bt_dwarf_die_next(struct bt_dwarf_die *die);
  * @param tag  Out parameter, the DIE's tag value
  * @returns    0 on success, -1 on failure.
  */
-BT_HIDDEN
 int bt_dwarf_die_get_tag(struct bt_dwarf_die *die, int *tag);
 
 /**
@@ -164,7 +155,6 @@ int bt_dwarf_die_get_tag(struct bt_dwarf_die *die, int *tag);
  * @param name Out parameter, the DIE's name
  * @returns    0 on success, -1 on failure
  */
-BT_HIDDEN
 int bt_dwarf_die_get_name(struct bt_dwarf_die *die, char **name);
 
 /**
@@ -178,7 +168,6 @@ int bt_dwarf_die_get_name(struct bt_dwarf_die *die, char **name);
  *                     callsite
  * @returns            0 on success, -1 on failure
  */
-BT_HIDDEN
 int bt_dwarf_die_get_call_file(struct bt_dwarf_die *die, char **filename);
 
 /**
@@ -192,7 +181,6 @@ int bt_dwarf_die_get_call_file(struct bt_dwarf_die *die, char **filename);
  *                     subroutine's callsite
  * @returns            0 on success, -1 on failure
  */
-BT_HIDDEN
 int bt_dwarf_die_get_call_line(struct bt_dwarf_die *die,
                uint64_t *line_no);
 
@@ -208,9 +196,8 @@ int bt_dwarf_die_get_call_line(struct bt_dwarf_die *die,
  * @param addr         The memory address to verify
  * @param contains     Out parameter, true if addr is contained,
  *                     false if not
- * @returns            0 on succes, -1 on failure
+ * @returns            0 on success, -1 on failure
  */
-BT_HIDDEN
 int bt_dwarf_die_contains_addr(struct bt_dwarf_die *die, uint64_t addr,
                bool *contains);
 
index 6cac787f1e60fbd51aa58017f9b0490261294909..51f76c003e00b9d9d0b93af97c48e9df3ad58271 100644 (file)
@@ -20,7 +20,6 @@
 
 #include "trace-ir-data-copy.h"
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_trace_content(
                const bt_trace *in_trace, bt_trace *out_trace,
                bt_logging_level log_level, bt_self_component *self_comp)
@@ -72,7 +71,7 @@ enum debug_info_trace_ir_mapping_status copy_trace_content(
                bt_trace_borrow_environment_entry_by_index_const(
                        in_trace, i, &value_name, &value);
 
-               BT_COMP_LOGD("Copying trace environnement entry: "
+               BT_COMP_LOGD("Copying trace environment entry: "
                        "index=%" PRId64 ", value-addr=%p, value-name=\"%s\"",
                        i, value, value_name);
 
@@ -108,7 +107,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_stream_content(
                const bt_stream *in_stream, bt_stream *out_stream,
                bt_logging_level log_level, bt_self_component *self_comp)
@@ -147,7 +145,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_packet_content(
                const bt_packet *in_packet, bt_packet *out_packet,
                bt_logging_level log_level, bt_self_component *self_comp)
@@ -181,7 +178,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_event_content(
                const bt_event *in_event, bt_event *out_event,
                bt_logging_level log_level, bt_self_component *self_comp)
@@ -250,7 +246,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_field_content(
                const bt_field *in_field, bt_field *out_field,
                bt_logging_level log_level, bt_self_component *self_comp)
index 2628b77e3605026c65911b9a971a58f25640b50c..319d366b661248c08d499a96d4c0b8b80993e861 100644 (file)
 #include "common/macros.h"
 #include "trace-ir-mapping.h"
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_trace_content(
                const bt_trace *in_trace, bt_trace *out_trace,
                bt_logging_level log_level, bt_self_component *self_comp);
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_stream_content(
                const bt_stream *in_stream, bt_stream *out_stream,
                bt_logging_level log_level, bt_self_component *self_comp);
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_packet_content(
                const bt_packet *in_packet, bt_packet *out_packet,
                bt_logging_level log_level, bt_self_component *self_comp);
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_event_content(
                const bt_event *in_event, bt_event *out_event,
                bt_logging_level log_level, bt_self_component *self_comp);
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_field_content(
                const bt_field *in_field, bt_field *out_field,
                bt_logging_level log_level, bt_self_component *self_comp);
index 1f891566718afce9ba422eb5051e3424e1b75ee2..5c805f2d5971867cbdc52e260c9f583b4e5331e8 100644 (file)
@@ -40,7 +40,7 @@ bt_trace_class *create_new_mapped_trace_class(struct trace_ir_maps *ir_maps,
 
        BT_ASSERT(!metadata_maps->output_trace_class);
 
-       /* Create the ouput trace class. */
+       /* Create the output trace class. */
        metadata_maps->output_trace_class  =
                bt_trace_class_create(ir_maps->self_comp);
        if (!metadata_maps->output_trace_class) {
@@ -133,7 +133,6 @@ end:
        return out_trace;
 }
 
-BT_HIDDEN
 bt_stream_class *trace_ir_mapping_borrow_mapped_stream_class(
                struct trace_ir_maps *ir_maps,
                const bt_stream_class *in_stream_class)
@@ -148,7 +147,6 @@ bt_stream_class *trace_ir_mapping_borrow_mapped_stream_class(
                (gpointer) in_stream_class);
 }
 
-BT_HIDDEN
 bt_stream_class *trace_ir_mapping_create_new_mapped_stream_class(
                struct trace_ir_maps *ir_maps,
                const bt_stream_class *in_stream_class)
@@ -217,7 +215,6 @@ bt_stream *borrow_mapped_stream(struct trace_ir_data_maps *d_maps,
        return g_hash_table_lookup(d_maps->stream_map, (gpointer) in_stream);
 }
 
-BT_HIDDEN
 bt_stream *trace_ir_mapping_create_new_mapped_stream(
                struct trace_ir_maps *ir_maps, const bt_stream *in_stream)
 {
@@ -298,7 +295,6 @@ end:
        return out_stream;
 }
 
-BT_HIDDEN
 bt_stream *trace_ir_mapping_borrow_mapped_stream(struct trace_ir_maps *ir_maps,
                const bt_stream *in_stream)
 {
@@ -320,7 +316,6 @@ bt_event_class *borrow_mapped_event_class(struct trace_ir_metadata_maps *md_maps
                (gpointer) in_event_class);
 }
 
-BT_HIDDEN
 bt_event_class *trace_ir_mapping_create_new_mapped_event_class(
                struct trace_ir_maps *ir_maps,
                const bt_event_class *in_event_class)
@@ -387,7 +382,6 @@ end:
        return out_event_class;
 }
 
-BT_HIDDEN
 bt_event_class *trace_ir_mapping_borrow_mapped_event_class(
                struct trace_ir_maps *ir_maps,
                const bt_event_class *in_event_class)
@@ -414,7 +408,6 @@ bt_packet *borrow_mapped_packet(struct trace_ir_data_maps *d_maps,
        return g_hash_table_lookup(d_maps->packet_map, (gpointer) in_packet);
 }
 
-BT_HIDDEN
 bt_packet *trace_ir_mapping_create_new_mapped_packet(
                struct trace_ir_maps *ir_maps,
                const bt_packet *in_packet)
@@ -473,7 +466,6 @@ end:
        return out_packet;
 }
 
-BT_HIDDEN
 bt_packet *trace_ir_mapping_borrow_mapped_packet(struct trace_ir_maps *ir_maps,
                const bt_packet *in_packet)
 {
@@ -486,7 +478,6 @@ bt_packet *trace_ir_mapping_borrow_mapped_packet(struct trace_ir_maps *ir_maps,
        return borrow_mapped_packet(d_maps, in_packet);
 }
 
-BT_HIDDEN
 void trace_ir_mapping_remove_mapped_packet(struct trace_ir_maps *ir_maps,
                const bt_packet *in_packet)
 {
@@ -503,7 +494,6 @@ void trace_ir_mapping_remove_mapped_packet(struct trace_ir_maps *ir_maps,
        BT_ASSERT(ret);
 }
 
-BT_HIDDEN
 void trace_ir_mapping_remove_mapped_stream(struct trace_ir_maps *ir_maps,
                const bt_stream *in_stream)
 {
@@ -626,7 +616,6 @@ error:
        return md_maps;
 }
 
-BT_HIDDEN
 void trace_ir_data_maps_destroy(struct trace_ir_data_maps *maps)
 {
        bt_trace_remove_listener_status status;
@@ -659,7 +648,6 @@ void trace_ir_data_maps_destroy(struct trace_ir_data_maps *maps)
        g_free(maps);
 }
 
-BT_HIDDEN
 void trace_ir_metadata_maps_destroy(struct trace_ir_metadata_maps *maps)
 {
        bt_trace_class_remove_listener_status status;
@@ -713,7 +701,6 @@ void trace_ir_maps_clear(struct trace_ir_maps *maps)
        }
 }
 
-BT_HIDDEN
 void trace_ir_maps_destroy(struct trace_ir_maps *maps)
 {
        if (!maps) {
@@ -735,7 +722,6 @@ void trace_ir_maps_destroy(struct trace_ir_maps *maps)
        g_free(maps);
 }
 
-BT_HIDDEN
 struct trace_ir_maps *trace_ir_maps_create(bt_self_component *self_comp,
                const char *debug_info_field_name, bt_logging_level log_level)
 {
index 500201c1ef7ab6fb6b24afd77873079c235641b9..37da3bcf73c7136207aeb170b7bfeac647cae9bb 100644 (file)
@@ -121,78 +121,61 @@ struct trace_ir_maps {
        bt_self_component *self_comp;
 };
 
-BT_HIDDEN
 struct trace_ir_maps *trace_ir_maps_create(bt_self_component *self_comp,
                const char *debug_info_field_name, bt_logging_level log_level);
 
-BT_HIDDEN
 void trace_ir_maps_clear(struct trace_ir_maps *maps);
 
-BT_HIDDEN
 void trace_ir_maps_destroy(struct trace_ir_maps *maps);
 
-BT_HIDDEN
 struct trace_ir_data_maps *trace_ir_data_maps_create(
                struct trace_ir_maps *ir_maps,
                const bt_trace *in_trace);
 
-BT_HIDDEN
 void trace_ir_data_maps_destroy(struct trace_ir_data_maps *d_maps);
 
-BT_HIDDEN
 struct trace_ir_metadata_maps *trace_ir_metadata_maps_create(
                struct trace_ir_maps *ir_maps,
                const bt_trace_class *in_trace_class);
 
-BT_HIDDEN
 void trace_ir_metadata_maps_destroy(struct trace_ir_metadata_maps *md_maps);
 
-BT_HIDDEN
 bt_stream_class *trace_ir_mapping_create_new_mapped_stream_class(
                struct trace_ir_maps *ir_maps,
                const bt_stream_class *in_stream_class);
 
-BT_HIDDEN
 bt_stream_class *trace_ir_mapping_borrow_mapped_stream_class(
                struct trace_ir_maps *ir_maps,
                const bt_stream_class *in_stream_class);
 
-BT_HIDDEN
 bt_stream *trace_ir_mapping_create_new_mapped_stream(
                struct trace_ir_maps *ir_maps,
                const bt_stream *in_stream);
 
-BT_HIDDEN
 bt_stream *trace_ir_mapping_borrow_mapped_stream(
                struct trace_ir_maps *ir_maps,
                const bt_stream *in_stream);
 
-BT_HIDDEN
 void trace_ir_mapping_remove_mapped_stream(
                struct trace_ir_maps *ir_maps,
                const bt_stream *in_stream);
 
-BT_HIDDEN
 bt_event_class *trace_ir_mapping_create_new_mapped_event_class(
                struct trace_ir_maps *ir_maps,
                const bt_event_class *in_event_class);
 
-BT_HIDDEN
 bt_event_class *trace_ir_mapping_borrow_mapped_event_class(
                struct trace_ir_maps *ir_maps,
                const bt_event_class *in_event_class);
 
-BT_HIDDEN
 bt_packet *trace_ir_mapping_create_new_mapped_packet(
                struct trace_ir_maps *ir_maps,
                const bt_packet *in_packet);
 
-BT_HIDDEN
 bt_packet *trace_ir_mapping_borrow_mapped_packet(
                struct trace_ir_maps *ir_maps,
                const bt_packet *in_packet);
 
-BT_HIDDEN
 void trace_ir_mapping_remove_mapped_packet(
                struct trace_ir_maps *ir_maps,
                const bt_packet *in_packet);
index d06e722a6df94496bf86dd33eb9fc1785c3206b7..67f52845bbc5319cb9c09d0f37c71b5b5bb7d604 100644 (file)
@@ -22,7 +22,6 @@
 #include "trace-ir-metadata-field-class-copy.h"
 #include "utils.h"
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_trace_class_content(
                struct trace_ir_maps *ir_maps,
                const bt_trace_class *in_trace_class,
@@ -202,7 +201,6 @@ end:
        return out_clock_class;
 }
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_stream_class_content(
                struct trace_ir_maps *ir_maps,
                const bt_stream_class *in_stream_class,
@@ -400,7 +398,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_event_class_content(
                struct trace_ir_maps *ir_maps,
                const bt_event_class *in_event_class,
@@ -549,7 +546,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status
 copy_event_common_context_field_class_content(
                struct trace_ir_metadata_maps *md_maps,
@@ -697,14 +693,12 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_field_class *create_field_class_copy(struct trace_ir_metadata_maps *md_maps,
                const bt_field_class *in_field_class)
 {
        return create_field_class_copy_internal(md_maps, in_field_class);
 }
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_field_class_content(
                struct trace_ir_metadata_maps *md_maps,
                const bt_field_class *in_field_class,
index 8d4bd4941b4c5bdadb11a74bb1bb615b7af9ff83..b853ada9f49ab4c266f8cbbd93ca8fb4e6cec536 100644 (file)
@@ -15,7 +15,6 @@
 #include "common/macros.h"
 #include "trace-ir-mapping.h"
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_trace_class_content(
                struct trace_ir_maps *trace_ir_maps,
                const bt_trace_class *in_trace_class,
@@ -23,32 +22,27 @@ enum debug_info_trace_ir_mapping_status copy_trace_class_content(
                bt_logging_level log_level,
                bt_self_component *self_comp);
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_stream_class_content(
                struct trace_ir_maps *trace_ir_maps,
                const bt_stream_class *in_stream_class,
                bt_stream_class *out_stream_class);
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_event_class_content(
                struct trace_ir_maps *trace_ir_maps,
                const bt_event_class *in_event_class,
                bt_event_class *out_event_class);
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_field_class_content(
                struct trace_ir_metadata_maps *trace_ir_metadata_maps,
                const bt_field_class *in_field_class,
                bt_field_class *out_field_class);
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_event_common_context_field_class_content(
                struct trace_ir_metadata_maps *trace_ir_metadata_maps,
                const char *debug_info_field_class_name,
                const bt_field_class *in_field_class,
                bt_field_class *out_field_class);
 
-BT_HIDDEN
 bt_field_class *create_field_class_copy(
                struct trace_ir_metadata_maps *trace_ir_metadata_maps,
                const bt_field_class *in_field_class);
index 0b75bb6f3bc9c473b9c1cd166489421df5ff370b..7dbb262c19dee7cd7dc24c422025c09bd3602142 100644 (file)
@@ -22,7 +22,7 @@
 #include "trace-ir-metadata-field-class-copy.h"
 
 /*
- * This fonction walks througth the nested structures field class to resolve a
+ * This function walks through the nested structures field class to resolve a
  * field path object. A field path is made of indexes inside possibly nested
  * structures ultimately leading to a field class.
  */
@@ -652,7 +652,6 @@ end:
        return out_elem_fc;
 }
 
-BT_HIDDEN
 bt_field_class *create_field_class_copy_internal(
                struct trace_ir_metadata_maps *md_maps,
                const bt_field_class *in_field_class)
@@ -883,7 +882,6 @@ error:
        return out_field_class;
 }
 
-BT_HIDDEN
 enum debug_info_trace_ir_mapping_status copy_field_class_content_internal(
                struct trace_ir_metadata_maps *md_maps,
                const bt_field_class *in_field_class,
index 85abef71161ab879503f9fccf263b75a824e9457..65c6ecd702685b482e23bd9ebd1f4ade4ed8f2b4 100644 (file)
 #include "common/macros.h"
 #include "trace-ir-mapping.h"
 
-BT_HIDDEN
-int copy_field_class_content_internal(struct trace_ir_metadata_maps *trace_ir_metadata_maps,
+enum debug_info_trace_ir_mapping_status copy_field_class_content_internal(
+               struct trace_ir_metadata_maps *trace_ir_metadata_maps,
                const bt_field_class *in_field_class,
                bt_field_class *out_field_class);
 
-BT_HIDDEN
 bt_field_class *create_field_class_copy_internal(
                struct trace_ir_metadata_maps *trace_ir_metadata_maps,
                const bt_field_class *in_field_class);
index 6b7b4c7ab18f4e22298737d501d93d3832010753..3c160bc4d4977797029b0c328ed94ea84fee23cd 100644 (file)
@@ -14,7 +14,6 @@
 #include "debug-info.h"
 #include "utils.h"
 
-BT_HIDDEN
 const char *get_filename_from_path(const char *path)
 {
        size_t i = strlen(path);
@@ -41,7 +40,6 @@ end:
        return path;
 }
 
-BT_HIDDEN
 bool is_event_common_ctx_dbg_info_compatible(const bt_field_class *in_field_class,
                const char *debug_info_field_class_name)
 {
index c9000e9d2d18f5d249050969f721b022aad66efe..27cf93ec1c575521fcfedee01e8807844f1ba859 100644 (file)
  * Return the location of a path's file (the last element of the path).
  * Returns the original path on error.
  */
-BT_HIDDEN
 const char *get_filename_from_path(const char *path);
 
-BT_HIDDEN
 bool is_event_common_ctx_dbg_info_compatible(
                const bt_field_class *in_field_class,
                const char *debug_info_field_class_name);
diff --git a/src/plugins/text/Makefile.am b/src/plugins/text/Makefile.am
deleted file mode 100644 (file)
index 9e69ad8..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = pretty dmesg details
-
-plugindir = "$(BABELTRACE_PLUGINS_DIR)"
-plugin_LTLIBRARIES = babeltrace-plugin-text.la
-
-babeltrace_plugin_text_la_SOURCES = plugin.c
-babeltrace_plugin_text_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LT_NO_UNDEFINED) \
-       -avoid-version -module $(LD_NOTEXT)
-
-babeltrace_plugin_text_la_LIBADD = \
-       pretty/libbabeltrace2-plugin-text-pretty-cc.la \
-       dmesg/libbabeltrace2-plugin-text-dmesg-cc.la \
-       details/libbabeltrace2-plugin-text-details-cc.la
-
-if !ENABLE_BUILT_IN_PLUGINS
-babeltrace_plugin_text_la_LIBADD += \
-       $(top_builddir)/src/lib/libbabeltrace2.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/compat/libcompat.la \
-       $(top_builddir)/src/plugins/common/param-validation/libbabeltrace2-param-validation.la
-endif
diff --git a/src/plugins/text/details/Makefile.am b/src/plugins/text/details/Makefile.am
deleted file mode 100644 (file)
index 3595fe9..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-text-details-cc.la
-libbabeltrace2_plugin_text_details_cc_la_SOURCES = \
-       details.c details.h \
-       write.c write.h \
-       obj-lifetime-mgmt.c obj-lifetime-mgmt.h \
-       colors.h
index f64a1e1ed782b886843231308cec94f08cb08931..0b94653eb0c36b8f6521bc96c59c0e5d2dc34a6f 100644 (file)
@@ -30,7 +30,6 @@
 #define WITH_UUID_PARAM_NAME "with-uuid"
 #define COMPACT_PARAM_NAME "compact"
 
-BT_HIDDEN
 void details_destroy_details_trace_class_meta(
                struct details_trace_class_meta *details_tc_meta)
 {
@@ -49,7 +48,6 @@ end:
        return;
 }
 
-BT_HIDDEN
 struct details_trace_class_meta *details_create_details_trace_class_meta(void)
 {
        struct details_trace_class_meta *details_tc_meta =
@@ -188,7 +186,6 @@ end:
        return details_comp;
 }
 
-BT_HIDDEN
 void details_finalize(bt_self_component_sink *comp)
 {
        struct details_comp *details_comp;
@@ -201,8 +198,7 @@ void details_finalize(bt_self_component_sink *comp)
 }
 
 static
-void configure_bool_opt(struct details_comp *details_comp,
-               const bt_value *params, const char *param_name,
+void configure_bool_opt(const bt_value *params, const char *param_name,
                bool default_value, bool *opt_value)
 {
        const bt_value *value;
@@ -273,39 +269,36 @@ bt_component_class_initialize_method_status configure_details_comp(
        }
 
        /* With metadata objects? */
-       configure_bool_opt(details_comp, params, WITH_METADATA_PARAM_NAME,
-               true, &details_comp->cfg.with_meta);
+       configure_bool_opt(params, WITH_METADATA_PARAM_NAME, true,
+               &details_comp->cfg.with_meta);
 
        /* With data objects? */
-       configure_bool_opt(details_comp, params, WITH_DATA_PARAM_NAME,
-               true, &details_comp->cfg.with_data);
+       configure_bool_opt(params, WITH_DATA_PARAM_NAME, true,
+               &details_comp->cfg.with_data);
 
        /* Compact? */
-       configure_bool_opt(details_comp, params, COMPACT_PARAM_NAME,
-               false, &details_comp->cfg.compact);
+       configure_bool_opt(params, COMPACT_PARAM_NAME, false,
+               &details_comp->cfg.compact);
 
        /* With time? */
-       configure_bool_opt(details_comp, params, WITH_TIME_PARAM_NAME,
-               true, &details_comp->cfg.with_time);
+       configure_bool_opt(params, WITH_TIME_PARAM_NAME, true,
+               &details_comp->cfg.with_time);
 
        /* With trace name? */
-       configure_bool_opt(details_comp, params,
-               WITH_TRACE_NAME_PARAM_NAME,
-               true, &details_comp->cfg.with_trace_name);
+       configure_bool_opt(params, WITH_TRACE_NAME_PARAM_NAME, true,
+               &details_comp->cfg.with_trace_name);
 
        /* With stream class name? */
-       configure_bool_opt(details_comp, params,
-               WITH_STREAM_CLASS_NAME_PARAM_NAME,
-               true, &details_comp->cfg.with_stream_class_name);
+       configure_bool_opt(params, WITH_STREAM_CLASS_NAME_PARAM_NAME, true,
+               &details_comp->cfg.with_stream_class_name);
 
        /* With stream name? */
-       configure_bool_opt(details_comp, params,
-               WITH_STREAM_NAME_PARAM_NAME,
-               true, &details_comp->cfg.with_stream_name);
+       configure_bool_opt(params, WITH_STREAM_NAME_PARAM_NAME, true,
+               &details_comp->cfg.with_stream_name);
 
        /* With UUID? */
-       configure_bool_opt(details_comp, params,
-               WITH_UUID_PARAM_NAME, true, &details_comp->cfg.with_uuid);
+       configure_bool_opt(params, WITH_UUID_PARAM_NAME, true,
+               &details_comp->cfg.with_uuid);
 
        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
        goto end;
@@ -334,12 +327,11 @@ void log_configuration(bt_self_component_sink *comp,
        BT_COMP_LOGI("  With UUID: %d", details_comp->cfg.with_uuid);
 }
 
-BT_HIDDEN
 bt_component_class_initialize_method_status details_init(
                bt_self_component_sink *comp,
-               bt_self_component_sink_configuration *config,
+               bt_self_component_sink_configuration *config __attribute__((unused)),
                const bt_value *params,
-               __attribute__((unused)) void *init_method_data)
+               void *init_method_data __attribute__((unused)))
 {
        bt_component_class_initialize_method_status status;
        bt_self_component_add_port_status add_port_status;
@@ -390,7 +382,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status
 details_graph_is_configured(bt_self_component_sink *comp)
 {
@@ -433,7 +424,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status
 details_consume(bt_self_component_sink *comp)
 {
index d9290c1fec948d04267ddfa97cb1e2afae103b24..05f470cd0a5a3c1b8e11ffbfe8215694bef7d0d1 100644 (file)
@@ -144,27 +144,21 @@ struct details_comp {
        GString *str;
 };
 
-BT_HIDDEN
 bt_component_class_initialize_method_status details_init(
                bt_self_component_sink *component,
                bt_self_component_sink_configuration *config,
                const bt_value *params, void *init_method_data);
 
-BT_HIDDEN
 void details_finalize(bt_self_component_sink *component);
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status details_graph_is_configured(
                bt_self_component_sink *comp);
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status details_consume(bt_self_component_sink *component);
 
-BT_HIDDEN
 void details_destroy_details_trace_class_meta(
                struct details_trace_class_meta *details_trace_class_meta);
 
-BT_HIDDEN
 struct details_trace_class_meta *details_create_details_trace_class_meta(void);
 
 #endif /* BABELTRACE_PLUGINS_TEXT_DETAILS_DETAILS_H */
index 97849a97dcc1fe1b71c210e00b12d27002a9a74e..7d670361410300ff83c16a2b0010d2460b031281 100644 (file)
@@ -67,7 +67,6 @@ end:
        return details_tc_meta;
 }
 
-BT_HIDDEN
 bool details_need_to_write_meta_object(struct details_write_ctx *ctx,
                const bt_trace_class *tc, const void *obj)
 {
@@ -89,7 +88,6 @@ end:
        return need_to_write;
 }
 
-BT_HIDDEN
 void details_did_write_meta_object(struct details_write_ctx *ctx,
                const bt_trace_class *tc, const void *obj)
 {
@@ -102,7 +100,6 @@ void details_did_write_meta_object(struct details_write_ctx *ctx,
                GUINT_TO_POINTER(1));
 }
 
-BT_HIDDEN
 bool details_need_to_write_trace_class(struct details_write_ctx *ctx,
                const bt_trace_class *tc)
 {
@@ -122,7 +119,6 @@ end:
        return need_to_write;
 }
 
-BT_HIDDEN
 int details_did_write_trace_class(struct details_write_ctx *ctx,
                const bt_trace_class *tc)
 {
@@ -169,7 +165,6 @@ end:
 }
 
 
-BT_HIDDEN
 int details_trace_unique_id(struct details_write_ctx *ctx,
                const bt_trace *trace, uint64_t *unique_id)
 {
index 18d83cf0a2e19f8fe5a313cde0be1efeaa5a5996..eaf95c10a83f09aa7c9612f0335e9c00dcdd8f3f 100644 (file)
@@ -18,7 +18,6 @@
  * Returns whether or not stream class or event class `obj`, which
  * belongs to `tc`, needs to be written.
  */
-BT_HIDDEN
 bool details_need_to_write_meta_object(struct details_write_ctx *ctx,
                const bt_trace_class *tc, const void *obj);
 
@@ -26,21 +25,18 @@ bool details_need_to_write_meta_object(struct details_write_ctx *ctx,
  * Marks stream class or event class `obj`, which belongs to `tc`, as
  * written.
  */
-BT_HIDDEN
 void details_did_write_meta_object(struct details_write_ctx *ctx,
                const bt_trace_class *tc, const void *obj);
 
 /*
  * Returns whether or not trace class `tc` needs to be written.
  */
-BT_HIDDEN
 bool details_need_to_write_trace_class(struct details_write_ctx *ctx,
                const bt_trace_class *tc);
 
 /*
  * Marks trace class `tc` as written.
  */
-BT_HIDDEN
 int details_did_write_trace_class(struct details_write_ctx *ctx,
                const bt_trace_class *tc);
 
@@ -48,7 +44,6 @@ int details_did_write_trace_class(struct details_write_ctx *ctx,
  * Writes the unique trace ID of `trace` to `*unique_id`, allocating a
  * new unique ID if none exists.
  */
-BT_HIDDEN
 int details_trace_unique_id(struct details_write_ctx *ctx,
                const bt_trace *trace, uint64_t *unique_id);
 
index fbefd809892e9145b36b809aa91ab5af6a864eb6..5c9f9e75591a6039a8ccd438ecf674aea8806004 100644 (file)
@@ -361,7 +361,9 @@ gint compare_strings(const char **a, const char **b)
 
 static
 bt_value_map_foreach_entry_const_func_status map_value_foreach_add_key_to_array(
-               const char *key, const bt_value *object, void *data)
+               const char *key,
+               const bt_value *object __attribute__((unused)),
+               void *data)
 {
        GPtrArray *keys = data;
 
@@ -411,8 +413,11 @@ void write_value(struct details_write_ctx *ctx, const bt_value *value,
                write_float_prop_value(ctx, bt_value_real_get(value));
                break;
        case BT_VALUE_TYPE_STRING:
-               write_sp(ctx);
-               write_str_prop_value(ctx, bt_value_string_get(value));
+               if (strlen(bt_value_string_get(value)) > 0) {
+                       write_sp(ctx);
+                       write_str_prop_value(ctx, bt_value_string_get(value));
+               }
+
                break;
        case BT_VALUE_TYPE_ARRAY:
        {
@@ -660,7 +665,7 @@ void destroy_enum_field_class_mapping(struct enum_field_class_mapping *mapping)
 static
 struct int_range *int_range_at(GArray *ranges, uint64_t index)
 {
-       return &g_array_index(ranges, struct int_range, index);
+       return &bt_g_array_index(ranges, struct int_range, index);
 }
 
 static
@@ -2120,16 +2125,19 @@ void write_trace(struct details_write_ctx *ctx, const bt_trace *trace)
 
                        BT_ASSERT_DBG(value);
                        write_compound_member_name(ctx, name);
-                       write_sp(ctx);
 
                        if (bt_value_get_type(value) ==
                                        BT_VALUE_TYPE_SIGNED_INTEGER) {
+                               write_sp(ctx);
                                write_int_prop_value(ctx,
                                        bt_value_integer_signed_get(value));
                        } else if (bt_value_get_type(value) ==
                                        BT_VALUE_TYPE_STRING) {
-                               write_str_prop_value(ctx,
-                                       bt_value_string_get(value));
+                               if (strlen(bt_value_string_get(value)) > 0) {
+                                       write_sp(ctx);
+                                       write_str_prop_value(ctx,
+                                               bt_value_string_get(value));
+                               }
                        } else {
                                bt_common_abort();
                        }
@@ -2530,7 +2538,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int details_write_message(struct details_comp *details_comp,
                const bt_message *msg)
 {
index 45bc27b5dea9edcb6e6329cd5c615d668a13cfc3..dbfeea5e73c159ddd65c0d32f84bcfddcedea652 100644 (file)
@@ -29,7 +29,6 @@ struct details_write_ctx {
  * Writes the message `msg` to the component's output buffer
  * (`details_comp->str`).
  */
-BT_HIDDEN
 int details_write_message(struct details_comp *details_comp,
                const bt_message *msg);
 
diff --git a/src/plugins/text/dmesg/Makefile.am b/src/plugins/text/dmesg/Makefile.am
deleted file mode 100644 (file)
index 63fcef3..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-text-dmesg-cc.la
-
-# ctf-text plugin
-libbabeltrace2_plugin_text_dmesg_cc_la_SOURCES = \
-       dmesg.c \
-       dmesg.h
index f2a575a1b58b624d2b0cc9f433948a6cd72dc335..790b58d4b15a8901d7c66f3309be2d7e82dfa4fe 100644 (file)
@@ -359,11 +359,11 @@ void destroy_dmesg_component(struct dmesg_component *dmesg_comp)
        g_free(dmesg_comp);
 }
 
-BT_HIDDEN
 bt_component_class_initialize_method_status dmesg_init(
                bt_self_component_source *self_comp_src,
-               bt_self_component_source_configuration *config,
-               const bt_value *params, void *init_method_data)
+               bt_self_component_source_configuration *config __attribute__((unused)),
+               const bt_value *params,
+               void *init_method_data __attribute__((unused)))
 {
        struct dmesg_component *dmesg_comp = g_new0(struct dmesg_component, 1);
        bt_component_class_initialize_method_status status;
@@ -437,7 +437,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 void dmesg_finalize(bt_self_component_source *self_comp)
 {
        destroy_dmesg_component(bt_self_component_get_data(
@@ -650,11 +649,10 @@ void destroy_dmesg_msg_iter(struct dmesg_msg_iter *dmesg_msg_iter)
 
 
 
-BT_HIDDEN
 bt_message_iterator_class_initialize_method_status dmesg_msg_iter_init(
                bt_self_message_iterator *self_msg_iter,
-               bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *self_port)
+               bt_self_message_iterator_configuration *config __attribute__((unused)),
+               bt_self_component_port_output *self_port __attribute__((unused)))
 {
        bt_self_component *self_comp =
                bt_self_message_iterator_borrow_component(self_msg_iter);
@@ -701,7 +699,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 void dmesg_msg_iter_finalize(
                bt_self_message_iterator *priv_msg_iter)
 {
@@ -823,7 +820,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_message_iterator_class_next_method_status dmesg_msg_iter_next(
                bt_self_message_iterator *self_msg_iter,
                bt_message_array_const msgs, uint64_t capacity,
@@ -856,7 +852,7 @@ bt_message_iterator_class_next_method_status dmesg_msg_iter_next(
                 * accumulated message objects in the output
                 * message array, so we need to return
                 * BT_SELF_MESSAGE_ITERATOR_STATUS_OK so that they
-                * are transfered to downstream. This other status
+                * are transferred to downstream. This other status
                 * occurs again the next time muxer_msg_iter_do_next()
                 * is called, possibly without any accumulated
                 * message, in which case we'll return it.
@@ -868,7 +864,6 @@ bt_message_iterator_class_next_method_status dmesg_msg_iter_next(
        return status;
 }
 
-BT_HIDDEN
 bt_message_iterator_class_can_seek_beginning_method_status
 dmesg_msg_iter_can_seek_beginning(
                bt_self_message_iterator *self_msg_iter, bt_bool *can_seek)
@@ -882,7 +877,6 @@ dmesg_msg_iter_can_seek_beginning(
        return BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_BEGINNING_METHOD_STATUS_OK;
 }
 
-BT_HIDDEN
 bt_message_iterator_class_seek_beginning_method_status
 dmesg_msg_iter_seek_beginning(
                bt_self_message_iterator *self_msg_iter)
index 5faae7403c84bee487f03797aad8218104550e72..f4cb3dc154c7ddb5a31404b46fe902c17527ac61 100644 (file)
 #include "common/macros.h"
 #include <babeltrace2/babeltrace.h>
 
-BT_HIDDEN
 bt_component_class_initialize_method_status dmesg_init(
                bt_self_component_source *self_comp,
                bt_self_component_source_configuration *config,
                const bt_value *params, void *init_method_data);
 
-BT_HIDDEN
 void dmesg_finalize(bt_self_component_source *self_comp);
 
-BT_HIDDEN
 bt_message_iterator_class_initialize_method_status dmesg_msg_iter_init(
                bt_self_message_iterator *self_msg_iter,
                bt_self_message_iterator_configuration *config,
                bt_self_component_port_output *self_port);
 
-BT_HIDDEN
 void dmesg_msg_iter_finalize(
                bt_self_message_iterator *self_msg_iter);
 
-BT_HIDDEN
 bt_message_iterator_class_next_method_status dmesg_msg_iter_next(
                bt_self_message_iterator *self_msg_iter,
                bt_message_array_const msgs, uint64_t capacity,
                uint64_t *count);
 
-BT_HIDDEN
 bt_message_iterator_class_can_seek_beginning_method_status
 dmesg_msg_iter_can_seek_beginning(
                bt_self_message_iterator *message_iterator, bt_bool *can_seek);
 
-BT_HIDDEN
 bt_message_iterator_class_seek_beginning_method_status dmesg_msg_iter_seek_beginning(
                bt_self_message_iterator *message_iterator);
 
diff --git a/src/plugins/text/pretty/Makefile.am b/src/plugins/text/pretty/Makefile.am
deleted file mode 100644 (file)
index 0753ebf..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-text-pretty-cc.la
-
-# ctf-text plugin
-libbabeltrace2_plugin_text_pretty_cc_la_SOURCES = \
-       pretty.c \
-       pretty.h \
-       print.c
index cad275145392b9391d74667d3abc1f6907e72ce0..5b66576475e62f9040a6fe5da94672a44a5d3835 100644 (file)
@@ -90,7 +90,6 @@ error:
        return NULL;
 }
 
-BT_HIDDEN
 void pretty_finalize(bt_self_component_sink *comp)
 {
        destroy_pretty_data(
@@ -131,7 +130,6 @@ bt_message_iterator_class_next_method_status handle_message(
        return ret;
 }
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status
 pretty_graph_is_configured(bt_self_component_sink *self_comp_sink)
 {
@@ -170,7 +168,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status pretty_consume(
                bt_self_component_sink *comp)
 {
@@ -543,12 +540,11 @@ void set_use_colors(struct pretty_component *pretty)
        }
 }
 
-BT_HIDDEN
 bt_component_class_initialize_method_status pretty_init(
                bt_self_component_sink *self_comp_sink,
-               bt_self_component_sink_configuration *config,
+               bt_self_component_sink_configuration *config __attribute__((unused)),
                const bt_value *params,
-               __attribute__((unused)) void *init_method_data)
+               void *init_method_data __attribute__((unused)))
 {
        bt_component_class_initialize_method_status status;
        bt_self_component_add_port_status add_port_status;
index abf192966680f3c75266e3f9b3bb8d9db5623756..b73f33455767fb0c93c80631b48c10ad6d40ac10 100644 (file)
@@ -14,7 +14,7 @@
 #include <babeltrace2/babeltrace.h>
 
 /*
- * `bt_field_*_enumeration` are backed by 64 bits integers so the maximun
+ * `bt_field_*_enumeration` are backed by 64 bits integers so the maximum
  * number of bitflags in any enumeration is 64.
  */
 #define ENUMERATION_MAX_BITFLAGS_COUNT (sizeof(uint64_t) * 8)
@@ -95,33 +95,26 @@ struct pretty_component {
        bt_self_component *self_comp;
 };
 
-BT_HIDDEN
 bt_component_class_initialize_method_status pretty_init(
                bt_self_component_sink *component,
                bt_self_component_sink_configuration *config,
                const bt_value *params,
                void *init_method_data);
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status pretty_consume(
                bt_self_component_sink *component);
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status pretty_graph_is_configured(
                bt_self_component_sink *component);
 
-BT_HIDDEN
 void pretty_finalize(bt_self_component_sink *component);
 
-BT_HIDDEN
 int pretty_print_event(struct pretty_component *pretty,
                const bt_message *event_msg);
 
-BT_HIDDEN
 int pretty_print_discarded_items(struct pretty_component *pretty,
                const bt_message *msg);
 
-BT_HIDDEN
 void pretty_print_init(void);
 
 #endif /* BABELTRACE_PLUGIN_TEXT_PRETTY_PRETTY_H */
index 61d79cdb9459d74dacf12fd945a6822ea47fdbf2..4c7e18337b5e459787ad2233080f1b4c8d1eea6a 100644 (file)
@@ -745,7 +745,7 @@ void print_enum_value_bit_flag_label_arrays(struct pretty_component *pretty)
  *
  * This function will set the count to the count of mapping labels that match
  * the value. If count == 0, the caller has nothing to do, if count > 0,
- * the label_array contains the labels to print and the caller is reponsible
+ * the label_array contains the labels to print and the caller is responsible
  * to call g_free on it once the values have been used.
  */
 static
@@ -835,7 +835,7 @@ end:
  *
  * This function will set the count to the count of mapping labels that match
  * the value. If count == 0, the caller has nothing to do, if count > 0,
- * the label_array contains the labels to print and the caller is reponsible
+ * the label_array contains the labels to print and the caller is responsible
  * to call g_free on it once the values have been used.
  */
 static
@@ -1311,11 +1311,9 @@ int print_field(struct pretty_component *pretty,
        } else if (bt_field_class_type_is(class_id,
                        BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY)) {
                return print_sequence(pretty, field, print_names);
-       } else {
-               // TODO: log instead
-               fprintf(pretty->err, "[error] Unknown type id: %d\n", (int) class_id);
-               return -1;
        }
+
+       bt_common_abort();
 }
 
 static
@@ -1440,7 +1438,6 @@ end:
        return ret;
 }
 
-BT_HIDDEN
 int pretty_print_event(struct pretty_component *pretty,
                const bt_message *event_msg)
 {
@@ -1599,7 +1596,6 @@ int print_discarded_elements_msg(struct pretty_component *pretty,
        return ret;
 }
 
-BT_HIDDEN
 int pretty_print_discarded_items(struct pretty_component *pretty,
                const bt_message *msg)
 {
@@ -1668,7 +1664,6 @@ int pretty_print_discarded_items(struct pretty_component *pretty,
        return 0;
 }
 
-BT_HIDDEN
 void pretty_print_init(void)
 {
        strcpy(color_name, bt_common_color_bold());
diff --git a/src/plugins/utils/Makefile.am b/src/plugins/utils/Makefile.am
deleted file mode 100644 (file)
index 7188131..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-SUBDIRS = dummy muxer counter trimmer
-
-plugindir = "$(BABELTRACE_PLUGINS_DIR)"
-plugin_LTLIBRARIES = babeltrace-plugin-utils.la
-
-babeltrace_plugin_utils_la_SOURCES = plugin.c
-babeltrace_plugin_utils_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LT_NO_UNDEFINED) \
-       -avoid-version -module $(LD_NOTEXT)
-babeltrace_plugin_utils_la_LIBADD = \
-       dummy/libbabeltrace2-plugin-dummy-cc.la \
-       muxer/libbabeltrace2-plugin-muxer.la \
-       counter/libbabeltrace2-plugin-counter-cc.la \
-       trimmer/libbabeltrace2-plugin-trimmer.la
-
-if !ENABLE_BUILT_IN_PLUGINS
-babeltrace_plugin_utils_la_LIBADD += \
-       $(top_builddir)/src/lib/libbabeltrace2.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/plugins/common/param-validation/libbabeltrace2-param-validation.la
-endif
diff --git a/src/plugins/utils/counter/Makefile.am b/src/plugins/utils/counter/Makefile.am
deleted file mode 100644 (file)
index 6766851..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-counter-cc.la
-libbabeltrace2_plugin_counter_cc_la_SOURCES = \
-       counter.c \
-       counter.h
index be03014d69ecdef9f8be4ea615fabbc51e6d4d7b..0ef6b8200483a43ea56261b7252835562e022588 100644 (file)
@@ -108,7 +108,6 @@ void destroy_private_counter_data(struct counter *counter)
        }
 }
 
-BT_HIDDEN
 void counter_finalize(bt_self_component_sink *comp)
 {
        struct counter *counter;
@@ -129,12 +128,11 @@ struct bt_param_validation_map_value_entry_descr counter_params[] = {
        BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
 };
 
-BT_HIDDEN
 bt_component_class_initialize_method_status counter_init(
                bt_self_component_sink *component,
-               bt_self_component_sink_configuration *config,
+               bt_self_component_sink_configuration *config __attribute__((unused)),
                const bt_value *params,
-               __attribute__((unused)) void *init_method_data)
+               void *init_method_data __attribute__((unused)))
 {
        bt_component_class_initialize_method_status status;
        bt_self_component_add_port_status add_port_status;
@@ -199,7 +197,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status
 counter_graph_is_configured(
                bt_self_component_sink *comp)
@@ -230,34 +227,23 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status counter_consume(
                bt_self_component_sink *comp)
 {
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
        struct counter *counter;
        bt_message_iterator_next_status next_status;
        uint64_t msg_count;
        bt_message_array_const msgs;
+       bt_self_component *self_comp =
+               bt_self_component_sink_as_self_component(comp);
 
-       counter = bt_self_component_get_data(
-                       bt_self_component_sink_as_self_component(comp));
+       counter = bt_self_component_get_data(self_comp);
        BT_ASSERT_DBG(counter);
-
-       if (G_UNLIKELY(!counter->msg_iter)) {
-               try_print_last(counter);
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_END;
-               goto end;
-       }
+       BT_ASSERT_DBG(counter->msg_iter);
 
        /* Consume messages */
        next_status = bt_message_iterator_next(
                counter->msg_iter, &msgs, &msg_count);
-       if (next_status < 0) {
-               status = (int) next_status;
-               goto end;
-       }
 
        switch (next_status) {
        case BT_MESSAGE_ITERATOR_NEXT_STATUS_OK:
@@ -300,25 +286,20 @@ bt_component_class_sink_consume_method_status counter_consume(
                        bt_message_put_ref(msg);
                }
 
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+               try_print_count(counter, msg_count);
                break;
        }
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_AGAIN:
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_AGAIN;
-               goto end;
        case BT_MESSAGE_ITERATOR_NEXT_STATUS_END:
                try_print_last(counter);
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_END;
-               goto end;
+               break;
+       case BT_MESSAGE_ITERATOR_NEXT_STATUS_ERROR:
        case BT_MESSAGE_ITERATOR_NEXT_STATUS_MEMORY_ERROR:
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_MEMORY_ERROR;
-               goto end;
+               BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(self_comp,
+                       "Failed to get messages from upstream component");
+               break;
        default:
                break;
        }
 
-       try_print_count(counter, msg_count);
-
-end:
-       return status;
+       return (int) next_status;
 }
index 28800909b4ede27a2668b9635b5ab3221fa4ba67..ad886bb3bfdea3c36af9885f9ab68b3d7585cb17 100644 (file)
 #include <stdint.h>
 #include "common/macros.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct counter {
        bt_message_iterator *msg_iter;
        struct {
@@ -34,20 +38,20 @@ struct counter {
        bt_self_component *self_comp;
 };
 
-BT_HIDDEN
 bt_component_class_initialize_method_status counter_init(
                bt_self_component_sink *component,
                bt_self_component_sink_configuration *config,
                const bt_value *params, void *init_method_data);
 
-BT_HIDDEN
 void counter_finalize(bt_self_component_sink *component);
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status counter_graph_is_configured(
                bt_self_component_sink *component);
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status counter_consume(bt_self_component_sink *component);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* BABELTRACE_PLUGINS_UTILS_COUNTER_H */
diff --git a/src/plugins/utils/dummy/Makefile.am b/src/plugins/utils/dummy/Makefile.am
deleted file mode 100644 (file)
index 7d6f993..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-dummy-cc.la
-libbabeltrace2_plugin_dummy_cc_la_SOURCES = dummy.c dummy.h
index 8138aa6e0ff7ee3b9c6ae5d8818bee1b772a22b5..e237bcbd257919f389137c214a6127b41f286e0e 100644 (file)
@@ -26,7 +26,6 @@ void destroy_private_dummy_data(struct dummy *dummy)
 
 }
 
-BT_HIDDEN
 void dummy_finalize(bt_self_component_sink *comp)
 {
        struct dummy *dummy;
@@ -43,12 +42,11 @@ struct bt_param_validation_map_value_entry_descr dummy_params[] = {
        BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
 };
 
-BT_HIDDEN
 bt_component_class_initialize_method_status dummy_init(
                bt_self_component_sink *self_comp_sink,
-               bt_self_component_sink_configuration *config,
+               bt_self_component_sink_configuration *config __attribute__((unused)),
                const bt_value *params,
-               __attribute__((unused)) void *init_method_data)
+               void *init_method_data __attribute__((unused)))
 {
        bt_self_component *self_comp =
                bt_self_component_sink_as_self_component(self_comp_sink);
@@ -97,7 +95,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status dummy_graph_is_configured(
                bt_self_component_sink *comp)
 {
@@ -127,55 +124,40 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status dummy_consume(
                bt_self_component_sink *component)
 {
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
        bt_message_array_const msgs;
        uint64_t count;
        struct dummy *dummy;
        bt_message_iterator_next_status next_status;
        uint64_t i;
+       bt_self_component *self_comp =
+               bt_self_component_sink_as_self_component(component);
 
-       dummy = bt_self_component_get_data(
-               bt_self_component_sink_as_self_component(component));
+       dummy = bt_self_component_get_data(self_comp);
        BT_ASSERT_DBG(dummy);
-
-       if (G_UNLIKELY(!dummy->msg_iter)) {
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_END;
-               goto end;
-       }
+       BT_ASSERT_DBG(dummy->msg_iter);
 
        /* Consume one message  */
        next_status = bt_message_iterator_next(
                dummy->msg_iter, &msgs, &count);
        switch (next_status) {
        case BT_MESSAGE_ITERATOR_NEXT_STATUS_OK:
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
-
                for (i = 0; i < count; i++) {
                        bt_message_put_ref(msgs[i]);
                }
 
                break;
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_AGAIN:
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_AGAIN;
-               goto end;
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_END:
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_END;
-               goto end;
        case BT_MESSAGE_ITERATOR_NEXT_STATUS_ERROR:
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
        case BT_MESSAGE_ITERATOR_NEXT_STATUS_MEMORY_ERROR:
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_MEMORY_ERROR;
-               goto end;
+               BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+                       self_comp,
+                       "Failed to get messages from upstream component");
+               break;
        default:
                break;
        }
 
-end:
-       return status;
+       return (int) next_status;
 }
index d2ab18a7a6ac9cb23adc439e1178ccf2671b541d..0a7c6a95f037cede02e5675a25c2ee78cff7c464 100644 (file)
 #include <babeltrace2/babeltrace.h>
 #include "common/macros.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct dummy {
        bt_message_iterator *msg_iter;
 };
 
-BT_HIDDEN
 bt_component_class_initialize_method_status dummy_init(
                bt_self_component_sink *component,
                bt_self_component_sink_configuration *config,
                const bt_value *params, void *init_method_data);
 
-BT_HIDDEN
 void dummy_finalize(bt_self_component_sink *component);
 
-BT_HIDDEN
 bt_component_class_sink_graph_is_configured_method_status dummy_graph_is_configured(
                bt_self_component_sink *comp);
 
-BT_HIDDEN
 bt_component_class_sink_consume_method_status dummy_consume(
                bt_self_component_sink *component);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* BABELTRACE_PLUGINS_UTILS_DUMMY_H */
diff --git a/src/plugins/utils/muxer/Makefile.am b/src/plugins/utils/muxer/Makefile.am
deleted file mode 100644 (file)
index 62c3748..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-muxer.la
-libbabeltrace2_plugin_muxer_la_SOURCES = muxer.c muxer.h
-
-libbabeltrace2_plugin_muxer_la_LIBADD = \
-       $(top_builddir)/src/plugins/common/muxing/libbabeltrace2-plugins-common-muxing.la
diff --git a/src/plugins/utils/muxer/comp.cpp b/src/plugins/utils/muxer/comp.cpp
new file mode 100644 (file)
index 0000000..ea2df4d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2017-2023 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include "cpp-common/vendor/fmt/core.h"
+
+#include "comp.hpp"
+
+namespace bt2mux {
+
+Comp::Comp(const bt2::SelfFilterComponent selfComp, const bt2::ConstMapValue params, void *) :
+    bt2::UserFilterComponent<Comp, MsgIter> {selfComp, "PLUGIN/FLT.UTILS.MUXER"}
+{
+    BT_CPPLOGI_STR("Initializing component.");
+
+    /* No parameters expected */
+    if (!params.isEmpty()) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+            bt2c::Error, "This component expects no parameters: param-count={}", params.length());
+    }
+
+    /* Add initial available input port */
+    this->_addAvailInputPort();
+
+    /* Add single output port */
+    try {
+        this->_addOutputPort("out");
+    } catch (const bt2c::Error&) {
+        BT_CPPLOGE_STR_APPEND_CAUSE_AND_RETHROW("Failed to add a single output port.");
+    }
+
+    BT_CPPLOGI_STR("Initialized component.");
+}
+
+void Comp::_inputPortConnected(const bt2::SelfComponentInputPort, const bt2::ConstOutputPort)
+{
+    this->_addAvailInputPort();
+}
+
+void Comp::_addAvailInputPort()
+{
+    try {
+        this->_addInputPort(fmt::format("in{}", this->_inputPorts().length()));
+    } catch (const bt2c::Error&) {
+        BT_CPPLOGE_STR_APPEND_CAUSE_AND_RETHROW("Failed to add an available input port.");
+    }
+
+    BT_CPPLOGI("Added one available input port: name={}", this->_inputPorts().back().name());
+}
+
+} /* namespace bt2mux */
diff --git a/src/plugins/utils/muxer/comp.hpp b/src/plugins/utils/muxer/comp.hpp
new file mode 100644 (file)
index 0000000..5fe50c2
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright 2017-2023 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef BABELTRACE_PLUGINS_UTILS_MUXER_COMP_HPP
+#define BABELTRACE_PLUGINS_UTILS_MUXER_COMP_HPP
+
+#include "cpp-common/bt2/plugin-dev.hpp"
+
+#include "msg-iter.hpp"
+
+namespace bt2mux {
+
+class MsgIter;
+
+class Comp final : public bt2::UserFilterComponent<Comp, MsgIter>
+{
+    friend class MsgIter;
+    friend bt2::UserFilterComponent<Comp, MsgIter>;
+
+public:
+    explicit Comp(bt2::SelfFilterComponent selfComp, bt2::ConstMapValue params, void *);
+
+private:
+    void _inputPortConnected(bt2::SelfComponentInputPort selfPort, bt2::ConstOutputPort otherPort);
+    void _addAvailInputPort();
+};
+
+} /* namespace bt2mux */
+
+#endif /* BABELTRACE_PLUGINS_UTILS_MUXER_COMP_HPP */
diff --git a/src/plugins/utils/muxer/msg-iter.cpp b/src/plugins/utils/muxer/msg-iter.cpp
new file mode 100644 (file)
index 0000000..c3e8c3d
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2017-2023 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <algorithm>
+
+#include <glib.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/fmt.hpp"
+#include "cpp-common/bt2s/make-unique.hpp"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "plugins/common/muxing/muxing.h"
+
+#include "comp.hpp"
+#include "msg-iter.hpp"
+
+namespace bt2mux {
+
+MsgIter::MsgIter(const bt2::SelfMessageIterator selfMsgIter,
+                 const bt2::SelfMessageIteratorConfiguration cfg, bt2::SelfComponentOutputPort) :
+    bt2::UserMessageIterator<MsgIter, Comp> {selfMsgIter, "MSG-ITER"},
+    _mHeap {_HeapComparator {_mLogger}}
+{
+    /*
+     * Create one upstream message iterator for each connected
+     * input port.
+     */
+    auto canSeekForward = true;
+
+    for (const auto inputPort : this->_component()._inputPorts()) {
+        if (!inputPort.isConnected()) {
+            BT_CPPLOGI("Ignoring disconnected port: name={}", inputPort.name());
+            continue;
+        }
+
+        /*
+         * Create new upstream message iterator and immediately make it
+         * part of `_mUpstreamMsgItersToReload` (_ensureFullHeap() will
+         * deal with it when downstream calls next()).
+         */
+        auto upstreamMsgIter = bt2s::make_unique<UpstreamMsgIter>(
+            this->_createMessageIterator(inputPort), inputPort.name(), _mLogger);
+
+        canSeekForward = canSeekForward && upstreamMsgIter->canSeekForward();
+        _mUpstreamMsgItersToReload.emplace_back(upstreamMsgIter.get());
+        _mUpstreamMsgIters.push_back(std::move(upstreamMsgIter));
+    }
+
+    /* Set the "can seek forward" configuration */
+    cfg.canSeekForward(canSeekForward);
+}
+
+namespace {
+
+std::string optMsgTsStr(const bt2s::optional<std::int64_t>& ts)
+{
+    if (ts) {
+        return fmt::to_string(*ts);
+    }
+
+    return "none";
+}
+
+} /* namespace */
+
+void MsgIter::_next(bt2::ConstMessageArray& msgs)
+{
+    /* Make sure all upstream message iterators are part of the heap */
+    this->_ensureFullHeap();
+
+    while (msgs.length() < msgs.capacity()) {
+        /* Empty heap? */
+        if (G_UNLIKELY(_mHeap.isEmpty())) {
+            /* No more upstream messages! */
+            return;
+        }
+
+        /*
+         * Retrieve the upstream message iterator having the oldest message.
+         */
+        auto& oldestUpstreamMsgIter = *_mHeap.top();
+
+        /* Validate the clock class of the oldest message */
+        this->_validateMsgClkCls(oldestUpstreamMsgIter.msg());
+
+        /* Append the oldest message and discard it */
+        msgs.append(oldestUpstreamMsgIter.msg().shared());
+
+        if (_mLogger.wouldLogD()) {
+            BT_CPPLOGD("Appended message to array: port-name={}, ts={}",
+                       oldestUpstreamMsgIter.portName(),
+                       optMsgTsStr(oldestUpstreamMsgIter.msgTs()));
+        }
+
+        oldestUpstreamMsgIter.discard();
+
+        /*
+         * Immediately try to reload `oldestUpstreamMsgIter`.
+         *
+         * The possible outcomes are:
+         *
+         * There's an available message:
+         *     Call `_mHeap.replaceTop()` to bring
+         *     `oldestUpstreamMsgIter` back to the heap, performing a
+         *     single heap rebalance.
+         *
+         * There isn't an available message (ended):
+         *     Remove `oldestUpstreamMsgIter` from the heap.
+         *
+         * `bt2::TryAgain` is thrown:
+         *     Remove `oldestUpstreamMsgIter` from the heap.
+         *
+         *     Add `oldestUpstreamMsgIter` to the set of upstream
+         *     message iterators to reload. The next call to _next()
+         *     will move it to the heap again (if not ended) after
+         *     having successfully called reload().
+         */
+        BT_CPPLOGD(
+            "Trying to reload upstream message iterator having the oldest message: port-name={}",
+            oldestUpstreamMsgIter.portName());
+
+        try {
+            if (G_LIKELY(oldestUpstreamMsgIter.reload() == UpstreamMsgIter::ReloadStatus::More)) {
+                /* New current message: update heap */
+                _mHeap.replaceTop(&oldestUpstreamMsgIter);
+                BT_CPPLOGD("More messages available; updated heap: port-name={}, heap-len={}",
+                           oldestUpstreamMsgIter.portName(), _mHeap.len());
+            } else {
+                _mHeap.removeTop();
+                BT_CPPLOGD("Upstream message iterator has no more messages; removed from heap: "
+                           "port-name{}, heap-len={}",
+                           oldestUpstreamMsgIter.portName(), _mHeap.len());
+            }
+        } catch (const bt2::TryAgain&) {
+            _mHeap.removeTop();
+            _mUpstreamMsgItersToReload.push_back(&oldestUpstreamMsgIter);
+            BT_CPPLOGD("Moved upstream message iterator from heap to \"to reload\" set: "
+                       "port-name={}, heap-len={}, to-reload-len={}",
+                       oldestUpstreamMsgIter.portName(), _mHeap.len(),
+                       _mUpstreamMsgItersToReload.size());
+            throw;
+        }
+    }
+}
+
+void MsgIter::_ensureFullHeap()
+{
+    /*
+     * Always remove from `_mUpstreamMsgItersToReload` when reload()
+     * doesn't throw.
+     *
+     * If reload() returns `UpstreamMsgIter::ReloadStatus::NO_MORE`,
+     * then we don't need it anymore (remains alive in
+     * `_mUpstreamMsgIters`).
+     */
+    for (auto it = _mUpstreamMsgItersToReload.begin(); it != _mUpstreamMsgItersToReload.end();
+         it = _mUpstreamMsgItersToReload.erase(it)) {
+        auto& upstreamMsgIter = **it;
+
+        BT_CPPLOGD("Handling upstream message iterator to reload: "
+                   "port-name={}, heap-len={}, to-reload-len={}",
+                   upstreamMsgIter.portName(), _mHeap.len(), _mUpstreamMsgItersToReload.size());
+
+        if (G_LIKELY(upstreamMsgIter.reload() == UpstreamMsgIter::ReloadStatus::More)) {
+            /* New current message: move to heap */
+            _mHeap.insert(&upstreamMsgIter);
+            BT_CPPLOGD("More messages available; "
+                       "inserted upstream message iterator into heap from \"to reload\" set: "
+                       "port-name={}, heap-len={}",
+                       upstreamMsgIter.portName(), _mHeap.len());
+        } else {
+            BT_CPPLOGD("Not inserting upstream message iterator into heap (no more messages): "
+                       "port-name={}",
+                       upstreamMsgIter.portName());
+        }
+    }
+}
+
+bool MsgIter::_canSeekBeginning()
+{
+    /*
+     * We can only seek our beginning if all our upstream message
+     * iterators also can.
+     */
+    return std::all_of(_mUpstreamMsgIters.begin(), _mUpstreamMsgIters.end(),
+                       [](UpstreamMsgIter::UP& upstreamMsgIter) {
+                           return upstreamMsgIter->canSeekBeginning();
+                       });
+}
+
+void MsgIter::_seekBeginning()
+{
+    /*
+     * The current approach is that this operation is either successful
+     * (all upstream message iterators seek) or not. If it's not, then
+     * we don't keep any state that some sought and some didn't: we'll
+     * restart the whole process when the user tries to seek again.
+     *
+     * The first step is to clear all the containers of upstream message
+     * iterator pointers so that we can process what's in
+     * `_mUpstreamMsgIters` only. This is irreversible, but it's okay:
+     * if any seeking fails below, the downstream user is required to
+     * try the "seek beginning" operation again and only call
+     * bt_message_iterator_next() if it was successful.
+     *
+     * This means if the first four upstream message iterators seek, and
+     * then the fifth one throws `bt2::TryAgain`, then the next time
+     * this method executes, the first four upstream message iterators
+     * will seek again. That being said, it's such an unlikely scenario
+     * that the simplicity outweighs performance concerns here.
+     */
+    _mHeap.clear();
+    _mUpstreamMsgItersToReload.clear();
+
+    /* Make each upstream message iterator seek */
+    for (auto& upstreamMsgIter : _mUpstreamMsgIters) {
+        /* This may throw! */
+        upstreamMsgIter->seekBeginning();
+    }
+
+    /*
+     * All sought successfully: fill `_mUpstreamMsgItersToReload`; the
+     * next call to _next() will deal with those.
+     */
+    for (auto& upstreamMsgIter : _mUpstreamMsgIters) {
+        _mUpstreamMsgItersToReload.push_back(upstreamMsgIter.get());
+    }
+}
+
+void MsgIter::_validateMsgClkCls(const bt2::ConstMessage msg)
+{
+    if (G_LIKELY(!msg.isStreamBeginning() && !msg.isMessageIteratorInactivity())) {
+        /*
+         * We don't care about the other types: all the messages related
+         * to a given stream shared the same default clock class, if
+         * any.
+         */
+        return;
+    }
+
+    BT_CPPLOGD("Validating the clock class of a message: msg-type={}", msg.type());
+
+    try {
+        _mClkCorrValidator.validate(msg);
+    } catch (const bt2ccv::ClockCorrelationError& error) {
+        using Type = bt2ccv::ClockCorrelationError::Type;
+
+        const auto actualClockCls = error.actualClockCls();
+
+        switch (error.type()) {
+        case Type::ExpectingNoClockClassGotOne:
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW(bt2::Error,
+                                              "Expecting no clock class, but got one: "
+                                              "clock-class-addr={}, clock-class-name={}",
+                                              fmt::ptr(actualClockCls->libObjPtr()),
+                                              actualClockCls->name());
+
+        case Type::ExpectingOriginUnixGotNone:
+        case Type::ExpectingOriginUuidGotNone:
+        case Type::ExpectingOriginNoUuidGotNone:
+        {
+            const auto streamCls = *error.streamCls();
+
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW(bt2::Error,
+                                              "Expecting a clock class, but got none: "
+                                              "stream-class-addr={}, stream-class-name=\"{}\", "
+                                              "stream-class-id={}",
+                                              fmt::ptr(streamCls.libObjPtr()), streamCls.name(),
+                                              streamCls.id());
+        }
+
+        case Type::ExpectingOriginUnixGotOther:
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW(bt2::Error,
+                                              "Expecting a clock class having a Unix epoch origin, "
+                                              "but got one not having a Unix epoch origin: "
+                                              "clock-class-addr={}, clock-class-name={}",
+                                              fmt::ptr(actualClockCls->libObjPtr()),
+                                              actualClockCls->name());
+
+        case Type::ExpectingOriginUuidGotUnix:
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+                bt2::Error,
+                "Expecting a clock class not having a Unix epoch origin, "
+                "but got one having a Unix epoch origin: "
+                "clock-class-addr={}, clock-class-name={}",
+                fmt::ptr(actualClockCls->libObjPtr()), actualClockCls->name());
+
+        case Type::ExpectingOriginUuidGotNoUuid:
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+                bt2::Error,
+                "Expecting a clock class with a UUID, but got one without a UUID: "
+                "clock-class-addr={}, clock-class-name={}",
+                fmt::ptr(actualClockCls->libObjPtr()), actualClockCls->name());
+
+        case Type::ExpectingOriginUuidGotOtherUuid:
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW(bt2::Error,
+                                              "Expecting a clock class with a specific UUID, "
+                                              "but got one with a different UUID: "
+                                              "clock-class-addr={}, clock-class-name={}, "
+                                              "expected-uuid=\"{}\", uuid=\"{}\"",
+                                              fmt::ptr(actualClockCls->libObjPtr()),
+                                              actualClockCls->name(), *error.expectedUuid(),
+                                              *actualClockCls->uuid());
+
+        case Type::ExpectingOriginNoUuidGotOther:
+        {
+            const auto expectedClockCls = error.expectedClockCls();
+
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+                bt2::Error,
+                "Unexpected clock class: "
+                "expected-clock-class-addr={}, expected-clock-class-name={}, "
+                "actual-clock-class-addr={}, actual-clock-class-name={}",
+                fmt::ptr(expectedClockCls->libObjPtr()), expectedClockCls->name(),
+                fmt::ptr(actualClockCls->libObjPtr()), actualClockCls->name());
+        }
+        }
+    }
+}
+
+MsgIter::_HeapComparator::_HeapComparator(const bt2c::Logger& logger) : _mLogger {logger}
+{
+}
+
+bool MsgIter::_HeapComparator::operator()(
+    const UpstreamMsgIter * const upstreamMsgIterA,
+    const UpstreamMsgIter * const upstreamMsgIterB) const noexcept
+{
+    /* The two messages to compare */
+    const auto msgA = upstreamMsgIterA->msg();
+    const auto msgB = upstreamMsgIterB->msg();
+    auto& msgTsA = upstreamMsgIterA->msgTs();
+    auto& msgTsB = upstreamMsgIterB->msgTs();
+
+    if (_mLogger.wouldLogT()) {
+        BT_CPPLOGT("Comparing two messages: "
+                   "port-name-a={}, msg-a-type={}, msg-a-ts={}, "
+                   "port-name-b={}, msg-b-type={}, msg-b-ts={}",
+                   upstreamMsgIterA->portName(), msgA.type(), optMsgTsStr(msgTsA),
+                   upstreamMsgIterB->portName(), msgB.type(), optMsgTsStr(msgTsB));
+    }
+
+    /*
+     * Try to compare using timestamps.
+     *
+     * If both timestamps are set and their values are different, then
+     * use this to establish the ordering of the two messages.
+     *
+     * If one timestamp is set, but not the other, the latter always
+     * wins. This is because, for a given upstream message iterator, we
+     * need to consume all the messages having no timestamp so that we
+     * can reach a message with a timestamp to compare it.
+     *
+     * Otherwise, we'll fall back to using
+     * common_muxing_compare_messages().
+     */
+    if (G_LIKELY(msgTsA && msgTsB)) {
+        if (*msgTsA < *msgTsB) {
+            /*
+             * Return `true` because `_mHeap.top()` provides the
+             * "greatest" element. For us, the "greatest" message is
+             * the oldest one, that is, the one having the smallest
+             * timestamp.
+             */
+            BT_CPPLOGT_STR("Timestamp of message A is less than timestamp of message B: oldest=A");
+            return true;
+        } else if (*msgTsA > *msgTsB) {
+            BT_CPPLOGT_STR(
+                "Timestamp of message A is greater than timestamp of message B: oldest=B");
+            return false;
+        }
+    } else if (msgTsA && !msgTsB) {
+        BT_CPPLOGT_STR("Message A has a timestamp, but message B has none: oldest=B");
+        return false;
+    } else if (!msgTsA && msgTsB) {
+        BT_CPPLOGT_STR("Message B has a timestamp, but message A has none: oldest=A");
+        return true;
+    }
+
+    /*
+     * Comparison failed using timestamps: determine an ordering using
+     * arbitrary properties, but in a deterministic way.
+     *
+     * common_muxing_compare_messages() returns less than 0 if the first
+     * message is considered older than the second, which corresponds to
+     * this comparator returning `true`.
+     */
+    const auto res = common_muxing_compare_messages(msgA.libObjPtr(), msgB.libObjPtr()) < 0;
+
+    BT_CPPLOGT("Timestamps are considered equal; comparing other properties: oldest={}",
+               res ? "A" : "B");
+    return res;
+}
+
+} /* namespace bt2mux */
diff --git a/src/plugins/utils/muxer/msg-iter.hpp b/src/plugins/utils/muxer/msg-iter.hpp
new file mode 100644 (file)
index 0000000..73620b5
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright 2017-2023 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef BABELTRACE_PLUGINS_UTILS_MUXER_MSG_ITER_HPP
+#define BABELTRACE_PLUGINS_UTILS_MUXER_MSG_ITER_HPP
+
+#include <vector>
+
+#include "cpp-common/bt2/component-class-dev.hpp"
+#include "cpp-common/bt2/self-message-iterator-configuration.hpp"
+#include "cpp-common/bt2c/prio-heap.hpp"
+
+#include "clock-correlation-validator/clock-correlation-validator.hpp"
+#include "upstream-msg-iter.hpp"
+
+namespace bt2mux {
+
+class Comp;
+
+class MsgIter final : public bt2::UserMessageIterator<MsgIter, Comp>
+{
+    friend bt2::UserMessageIterator<MsgIter, Comp>;
+
+private:
+    /* Comparator for `_mHeap` with its own logger */
+    class _HeapComparator final
+    {
+    public:
+        explicit _HeapComparator(const bt2c::Logger& logger);
+
+        bool operator()(const UpstreamMsgIter *upstreamMsgIterA,
+                        const UpstreamMsgIter *upstreamMsgIterB) const noexcept;
+
+    private:
+        bt2c::Logger _mLogger;
+    };
+
+public:
+    explicit MsgIter(bt2::SelfMessageIterator selfMsgIter,
+                     bt2::SelfMessageIteratorConfiguration config,
+                     bt2::SelfComponentOutputPort selfPort);
+
+private:
+    bool _canSeekBeginning();
+    void _seekBeginning();
+    void _next(bt2::ConstMessageArray& msgs);
+
+    /*
+     * Makes sure `_mUpstreamMsgItersToReload` is empty so that `_mHeap`
+     * is ready for the next message selection.
+     *
+     * This may throw whatever UpstreamMsgIter::reload() may throw.
+     */
+    void _ensureFullHeap();
+
+    /*
+     * Validates the clock class of the received message `msg`, setting
+     * the expectation if this is the first one.
+     *
+     * Throws `bt2::Error` on error.
+     */
+    void _validateMsgClkCls(bt2::ConstMessage msg);
+
+    /*
+     * Container of all the upstream message iterators.
+     *
+     * The only purpose of this is to own them; where they are below
+     * indicates their state.
+     */
+    std::vector<UpstreamMsgIter::UP> _mUpstreamMsgIters;
+
+    /*
+     * Heap of ready-to-use upstream message iterators (pointers to
+     * owned objects in `_mUpstreamMsgIters` above).
+     */
+    bt2c::PrioHeap<UpstreamMsgIter *, _HeapComparator> _mHeap;
+
+    /*
+     * Current upstream message iterators to reload, on which we must
+     * call reload() before moving them to `_mHeap` or to
+     * `_mEndedUpstreamMsgIters`.
+     *
+     * Using `std::vector` instead of some linked list because the
+     * typical scenario is to add a single one and then remove it
+     * shortly after.
+     */
+    std::vector<UpstreamMsgIter *> _mUpstreamMsgItersToReload;
+
+    /* Clock class correlation validator */
+    bt2ccv::ClockCorrelationValidator _mClkCorrValidator;
+};
+
+} /* namespace bt2mux */
+
+#endif /* BABELTRACE_PLUGINS_UTILS_MUXER_MSG_ITER_HPP */
diff --git a/src/plugins/utils/muxer/muxer.c b/src/plugins/utils/muxer/muxer.c
deleted file mode 100644 (file)
index fa049cf..0000000
+++ /dev/null
@@ -1,1607 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (muxer_comp->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (muxer_comp->log_level)
-#define BT_LOG_TAG "PLUGIN/FLT.UTILS.MUXER"
-#include "logging/comp-logging.h"
-
-#include "common/macros.h"
-#include "common/uuid.h"
-#include <babeltrace2/babeltrace.h>
-#include <glib.h>
-#include <stdbool.h>
-#include <inttypes.h>
-#include "common/assert.h"
-#include "common/common.h"
-#include <stdlib.h>
-#include <string.h>
-
-#include "plugins/common/muxing/muxing.h"
-#include "plugins/common/param-validation/param-validation.h"
-
-#include "muxer.h"
-
-struct muxer_comp {
-       /* Weak refs */
-       bt_self_component_filter *self_comp_flt;
-       bt_self_component *self_comp;
-
-       unsigned int next_port_num;
-       size_t available_input_ports;
-       bool initializing_muxer_msg_iter;
-       bt_logging_level log_level;
-};
-
-struct muxer_upstream_msg_iter {
-       struct muxer_comp *muxer_comp;
-
-       /* Owned by this, NULL if ended */
-       bt_message_iterator *msg_iter;
-
-       /* Contains `const bt_message *`, owned by this */
-       GPtrArray *msgs;
-
-       /* Index of the next message in `msgs` to return */
-       guint next_msg;
-};
-
-enum muxer_msg_iter_clock_class_expectation {
-       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY = 0,
-       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE,
-       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ABSOLUTE,
-       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID,
-       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_NO_UUID,
-};
-
-struct muxer_msg_iter {
-       struct muxer_comp *muxer_comp;
-
-       /* Weak */
-       bt_self_message_iterator *self_msg_iter;
-
-       /*
-        * Array of struct muxer_upstream_msg_iter * (owned by this).
-        *
-        * NOTE: This array is searched in linearly to find the youngest
-        * current message. Keep this until benchmarks confirm that
-        * another data structure is faster than this for our typical
-        * use cases.
-        */
-       GPtrArray *active_muxer_upstream_msg_iters;
-
-       /*
-        * Array of struct muxer_upstream_msg_iter * (owned by this).
-        *
-        * We move ended message iterators from
-        * `active_muxer_upstream_msg_iters` to this array so as to be
-        * able to restore them when seeking.
-        */
-       GPtrArray *ended_muxer_upstream_msg_iters;
-
-       /* Last time returned in a message */
-       int64_t last_returned_ts_ns;
-
-       /* Clock class expectation state */
-       enum muxer_msg_iter_clock_class_expectation clock_class_expectation;
-
-       /*
-        * Expected clock class UUID, only valid when
-        * clock_class_expectation is
-        * MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID.
-        */
-       bt_uuid_t expected_clock_class_uuid;
-
-       /*
-        * Saved error.  If we hit an error in the _next method, but have some
-        * messages ready to return, we save the error here and return it on
-        * the next _next call.
-        */
-       bt_message_iterator_class_next_method_status next_saved_status;
-       const struct bt_error *next_saved_error;
-};
-
-static
-void empty_message_queue(struct muxer_upstream_msg_iter *upstream_msg_iter)
-{
-       g_ptr_array_set_size(upstream_msg_iter->msgs, 0);
-}
-
-static
-void destroy_muxer_upstream_msg_iter(
-               struct muxer_upstream_msg_iter *muxer_upstream_msg_iter)
-{
-       struct muxer_comp *muxer_comp;
-
-       if (!muxer_upstream_msg_iter) {
-               return;
-       }
-
-       muxer_comp = muxer_upstream_msg_iter->muxer_comp;
-       BT_COMP_LOGD("Destroying muxer's upstream message iterator wrapper: "
-               "addr=%p, msg-iter-addr=%p, queue-len=%u, next-msg=%u",
-               muxer_upstream_msg_iter,
-               muxer_upstream_msg_iter->msg_iter,
-               muxer_upstream_msg_iter->msgs->len,
-               muxer_upstream_msg_iter->next_msg);
-
-       bt_message_iterator_put_ref(
-               muxer_upstream_msg_iter->msg_iter);
-
-       if (muxer_upstream_msg_iter->msgs) {
-               g_ptr_array_free(muxer_upstream_msg_iter->msgs, TRUE);
-       }
-
-       g_free(muxer_upstream_msg_iter);
-}
-
-static
-int muxer_msg_iter_add_upstream_msg_iter(struct muxer_msg_iter *muxer_msg_iter,
-               bt_message_iterator *self_msg_iter)
-{
-       int ret = 0;
-       struct muxer_upstream_msg_iter *muxer_upstream_msg_iter =
-               g_new0(struct muxer_upstream_msg_iter, 1);
-       struct muxer_comp *muxer_comp = muxer_msg_iter->muxer_comp;
-
-       if (!muxer_upstream_msg_iter) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Failed to allocate one muxer's upstream message iterator wrapper.");
-               goto error;
-       }
-
-       muxer_upstream_msg_iter->muxer_comp = muxer_comp;
-       muxer_upstream_msg_iter->msg_iter = self_msg_iter;
-       bt_message_iterator_get_ref(muxer_upstream_msg_iter->msg_iter);
-       muxer_upstream_msg_iter->msgs =
-               g_ptr_array_new_with_free_func((GDestroyNotify) bt_message_put_ref);
-       if (!muxer_upstream_msg_iter->msgs) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Failed to allocate a GPtrArray.");
-               goto error;
-       }
-
-       g_ptr_array_add(muxer_msg_iter->active_muxer_upstream_msg_iters,
-               muxer_upstream_msg_iter);
-       BT_COMP_LOGD("Added muxer's upstream message iterator wrapper: "
-               "addr=%p, muxer-msg-iter-addr=%p, msg-iter-addr=%p",
-               muxer_upstream_msg_iter, muxer_msg_iter,
-               self_msg_iter);
-
-       goto end;
-
-error:
-       destroy_muxer_upstream_msg_iter(muxer_upstream_msg_iter);
-       ret = -1;
-
-end:
-       return ret;
-}
-
-static
-bt_self_component_add_port_status add_available_input_port(
-               bt_self_component_filter *self_comp)
-{
-       struct muxer_comp *muxer_comp = bt_self_component_get_data(
-               bt_self_component_filter_as_self_component(self_comp));
-       bt_self_component_add_port_status status =
-               BT_SELF_COMPONENT_ADD_PORT_STATUS_OK;
-       GString *port_name = NULL;
-
-       BT_ASSERT(muxer_comp);
-       port_name = g_string_new("in");
-       if (!port_name) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp, "Failed to allocate a GString.");
-               status = BT_SELF_COMPONENT_ADD_PORT_STATUS_MEMORY_ERROR;
-               goto end;
-       }
-
-       g_string_append_printf(port_name, "%u", muxer_comp->next_port_num);
-       status = bt_self_component_filter_add_input_port(
-               self_comp, port_name->str, NULL, NULL);
-       if (status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Cannot add input port to muxer component: "
-                       "port-name=\"%s\", comp-addr=%p, status=%s",
-                       port_name->str, self_comp,
-                       bt_common_func_status_string(status));
-               goto end;
-       }
-
-       muxer_comp->available_input_ports++;
-       muxer_comp->next_port_num++;
-       BT_COMP_LOGI("Added one input port to muxer component: "
-               "port-name=\"%s\", comp-addr=%p",
-               port_name->str, self_comp);
-
-end:
-       if (port_name) {
-               g_string_free(port_name, TRUE);
-       }
-
-       return status;
-}
-
-static
-bt_self_component_add_port_status create_output_port(
-               bt_self_component_filter *self_comp)
-{
-       return bt_self_component_filter_add_output_port(
-               self_comp, "out", NULL, NULL);
-}
-
-static
-void destroy_muxer_comp(struct muxer_comp *muxer_comp)
-{
-       if (!muxer_comp) {
-               return;
-       }
-
-       g_free(muxer_comp);
-}
-
-static
-struct bt_param_validation_map_value_entry_descr muxer_params[] = {
-       BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-};
-
-BT_HIDDEN
-bt_component_class_initialize_method_status muxer_init(
-               bt_self_component_filter *self_comp_flt,
-               bt_self_component_filter_configuration *config,
-               const bt_value *params, void *init_data)
-{
-       bt_component_class_initialize_method_status status;
-       bt_self_component_add_port_status add_port_status;
-       bt_self_component *self_comp =
-               bt_self_component_filter_as_self_component(self_comp_flt);
-       struct muxer_comp *muxer_comp = g_new0(struct muxer_comp, 1);
-       bt_logging_level log_level = bt_component_get_logging_level(
-               bt_self_component_as_component(self_comp));
-       enum bt_param_validation_status validation_status;
-       gchar *validate_error = NULL;
-
-       BT_COMP_LOG_CUR_LVL(BT_LOG_INFO, log_level, self_comp,
-               "Initializing muxer component: "
-               "comp-addr=%p, params-addr=%p", self_comp, params);
-
-       if (!muxer_comp) {
-               /*
-                * Don't use BT_COMP_LOGE_APPEND_CAUSE, as `muxer_comp` is not
-                * initialized.
-                */
-               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                       "Failed to allocate one muxer component.");
-               BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(self_comp,
-                       "Failed to allocate one muxer component.");
-                       status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       muxer_comp->log_level = log_level;
-       muxer_comp->self_comp = self_comp;
-       muxer_comp->self_comp_flt = self_comp_flt;
-
-       validation_status = bt_param_validation_validate(params,
-               muxer_params, &validate_error);
-       if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       } else if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "%s", validate_error);
-               goto error;
-       }
-
-       bt_self_component_set_data(self_comp, muxer_comp);
-       add_port_status = add_available_input_port(self_comp_flt);
-       if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot ensure that at least one muxer component's input port is available: "
-                       "muxer-comp-addr=%p, status=%s",
-                       muxer_comp, bt_common_func_status_string(add_port_status));
-               status = (int) add_port_status;
-               goto error;
-       }
-
-       add_port_status = create_output_port(self_comp_flt);
-       if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create muxer component's output port: "
-                       "muxer-comp-addr=%p, status=%s",
-                       muxer_comp, bt_common_func_status_string(add_port_status));
-               status = (int) add_port_status;
-               goto error;
-       }
-
-       BT_COMP_LOGI("Initialized muxer component: "
-               "comp-addr=%p, params-addr=%p, muxer-comp-addr=%p",
-               self_comp, params, muxer_comp);
-
-       status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-       goto end;
-
-error:
-       destroy_muxer_comp(muxer_comp);
-       bt_self_component_set_data(self_comp, NULL);
-
-end:
-       g_free(validate_error);
-       return status;
-}
-
-BT_HIDDEN
-void muxer_finalize(bt_self_component_filter *self_comp)
-{
-       struct muxer_comp *muxer_comp = bt_self_component_get_data(
-               bt_self_component_filter_as_self_component(self_comp));
-
-       BT_COMP_LOGI("Finalizing muxer component: comp-addr=%p",
-               self_comp);
-       destroy_muxer_comp(muxer_comp);
-}
-
-static
-bt_message_iterator_create_from_message_iterator_status
-create_msg_iter_on_input_port(struct muxer_comp *muxer_comp,
-               struct muxer_msg_iter *muxer_msg_iter,
-               bt_self_component_port_input *self_port,
-               bt_message_iterator **msg_iter)
-{
-       const bt_port *port = bt_self_component_port_as_port(
-               bt_self_component_port_input_as_self_component_port(
-                       self_port));
-       bt_message_iterator_create_from_message_iterator_status
-               status;
-
-       BT_ASSERT(port);
-       BT_ASSERT(bt_port_is_connected(port));
-
-       // TODO: Advance the iterator to >= the time of the latest
-       //       returned message by the muxer message
-       //       iterator which creates it.
-       status = bt_message_iterator_create_from_message_iterator(
-               muxer_msg_iter->self_msg_iter, self_port, msg_iter);
-       if (status != BT_MESSAGE_ITERATOR_CREATE_FROM_MESSAGE_ITERATOR_STATUS_OK) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Cannot create upstream message iterator on input port: "
-                       "port-addr=%p, port-name=\"%s\"",
-                       port, bt_port_get_name(port));
-               goto end;
-       }
-
-       BT_COMP_LOGI("Created upstream message iterator on input port: "
-               "port-addr=%p, port-name=\"%s\", msg-iter-addr=%p",
-               port, bt_port_get_name(port), msg_iter);
-
-end:
-       return status;
-}
-
-static
-bt_message_iterator_class_next_method_status muxer_upstream_msg_iter_next(
-               struct muxer_upstream_msg_iter *muxer_upstream_msg_iter,
-               bool *is_ended)
-{
-       struct muxer_comp *muxer_comp = muxer_upstream_msg_iter->muxer_comp;
-       bt_message_iterator_class_next_method_status status;
-       bt_message_iterator_next_status input_port_iter_status;
-       bt_message_array_const msgs;
-       uint64_t i;
-       uint64_t count;
-
-       BT_COMP_LOGD("Calling upstream message iterator's \"next\" method: "
-               "muxer-upstream-msg-iter-wrap-addr=%p, msg-iter-addr=%p",
-               muxer_upstream_msg_iter,
-               muxer_upstream_msg_iter->msg_iter);
-       input_port_iter_status = bt_message_iterator_next(
-               muxer_upstream_msg_iter->msg_iter, &msgs, &count);
-       BT_COMP_LOGD("Upstream message iterator's \"next\" method returned: "
-               "status=%s",
-               bt_common_func_status_string(input_port_iter_status));
-
-       switch (input_port_iter_status) {
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_OK:
-               /*
-                * Message iterator's current message is
-                * valid: it must be considered for muxing operations.
-                */
-               BT_COMP_LOGD_STR("Validated upstream message iterator wrapper.");
-               BT_ASSERT_DBG(count > 0);
-
-               g_ptr_array_set_size(muxer_upstream_msg_iter->msgs, count);
-               muxer_upstream_msg_iter->next_msg = 0;
-
-               /* Move messages to our queue */
-               for (i = 0; i < count; i++) {
-                       /*
-                        * Push to tail in order; other side
-                        * (muxer_msg_iter_do_next_one()) consumes
-                        * from the head first.
-                        */
-                       g_ptr_array_index(muxer_upstream_msg_iter->msgs, i)
-                               = (gpointer *) msgs[i];
-               }
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-               break;
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_AGAIN:
-               /*
-                * Message iterator's current message is not
-                * valid anymore. Return
-                * BT_MESSAGE_ITERATOR_NEXT_STATUS_AGAIN immediately.
-                */
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
-               break;
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_END:       /* Fall-through. */
-               /*
-                * Message iterator reached the end: release it. It
-                * won't be considered again to find the youngest
-                * message.
-                */
-               *is_ended = true;
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-               break;
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_ERROR:
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_MEMORY_ERROR:
-               /* Error status code */
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Upstream iterator's next method returned an error: status=%s",
-                       bt_common_func_status_string(input_port_iter_status));
-               status = (int) input_port_iter_status;
-               break;
-       default:
-               /* Unsupported status code */
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Unsupported status code: status=%s",
-                       bt_common_func_status_string(input_port_iter_status));
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-               break;
-       }
-
-       return status;
-}
-
-static
-int get_msg_ts_ns(struct muxer_comp *muxer_comp,
-               struct muxer_msg_iter *muxer_msg_iter,
-               const bt_message *msg, int64_t last_returned_ts_ns,
-               int64_t *ts_ns)
-{
-       const bt_clock_snapshot *clock_snapshot = NULL;
-       int ret = 0;
-       const bt_stream_class *stream_class = NULL;
-       bt_message_type msg_type;
-
-       BT_ASSERT_DBG(msg);
-       BT_ASSERT_DBG(ts_ns);
-       BT_COMP_LOGD("Getting message's timestamp: "
-               "muxer-msg-iter-addr=%p, msg-addr=%p, "
-               "last-returned-ts=%" PRId64,
-               muxer_msg_iter, msg, last_returned_ts_ns);
-
-       if (G_UNLIKELY(muxer_msg_iter->clock_class_expectation ==
-                       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE)) {
-               *ts_ns = last_returned_ts_ns;
-               goto end;
-       }
-
-       msg_type = bt_message_get_type(msg);
-
-       if (G_UNLIKELY(msg_type == BT_MESSAGE_TYPE_PACKET_BEGINNING)) {
-               stream_class = bt_stream_borrow_class_const(
-                       bt_packet_borrow_stream_const(
-                               bt_message_packet_beginning_borrow_packet_const(
-                                       msg)));
-       } else if (G_UNLIKELY(msg_type == BT_MESSAGE_TYPE_PACKET_END)) {
-               stream_class = bt_stream_borrow_class_const(
-                       bt_packet_borrow_stream_const(
-                               bt_message_packet_end_borrow_packet_const(
-                                       msg)));
-       } else if (G_UNLIKELY(msg_type == BT_MESSAGE_TYPE_DISCARDED_EVENTS)) {
-               stream_class = bt_stream_borrow_class_const(
-                       bt_message_discarded_events_borrow_stream_const(msg));
-       } else if (G_UNLIKELY(msg_type == BT_MESSAGE_TYPE_DISCARDED_PACKETS)) {
-               stream_class = bt_stream_borrow_class_const(
-                       bt_message_discarded_packets_borrow_stream_const(msg));
-       }
-
-       switch (msg_type) {
-       case BT_MESSAGE_TYPE_EVENT:
-               BT_ASSERT_DBG(bt_message_event_borrow_stream_class_default_clock_class_const(
-                               msg));
-               clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const(
-                       msg);
-               break;
-       case BT_MESSAGE_TYPE_PACKET_BEGINNING:
-               if (bt_stream_class_packets_have_beginning_default_clock_snapshot(
-                               stream_class)) {
-                       clock_snapshot = bt_message_packet_beginning_borrow_default_clock_snapshot_const(
-                               msg);
-               } else {
-                       goto no_clock_snapshot;
-               }
-
-               break;
-       case BT_MESSAGE_TYPE_PACKET_END:
-               if (bt_stream_class_packets_have_end_default_clock_snapshot(
-                               stream_class)) {
-                       clock_snapshot = bt_message_packet_end_borrow_default_clock_snapshot_const(
-                               msg);
-               } else {
-                       goto no_clock_snapshot;
-               }
-
-               break;
-       case BT_MESSAGE_TYPE_STREAM_BEGINNING:
-       {
-               enum bt_message_stream_clock_snapshot_state snapshot_state =
-                       bt_message_stream_beginning_borrow_default_clock_snapshot_const(
-                               msg, &clock_snapshot);
-               if (snapshot_state == BT_MESSAGE_STREAM_CLOCK_SNAPSHOT_STATE_UNKNOWN) {
-                       goto no_clock_snapshot;
-               }
-
-               break;
-       }
-       case BT_MESSAGE_TYPE_STREAM_END:
-       {
-               enum bt_message_stream_clock_snapshot_state snapshot_state =
-                       bt_message_stream_end_borrow_default_clock_snapshot_const(
-                               msg, &clock_snapshot);
-               if (snapshot_state == BT_MESSAGE_STREAM_CLOCK_SNAPSHOT_STATE_UNKNOWN) {
-                       goto no_clock_snapshot;
-               }
-
-               break;
-       }
-       case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
-               if (bt_stream_class_discarded_events_have_default_clock_snapshots(
-                               stream_class)) {
-                       clock_snapshot = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
-                               msg);
-               } else {
-                       goto no_clock_snapshot;
-               }
-
-               break;
-       case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
-               if (bt_stream_class_discarded_packets_have_default_clock_snapshots(
-                               stream_class)) {
-                       clock_snapshot = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
-                               msg);
-               } else {
-                       goto no_clock_snapshot;
-               }
-
-               break;
-       case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
-               clock_snapshot = bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(
-                       msg);
-               break;
-       default:
-               /* All the other messages have a higher priority */
-               BT_COMP_LOGD_STR("Message has no timestamp: using the last returned timestamp.");
-               *ts_ns = last_returned_ts_ns;
-               goto end;
-       }
-
-       ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, ts_ns);
-       if (ret) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Cannot get nanoseconds from Epoch of clock snapshot: "
-                       "clock-snapshot-addr=%p", clock_snapshot);
-               goto error;
-       }
-
-       goto end;
-
-no_clock_snapshot:
-       BT_COMP_LOGD_STR("Message's default clock snapshot is missing: "
-               "using the last returned timestamp.");
-       *ts_ns = last_returned_ts_ns;
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-       if (ret == 0) {
-               BT_COMP_LOGD("Found message's timestamp: "
-                       "muxer-msg-iter-addr=%p, msg-addr=%p, "
-                       "last-returned-ts=%" PRId64 ", ts=%" PRId64,
-                       muxer_msg_iter, msg, last_returned_ts_ns,
-                       *ts_ns);
-       }
-
-       return ret;
-}
-
-static inline
-int validate_clock_class(struct muxer_msg_iter *muxer_msg_iter,
-               struct muxer_comp *muxer_comp,
-               const bt_clock_class *clock_class)
-{
-       int ret = 0;
-       const uint8_t *cc_uuid;
-       const char *cc_name;
-
-       BT_ASSERT_DBG(clock_class);
-       cc_uuid = bt_clock_class_get_uuid(clock_class);
-       cc_name = bt_clock_class_get_name(clock_class);
-
-       if (muxer_msg_iter->clock_class_expectation ==
-                       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY) {
-               /*
-                * This is the first clock class that this muxer message
-                * iterator encounters. Its properties determine what to expect
-                * for the whole lifetime of the iterator.
-                */
-               if (bt_clock_class_origin_is_unix_epoch(clock_class)) {
-                       /* Expect absolute clock classes */
-                       muxer_msg_iter->clock_class_expectation =
-                               MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ABSOLUTE;
-               } else {
-                       if (cc_uuid) {
-                               /*
-                                * Expect non-absolute clock classes
-                                * with a specific UUID.
-                                */
-                               muxer_msg_iter->clock_class_expectation =
-                                       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID;
-                               bt_uuid_copy(muxer_msg_iter->expected_clock_class_uuid, cc_uuid);
-                       } else {
-                               /*
-                                * Expect non-absolute clock classes
-                                * with no UUID.
-                                */
-                               muxer_msg_iter->clock_class_expectation =
-                                       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_NO_UUID;
-                       }
-               }
-       }
-
-       switch (muxer_msg_iter->clock_class_expectation) {
-       case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ABSOLUTE:
-               if (!bt_clock_class_origin_is_unix_epoch(clock_class)) {
-                       BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                               "Expecting an absolute clock class, "
-                               "but got a non-absolute one: "
-                               "clock-class-addr=%p, clock-class-name=\"%s\"",
-                               clock_class, cc_name);
-                       goto error;
-               }
-               break;
-       case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_NO_UUID:
-               if (bt_clock_class_origin_is_unix_epoch(clock_class)) {
-                       BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                               "Expecting a non-absolute clock class with no UUID, "
-                               "but got an absolute one: "
-                               "clock-class-addr=%p, clock-class-name=\"%s\"",
-                               clock_class, cc_name);
-                       goto error;
-               }
-
-               if (cc_uuid) {
-                       BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                               "Expecting a non-absolute clock class with no UUID, "
-                               "but got one with a UUID: "
-                               "clock-class-addr=%p, clock-class-name=\"%s\", "
-                               "uuid=\"" BT_UUID_FMT "\"",
-                               clock_class, cc_name, BT_UUID_FMT_VALUES(cc_uuid));
-                       goto error;
-               }
-               break;
-       case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID:
-               if (bt_clock_class_origin_is_unix_epoch(clock_class)) {
-                       BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                               "Expecting a non-absolute clock class with a specific UUID, "
-                               "but got an absolute one: "
-                               "clock-class-addr=%p, clock-class-name=\"%s\"",
-                               clock_class, cc_name);
-                       goto error;
-               }
-
-               if (!cc_uuid) {
-                       BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                               "Expecting a non-absolute clock class with a specific UUID, "
-                               "but got one with no UUID: "
-                               "clock-class-addr=%p, clock-class-name=\"%s\"",
-                               clock_class, cc_name);
-                       goto error;
-               }
-
-               if (bt_uuid_compare(muxer_msg_iter->expected_clock_class_uuid, cc_uuid) != 0) {
-                       BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                               "Expecting a non-absolute clock class with a specific UUID, "
-                               "but got one with different UUID: "
-                               "clock-class-addr=%p, clock-class-name=\"%s\", "
-                               "expected-uuid=\"" BT_UUID_FMT "\", "
-                               "uuid=\"" BT_UUID_FMT "\"",
-                               clock_class, cc_name,
-                               BT_UUID_FMT_VALUES(muxer_msg_iter->expected_clock_class_uuid),
-                               BT_UUID_FMT_VALUES(cc_uuid));
-                       goto error;
-               }
-               break;
-       case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE:
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Expecting no clock class, but got one: "
-                       "clock-class-addr=%p, clock-class-name=\"%s\"",
-                       clock_class, cc_name);
-               goto error;
-       default:
-               /* Unexpected */
-               BT_COMP_LOGF("Unexpected clock class expectation: "
-                       "expectation-code=%d",
-                       muxer_msg_iter->clock_class_expectation);
-               bt_common_abort();
-       }
-
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-       return ret;
-}
-
-static inline
-int validate_new_stream_clock_class(struct muxer_msg_iter *muxer_msg_iter,
-               struct muxer_comp *muxer_comp, const bt_stream *stream)
-{
-       int ret = 0;
-       const bt_stream_class *stream_class =
-               bt_stream_borrow_class_const(stream);
-       const bt_clock_class *clock_class =
-               bt_stream_class_borrow_default_clock_class_const(stream_class);
-
-       if (!clock_class) {
-               if (muxer_msg_iter->clock_class_expectation ==
-                       MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY) {
-                       /* Expect no clock class */
-                       muxer_msg_iter->clock_class_expectation =
-                               MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE;
-               } else if (muxer_msg_iter->clock_class_expectation !=
-                               MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE) {
-                       BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                               "Expecting stream class without a default clock class: "
-                               "stream-class-addr=%p, stream-class-name=\"%s\", "
-                               "stream-class-id=%" PRIu64,
-                               stream_class, bt_stream_class_get_name(stream_class),
-                               bt_stream_class_get_id(stream_class));
-                       ret = -1;
-               }
-
-               goto end;
-       }
-
-       ret = validate_clock_class(muxer_msg_iter, muxer_comp, clock_class);
-
-end:
-       return ret;
-}
-
-/*
- * This function finds the youngest available message amongst the
- * non-ended upstream message iterators and returns the upstream
- * message iterator which has it, or
- * BT_MESSAGE_ITERATOR_STATUS_END if there's no available
- * message.
- *
- * This function does NOT:
- *
- * * Update any upstream message iterator.
- * * Check the upstream message iterators to retry.
- *
- * On sucess, this function sets *muxer_upstream_msg_iter to the
- * upstream message iterator of which the current message is
- * the youngest, and sets *ts_ns to its time.
- */
-static
-bt_message_iterator_class_next_method_status
-muxer_msg_iter_youngest_upstream_msg_iter(
-               struct muxer_comp *muxer_comp,
-               struct muxer_msg_iter *muxer_msg_iter,
-               struct muxer_upstream_msg_iter **muxer_upstream_msg_iter,
-               int64_t *ts_ns)
-{
-       size_t i;
-       int ret;
-       int64_t youngest_ts_ns = INT64_MAX;
-       bt_message_iterator_class_next_method_status status =
-               BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-
-       BT_ASSERT_DBG(muxer_comp);
-       BT_ASSERT_DBG(muxer_msg_iter);
-       BT_ASSERT_DBG(muxer_upstream_msg_iter);
-       *muxer_upstream_msg_iter = NULL;
-
-       for (i = 0; i < muxer_msg_iter->active_muxer_upstream_msg_iters->len;
-                       i++) {
-               const bt_message *msg;
-               struct muxer_upstream_msg_iter *cur_muxer_upstream_msg_iter =
-                       g_ptr_array_index(
-                               muxer_msg_iter->active_muxer_upstream_msg_iters,
-                               i);
-               int64_t msg_ts_ns;
-
-               if (!cur_muxer_upstream_msg_iter->msg_iter) {
-                       /* This upstream message iterator is ended */
-                       BT_COMP_LOGT("Skipping ended upstream message iterator: "
-                               "muxer-upstream-msg-iter-wrap-addr=%p",
-                               cur_muxer_upstream_msg_iter);
-                       continue;
-               }
-
-               BT_ASSERT_DBG(cur_muxer_upstream_msg_iter->next_msg <
-                       cur_muxer_upstream_msg_iter->msgs->len);
-               msg = g_ptr_array_index(cur_muxer_upstream_msg_iter->msgs,
-                       cur_muxer_upstream_msg_iter->next_msg);
-               BT_ASSERT_DBG(msg);
-
-               if (G_UNLIKELY(bt_message_get_type(msg) ==
-                               BT_MESSAGE_TYPE_STREAM_BEGINNING)) {
-                       ret = validate_new_stream_clock_class(
-                               muxer_msg_iter, muxer_comp,
-                               bt_message_stream_beginning_borrow_stream_const(
-                                       msg));
-                       if (ret) {
-                               /*
-                                * validate_new_stream_clock_class() logs
-                                * errors.
-                                */
-                               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-                               goto end;
-                       }
-               } else if (G_UNLIKELY(bt_message_get_type(msg) ==
-                               BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY)) {
-                       const bt_clock_snapshot *cs;
-
-                       cs = bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(
-                               msg);
-                       ret = validate_clock_class(muxer_msg_iter, muxer_comp,
-                               bt_clock_snapshot_borrow_clock_class_const(cs));
-                       if (ret) {
-                               /* validate_clock_class() logs errors */
-                               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-                               goto end;
-                       }
-               }
-
-               ret = get_msg_ts_ns(muxer_comp, muxer_msg_iter, msg,
-                       muxer_msg_iter->last_returned_ts_ns, &msg_ts_ns);
-               if (ret) {
-                       /* get_msg_ts_ns() logs errors */
-                       *muxer_upstream_msg_iter = NULL;
-                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-
-               /*
-                * Update the current message iterator if it has not been set
-                * yet, or if its current message has a timestamp smaller than
-                * the previously selected youngest message.
-                */
-               if (G_UNLIKELY(*muxer_upstream_msg_iter == NULL) ||
-                               msg_ts_ns < youngest_ts_ns) {
-                       *muxer_upstream_msg_iter =
-                               cur_muxer_upstream_msg_iter;
-                       youngest_ts_ns = msg_ts_ns;
-                       *ts_ns = youngest_ts_ns;
-               } else if (msg_ts_ns == youngest_ts_ns) {
-                       /*
-                        * The currently selected message to be sent downstream
-                        * next has the exact same timestamp that of the
-                        * current candidate message. We must break the tie
-                        * in a predictable manner.
-                        */
-                       BT_ASSERT_DBG((*muxer_upstream_msg_iter)->next_msg <
-                               (*muxer_upstream_msg_iter)->msgs->len);
-                       const bt_message *selected_msg =
-                               g_ptr_array_index((*muxer_upstream_msg_iter)->msgs,
-                                       (*muxer_upstream_msg_iter)->next_msg);
-                       BT_COMP_LOGD_STR("Two of the next message candidates have the same timestamps, pick one deterministically.");
-
-                       /*
-                        * Order the messages in an arbitrary but determinitic
-                        * way.
-                        */
-                       ret = common_muxing_compare_messages(msg, selected_msg);
-                       if (ret < 0) {
-                               /*
-                                * The `msg` should go first. Update the next
-                                * iterator and the current timestamp.
-                                */
-                               *muxer_upstream_msg_iter =
-                                       cur_muxer_upstream_msg_iter;
-                               youngest_ts_ns = msg_ts_ns;
-                               *ts_ns = youngest_ts_ns;
-                       } else if (ret == 0) {
-                               /* Unable to pick which one should go first. */
-                               BT_COMP_LOGW("Cannot deterministically pick next upstream message iterator because they have identical next messages: "
-                                       "muxer-upstream-msg-iter-wrap-addr=%p"
-                                       "cur-muxer-upstream-msg-iter-wrap-addr=%p",
-                                       *muxer_upstream_msg_iter,
-                                       cur_muxer_upstream_msg_iter);
-                       }
-               }
-       }
-
-       if (!*muxer_upstream_msg_iter) {
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
-               *ts_ns = INT64_MIN;
-       }
-
-end:
-       return status;
-}
-
-static
-bt_message_iterator_class_next_method_status
-validate_muxer_upstream_msg_iter(
-       struct muxer_upstream_msg_iter *muxer_upstream_msg_iter,
-       bool *is_ended)
-{
-       struct muxer_comp *muxer_comp = muxer_upstream_msg_iter->muxer_comp;
-       bt_message_iterator_class_next_method_status status;
-
-       BT_COMP_LOGD("Validating muxer's upstream message iterator wrapper: "
-               "muxer-upstream-msg-iter-wrap-addr=%p",
-               muxer_upstream_msg_iter);
-
-       if (muxer_upstream_msg_iter->next_msg < muxer_upstream_msg_iter->msgs->len ||
-                       !muxer_upstream_msg_iter->msg_iter) {
-               BT_COMP_LOGD("Already valid or not considered: "
-                       "queue-len=%u, next-msg=%u, upstream-msg-iter-addr=%p",
-                       muxer_upstream_msg_iter->msgs->len,
-                       muxer_upstream_msg_iter->next_msg,
-                       muxer_upstream_msg_iter->msg_iter);
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-               goto end;
-       }
-
-       /* muxer_upstream_msg_iter_next() logs details/errors */
-       status = muxer_upstream_msg_iter_next(muxer_upstream_msg_iter,
-               is_ended);
-
-end:
-       return status;
-}
-
-static
-bt_message_iterator_class_next_method_status
-validate_muxer_upstream_msg_iters(
-               struct muxer_msg_iter *muxer_msg_iter)
-{
-       struct muxer_comp *muxer_comp = muxer_msg_iter->muxer_comp;
-       bt_message_iterator_class_next_method_status status;
-       size_t i;
-
-       BT_COMP_LOGD("Validating muxer's upstream message iterator wrappers: "
-               "muxer-msg-iter-addr=%p", muxer_msg_iter);
-
-       for (i = 0; i < muxer_msg_iter->active_muxer_upstream_msg_iters->len;
-                       i++) {
-               bool is_ended = false;
-               struct muxer_upstream_msg_iter *muxer_upstream_msg_iter =
-                       g_ptr_array_index(
-                               muxer_msg_iter->active_muxer_upstream_msg_iters,
-                               i);
-
-               status = validate_muxer_upstream_msg_iter(
-                       muxer_upstream_msg_iter, &is_ended);
-               if (status != BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK) {
-                       if (status < 0) {
-                               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                                       "Cannot validate muxer's upstream message iterator wrapper: "
-                                       "muxer-msg-iter-addr=%p, "
-                                       "muxer-upstream-msg-iter-wrap-addr=%p",
-                                       muxer_msg_iter,
-                                       muxer_upstream_msg_iter);
-                       } else {
-                               BT_COMP_LOGD("Cannot validate muxer's upstream message iterator wrapper: "
-                                       "muxer-msg-iter-addr=%p, "
-                                       "muxer-upstream-msg-iter-wrap-addr=%p",
-                                       muxer_msg_iter,
-                                       muxer_upstream_msg_iter);
-                       }
-
-                       goto end;
-               }
-
-               /*
-                * Move this muxer upstream message iterator to the
-                * array of ended iterators if it's ended.
-                */
-               if (G_UNLIKELY(is_ended)) {
-                       BT_COMP_LOGD("Muxer's upstream message iterator wrapper: ended or canceled: "
-                               "muxer-msg-iter-addr=%p, "
-                               "muxer-upstream-msg-iter-wrap-addr=%p",
-                               muxer_msg_iter, muxer_upstream_msg_iter);
-                       g_ptr_array_add(
-                               muxer_msg_iter->ended_muxer_upstream_msg_iters,
-                               muxer_upstream_msg_iter);
-                       muxer_msg_iter->active_muxer_upstream_msg_iters->pdata[i] = NULL;
-
-                       /*
-                        * Use g_ptr_array_remove_fast() because the
-                        * order of those elements is not important.
-                        */
-                       g_ptr_array_remove_index_fast(
-                               muxer_msg_iter->active_muxer_upstream_msg_iters,
-                               i);
-                       i--;
-               }
-       }
-
-       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-
-end:
-       return status;
-}
-
-static inline
-bt_message_iterator_class_next_method_status muxer_msg_iter_do_next_one(
-               struct muxer_comp *muxer_comp,
-               struct muxer_msg_iter *muxer_msg_iter,
-               const bt_message **msg)
-{
-       bt_message_iterator_class_next_method_status status;
-       struct muxer_upstream_msg_iter *muxer_upstream_msg_iter = NULL;
-       /* Initialize to avoid -Wmaybe-uninitialized warning with gcc 4.8. */
-       int64_t next_return_ts = 0;
-
-       status = validate_muxer_upstream_msg_iters(muxer_msg_iter);
-       if (status != BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK) {
-               /* validate_muxer_upstream_msg_iters() logs details */
-               goto end;
-       }
-
-       /*
-        * At this point we know that all the existing upstream
-        * message iterators are valid. We can find the one,
-        * amongst those, of which the current message is the
-        * youngest.
-        */
-       status = muxer_msg_iter_youngest_upstream_msg_iter(muxer_comp,
-                       muxer_msg_iter, &muxer_upstream_msg_iter,
-                       &next_return_ts);
-       if (status < 0 || status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END) {
-               if (status < 0) {
-                       BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                               "Cannot find the youngest upstream message iterator wrapper: "
-                               "status=%s",
-                               bt_common_func_status_string(status));
-               } else {
-                       BT_COMP_LOGD("Cannot find the youngest upstream message iterator wrapper: "
-                               "status=%s",
-                               bt_common_func_status_string(status));
-               }
-
-               goto end;
-       }
-
-       if (next_return_ts < muxer_msg_iter->last_returned_ts_ns) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Youngest upstream message iterator wrapper's timestamp is less than muxer's message iterator's last returned timestamp: "
-                       "muxer-msg-iter-addr=%p, ts=%" PRId64 ", "
-                       "last-returned-ts=%" PRId64,
-                       muxer_msg_iter, next_return_ts,
-                       muxer_msg_iter->last_returned_ts_ns);
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       BT_COMP_LOGD("Found youngest upstream message iterator wrapper: "
-               "muxer-msg-iter-addr=%p, "
-               "muxer-upstream-msg-iter-wrap-addr=%p, "
-               "ts=%" PRId64,
-               muxer_msg_iter, muxer_upstream_msg_iter, next_return_ts);
-       BT_ASSERT_DBG(status ==
-               BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK);
-       BT_ASSERT_DBG(muxer_upstream_msg_iter);
-
-       /*
-        * Consume from the queue's head: other side
-        * (muxer_upstream_msg_iter_next()) writes to the tail.
-        */
-       *msg = g_ptr_array_index(muxer_upstream_msg_iter->msgs,
-               muxer_upstream_msg_iter->next_msg);
-       g_ptr_array_index(muxer_upstream_msg_iter->msgs,
-               muxer_upstream_msg_iter->next_msg) = NULL;
-       ++muxer_upstream_msg_iter->next_msg;
-       BT_ASSERT_DBG(*msg);
-       muxer_msg_iter->last_returned_ts_ns = next_return_ts;
-
-end:
-       return status;
-}
-
-static
-bt_message_iterator_class_next_method_status muxer_msg_iter_do_next(
-               struct muxer_comp *muxer_comp,
-               struct muxer_msg_iter *muxer_msg_iter,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       bt_message_iterator_class_next_method_status status;
-       uint64_t i = 0;
-
-       if (G_UNLIKELY(muxer_msg_iter->next_saved_error)) {
-               /*
-                * Last time we were called, we hit an error but had some
-                * messages to deliver, so we stashed the error here.  Return
-                * it now.
-                */
-               BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(muxer_msg_iter->next_saved_error);
-               status = muxer_msg_iter->next_saved_status;
-               goto end;
-       }
-
-       do {
-               status = muxer_msg_iter_do_next_one(muxer_comp,
-                       muxer_msg_iter, &msgs[i]);
-               if (status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK) {
-                       i++;
-               }
-       } while (i < capacity && status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK);
-
-       if (i > 0) {
-               /*
-                * Even if muxer_msg_iter_do_next_one() returned
-                * something else than
-                * BT_MESSAGE_ITERATOR_STATUS_OK, we accumulated
-                * message objects in the output message
-                * array, so we need to return
-                * BT_MESSAGE_ITERATOR_STATUS_OK so that they are
-                * transfered to downstream. This other status occurs
-                * again the next time muxer_msg_iter_do_next() is
-                * called, possibly without any accumulated
-                * message, in which case we'll return it.
-                */
-               if (status < 0) {
-                       /*
-                        * Save this error for the next _next call.  Assume that
-                        * this component always appends error causes when
-                        * returning an error status code, which will cause the
-                        * current thread error to be non-NULL.
-                        */
-                       muxer_msg_iter->next_saved_error = bt_current_thread_take_error();
-                       BT_ASSERT(muxer_msg_iter->next_saved_error);
-                       muxer_msg_iter->next_saved_status = status;
-               }
-
-               *count = i;
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-       }
-
-end:
-       return status;
-}
-
-static
-void destroy_muxer_msg_iter(struct muxer_msg_iter *muxer_msg_iter)
-{
-       struct muxer_comp *muxer_comp;
-
-       if (!muxer_msg_iter) {
-               return;
-       }
-
-       muxer_comp = muxer_msg_iter->muxer_comp;
-       BT_COMP_LOGD("Destroying muxer component's message iterator: "
-               "muxer-msg-iter-addr=%p", muxer_msg_iter);
-
-       if (muxer_msg_iter->active_muxer_upstream_msg_iters) {
-               BT_COMP_LOGD_STR("Destroying muxer's active upstream message iterator wrappers.");
-               g_ptr_array_free(
-                       muxer_msg_iter->active_muxer_upstream_msg_iters, TRUE);
-       }
-
-       if (muxer_msg_iter->ended_muxer_upstream_msg_iters) {
-               BT_COMP_LOGD_STR("Destroying muxer's ended upstream message iterator wrappers.");
-               g_ptr_array_free(
-                       muxer_msg_iter->ended_muxer_upstream_msg_iters, TRUE);
-       }
-
-       g_free(muxer_msg_iter);
-}
-
-static
-bt_message_iterator_class_initialize_method_status
-muxer_msg_iter_init_upstream_iterators(struct muxer_comp *muxer_comp,
-               struct muxer_msg_iter *muxer_msg_iter,
-               struct bt_self_message_iterator_configuration *config)
-{
-       int64_t count;
-       int64_t i;
-       bt_message_iterator_class_initialize_method_status status;
-       bool can_seek_forward = true;
-
-       count = bt_component_filter_get_input_port_count(
-               bt_self_component_filter_as_component_filter(
-                       muxer_comp->self_comp_flt));
-       if (count < 0) {
-               BT_COMP_LOGD("No input port to initialize for muxer component's message iterator: "
-                       "muxer-comp-addr=%p, muxer-msg-iter-addr=%p",
-                       muxer_comp, muxer_msg_iter);
-               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
-               goto end;
-       }
-
-       for (i = 0; i < count; i++) {
-               bt_message_iterator *upstream_msg_iter;
-               bt_self_component_port_input *self_port =
-                       bt_self_component_filter_borrow_input_port_by_index(
-                               muxer_comp->self_comp_flt, i);
-               const bt_port *port;
-               bt_message_iterator_create_from_message_iterator_status
-                       msg_iter_status;
-               int int_status;
-
-               BT_ASSERT(self_port);
-               port = bt_self_component_port_as_port(
-                       bt_self_component_port_input_as_self_component_port(
-                               self_port));
-               BT_ASSERT(port);
-
-               if (!bt_port_is_connected(port)) {
-                       /* Skip non-connected port */
-                       continue;
-               }
-
-               msg_iter_status = create_msg_iter_on_input_port(muxer_comp,
-                       muxer_msg_iter, self_port, &upstream_msg_iter);
-               if (msg_iter_status != BT_MESSAGE_ITERATOR_CREATE_FROM_MESSAGE_ITERATOR_STATUS_OK) {
-                       /* create_msg_iter_on_input_port() logs errors */
-                       status = (int) msg_iter_status;
-                       goto end;
-               }
-
-               int_status = muxer_msg_iter_add_upstream_msg_iter(muxer_msg_iter,
-                       upstream_msg_iter);
-               bt_message_iterator_put_ref(
-                       upstream_msg_iter);
-               if (int_status) {
-                       status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-                       /* muxer_msg_iter_add_upstream_msg_iter() logs errors */
-                       goto end;
-               }
-
-               can_seek_forward = can_seek_forward &&
-                       bt_message_iterator_can_seek_forward(
-                               upstream_msg_iter);
-       }
-
-       /*
-        * This iterator can seek forward if all of its iterators can seek
-        * forward.
-        */
-       bt_self_message_iterator_configuration_set_can_seek_forward(
-               config, can_seek_forward);
-
-       status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-bt_message_iterator_class_initialize_method_status muxer_msg_iter_init(
-               bt_self_message_iterator *self_msg_iter,
-               bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *port)
-{
-       struct muxer_comp *muxer_comp = NULL;
-       struct muxer_msg_iter *muxer_msg_iter = NULL;
-       bt_message_iterator_class_initialize_method_status status;
-       bt_self_component *self_comp =
-               bt_self_message_iterator_borrow_component(self_msg_iter);
-
-       muxer_comp = bt_self_component_get_data(self_comp);
-       BT_ASSERT(muxer_comp);
-       BT_COMP_LOGD("Initializing muxer component's message iterator: "
-               "comp-addr=%p, muxer-comp-addr=%p, msg-iter-addr=%p",
-               self_comp, muxer_comp, self_msg_iter);
-
-       if (muxer_comp->initializing_muxer_msg_iter) {
-               /*
-                * Weird, unhandled situation detected: downstream
-                * creates a muxer message iterator while creating
-                * another muxer message iterator (same component).
-                */
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Recursive initialization of muxer component's message iterator: "
-                       "comp-addr=%p, muxer-comp-addr=%p, msg-iter-addr=%p",
-                       self_comp, muxer_comp, self_msg_iter);
-               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-               goto error;
-       }
-
-       muxer_comp->initializing_muxer_msg_iter = true;
-       muxer_msg_iter = g_new0(struct muxer_msg_iter, 1);
-       if (!muxer_msg_iter) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Failed to allocate one muxer component's message iterator.");
-               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       muxer_msg_iter->muxer_comp = muxer_comp;
-       muxer_msg_iter->self_msg_iter = self_msg_iter;
-       muxer_msg_iter->last_returned_ts_ns = INT64_MIN;
-       muxer_msg_iter->active_muxer_upstream_msg_iters =
-               g_ptr_array_new_with_free_func(
-                       (GDestroyNotify) destroy_muxer_upstream_msg_iter);
-       if (!muxer_msg_iter->active_muxer_upstream_msg_iters) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp, "Failed to allocate a GPtrArray.");
-               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       muxer_msg_iter->ended_muxer_upstream_msg_iters =
-               g_ptr_array_new_with_free_func(
-                       (GDestroyNotify) destroy_muxer_upstream_msg_iter);
-       if (!muxer_msg_iter->ended_muxer_upstream_msg_iters) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp, "Failed to allocate a GPtrArray.");
-               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       status = muxer_msg_iter_init_upstream_iterators(muxer_comp,
-               muxer_msg_iter, config);
-       if (status) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Cannot initialize connected input ports for muxer component's message iterator: "
-                       "comp-addr=%p, muxer-comp-addr=%p, "
-                       "muxer-msg-iter-addr=%p, msg-iter-addr=%p, ret=%d",
-                       self_comp, muxer_comp, muxer_msg_iter,
-                       self_msg_iter, status);
-               goto error;
-       }
-
-       bt_self_message_iterator_set_data(self_msg_iter, muxer_msg_iter);
-       BT_COMP_LOGD("Initialized muxer component's message iterator: "
-               "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, "
-               "msg-iter-addr=%p",
-               self_comp, muxer_comp, muxer_msg_iter, self_msg_iter);
-       goto end;
-
-error:
-       destroy_muxer_msg_iter(muxer_msg_iter);
-       bt_self_message_iterator_set_data(self_msg_iter, NULL);
-
-end:
-       muxer_comp->initializing_muxer_msg_iter = false;
-       return status;
-}
-
-BT_HIDDEN
-void muxer_msg_iter_finalize(bt_self_message_iterator *self_msg_iter)
-{
-       struct muxer_msg_iter *muxer_msg_iter =
-               bt_self_message_iterator_get_data(self_msg_iter);
-       bt_self_component *self_comp = NULL;
-       struct muxer_comp *muxer_comp = NULL;
-
-       self_comp = bt_self_message_iterator_borrow_component(
-               self_msg_iter);
-       BT_ASSERT(self_comp);
-       muxer_comp = bt_self_component_get_data(self_comp);
-       BT_COMP_LOGD("Finalizing muxer component's message iterator: "
-               "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, "
-               "msg-iter-addr=%p",
-               self_comp, muxer_comp, muxer_msg_iter, self_msg_iter);
-
-       if (muxer_msg_iter) {
-               destroy_muxer_msg_iter(muxer_msg_iter);
-       }
-}
-
-BT_HIDDEN
-bt_message_iterator_class_next_method_status muxer_msg_iter_next(
-               bt_self_message_iterator *self_msg_iter,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       bt_message_iterator_class_next_method_status status;
-       struct muxer_msg_iter *muxer_msg_iter =
-               bt_self_message_iterator_get_data(self_msg_iter);
-       bt_self_component *self_comp = NULL;
-       struct muxer_comp *muxer_comp = NULL;
-
-       BT_ASSERT_DBG(muxer_msg_iter);
-       self_comp = bt_self_message_iterator_borrow_component(
-               self_msg_iter);
-       BT_ASSERT_DBG(self_comp);
-       muxer_comp = bt_self_component_get_data(self_comp);
-       BT_ASSERT_DBG(muxer_comp);
-       BT_COMP_LOGT("Muxer component's message iterator's \"next\" method called: "
-               "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, "
-               "msg-iter-addr=%p",
-               self_comp, muxer_comp, muxer_msg_iter, self_msg_iter);
-
-       status = muxer_msg_iter_do_next(muxer_comp, muxer_msg_iter,
-               msgs, capacity, count);
-       if (status < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot get next message: "
-                       "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, "
-                       "msg-iter-addr=%p, status=%s",
-                       self_comp, muxer_comp, muxer_msg_iter, self_msg_iter,
-                       bt_common_func_status_string(status));
-       } else {
-               BT_COMP_LOGT("Returning from muxer component's message iterator's \"next\" method: "
-                       "status=%s",
-                       bt_common_func_status_string(status));
-       }
-
-       return status;
-}
-
-BT_HIDDEN
-bt_component_class_port_connected_method_status muxer_input_port_connected(
-               bt_self_component_filter *self_comp,
-               bt_self_component_port_input *self_port,
-               const bt_port_output *other_port)
-{
-       bt_component_class_port_connected_method_status status =
-               BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_OK;
-       bt_self_component_add_port_status add_port_status;
-       struct muxer_comp *muxer_comp = bt_self_component_get_data(
-               bt_self_component_filter_as_self_component(self_comp));
-
-       add_port_status = add_available_input_port(self_comp);
-       if (add_port_status) {
-               BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                       "Cannot add one muxer component's input port: status=%s",
-                       bt_common_func_status_string(add_port_status));
-
-               if (add_port_status ==
-                               BT_SELF_COMPONENT_ADD_PORT_STATUS_MEMORY_ERROR) {
-                       status = BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_MEMORY_ERROR;
-               } else {
-                       status = BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_ERROR;
-               }
-
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-static inline
-bt_message_iterator_class_can_seek_beginning_method_status
-muxer_upstream_msg_iters_can_all_seek_beginning(
-               struct muxer_comp *muxer_comp,
-               GPtrArray *muxer_upstream_msg_iters, bt_bool *can_seek)
-{
-       bt_message_iterator_class_can_seek_beginning_method_status status =
-               BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_BEGINNING_METHOD_STATUS_OK;
-       uint64_t i;
-
-       for (i = 0; i < muxer_upstream_msg_iters->len; i++) {
-               struct muxer_upstream_msg_iter *upstream_msg_iter =
-                       muxer_upstream_msg_iters->pdata[i];
-               status = (int) bt_message_iterator_can_seek_beginning(
-                       upstream_msg_iter->msg_iter, can_seek);
-               if (status != BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_BEGINNING_METHOD_STATUS_OK) {
-                       BT_COMP_LOGE_APPEND_CAUSE(muxer_comp->self_comp,
-                               "Failed to determine whether upstream message iterator can seek beginning: "
-                               "msg-iter-addr=%p", upstream_msg_iter->msg_iter);
-                       goto end;
-               }
-
-               if (!*can_seek) {
-                       goto end;
-               }
-       }
-
-       *can_seek = BT_TRUE;
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-bt_message_iterator_class_can_seek_beginning_method_status
-muxer_msg_iter_can_seek_beginning(
-               bt_self_message_iterator *self_msg_iter, bt_bool *can_seek)
-{
-       struct muxer_msg_iter *muxer_msg_iter =
-               bt_self_message_iterator_get_data(self_msg_iter);
-       bt_message_iterator_class_can_seek_beginning_method_status status;
-
-       status = muxer_upstream_msg_iters_can_all_seek_beginning(
-               muxer_msg_iter->muxer_comp,
-               muxer_msg_iter->active_muxer_upstream_msg_iters, can_seek);
-       if (status != BT_MESSAGE_ITERATOR_CLASS_CAN_SEEK_BEGINNING_METHOD_STATUS_OK) {
-               goto end;
-       }
-
-       if (!*can_seek) {
-               goto end;
-       }
-
-       status = muxer_upstream_msg_iters_can_all_seek_beginning(
-               muxer_msg_iter->muxer_comp,
-               muxer_msg_iter->ended_muxer_upstream_msg_iters, can_seek);
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-bt_message_iterator_class_seek_beginning_method_status muxer_msg_iter_seek_beginning(
-               bt_self_message_iterator *self_msg_iter)
-{
-       struct muxer_msg_iter *muxer_msg_iter =
-               bt_self_message_iterator_get_data(self_msg_iter);
-       bt_message_iterator_class_seek_beginning_method_status status =
-               BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_OK;
-       bt_message_iterator_seek_beginning_status seek_beg_status;
-       uint64_t i;
-
-       /* Seek all ended upstream iterators first */
-       for (i = 0; i < muxer_msg_iter->ended_muxer_upstream_msg_iters->len;
-                       i++) {
-               struct muxer_upstream_msg_iter *upstream_msg_iter =
-                       muxer_msg_iter->ended_muxer_upstream_msg_iters->pdata[i];
-
-               seek_beg_status = bt_message_iterator_seek_beginning(
-                       upstream_msg_iter->msg_iter);
-               if (seek_beg_status != BT_MESSAGE_ITERATOR_SEEK_BEGINNING_STATUS_OK) {
-                       status = (int) seek_beg_status;
-                       goto end;
-               }
-
-               empty_message_queue(upstream_msg_iter);
-       }
-
-       /* Seek all previously active upstream iterators */
-       for (i = 0; i < muxer_msg_iter->active_muxer_upstream_msg_iters->len;
-                       i++) {
-               struct muxer_upstream_msg_iter *upstream_msg_iter =
-                       muxer_msg_iter->active_muxer_upstream_msg_iters->pdata[i];
-
-               seek_beg_status = bt_message_iterator_seek_beginning(
-                       upstream_msg_iter->msg_iter);
-               if (seek_beg_status != BT_MESSAGE_ITERATOR_SEEK_BEGINNING_STATUS_OK) {
-                       status = (int) seek_beg_status;
-                       goto end;
-               }
-
-               empty_message_queue(upstream_msg_iter);
-       }
-
-       /* Make them all active */
-       for (i = 0; i < muxer_msg_iter->ended_muxer_upstream_msg_iters->len;
-                       i++) {
-               struct muxer_upstream_msg_iter *upstream_msg_iter =
-                       muxer_msg_iter->ended_muxer_upstream_msg_iters->pdata[i];
-
-               g_ptr_array_add(muxer_msg_iter->active_muxer_upstream_msg_iters,
-                       upstream_msg_iter);
-               muxer_msg_iter->ended_muxer_upstream_msg_iters->pdata[i] = NULL;
-       }
-
-       /*
-        * GLib < 2.48.0 asserts when g_ptr_array_remove_range() is
-        * called on an empty array.
-        */
-       if (muxer_msg_iter->ended_muxer_upstream_msg_iters->len > 0) {
-               g_ptr_array_remove_range(muxer_msg_iter->ended_muxer_upstream_msg_iters,
-                       0, muxer_msg_iter->ended_muxer_upstream_msg_iters->len);
-       }
-       muxer_msg_iter->last_returned_ts_ns = INT64_MIN;
-       muxer_msg_iter->clock_class_expectation =
-               MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY;
-
-end:
-       return status;
-}
diff --git a/src/plugins/utils/muxer/muxer.h b/src/plugins/utils/muxer/muxer.h
deleted file mode 100644 (file)
index 23a4c8a..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef BABELTRACE_PLUGINS_UTILS_MUXER_H
-#define BABELTRACE_PLUGINS_UTILS_MUXER_H
-
-#include <stdint.h>
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-
-BT_HIDDEN
-bt_component_class_initialize_method_status muxer_init(
-               bt_self_component_filter *self_comp,
-               bt_self_component_filter_configuration *config,
-               const bt_value *params, void *init_data);
-
-BT_HIDDEN
-void muxer_finalize(bt_self_component_filter *self_comp);
-
-BT_HIDDEN
-bt_message_iterator_class_initialize_method_status muxer_msg_iter_init(
-               bt_self_message_iterator *self_msg_iter,
-               bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *self_port);
-
-BT_HIDDEN
-void muxer_msg_iter_finalize(
-               bt_self_message_iterator *self_msg_iter);
-
-BT_HIDDEN
-bt_message_iterator_class_next_method_status muxer_msg_iter_next(
-               bt_self_message_iterator *self_msg_iter,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count);
-
-BT_HIDDEN
-bt_component_class_port_connected_method_status muxer_input_port_connected(
-               bt_self_component_filter *comp,
-               bt_self_component_port_input *self_port,
-               const bt_port_output *other_port);
-
-BT_HIDDEN
-bt_message_iterator_class_can_seek_beginning_method_status
-muxer_msg_iter_can_seek_beginning(
-               bt_self_message_iterator *message_iterator, bt_bool *can_seek);
-
-BT_HIDDEN
-bt_message_iterator_class_seek_beginning_method_status muxer_msg_iter_seek_beginning(
-               bt_self_message_iterator *message_iterator);
-
-#endif /* BABELTRACE_PLUGINS_UTILS_MUXER_H */
diff --git a/src/plugins/utils/muxer/upstream-msg-iter.cpp b/src/plugins/utils/muxer/upstream-msg-iter.cpp
new file mode 100644 (file)
index 0000000..ea597da
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2017-2023 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <glib.h>
+
+#include "cpp-common/bt2/optional-borrowed-object.hpp"
+#include "cpp-common/bt2c/logging.hpp"
+#include "cpp-common/vendor/fmt/core.h"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "upstream-msg-iter.hpp"
+
+namespace bt2mux {
+
+UpstreamMsgIter::UpstreamMsgIter(bt2::MessageIterator::Shared msgIter, std::string portName,
+                                 const bt2c::Logger& parentLogger) :
+    _mMsgIter {std::move(msgIter)},
+    _mLogger {parentLogger, fmt::format("{}/[{}]", parentLogger.tag(), portName)},
+    _mPortName {std::move(portName)}
+{
+    BT_CPPLOGI("Created an upstream message iterator: this={}, port-name={}", fmt::ptr(this),
+               _mPortName);
+}
+
+namespace {
+
+/*
+ * Returns the clock snapshot of `msg`, possibly missing.
+ */
+bt2::OptionalBorrowedObject<bt2::ConstClockSnapshot> msgCs(const bt2::ConstMessage msg) noexcept
+{
+    switch (msg.type()) {
+    case bt2::MessageType::Event:
+        if (msg.asEvent().streamClassDefaultClockClass()) {
+            return msg.asEvent().defaultClockSnapshot();
+        }
+
+        break;
+    case bt2::MessageType::PacketBeginning:
+        if (msg.asPacketBeginning().packet().stream().cls().packetsHaveBeginningClockSnapshot()) {
+            return msg.asPacketBeginning().defaultClockSnapshot();
+        }
+
+        break;
+    case bt2::MessageType::PacketEnd:
+        if (msg.asPacketEnd().packet().stream().cls().packetsHaveEndClockSnapshot()) {
+            return msg.asPacketEnd().defaultClockSnapshot();
+        }
+
+        break;
+    case bt2::MessageType::DiscardedEvents:
+        if (msg.asDiscardedEvents().stream().cls().discardedEventsHaveDefaultClockSnapshots()) {
+            return msg.asDiscardedEvents().beginningDefaultClockSnapshot();
+        }
+
+        break;
+    case bt2::MessageType::DiscardedPackets:
+        if (msg.asDiscardedPackets().stream().cls().discardedPacketsHaveDefaultClockSnapshots()) {
+            return msg.asDiscardedPackets().beginningDefaultClockSnapshot();
+        }
+
+        break;
+    case bt2::MessageType::MessageIteratorInactivity:
+        return msg.asMessageIteratorInactivity().clockSnapshot();
+    case bt2::MessageType::StreamBeginning:
+        if (msg.asStreamBeginning().streamClassDefaultClockClass()) {
+            return msg.asStreamBeginning().defaultClockSnapshot();
+        }
+
+        break;
+    case bt2::MessageType::StreamEnd:
+        if (msg.asStreamEnd().streamClassDefaultClockClass()) {
+            return msg.asStreamEnd().defaultClockSnapshot();
+        }
+
+        break;
+    default:
+        bt_common_abort();
+    }
+
+    return {};
+}
+
+} /* namespace */
+
+UpstreamMsgIter::ReloadStatus UpstreamMsgIter::reload()
+{
+    BT_ASSERT_DBG(!_mDiscardRequired);
+
+    if (G_UNLIKELY(!_mMsgs.msgs)) {
+        /*
+         * This will either:
+         *
+         * 1. Set `_mMsgs.msgs` to new messages (we'll return
+         *    `ReloadStatus::MORE`).
+         *
+         * 2. Not set `_mMsgs.msgs` (ended, we'll return
+         *    `ReloadStatus::NO_MORE`).
+         *
+         * 3. Throw.
+         */
+        this->_tryGetNewMsgs();
+    }
+
+    if (G_UNLIKELY(!_mMsgs.msgs)) {
+        /* Still none: no more */
+        _mMsgTs.reset();
+        return ReloadStatus::NoMore;
+    } else {
+        if (const auto cs = msgCs(this->msg())) {
+            _mMsgTs = cs->nsFromOrigin();
+            BT_CPPLOGD("Cached the timestamp of the current message: this={}, ts={}",
+                       fmt::ptr(this), *_mMsgTs);
+        } else {
+            _mMsgTs.reset();
+            BT_CPPLOGD("Reset the timestamp of the current message: this={}", fmt::ptr(this));
+        }
+
+        _mDiscardRequired = true;
+        return ReloadStatus::More;
+    }
+}
+
+void UpstreamMsgIter::_tryGetNewMsgs()
+{
+    BT_ASSERT_DBG(_mMsgIter);
+    BT_CPPLOGD("Calling the \"next\" method of the upstream message iterator: this={}",
+               fmt::ptr(this));
+
+    /*
+     * Replace with next batch!
+     *
+     * This may throw, in which case we'll keep our current
+     * `_mMsgs.msgs` (set), still requiring to get new messages the next
+     * time the user calls reload().
+     */
+    _mMsgs.msgs = _mMsgIter->next();
+
+    if (!_mMsgs.msgs) {
+        /*
+         * Don't destroy `*_mMsgIter` here because the user may still
+         * call seekBeginning() afterwards.
+         */
+        BT_CPPLOGD("End of upstream message iterator: this={}", fmt::ptr(this));
+        return;
+    }
+
+    _mMsgs.index = 0;
+    BT_CPPLOGD("Got {1} messages from upstream: this={0}, count={1}", fmt::ptr(this),
+               _mMsgs.msgs->length());
+}
+
+bool UpstreamMsgIter::canSeekBeginning()
+{
+    return _mMsgIter->canSeekBeginning();
+}
+
+void UpstreamMsgIter::seekBeginning()
+{
+    _mMsgIter->seekBeginning();
+    _mMsgs.msgs.reset();
+    _mMsgTs.reset();
+    _mDiscardRequired = false;
+}
+
+bool UpstreamMsgIter::canSeekForward() const noexcept
+{
+    return _mMsgIter->canSeekForward();
+}
+
+} /* namespace bt2mux */
diff --git a/src/plugins/utils/muxer/upstream-msg-iter.hpp b/src/plugins/utils/muxer/upstream-msg-iter.hpp
new file mode 100644 (file)
index 0000000..4d89da3
--- /dev/null
@@ -0,0 +1,188 @@
+
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright 2017-2023 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef BABELTRACE_PLUGINS_UTILS_MUXER_UPSTREAM_MSG_ITER_HPP
+#define BABELTRACE_PLUGINS_UTILS_MUXER_UPSTREAM_MSG_ITER_HPP
+
+#include <memory>
+
+#include "common/assert.h"
+#include "cpp-common/bt2/message-array.hpp"
+#include "cpp-common/bt2/message-iterator.hpp"
+#include "cpp-common/bt2c/logging.hpp"
+#include "cpp-common/bt2s/optional.hpp"
+
+namespace bt2mux {
+
+/*
+ * An instance of this wraps an upstream libbabeltrace2 message
+ * iterator, keeping an internal array of receives messages, and making
+ * the oldest one available (msg() method).
+ */
+class UpstreamMsgIter final
+{
+public:
+    /* Unique pointer to upstream message iterator */
+    using UP = std::unique_ptr<UpstreamMsgIter>;
+
+    /* Return type of reload() */
+    enum class ReloadStatus
+    {
+        More,
+        NoMore,
+    };
+
+    /*
+     * Builds an upstream message iterator wrapper using the
+     * libbabeltrace2 message iterator `msgIter`.
+     *
+     * This constructor doesn't immediately gets the next messages from
+     * `*msgIter` (you always need to call reload() before you call
+     * msg()), therefore it won't throw `bt2::Error` or `bt2::TryAgain`.
+     */
+    explicit UpstreamMsgIter(bt2::MessageIterator::Shared msgIter, std::string portName,
+                             const bt2c::Logger& parentLogger);
+
+    /* Some protection */
+    UpstreamMsgIter(const UpstreamMsgIter&) = delete;
+    UpstreamMsgIter& operator=(const UpstreamMsgIter&) = delete;
+
+    /*
+     * Current message.
+     *
+     * Before you call this method:
+     *
+     * 1. If needed, you must call discard().
+     *
+     *    This is not the case immediately after construction and
+     *    immediately after seeking.
+     *
+     * 2. You must call reload() successfully (not ended).
+     *
+     *    This is always the case.
+     *
+     *    This makes it possible to build an `UpstreamMsgIter` instance
+     *    without libbabeltrace2 message iterator exceptions.
+     */
+    bt2::ConstMessage msg() const noexcept
+    {
+        BT_ASSERT_DBG(_mMsgs.msgs && _mMsgs.index < _mMsgs.msgs->length());
+        return (*_mMsgs.msgs)[_mMsgs.index];
+    }
+
+    /*
+     * Timestamp, if any, of the current message.
+     *
+     * It must be valid to call msg() when you call this method.
+     */
+    const bt2s::optional<std::int64_t> msgTs() const noexcept
+    {
+        return _mMsgTs;
+    }
+
+    /*
+     * Discards the current message, making this upstream message
+     * iterator ready for a reload (reload()).
+     *
+     * You may only call reload() or seekBeginning() after having called
+     * this.
+     */
+    void discard() noexcept
+    {
+        BT_ASSERT_DBG(_mMsgs.msgs && _mMsgs.index < _mMsgs.msgs->length());
+        BT_ASSERT_DBG(_mDiscardRequired);
+        _mDiscardRequired = false;
+        ++_mMsgs.index;
+
+        if (_mMsgs.index == _mMsgs.msgs->length()) {
+            _mMsgs.msgs.reset();
+        }
+    }
+
+    /*
+     * Retrieves the next message, making it available afterwards
+     * through the msg() method.
+     *
+     * You must have called discard() to discard the current message, if
+     * any, before you call this method.
+     *
+     * This method may throw anything bt2::MessageIterator::next() may
+     * throw.
+     *
+     * If this method returns `ReloadStatus::NO_MORE`, then the
+     * underlying libbabeltrace2 message iterator is ended, meaning you
+     * may not call msg(), msgTs(), or reload() again for this message
+     * iterator until you successfully call seekBeginning().
+     */
+    ReloadStatus reload();
+
+    /*
+     * Forwards to bt2::MessageIterator::canSeekBeginning().
+     */
+    bool canSeekBeginning();
+
+    /*
+     * Forwards to bt2::MessageIterator::seekBeginning().
+     *
+     * On success, you may call reload() afterwards. With any exception,
+     * you must call this method again, successfully, before you may
+     * call reload().
+     */
+    void seekBeginning();
+
+    /*
+     * Forwards to bt2::MessageIterator::canSeekForward().
+     */
+    bool canSeekForward() const noexcept;
+
+    /*
+     * Name of the input port on which the libbabeltrace2 message
+     * iterator was created.
+     */
+    const std::string& portName() const noexcept
+    {
+        return _mPortName;
+    }
+
+private:
+    /*
+     * Tries to get new messages into `_mMsgs.msgs`.
+     */
+    void _tryGetNewMsgs();
+
+    /* Actual upstream message iterator */
+    bt2::MessageIterator::Shared _mMsgIter;
+
+    /*
+     * Currently contained messages.
+     *
+     * `index` is the index of the current message (msg()/msgTs())
+     * within `msgs`.
+     */
+    struct
+    {
+        bt2s::optional<bt2::ConstMessageArray> msgs;
+        std::size_t index;
+    } _mMsgs;
+
+    /* Timestamp of the current message, if any */
+    bt2s::optional<std::int64_t> _mMsgTs;
+
+    /*
+     * Only relevant in debug mode: true if a call to discard() is
+     * required before calling reload().
+     */
+    bool _mDiscardRequired = false;
+
+    bt2c::Logger _mLogger;
+    std::string _mPortName;
+};
+
+} /* namespace bt2mux */
+
+#endif /* BABELTRACE_PLUGINS_UTILS_MUXER_UPSTREAM_MSG_ITER_HPP */
diff --git a/src/plugins/utils/plugin.c b/src/plugins/utils/plugin.c
deleted file mode 100644 (file)
index 536224d..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "dummy/dummy.h"
-#include "counter/counter.h"
-#include "muxer/muxer.h"
-#include "trimmer/trimmer.h"
-
-#ifndef BT_BUILT_IN_PLUGINS
-BT_PLUGIN_MODULE();
-#endif
-
-BT_PLUGIN(utils);
-BT_PLUGIN_DESCRIPTION("Common graph utilities");
-BT_PLUGIN_AUTHOR("EfficiOS <https://www.efficios.com/>");
-BT_PLUGIN_LICENSE("MIT");
-
-/* sink.utils.dummy */
-BT_PLUGIN_SINK_COMPONENT_CLASS(dummy, dummy_consume);
-BT_PLUGIN_SINK_COMPONENT_CLASS_INITIALIZE_METHOD(dummy, dummy_init);
-BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(dummy, dummy_finalize);
-BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(dummy,
-       dummy_graph_is_configured);
-BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(dummy,
-       "Consume messages and discard them.");
-BT_PLUGIN_SINK_COMPONENT_CLASS_HELP(dummy,
-       "See the babeltrace2-sink.utils.dummy(7) manual page.");
-
-/* sink.utils.counter */
-BT_PLUGIN_SINK_COMPONENT_CLASS(counter, counter_consume);
-BT_PLUGIN_SINK_COMPONENT_CLASS_INITIALIZE_METHOD(counter, counter_init);
-BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(counter, counter_finalize);
-BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(counter,
-       counter_graph_is_configured);
-BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(counter,
-       "Count messages and print the statistics.");
-BT_PLUGIN_SINK_COMPONENT_CLASS_HELP(counter,
-       "See the babeltrace2-sink.utils.counter(7) manual page.");
-
-/* flt.utils.trimmer */
-BT_PLUGIN_FILTER_COMPONENT_CLASS(trimmer, trimmer_msg_iter_next);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(trimmer,
-       "Discard messages that occur outside a specific time range.");
-BT_PLUGIN_FILTER_COMPONENT_CLASS_HELP(trimmer,
-       "See the babeltrace2-filter.utils.trimmer(7) manual page.");
-BT_PLUGIN_FILTER_COMPONENT_CLASS_INITIALIZE_METHOD(trimmer, trimmer_init);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD(trimmer, trimmer_finalize);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(trimmer,
-       trimmer_msg_iter_init);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(trimmer,
-       trimmer_msg_iter_finalize);
-
-/* flt.utils.muxer */
-BT_PLUGIN_FILTER_COMPONENT_CLASS(muxer, muxer_msg_iter_next);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(muxer,
-       "Sort messages from multiple input ports to a single output port by time.");
-BT_PLUGIN_FILTER_COMPONENT_CLASS_HELP(muxer,
-       "See the babeltrace2-filter.utils.muxer(7) manual page.");
-BT_PLUGIN_FILTER_COMPONENT_CLASS_INITIALIZE_METHOD(muxer, muxer_init);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD(muxer, muxer_finalize);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_INPUT_PORT_CONNECTED_METHOD(muxer,
-       muxer_input_port_connected);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(muxer,
-       muxer_msg_iter_init);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(muxer,
-       muxer_msg_iter_finalize);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHODS(muxer,
-       muxer_msg_iter_seek_beginning, muxer_msg_iter_can_seek_beginning);
diff --git a/src/plugins/utils/plugin.cpp b/src/plugins/utils/plugin.cpp
new file mode 100644 (file)
index 0000000..4878ab8
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2/plugin-dev.hpp"
+
+#include "counter/counter.h"
+#include "dummy/dummy.h"
+#include "muxer/comp.hpp"
+#include "muxer/msg-iter.hpp"
+#include "trimmer/trimmer.h"
+
+#ifndef BT_BUILT_IN_PLUGINS
+BT_PLUGIN_MODULE();
+#endif
+
+BT_PLUGIN(utils);
+BT_PLUGIN_DESCRIPTION("Common graph utilities");
+BT_PLUGIN_AUTHOR("EfficiOS <https://www.efficios.com/>");
+BT_PLUGIN_LICENSE("MIT");
+
+/* sink.utils.dummy */
+BT_PLUGIN_SINK_COMPONENT_CLASS(dummy, dummy_consume);
+BT_PLUGIN_SINK_COMPONENT_CLASS_INITIALIZE_METHOD(dummy, dummy_init);
+BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(dummy, dummy_finalize);
+BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(dummy, dummy_graph_is_configured);
+BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(dummy, "Consume messages and discard them.");
+BT_PLUGIN_SINK_COMPONENT_CLASS_HELP(dummy, "See the babeltrace2-sink.utils.dummy(7) manual page.");
+
+/* sink.utils.counter */
+BT_PLUGIN_SINK_COMPONENT_CLASS(counter, counter_consume);
+BT_PLUGIN_SINK_COMPONENT_CLASS_INITIALIZE_METHOD(counter, counter_init);
+BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(counter, counter_finalize);
+BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(counter, counter_graph_is_configured);
+BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(counter, "Count messages and print the statistics.");
+BT_PLUGIN_SINK_COMPONENT_CLASS_HELP(counter,
+                                    "See the babeltrace2-sink.utils.counter(7) manual page.");
+
+/* flt.utils.trimmer */
+BT_PLUGIN_FILTER_COMPONENT_CLASS(trimmer, trimmer_msg_iter_next);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(
+    trimmer, "Discard messages that occur outside a specific time range.");
+BT_PLUGIN_FILTER_COMPONENT_CLASS_HELP(trimmer,
+                                      "See the babeltrace2-filter.utils.trimmer(7) manual page.");
+BT_PLUGIN_FILTER_COMPONENT_CLASS_INITIALIZE_METHOD(trimmer, trimmer_init);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD(trimmer, trimmer_finalize);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(trimmer,
+                                                                          trimmer_msg_iter_init);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(trimmer,
+                                                                        trimmer_msg_iter_finalize);
+
+/* flt.utils.muxer */
+BT_CPP_PLUGIN_FILTER_COMPONENT_CLASS(muxer, bt2mux::Comp);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(
+    muxer, "Sort messages from multiple input ports to a single output port by time.");
+BT_PLUGIN_FILTER_COMPONENT_CLASS_HELP(muxer,
+                                      "See the babeltrace2-filter.utils.muxer(7) manual page.");
diff --git a/src/plugins/utils/trimmer/Makefile.am b/src/plugins/utils/trimmer/Makefile.am
deleted file mode 100644 (file)
index b1f1b6b..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-plugin-trimmer.la
-libbabeltrace2_plugin_trimmer_la_SOURCES = \
-       trimmer.c \
-       trimmer.h
index a65b2ecc881fcd5928e969a353a35bae9a3f58bb..d13a9fef3d4227f3abc7b741c2f2796e7c096501 100644 (file)
@@ -140,7 +140,6 @@ struct trimmer_comp *create_trimmer_comp(void)
        return g_new0(struct trimmer_comp, 1);
 }
 
-BT_HIDDEN
 void trimmer_finalize(bt_self_component_filter *self_comp)
 {
        struct trimmer_comp *trimmer_comp =
@@ -174,7 +173,7 @@ bool compile_and_match(const char *pattern, const char *string, GMatchInfo **mat
                /*
                 * g_regex_match allocates `*match_info` even if it returns
                 * FALSE.  If there's no match, we have no use for it, so free
-                * it immediatly and don't return it to the caller.
+                * it immediately and don't return it to the caller.
                 */
                g_match_info_free(*match_info);
                *match_info = NULL;
@@ -426,7 +425,7 @@ end:
  */
 static
 int set_bound_from_param(struct trimmer_comp *trimmer_comp,
-               const char *param_name, const bt_value *param,
+               const bt_value *param,
                struct trimmer_bound *bound, bool is_gmt)
 {
        int ret;
@@ -440,7 +439,7 @@ int set_bound_from_param(struct trimmer_comp *trimmer_comp,
                 * Just convert it to a temporary string to handle
                 * everything the same way.
                 */
-               sprintf(tmp_arg, "%" PRId64, value);
+               snprintf(tmp_arg, sizeof(tmp_arg), "%" PRId64, value);
                arg = tmp_arg;
        } else {
                BT_ASSERT(bt_value_is_string(param));
@@ -552,7 +551,7 @@ bt_component_class_initialize_method_status init_trimmer_comp_from_params(
 
         value = bt_value_map_borrow_entry_value_const(params, "begin");
        if (value) {
-               if (set_bound_from_param(trimmer_comp, "begin", value,
+               if (set_bound_from_param(trimmer_comp, value,
                                &trimmer_comp->begin, trimmer_comp->is_gmt)) {
                        /* set_bound_from_param() logs errors */
                        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
@@ -565,7 +564,7 @@ bt_component_class_initialize_method_status init_trimmer_comp_from_params(
 
         value = bt_value_map_borrow_entry_value_const(params, "end");
        if (value) {
-               if (set_bound_from_param(trimmer_comp, "end", value,
+               if (set_bound_from_param(trimmer_comp, value,
                                &trimmer_comp->end, trimmer_comp->is_gmt)) {
                        /* set_bound_from_param() logs errors */
                        status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
@@ -595,8 +594,9 @@ end:
 
 bt_component_class_initialize_method_status trimmer_init(
                bt_self_component_filter *self_comp_flt,
-               bt_self_component_filter_configuration *config,
-               const bt_value *params, void *init_data)
+               bt_self_component_filter_configuration *config __attribute__((unused)),
+               const bt_value *params,
+               void *init_data __attribute__((unused)))
 {
        bt_component_class_initialize_method_status status;
        bt_self_component_add_port_status add_port_status;
@@ -679,11 +679,10 @@ void destroy_trimmer_iterator_stream_state(
        g_free(sstate);
 }
 
-BT_HIDDEN
 bt_message_iterator_class_initialize_method_status trimmer_msg_iter_init(
                bt_self_message_iterator *self_msg_iter,
                bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *port)
+               bt_self_component_port_output *port __attribute__((unused)))
 {
        bt_message_iterator_class_initialize_method_status status;
        bt_message_iterator_create_from_message_iterator_status
@@ -1875,7 +1874,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 bt_message_iterator_class_next_method_status trimmer_msg_iter_next(
                bt_self_message_iterator *self_msg_iter,
                bt_message_array_const msgs, uint64_t capacity,
@@ -1944,7 +1942,6 @@ end:
        return status;
 }
 
-BT_HIDDEN
 void trimmer_msg_iter_finalize(bt_self_message_iterator *self_msg_iter)
 {
        struct trimmer_iterator *trimmer_it =
index 3ed0d3ff0623fde9b6989e44a3a08d2e888bee38..0cb997a430e047d06facee3102dd4a927c7b414d 100644 (file)
 #include "common/macros.h"
 #include <babeltrace2/babeltrace.h>
 
-BT_HIDDEN
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 void trimmer_finalize(bt_self_component_filter *self_comp);
 
-BT_HIDDEN
 bt_component_class_initialize_method_status trimmer_init(
                bt_self_component_filter *self_comp,
                bt_self_component_filter_configuration *config,
                const bt_value *params, void *init_data);
 
-BT_HIDDEN
 bt_message_iterator_class_initialize_method_status trimmer_msg_iter_init(
                bt_self_message_iterator *self_msg_iter,
                bt_self_message_iterator_configuration *config,
                bt_self_component_port_output *port);
 
-BT_HIDDEN
 bt_message_iterator_class_next_method_status trimmer_msg_iter_next(
                bt_self_message_iterator *self_msg_iter,
                bt_message_array_const msgs, uint64_t capacity,
                uint64_t *count);
 
-BT_HIDDEN
 void trimmer_msg_iter_finalize(bt_self_message_iterator *self_msg_iter);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* BABELTRACE_PLUGINS_UTILS_TRIMMER_H */
diff --git a/src/py-common/Makefile.am b/src/py-common/Makefile.am
deleted file mode 100644 (file)
index 219ec20..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-if ENABLE_PYTHON_COMMON_DEPS
-AM_CPPFLAGS += $(PYTHON_INCLUDE)
-
-noinst_LTLIBRARIES = libbabeltrace2-py-common.la
-
-libbabeltrace2_py_common_la_SOURCES = \
-       py-common.c \
-       py-common.h
-endif # ENABLE_PYTHON_COMMON_DEPS
index feb4cffab1a4e0b8451d66763da10e608cca4946..b2177f5cf0eb2a114ddecbefe9dd51fe2ff98c68 100644 (file)
@@ -73,7 +73,6 @@ end:
        return gstr;
 }
 
-BT_HIDDEN
 GString *bt_py_common_format_tb(PyObject *py_exc_tb, int log_level)
 {
        PyObject *traceback_module = NULL;
@@ -133,7 +132,6 @@ error:
        return msg_buf;
 }
 
-BT_HIDDEN
 GString *bt_py_common_format_exception(PyObject *py_exc_type,
                PyObject *py_exc_value, PyObject *py_exc_tb,
                int log_level, bool chain)
@@ -206,7 +204,6 @@ error:
        return msg_buf;
 }
 
-BT_HIDDEN
 GString *bt_py_common_format_current_exception(int log_level)
 {
        GString *result;
index ded340b19224c7b343a4f55c9c9f902d2ede79b4..7d4e3f14d4687d079553baa10d5f46a59cb8b9d3 100644 (file)
@@ -19,7 +19,6 @@
  * Formats the Python traceback `py_exc_tb` using traceback.format_tb, from the
  * Python standard library, and return it as a Gstring.
  */
-BT_HIDDEN
 GString *bt_py_common_format_tb(PyObject *py_exc_tb, int log_level);
 
 /*
@@ -30,7 +29,6 @@ GString *bt_py_common_format_tb(PyObject *py_exc_tb, int log_level);
  * If `chain` is true, include all exceptions in the causality chain
  * (see parameter `chain` of Python's traceback.format_exception).
  */
-BT_HIDDEN
 GString *bt_py_common_format_exception(PyObject *py_exc_type,
                PyObject *py_exc_value, PyObject *py_exc_tb,
                int log_level, bool chain);
@@ -46,7 +44,6 @@ GString *bt_py_common_format_exception(PyObject *py_exc_type,
  * This function does not modify the error indicator, that is, anything
  * that is fetched is always restored.
  */
-BT_HIDDEN
 GString *bt_py_common_format_current_exception(int log_level);
 
 #endif /* BABELTRACE_PY_COMMON_INTERNAL_H */
diff --git a/src/python-plugin-provider/Makefile.am b/src/python-plugin-provider/Makefile.am
deleted file mode 100644 (file)
index 3d3a11f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-if ENABLE_PYTHON_PLUGINS
-AM_CPPFLAGS += $(PYTHON_INCLUDE)
-
-pluginproviderdir = "$(BABELTRACE_PLUGIN_PROVIDERS_DIR)"
-pluginprovider_LTLIBRARIES = babeltrace2-python-plugin-provider.la
-
-babeltrace2_python_plugin_provider_la_SOURCES = \
-       python-plugin-provider.c \
-       python-plugin-provider.h
-
-babeltrace2_python_plugin_provider_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LT_NO_UNDEFINED) \
-       -avoid-version -module \
-       $(PYTHON_LDFLAGS)
-
-babeltrace2_python_plugin_provider_la_LIBADD =
-
-# Link the Python plugin provider library with libbabeltrace2
-# when it's not built-in the babeltrace2 executable.
-if !ENABLE_BUILT_IN_PLUGINS
-babeltrace2_python_plugin_provider_la_LIBADD += \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/py-common/libbabeltrace2-py-common.la \
-       $(top_builddir)/src/lib/libbabeltrace2.la
-endif
-endif # ENABLE_PYTHON_PLUGINS
index e6e31099713393acfb35e864529ad581bd0a7193..4df648527d93afc824e64cd8e675969f3a7b2a88 100644 (file)
@@ -24,7 +24,6 @@
 #include <signal.h>
 #include <Python.h>
 #include <glib.h>
-#include <gmodule.h>
 
 #define PYTHON_PLUGIN_FILE_PREFIX      "bt_plugin_"
 #define PYTHON_PLUGIN_FILE_PREFIX_LEN  (sizeof(PYTHON_PLUGIN_FILE_PREFIX) - 1)
@@ -85,7 +84,7 @@ void log_python_traceback(int log_level)
                        goto end;
                }
 
-               BT_LOG_WRITE(log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF(log_level, BT_LOG_TAG,
                        "Exception occurred: Python traceback:\n%s", exc->str);
        }
 
@@ -596,7 +595,7 @@ end:
        return status;
 }
 
-G_MODULE_EXPORT
+BT_EXPORT
 int bt_plugin_python_create_all_from_file(const char *path,
                bool fail_on_load_error, struct bt_plugin_set **plugin_set_out)
 {
@@ -664,7 +663,7 @@ int bt_plugin_python_create_all_from_file(const char *path,
        /*
         * Initialize Python now.
         *
-        * This is not done in the library contructor because the
+        * This is not done in the library constructor because the
         * interpreter is somewhat slow to initialize. If you don't
         * have any potential Python plugins, you don't need to endure
         * this waiting time everytime you load the library.
diff --git a/src/string-format/Makefile.am b/src/string-format/Makefile.am
deleted file mode 100644 (file)
index 4049ba9..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = libbabeltrace2-string-format.la
-
-libbabeltrace2_string_format_la_SOURCES = \
-       format-plugin-comp-cls-name.c \
-       format-plugin-comp-cls-name.h \
-       format-error.c \
-       format-error.h
index f6e74390737e5b16c809ac46832e7c7d0aeb802b..ff24afd5c42afefee4dd956f53f7366bb8d62cef 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright EfficiOS, Inc.
  */
 
-#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_OUTPUT_LEVEL ((enum bt_log_level) log_level)
 #define BT_LOG_TAG "COMMON/FORMAT-ERROR"
 #include <logging/log.h>
 
index 40b466576ba0328d4c0e5a5f764872d6bc64fbb7..25da4648d34490bcbada7403c70753c41d2eb05f 100644 (file)
 #include <common/macros.h>
 #include <glib.h>
 
-BT_HIDDEN
 gchar *format_bt_error_cause(
                const bt_error_cause *error_cause,
                unsigned int columns,
                bt_logging_level log_level,
                enum bt_common_color_when use_colors);
 
-BT_HIDDEN
 gchar *format_bt_error(
                const bt_error *error,
                unsigned int columns,
index f3fc60d792416516cde8b83845fc627f17334bc8..25050042615f54b9cc355f05e2a2cb6052cb1386 100644 (file)
@@ -12,7 +12,6 @@
 #include <common/macros.h>
 #include <glib.h>
 
-BT_HIDDEN
 gchar *format_plugin_comp_cls_opt(const char *plugin_name,
                const char *comp_cls_name, bt_component_class_type type,
                enum bt_common_color_when use_colors);
index 1b44a9ccbb9711fa0d624f05ddde4bd30ca3a23d..6136c771b6d201765150ca83d45b1d302983f888 100644 (file)
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: MIT
 
+include $(top_srcdir)/src/Makefile.common.inc
+
 SUBDIRS = \
        utils \
        lib \
@@ -8,11 +10,19 @@ SUBDIRS = \
        plugins \
        param-validation
 
+AM_CPPFLAGS += -I$(top_srcdir)/tests/utils
+
+COMMON_TEST_LDADD = \
+       $(top_builddir)/tests/utils/tap/libtap.la \
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/logging/liblogging.la
+
 # Directories added to EXTRA_DIST will be recursively copied to the distribution.
 EXTRA_DIST = $(srcdir)/data \
             bindings/python/bt2/.coveragerc
 
 dist_check_SCRIPTS = \
+       bindings/python/bt2/test-python-bt2.sh \
        bindings/python/bt2/test_clock_class.py \
        bindings/python/bt2/test_component_class.py \
        bindings/python/bt2/test_component.py \
@@ -30,7 +40,6 @@ dist_check_SCRIPTS = \
        bindings/python/bt2/test_packet.py \
        bindings/python/bt2/test_plugin.py \
        bindings/python/bt2/test_port.py \
-       bindings/python/bt2/test_python_bt2 \
        bindings/python/bt2/test_query_executor.py \
        bindings/python/bt2/test_stream_class.py \
        bindings/python/bt2/test_stream.py \
@@ -38,132 +47,181 @@ dist_check_SCRIPTS = \
        bindings/python/bt2/test_trace.py \
        bindings/python/bt2/test_value.py \
        bindings/python/bt2/utils.py \
-       cli/convert/test_auto_source_discovery_grouping \
-       cli/convert/test_auto_source_discovery_params \
-       cli/convert/test_auto_source_discovery_log_level \
-       cli/convert/test_convert_args \
-       cli/list-plugins/test_list_plugins \
-       cli/params/test_params \
-       cli/query/test_query \
-       cli/test_exit_status \
-       cli/test_help \
-       cli/test_intersection \
-       cli/test_output_ctf_metadata \
-       cli/test_output_path_ctf_non_lttng_trace \
-       cli/test_packet_seq_num \
-       cli/test_trace_copy \
-       cli/test_trace_read \
-       cli/test_trimmer \
-       plugins/sink.text.details/succeed/test_succeed \
-       plugins/sink.text.pretty/test_enum \
+       cli/convert/test-auto-source-discovery-grouping.sh \
+       cli/convert/test-auto-source-discovery-params.sh \
+       cli/convert/test-auto-source-discovery-log-level.sh \
+       cli/convert/test-convert-args.sh \
+       cli/list-plugins/test-list-plugins.sh \
+       cli/params/test-params.sh \
+       cli/query/test-query.sh \
+       cli/test-exit-status.sh \
+       cli/test-help.sh \
+       cli/test-intersection.sh \
+       cli/test-output-ctf-metadata.sh \
+       cli/test-output-path-ctf-non-lttng-trace.sh \
+       cli/test-packet-seq-num.sh \
+       cli/test-trace-copy.sh \
+       cli/test-trace-read.sh \
+       cli/test-trimmer.sh \
+       plugins/sink.text.details/succeed/test-succeed.sh \
+       plugins/sink.text.pretty/test-enum.sh \
        plugins/sink.text.pretty/test_pretty.py \
-       plugins/sink.text.pretty/test_pretty_python \
-       plugins/src.ctf.lttng-live/test_live \
+       plugins/sink.text.pretty/test-pretty-python.sh \
+       plugins/src.ctf.lttng-live/test-live.sh \
        python-plugin-provider/bt_plugin_test_python_plugin_provider.py \
-       python-plugin-provider/test_python_plugin_provider \
+       python-plugin-provider/test-python-plugin-provider.sh \
        python-plugin-provider/test_python_plugin_provider.py
 
+noinst_PROGRAMS =
+
 TESTS_BINDINGS =
 
 if ENABLE_PYTHON_BINDINGS
-TESTS_BINDINGS += bindings/python/bt2/test_python_bt2
+TESTS_BINDINGS += bindings/python/bt2/test-python-bt2.sh
 endif
 
 TESTS_CLI = \
-       cli/convert/test_convert_args \
-       cli/test_help \
-       cli/test_intersection \
-       cli/test_output_path_ctf_non_lttng_trace \
-       cli/test_packet_seq_num \
-       cli/test_trace_copy \
-       cli/test_trace_read \
-       cli/test_trimmer
+       cli/convert/test-convert-args.sh \
+       cli/test-help.sh \
+       cli/test-intersection.sh \
+       cli/test-output-ctf-metadata.sh \
+       cli/test-output-path-ctf-non-lttng-trace.sh \
+       cli/test-packet-seq-num.sh \
+       cli/test-trace-copy.sh \
+       cli/test-trace-read.sh \
+       cli/test-trimmer.sh
+
+noinst_PROGRAMS += \
+       cpp-common/test-c-string-view
+
+cpp_common_test_c_string_view_SOURCES = \
+       cpp-common/test-c-string-view.cpp
+
+cpp_common_test_c_string_view_LDADD = \
+       $(COMMON_TEST_LDADD)
+
+noinst_PROGRAMS += \
+       cpp-common/test-uuid
+
+cpp_common_test_uuid_SOURCES = \
+       cpp-common/test-uuid.cpp
+
+cpp_common_test_uuid_LDADD = \
+       $(COMMON_TEST_LDADD) \
+       $(top_builddir)/src/cpp-common/vendor/fmt/libfmt.la
+
+TESTS_CPP_COMMON = \
+       cpp-common/test-c-string-view \
+       cpp-common/test-uuid
 
 TESTS_LIB = \
-       lib/test_bt_uuid \
-       lib/test_bt_values \
-       lib/test_graph_topo \
-       lib/test_remove_destruction_listener_in_destruction_listener \
-       lib/test_simple_sink \
-       lib/test_trace_ir_ref
+       lib/test-bt-uuid \
+       lib/test-bt-values \
+       lib/test-fields.sh \
+       lib/test-graph-topo \
+       lib/test-remove-destruction-listener-in-destruction-listener \
+       lib/test-simple-sink \
+       lib/test-trace-ir-ref
 
 TESTS_BITFIELD = \
-       bitfield/test_bitfield
+       bitfield/test-bitfield
 
 TESTS_CTF_WRITER = \
-       ctf-writer/test_ctf_writer
+       ctf-writer/test-ctf-writer.sh
 
 if !ENABLE_BUILT_IN_PLUGINS
-TESTS_LIB += lib/test_plugin
+TESTS_LIB += lib/test-plugins.sh
 endif
 
+# plugins/flt.utils.muxer
+
+noinst_PROGRAMS += plugins/flt.utils.muxer/test-clock-compatibility
+
+plugins_flt_utils_muxer_test_clock_compatibility_SOURCES = \
+       plugins/flt.utils.muxer/test-clock-compatibility.cpp
+
+plugins_flt_utils_muxer_test_clock_compatibility_LDADD = \
+       $(COMMON_TEST_LDADD) \
+       $(top_builddir)/src/lib/libbabeltrace2.la \
+       $(top_builddir)/src/cpp-common/vendor/fmt/libfmt.la
+
+dist_check_SCRIPTS += plugins/flt.utils.muxer/test-clock-compatibility.sh
+
+if ENABLE_BUILT_IN_PLUGINS
+plugins_flt_utils_muxer_test_clock_compatibility_LDFLAGS = $(call pluginarchive,utils)
+plugins_flt_utils_muxer_test_clock_compatibility_LDADD += \
+       $(top_builddir)/src/plugins/common/param-validation/libparam-validation.la
+endif # ENABLE_BUILT_IN_PLUGINS
+
 TESTS_PLUGINS = \
-       plugins/src.ctf.fs/fail/test_fail \
-       plugins/src.ctf.fs/succeed/test_succeed \
-       plugins/src.ctf.fs/test_deterministic_ordering \
-       plugins/sink.ctf.fs/succeed/test_succeed \
-       plugins/sink.text.details/succeed/test_succeed
+       plugins/src.ctf.fs/fail/test-fail.sh \
+       plugins/src.ctf.fs/succeed/test-succeed.sh \
+       plugins/src.ctf.fs/test-deterministic-ordering.sh \
+       plugins/sink.ctf.fs/succeed/test-succeed.sh \
+       plugins/sink.text.details/succeed/test-succeed.sh \
+       plugins/flt.utils.muxer/test-clock-compatibility.sh
 
 if !ENABLE_BUILT_IN_PLUGINS
 if ENABLE_PYTHON_BINDINGS
-TESTS_PLUGINS += plugins/src.ctf.fs/query/test_query_support_info
-TESTS_PLUGINS += plugins/src.ctf.fs/query/test_query_trace_info
-TESTS_PLUGINS += plugins/src.ctf.fs/query/test_query_metadata_info
-TESTS_PLUGINS += plugins/sink.ctf.fs/test_assume_single_trace
-TESTS_PLUGINS += plugins/sink.ctf.fs/test_stream_names
+TESTS_PLUGINS += plugins/src.ctf.fs/query/test-query-support-info.sh
+TESTS_PLUGINS += plugins/src.ctf.fs/query/test-query-trace-info.sh
+TESTS_PLUGINS += plugins/src.ctf.fs/query/test-query-metadata-info.sh
+TESTS_PLUGINS += plugins/sink.ctf.fs/test-assume-single-trace.sh
+TESTS_PLUGINS += plugins/sink.ctf.fs/test-stream-names.sh
 endif
 endif
 
 if ENABLE_DEBUG_INFO
 TESTS_PLUGINS += \
-       plugins/flt.lttng-utils.debug-info/test_dwarf_i386-linux-gnu \
-       plugins/flt.lttng-utils.debug-info/test_dwarf_powerpc-linux-gnu \
-       plugins/flt.lttng-utils.debug-info/test_dwarf_powerpc64le-linux-gnu \
-       plugins/flt.lttng-utils.debug-info/test_dwarf_x86_64-linux-gnu \
-       plugins/flt.lttng-utils.debug-info/test_bin_info_i386-linux-gnu \
-       plugins/flt.lttng-utils.debug-info/test_bin_info_powerpc-linux-gnu \
-       plugins/flt.lttng-utils.debug-info/test_bin_info_powerpc64le-linux-gnu \
-       plugins/flt.lttng-utils.debug-info/test_bin_info_x86_64-linux-gnu
+       plugins/flt.lttng-utils.debug-info/test-dwarf-i386-linux-gnu.sh \
+       plugins/flt.lttng-utils.debug-info/test-dwarf-powerpc-linux-gnu.sh \
+       plugins/flt.lttng-utils.debug-info/test-dwarf-powerpc64le-linux-gnu.sh \
+       plugins/flt.lttng-utils.debug-info/test-dwarf-x86-64-linux-gnu.sh \
+       plugins/flt.lttng-utils.debug-info/test-bin-info-i386-linux-gnu.sh \
+       plugins/flt.lttng-utils.debug-info/test-bin-info-powerpc-linux-gnu.sh \
+       plugins/flt.lttng-utils.debug-info/test-bin-info-powerpc64le-linux-gnu.sh \
+       plugins/flt.lttng-utils.debug-info/test-bin-info-x86-64-linux-gnu.sh
 endif
 
 if ENABLE_PYTHON_PLUGINS
 if ENABLE_PYTHON_BINDINGS
 TESTS_CLI += \
-       cli/convert/test_auto_source_discovery_grouping \
-       cli/convert/test_auto_source_discovery_log_level \
-       cli/convert/test_auto_source_discovery_params \
-       cli/list-plugins/test_list_plugins \
-       cli/params/test_params \
-       cli/query/test_query \
-       cli/test_exit_status
-
-TESTS_PLUGINS += plugins/flt.utils.trimmer/test_trimming \
-       plugins/flt.utils.muxer/succeed/test_succeed \
-       plugins/sink.text.pretty/test_enum
+       cli/convert/test-auto-source-discovery-grouping.sh \
+       cli/convert/test-auto-source-discovery-log-level.sh \
+       cli/convert/test-auto-source-discovery-params.sh \
+       cli/list-plugins/test-list-plugins.sh \
+       cli/params/test-params.sh \
+       cli/query/test-query.sh \
+       cli/test-exit-status.sh
+
+TESTS_PLUGINS += plugins/flt.utils.trimmer/test-trimming.sh \
+       plugins/flt.utils.muxer/succeed/test-succeed.sh \
+       plugins/sink.text.pretty/test-enum.sh \
+       plugins/src.ctf.fs/field/test-field.sh
 endif
 endif
 
 if HAVE_PYTHON
-TESTS_PLUGINS += plugins/src.ctf.lttng-live/test_live
+TESTS_PLUGINS += plugins/src.ctf.lttng-live/test-live.sh
 
 if DEV_MODE
-TESTS_LIB += lib/conds/test_conds
+TESTS_LIB += lib/conds/test-conds.sh
 endif
 endif
 
 TESTS_PYTHON_PLUGIN_PROVIDER =
 
 if ENABLE_PYTHON_PLUGINS
-TESTS_PYTHON_PLUGIN_PROVIDER += python-plugin-provider/test_python_plugin_provider
-TESTS_PLUGINS += plugins/sink.text.pretty/test_pretty_python
+TESTS_PYTHON_PLUGIN_PROVIDER += python-plugin-provider/test-python-plugin-provider.sh
+TESTS_PLUGINS += plugins/sink.text.pretty/test-pretty-python.sh
 if ENABLE_DEBUG_INFO
 TESTS_PLUGINS += \
-       plugins/flt.lttng-utils.debug-info/test_succeed
+       plugins/flt.lttng-utils.debug-info/test-succeed.sh
 endif
 endif
 
 TESTS_PARAM_VALIDATION = \
-       param-validation/test_param_validation
+       param-validation/test-param-validation
 
 LOG_DRIVER_FLAGS = --merge --comments
 LOG_DRIVER = env AM_TAP_AWK='$(AWK)' \
@@ -174,6 +232,7 @@ LOG_DRIVER = env AM_TAP_AWK='$(AWK)' \
 TESTS_NO_BITFIELD = \
        $(TESTS_BINDINGS) \
        $(TESTS_CLI) \
+       $(TESTS_CPP_COMMON) \
        $(TESTS_CTF_WRITER) \
        $(TESTS_LIB) \
        $(TESTS_PARAM_VALIDATION) \
@@ -190,6 +249,7 @@ endef
 $(eval $(call check_target,bindings,$(TESTS_BINDINGS)))
 $(eval $(call check_target,bitfield,$(TESTS_BITFIELD)))
 $(eval $(call check_target,cli,$(TESTS_CLI)))
+$(eval $(call check_target,cpp-common,$(TESTS_CPP_COMMON)))
 $(eval $(call check_target,ctf-writer,$(TESTS_CTF_WRITER)))
 $(eval $(call check_target,lib,$(TESTS_LIB)))
 $(eval $(call check_target,plugins,$(TESTS_PLUGINS)))
diff --git a/tests/bindings/python/bt2/test-python-bt2.sh b/tests/bindings/python/bt2/test-python-bt2.sh
new file mode 100755 (executable)
index 0000000..e05cd8e
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+bt_run_py_test "${BT_TESTS_SRCDIR}/bindings/python/bt2" "test_*"
index 9707052f6392d3560c03be68faa46cdbf521cac2..843333c8d6df91e0ccf0047d00dff3406ffaf04c 100644 (file)
@@ -3,13 +3,14 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import unittest
 import uuid
+import unittest
+
 import bt2
 import utils
-from utils import run_in_component_init, TestOutputPortMessageIterator
 from bt2 import value as bt2_value
 from bt2 import clock_class as bt2_clock_class
+from utils import TestOutputPortMessageIterator, run_in_component_init
 
 
 class ClockClassOffsetTestCase(unittest.TestCase):
index 71808fa52dbb77f901df94e2fcf40701f7638f23..b99d632a2d94af0fa20fa8aa78e4bb517436cb90 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 import unittest
+
 import bt2
 from bt2 import component as bt2_component
 
index d944c909bec7b9c8dd92adc982926e699d9204a5..a405c005d5c858bac09ce354da0029bc2e507f6c 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 import unittest
+
 import bt2
 
 
index 818b245eb716e62a08386483378fb2d986e7ae22..dcad514eb902a75a44286fbf6226ac3a8f75fc35 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 import unittest
+
 import bt2
 
 
index 7ec6bb01188980c04bc22a0cfa1673b3bd67603a..4664ffce6ee19da413e22f48df5d8c111d59cd75 100644 (file)
@@ -4,9 +4,10 @@
 #
 
 import unittest
+
 import bt2
-from bt2 import connection as bt2_connection
 from bt2 import port as bt2_port
+from bt2 import connection as bt2_connection
 
 
 class ConnectionTestCase(unittest.TestCase):
index b4b90ad5e9d532664f04884751e97cc71a8b4fcd..95d20ebc96b574416b062894db2dad8e0a97f9b9 100644 (file)
@@ -3,10 +3,11 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-from bt2 import native_bt
-import bt2
 import unittest
 
+import bt2
+from bt2 import native_bt
+
 
 class FailingIter(bt2._UserMessageIterator):
     def __next__(self):
index 970b7c2210470f879a2780ed1b64d77c239290e3..72490603475478aae10e2736ce85fd3d6835cf8f 100644 (file)
@@ -4,14 +4,14 @@
 #
 
 import unittest
+
 import bt2
 import utils
-from utils import TestOutputPortMessageIterator
-
 from bt2 import field as bt2_field
 from bt2 import stream as bt2_stream
 from bt2 import event_class as bt2_event_class
 from bt2 import clock_snapshot as bt2_clock_snapshot
+from utils import TestOutputPortMessageIterator
 
 
 class EventTestCase(unittest.TestCase):
index ae6f098137c6c95ea4b4924459eef0546c1bc5a1..295d8fb8217bd578c5313c738a48e9a15e7853d5 100644 (file)
@@ -4,13 +4,13 @@
 #
 
 import unittest
+
 import bt2
-from utils import get_default_trace_class
-from bt2 import stream_class as bt2_stream_class
+from bt2 import value as bt2_value
 from bt2 import event_class as bt2_event_class
 from bt2 import field_class as bt2_field_class
-from bt2 import value as bt2_value
-from utils import TestOutputPortMessageIterator
+from bt2 import stream_class as bt2_stream_class
+from utils import TestOutputPortMessageIterator, get_default_trace_class
 
 
 def _create_const_event_class(tc, stream_class):
@@ -22,7 +22,6 @@ def _create_const_event_class(tc, stream_class):
 
     class MyIter(bt2._UserMessageIterator):
         def __init__(self, config, self_port_output):
-
             trace = tc()
             stream = trace.create_stream(stream_class)
             self._msgs = [
index e7517941335c84b3506e9ea3ee23766a37d4c226..e72c0ef09ee7c63554321e2a63893eb0c89b5d61 100644 (file)
@@ -3,16 +3,16 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-from functools import partial, partialmethod
+import copy
+import math
 import operator
 import unittest
-import math
-import copy
 import itertools
 import collections
-import bt2
-from utils import get_default_trace_class, create_const_field
+from functools import partial, partialmethod
 
+import bt2
+from utils import create_const_field, get_default_trace_class
 
 _COMP_BINOPS = (operator.eq, operator.ne)
 
index d12e3987850f4d01d6e433189b1946fce04d601b..cb011a6e8c7ee511dfb987362f4128b795d971c8 100644 (file)
@@ -4,10 +4,11 @@
 #
 
 import unittest
+
 import bt2
-from utils import get_default_trace_class, TestOutputPortMessageIterator
 from bt2 import value as bt2_value
 from bt2 import field_class as bt2_field_class
+from utils import TestOutputPortMessageIterator, get_default_trace_class
 
 
 def _create_stream(tc, ctx_field_classes):
index acf283ceab5e41fae4c0392cdb6011825cc933e5..579572ed4d8cdc1ec51bcf049a9314fb58387ff2 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 import unittest
+
 import bt2
 
 
index 808a4045c29c784146fa8061116a2715cb7fc4d7..d15dbaf27c62693dae2a559f58b82f69ad406037 100644 (file)
@@ -3,10 +3,10 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import bt2
 import unittest
 
-from utils import get_default_trace_class, create_const_field
+import bt2
+from utils import create_const_field, get_default_trace_class
 
 
 def get_const_signed_integer_range(int_ranges):
index 636c03e477554b019a5f70ded696a04dc28bd7dc..c7f44350d83ebee8b0c2f9eae2991192269ebc92 100644 (file)
@@ -3,9 +3,10 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import bt2
 import unittest
 
+import bt2
+
 
 class InterrupterTestCase(unittest.TestCase):
     def setUp(self):
index 11d12a024b09526349698648d7d6db676e4fe42b..fbd5b5b96c004b1cddf98c3b1b9a140d626718c0 100644 (file)
@@ -4,18 +4,19 @@
 #
 
 import unittest
+
 import bt2
 import utils
-from utils import TestOutputPortMessageIterator
-from bt2 import clock_snapshot as bt2_clock_snapshot
 from bt2 import event as bt2_event
-from bt2 import event_class as bt2_event_class
 from bt2 import field as bt2_field
+from bt2 import trace as bt2_trace
 from bt2 import packet as bt2_packet
 from bt2 import stream as bt2_stream
-from bt2 import stream_class as bt2_stream_class
-from bt2 import trace as bt2_trace
+from bt2 import event_class as bt2_event_class
 from bt2 import trace_class as bt2_trace_class
+from bt2 import stream_class as bt2_stream_class
+from bt2 import clock_snapshot as bt2_clock_snapshot
+from utils import TestOutputPortMessageIterator
 
 
 class AllMessagesTestCase(unittest.TestCase):
index 71f636394f799fd0647f9a5eba48037bbd74bf7f..62600ac5d5b0b9b993d41b30d80aad220aeb4647 100644 (file)
@@ -3,12 +3,13 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
+import sys
 import unittest
+
 import bt2
-import sys
-from utils import TestOutputPortMessageIterator
 from bt2 import port as bt2_port
 from bt2 import message_iterator as bt2_message_iterator
+from utils import TestOutputPortMessageIterator
 
 
 class SimpleSink(bt2._UserSinkComponent):
@@ -101,7 +102,7 @@ class UserMessageIteratorTestCase(unittest.TestCase):
         self.assertTrue(flt_iter_initialized)
 
     # Test that creating a message iterator from a sink component on a
-    # non-connected inport port raises.
+    # non-connected input port raises.
     def test_create_from_sink_component_unconnected_port_raises(self):
         class MySink(bt2._UserSinkComponent):
             def __init__(comp_self, config, params, obj):
@@ -123,8 +124,8 @@ class UserMessageIteratorTestCase(unittest.TestCase):
         graph.run()
         self.assertTrue(seen)
 
-    # Test that creating a message iterator from a message iteartor on a
-    # non-connected inport port raises.
+    # Test that creating a message iterator from a message iterator on a
+    # non-connected input port raises.
     def test_create_from_message_iterator_unconnected_port_raises(self):
         class MyFilterIter(bt2._UserMessageIterator):
             def __init__(iter_self, config, port):
@@ -379,6 +380,14 @@ class UserMessageIteratorTestCase(unittest.TestCase):
     # This verifies that we are not missing an incref of Py_None, making the
     # refcount of Py_None reach 0.
     def test_try_again_many_times(self):
+        # Starting with Python 3.12, `None` is immortal: its reference
+        # count operations are no-op. Skip this test in that case.
+        before = sys.getrefcount(None)
+        dummy = None  # noqa: F841
+
+        if before == sys.getrefcount(None):
+            raise unittest.SkipTest("`None` is immortal")
+
         class MyIter(bt2._UserMessageIterator):
             def __next__(self):
                 raise bt2.TryAgain
@@ -387,25 +396,6 @@ class UserMessageIteratorTestCase(unittest.TestCase):
             def __init__(self, config, params, obj):
                 self._add_output_port("out")
 
-        class MyFilterIter(bt2._UserMessageIterator):
-            def __init__(self, port):
-                input_port = port.user_data
-                self._upstream_iter = self._create_message_iterator(input_port)
-
-            def __next__(self):
-                return next(self._upstream_iter)
-
-            def _user_seek_beginning(self):
-                self._upstream_iter.seek_beginning()
-
-            def _user_can_seek_beginning(self):
-                return self._upstream_iter.can_seek_beginning()
-
-        class MyFilter(bt2._UserFilterComponent, message_iterator_class=MyFilterIter):
-            def __init__(self, config, params, obj):
-                input_port = self._add_input_port("in")
-                self._add_output_port("out", input_port)
-
         graph = bt2.Graph()
         src = graph.add_component(MySource, "src")
         it = TestOutputPortMessageIterator(graph, src.output_ports["out"])
index a73054fad236c3e5fc65d99dc6af10facb5f9a0d..873da22ababfc3672ab12c0dbe443683cfc88234 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 import unittest
+
 import bt2
 
 
index f13f8eb0d4dfa863d2d096608c325b4004e582f1..6fd8425aec5cbfafb9bc2317c779320c46bc8565 100644 (file)
@@ -3,9 +3,10 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import bt2
 import unittest
 
+import bt2
+
 
 class PackageTestCase(unittest.TestCase):
     def _assert_in_bt2(self, name):
index 5448c03c8fa5df893a5670d7924d92009790b767..77effadaddd0388232a64f6ffc93b458e51ffa66 100644 (file)
@@ -4,10 +4,11 @@
 #
 
 import unittest
+
 import utils
-from utils import run_in_component_init
-from bt2 import stream as bt2_stream
 from bt2 import field as bt2_field
+from bt2 import stream as bt2_stream
+from utils import run_in_component_init
 
 
 class PacketTestCase(unittest.TestCase):
index 7c766c6994f14fc8f29f96db26eee3077a0fe2b0..d6f4c4a02e58ad4590dac1c9f5765d90933e52ef 100644 (file)
@@ -3,10 +3,10 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import unittest
-import bt2
 import os
+import unittest
 
+import bt2
 
 _TEST_PLUGIN_PLUGINS_PATH = os.environ["BT_PLUGINS_PATH"]
 _TEST_PLUGIN_PLUGIN_EXTENSION_BY_OS = {"cygwin": "dll", "mingw": "dll"}
index 17945cb0a5119a3cd01bec6a96cc86a978ad4b57..3a4461455ccc34b3d8b5dae3802dccfcc2f87266 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 import unittest
+
 import bt2
 from bt2 import port as bt2_port
 
diff --git a/tests/bindings/python/bt2/test_python_bt2 b/tests/bindings/python/bt2/test_python_bt2
deleted file mode 100755 (executable)
index a32362f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
-fi
-
-# shellcheck source=../../../utils/utils.sh
-source "$UTILSSH"
-
-run_python_bt2_test "${BT_TESTS_SRCDIR}/bindings/python/bt2" "test_*"
index 0b7296a485c8a9bb882f0a46c35afe9ba84a220b..466ebb5f29bd6003b73d0559bda1a2ccf3348254 100644 (file)
@@ -3,9 +3,10 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
+import re
 import unittest
+
 import bt2
-import re
 
 
 class QueryExecutorTestCase(unittest.TestCase):
index 25dc5600a7030b851faf15acdc068d7386a287e0..bae158b16565e4dfc335948fa6a1b295a62d69fc 100644 (file)
@@ -4,12 +4,13 @@
 #
 
 import unittest
+
 import utils
-from utils import run_in_component_init
 from bt2 import trace as bt2_trace
-from bt2 import stream as bt2_stream
 from bt2 import value as bt2_value
+from bt2 import stream as bt2_stream
 from bt2 import stream_class as bt2_stream_class
+from utils import run_in_component_init
 
 
 class StreamTestCase(unittest.TestCase):
index 09e6e5e0d12ea09acbefd9f7a3cceb1616d98cd0..3668fe865bb8df2c33ec8ee76d08e7b0afc49ebc 100644 (file)
@@ -4,12 +4,13 @@
 #
 
 import unittest
-from utils import run_in_component_init
-from bt2 import stream_class as bt2_stream_class
-from bt2 import trace_class as bt2_trace_class
+
 from bt2 import clock_class as bt2_clock_class
 from bt2 import event_class as bt2_event_class
 from bt2 import field_class as bt2_field_class
+from bt2 import trace_class as bt2_trace_class
+from bt2 import stream_class as bt2_stream_class
+from utils import run_in_component_init
 
 
 class StreamClassTestCase(unittest.TestCase):
index ad0ef39e8a09c9c6b86d9661572d7eddd1f86422..0ce6e1358a89c886043e9eb7545b4ef55300a1e2 100644 (file)
@@ -5,13 +5,14 @@
 
 import uuid
 import unittest
+
 import utils
-from utils import get_default_trace_class
-from bt2 import trace_class as bt2_trace_class
-from bt2 import value as bt2_value
 from bt2 import trace as bt2_trace
-from bt2 import stream as bt2_stream
 from bt2 import utils as bt2_utils
+from bt2 import value as bt2_value
+from bt2 import stream as bt2_stream
+from bt2 import trace_class as bt2_trace_class
+from utils import get_default_trace_class
 
 
 class TraceTestCase(unittest.TestCase):
@@ -136,11 +137,15 @@ class TraceTestCase(unittest.TestCase):
             num_trace_class_destroyed_calls += 1
 
         def on_trace_destruction(trace):
+            nonlocal type_of_passed_trace
+            type_of_passed_trace = type(trace)
+
             nonlocal num_trace_destroyed_calls
             num_trace_destroyed_calls += 1
 
         num_trace_class_destroyed_calls = 0
         num_trace_destroyed_calls = 0
+        type_of_passed_trace = None
 
         trace_class = get_default_trace_class()
         stream_class = trace_class.create_stream_class()
@@ -167,6 +172,7 @@ class TraceTestCase(unittest.TestCase):
 
         self.assertEqual(num_trace_class_destroyed_calls, 0)
         self.assertEqual(num_trace_destroyed_calls, 1)
+        self.assertIs(type_of_passed_trace, bt2_trace._TraceConst)
 
         del trace_class
 
index f9a21a0ffb276c2ac94a8a64bf88280ce34e54c2..fd5f8deb26b1a1f75691bbb14176103e7335a967 100644 (file)
@@ -4,14 +4,15 @@
 #
 
 import unittest
+
+from bt2 import utils as bt2_utils
+from bt2 import trace_class as bt2_trace_class
+from bt2 import stream_class as bt2_stream_class
 from utils import (
     run_in_component_init,
     get_default_trace_class,
     get_const_stream_beginning_message,
 )
-from bt2 import stream_class as bt2_stream_class
-from bt2 import trace_class as bt2_trace_class
-from bt2 import utils as bt2_utils
 
 
 class TraceClassTestCase(unittest.TestCase):
@@ -171,9 +172,13 @@ class TraceClassTestCase(unittest.TestCase):
 
     def test_destruction_listener(self):
         def on_trace_class_destruction(trace_class):
+            nonlocal type_of_passed_trace_class
+            type_of_passed_trace_class = type(trace_class)
+
             nonlocal num_destruct_calls
             num_destruct_calls += 1
 
+        type_of_passed_trace_class = None
         num_destruct_calls = 0
 
         trace_class = get_default_trace_class()
@@ -190,6 +195,7 @@ class TraceClassTestCase(unittest.TestCase):
         del trace_class
 
         self.assertEqual(num_destruct_calls, 1)
+        self.assertIs(type_of_passed_trace_class, bt2_trace_class._TraceClassConst)
 
     def test_remove_destruction_listener_wrong_type(self):
         trace_class = get_default_trace_class()
index 954b149c33645fc47c625b60a7681f1c130f08b1..53d838fc7a99a4783837094c9861208cd7de4d77 100644 (file)
@@ -3,12 +3,12 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import unittest
-import datetime
-import bt2
 import os
 import os.path
+import datetime
+import unittest
 
+import bt2
 
 _BT_TESTS_DATADIR = os.environ["BT_TESTS_DATADIR"]
 _BT_CTF_TRACES_PATH = os.environ["BT_CTF_TRACES_PATH"]
@@ -29,6 +29,7 @@ _AUTO_SOURCE_DISCOVERY_PARAMS_LOG_LEVEL_PATH = os.path.join(
 _METADATA_SYNTAX_ERROR_TRACE_PATH = os.path.join(
     _BT_CTF_TRACES_PATH, "fail", "metadata-syntax-error"
 )
+_BT_ENABLE_PYTHON_PLUGINS = os.environ["BT_TESTS_ENABLE_PYTHON_PLUGINS"] == "1"
 
 
 class _SomeSource(
@@ -407,6 +408,10 @@ class _TestAutoDiscoverSourceComponentSpecs(unittest.TestCase):
         os.environ["BABELTRACE_PLUGIN_PATH"] = self._saved_babeltrace_plugin_path
 
 
+@unittest.skipUnless(
+    _BT_ENABLE_PYTHON_PLUGINS,
+    "Support for Python plugins is disabled",
+)
 class TestAutoDiscoverSourceComponentSpecsGrouping(
     _TestAutoDiscoverSourceComponentSpecs
 ):
@@ -432,6 +437,10 @@ class TestAutoDiscoverSourceComponentSpecsGrouping(
         self.assertEqual(msgs[7].stream.name, "TestSourceSomeDir: some-dir")
 
 
+@unittest.skipUnless(
+    _BT_ENABLE_PYTHON_PLUGINS,
+    "Support for Python plugins is disabled",
+)
 class TestAutoDiscoverSourceComponentSpecsParamsObjLogLevel(
     _TestAutoDiscoverSourceComponentSpecs
 ):
index 62b09888d796043c75f887f2a3aac772f799a72b..4bd75a2e4760582db6acd097498ace3d39f3e4de 100644 (file)
@@ -3,12 +3,13 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-from functools import partial, partialmethod
+import copy
+import math
 import operator
-import collections
 import unittest
-import math
-import copy
+import collections
+from functools import partial, partialmethod
+
 import bt2
 
 
@@ -1227,9 +1228,6 @@ class _TestIntegerValue(_TestNumericValue):
     def test_create_pos(self):
         self.assertEqual(self._ip, self._pv)
 
-    def test_create_neg(self):
-        self.assertEqual(self._in, self._nv)
-
     def test_create_from_vint(self):
         i = self._CLS(self._ip)
         self.assertEqual(i, self._pv)
index 410bb78cdb743c8483efbf0a700c96c3a279bfd2..3a5fcb058fdad8327442aa714506e1b0216ca842 100644 (file)
@@ -3,9 +3,10 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import bt2
 import collections.abc
 
+import bt2
+
 
 # Run callable `func` in the context of a component's __init__ method.  The
 # callable is passed the Component being instantiated.
@@ -53,7 +54,6 @@ def _get_all_message_types(with_packet=True):
 
     class MyIter(bt2._UserMessageIterator):
         def __init__(self, config, self_output_port):
-
             nonlocal _msgs
             self._at = 0
             self._msgs = [
index 42cbc2848b952a91c94775d80a0f3235a5c4b3bf..f9f3d9e5d5d485eec091be3470afe6323d58f33f 100644 (file)
@@ -2,9 +2,9 @@
 
 AM_CPPFLAGS += -I$(top_srcdir)/tests/utils
 
-test_bitfield_SOURCES = test_bitfield.c
+test_bitfield_SOURCES = test-bitfield.c
 test_bitfield_LDADD = \
        $(top_builddir)/tests/utils/tap/libtap.la \
        $(top_builddir)/tests/utils/libtestcommon.la
 
-noinst_PROGRAMS = test_bitfield
+noinst_PROGRAMS = test-bitfield
diff --git a/tests/bitfield/test-bitfield.c b/tests/bitfield/test-bitfield.c
new file mode 100644 (file)
index 0000000..ea5efc7
--- /dev/null
@@ -0,0 +1,754 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2010-2019 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * BabelTrace - bitfield test program
+ */
+
+#include "compat/bitfield.h"
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "tap/tap.h"
+
+unsigned int glob;
+
+/*
+ * This function is only declared to show the size of a bitfield write in
+ * objdump.  The declaration is there to avoid a -Wmissing-prototypes warning.
+ */
+void fct(void);
+void fct(void)
+{
+       bt_bitfield_write(&glob, unsigned int, 12, 15, 0x12345678);
+}
+
+/* Test array size, in bytes */
+#define TEST_LEN 128
+#define NR_TESTS 10
+#define SIGNED_INT_READ_TEST_DESC_FMT_STR "Writing and reading back 0x%X, signed int dest, varying read unit size"
+#define SIGNED_INT_WRITE_TEST_DESC_FMT_STR "Writing and reading back 0x%X, signed int source, varying write unit size"
+#define SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR "Writing and reading back 0x%llX, signed long long dest, varying read unit size"
+#define SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR "Writing and reading back 0x%llX, signed long long source, varying write unit size"
+#define UNSIGNED_INT_READ_TEST_DESC_FMT_STR "Writing and reading back 0x%X, unsigned int dest, varying read unit size"
+#define UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR "Writing and reading back 0x%X, unsigned int source, varying write unit size"
+#define UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR "Writing and reading back 0x%llX, unsigned long long dest, varying read unit size"
+#define UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR "Writing and reading back 0x%llX, unsigned long long source, varying write unit size"
+#define DIAG_FMT_STR(val_type_fmt) "Failed reading value written \"%s\"-wise, with start=%i" \
+       " and length=%i. Read 0x" val_type_fmt
+
+static
+unsigned int fls_u64(uint64_t x)
+{
+       unsigned int r = 64;
+
+       if (!x)
+               return 0;
+
+       if (!(x & 0xFFFFFFFF00000000ULL)) {
+               x <<= 32;
+               r -= 32;
+       }
+       if (!(x & 0xFFFF000000000000ULL)) {
+               x <<= 16;
+               r -= 16;
+       }
+       if (!(x & 0xFF00000000000000ULL)) {
+               x <<= 8;
+               r -= 8;
+       }
+       if (!(x & 0xF000000000000000ULL)) {
+               x <<= 4;
+               r -= 4;
+       }
+       if (!(x & 0xC000000000000000ULL)) {
+               x <<= 2;
+               r -= 2;
+       }
+       if (!(x & 0x8000000000000000ULL)) {
+               x <<= 1;
+               r -= 1;
+       }
+       return r;
+}
+
+static
+unsigned int fls_u32(uint32_t x)
+{
+       unsigned int r = 32;
+
+       if (!x)
+               return 0;
+       if (!(x & 0xFFFF0000U)) {
+               x <<= 16;
+               r -= 16;
+       }
+       if (!(x & 0xFF000000U)) {
+               x <<= 8;
+               r -= 8;
+       }
+       if (!(x & 0xF0000000U)) {
+               x <<= 4;
+               r -= 4;
+       }
+       if (!(x & 0xC0000000U)) {
+               x <<= 2;
+               r -= 2;
+       }
+       if (!(x & 0x80000000U)) {
+               x <<= 1;
+               r -= 1;
+       }
+       return r;
+}
+
+#define print_byte_array(c, len)       \
+do {                                   \
+       unsigned long i;                \
+                                       \
+       for (i = 0; i < (len); i++) {   \
+               printf("0x%X", (c)[i]); \
+               if (i != (len) - 1)     \
+                       printf(" ");    \
+       }                               \
+       printf("\n");                   \
+} while (0)
+
+#define init_byte_array(c, len, val)   \
+do {                                   \
+       unsigned long i;                \
+                                       \
+       for (i = 0; i < (len); i++)     \
+               (c)[i] = (val);         \
+} while (0)
+
+#define check_result(ref, val, buffer, typename, start, len,                   \
+               desc_fmt_str, val_type_fmt)                                     \
+({                                                                             \
+       if ((val) != (ref)) {                                                   \
+               fail(desc_fmt_str, ref);                                        \
+               diag(DIAG_FMT_STR(val_type_fmt), #typename, start, len, val);   \
+               printf("# ");                                                   \
+               print_byte_array(buffer, TEST_LEN);                             \
+       }                                                                       \
+       (val) != (ref);                                                         \
+})
+
+static
+void run_test_unsigned_write(unsigned int src_ui, unsigned long long src_ull)
+{
+       unsigned int nrbits_ui, nrbits_ull;
+       union {
+               unsigned char c[TEST_LEN];
+               unsigned short s[TEST_LEN/sizeof(unsigned short)];
+               unsigned int i[TEST_LEN/sizeof(unsigned int)];
+               unsigned long l[TEST_LEN/sizeof(unsigned long)];
+               unsigned long long ll[TEST_LEN/sizeof(unsigned long long)];
+       } target;
+       unsigned long long readval;
+       unsigned int s, l;
+
+       /* The number of bits needed to represent 0 is 0. */
+       nrbits_ui = fls_u32(src_ui);
+
+       /* Write from unsigned integer src input. */
+       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
+               for (l = nrbits_ui; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ui, readval, target.c, unsigned char,
+                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.s, unsigned short, s, l, src_ui);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ui, readval, target.c, unsigned short,
+                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.i, unsigned int, s, l, src_ui);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ui, readval, target.c, unsigned int,
+                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.l, unsigned long, s, l, src_ui);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ui, readval, target.c, unsigned long,
+                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.ll, unsigned long long, s, l, src_ui);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ui, readval, target.c, unsigned long long,
+                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+               }
+       }
+       pass(UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR, src_ui);
+
+       /* The number of bits needed to represent 0 is 0. */
+       nrbits_ull = fls_u64(src_ull);
+
+       /* Write from unsigned long long src input. */
+       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
+               for (l = nrbits_ull; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ull, readval, target.c, unsigned char,
+                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.s, unsigned short, s, l, src_ull);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ull, readval, target.c, unsigned short,
+                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.i, unsigned int, s, l, src_ull);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ull, readval, target.c, unsigned int,
+                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.l, unsigned long, s, l, src_ull);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ull, readval, target.c, unsigned long,
+                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.ll, unsigned long long, s, l, src_ull);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
+                       if (check_result(src_ull, readval, target.c, unsigned long long,
+                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+               }
+       }
+       pass(UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR, src_ull);
+}
+
+static
+void run_test_unsigned_read(unsigned int src_ui, unsigned long long src_ull)
+{
+       unsigned int nrbits_ui, nrbits_ull, readval_ui;
+       union {
+               unsigned char c[TEST_LEN];
+               unsigned short s[TEST_LEN/sizeof(unsigned short)];
+               unsigned int i[TEST_LEN/sizeof(unsigned int)];
+               unsigned long l[TEST_LEN/sizeof(unsigned long)];
+               unsigned long long ll[TEST_LEN/sizeof(unsigned long long)];
+       } target;
+       unsigned long long readval_ull;
+       unsigned int s, l;
+
+       /* The number of bits needed to represent 0 is 0. */
+       nrbits_ui = fls_u32(src_ui);
+
+       /* Read to unsigned integer readval output. */
+       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
+               for (l = nrbits_ui; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval_ui);
+                       if (check_result(src_ui, readval_ui, target.c, unsigned char,
+                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
+                       bt_bitfield_read(target.s, unsigned short, s, l, &readval_ui);
+                       if (check_result(src_ui, readval_ui, target.c, unsigned short,
+                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
+                       bt_bitfield_read(target.i, unsigned int, s, l, &readval_ui);
+                       if (check_result(src_ui, readval_ui, target.c, unsigned int,
+                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
+                       bt_bitfield_read(target.l, unsigned long, s, l, &readval_ui);
+                       if (check_result(src_ui, readval_ui, target.c, unsigned long,
+                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
+                       bt_bitfield_read(target.ll, unsigned long long, s, l, &readval_ui);
+                       if (check_result(src_ui, readval_ui, target.c, unsigned long long,
+                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+               }
+       }
+       pass(UNSIGNED_INT_READ_TEST_DESC_FMT_STR, src_ui);
+
+       /* The number of bits needed to represent 0 is 0. */
+       nrbits_ull = fls_u64(src_ull);
+
+       /* Read to unsigned long long readval output. */
+       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
+               for (l = nrbits_ull; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
+                       bt_bitfield_read(target.c, unsigned char, s, l, &readval_ull);
+                       if (check_result(src_ull, readval_ull, target.c, unsigned char,
+                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
+                       bt_bitfield_read(target.s, unsigned short, s, l, &readval_ull);
+                       if (check_result(src_ull, readval_ull, target.c, unsigned short,
+                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
+                       bt_bitfield_read(target.i, unsigned int, s, l, &readval_ull);
+                       if (check_result(src_ull, readval_ull, target.c, unsigned int,
+                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
+                       bt_bitfield_read(target.l, unsigned long, s, l, &readval_ull);
+                       if (check_result(src_ull, readval_ull, target.c, unsigned long,
+                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
+                       bt_bitfield_read(target.ll, unsigned long long, s, l, &readval_ull);
+                       if (check_result(src_ull, readval_ull, target.c, unsigned long long,
+                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+               }
+       }
+       pass(UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR, src_ull);
+}
+
+static
+void run_test_unsigned(unsigned int src_ui, unsigned long long src_ull)
+{
+       run_test_unsigned_write(src_ui, src_ull);
+       run_test_unsigned_read(src_ui, src_ull);
+}
+
+static
+void run_test_signed_write(int src_i, long long src_ll)
+{
+       unsigned int nrbits_i, nrbits_ll;
+       union {
+               signed char c[TEST_LEN];
+               short s[TEST_LEN/sizeof(short)];
+               int i[TEST_LEN/sizeof(int)];
+               long l[TEST_LEN/sizeof(long)];
+               long long ll[TEST_LEN/sizeof(long long)];
+       } target;
+       long long readval;
+       unsigned int s, l;
+
+       if (!src_i)
+               nrbits_i = 0;                   /* The number of bits needed to represent 0 is 0. */
+       else if (src_i & 0x80000000U)
+               nrbits_i = fls_u32(~src_i) + 1; /* Find least significant bit conveying sign */
+       else
+               nrbits_i = fls_u32(src_i) + 1;  /* Keep sign at 0 */
+
+       /* Write from signed integer src input. */
+       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
+               for (l = nrbits_i; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.c, signed char, s, l, src_i);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_i, readval, target.c, signed char,
+                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.s, short, s, l, src_i);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_i, readval, target.c, short,
+                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.i, int, s, l, src_i);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_i, readval, target.c, int,
+                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.l, long, s, l, src_i);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_i, readval, target.c, long,
+                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.ll, long long, s, l, src_i);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_i, readval, target.c, long long,
+                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+               }
+       }
+       pass(SIGNED_INT_WRITE_TEST_DESC_FMT_STR, src_i);
+
+       if (!src_ll)
+               nrbits_ll = 0;                          /* The number of bits needed to represent 0 is 0. */
+       else if (src_ll & 0x8000000000000000ULL)
+               nrbits_ll = fls_u64(~src_ll) + 1;       /* Find least significant bit conveying sign */
+       else
+               nrbits_ll = fls_u64(src_ll) + 1;        /* Keep sign at 0 */
+
+       /* Write from signed long long src input. */
+       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
+               for (l = nrbits_ll; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_ll, readval, target.c, signed char,
+                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.s, short, s, l, src_ll);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_ll, readval, target.c, short,
+                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.i, int, s, l, src_ll);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_ll, readval, target.c, int,
+                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.l, long, s, l, src_ll);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_ll, readval, target.c, long,
+                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0x0);
+                       bt_bitfield_write(target.ll, long long, s, l, src_ll);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval);
+                       if (check_result(src_ll, readval, target.c, long long,
+                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+               }
+       }
+       pass(SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR, src_ll);
+}
+
+static
+void run_test_signed_read(int src_i, long long src_ll)
+{
+       unsigned int nrbits_i, nrbits_ll;
+       int readval_i;
+       union {
+               unsigned char c[TEST_LEN];
+               unsigned short s[TEST_LEN/sizeof(unsigned short)];
+               unsigned int i[TEST_LEN/sizeof(unsigned int)];
+               unsigned long l[TEST_LEN/sizeof(unsigned long)];
+               unsigned long long ll[TEST_LEN/sizeof(unsigned long long)];
+       } target;
+       long long readval_ll;
+       unsigned int s, l;
+
+       if (!src_i)
+               nrbits_i = 0;                   /* The number of bits needed to represent 0 is 0. */
+       else if (src_i & 0x80000000U)
+               nrbits_i = fls_u32(~src_i) + 1; /* Find least significant bit conveying sign */
+       else
+               nrbits_i = fls_u32(src_i) + 1;  /* Keep sign at 0 */
+
+       /* Read to signed integer readval output. */
+       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
+               for (l = nrbits_i; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_i);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval_i);
+                       if (check_result(src_i, readval_i, target.c, signed char,
+                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_i);
+                       bt_bitfield_read(target.s, short, s, l, &readval_i);
+                       if (check_result(src_i, readval_i, target.c, short,
+                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_i);
+                       bt_bitfield_read(target.i, int, s, l, &readval_i);
+                       if (check_result(src_i, readval_i, target.c, int,
+                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_i);
+                       bt_bitfield_read(target.l, long, s, l, &readval_i);
+                       if (check_result(src_i, readval_i, target.c, long,
+                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_i);
+                       bt_bitfield_read(target.ll, long long, s, l, &readval_i);
+                       if (check_result(src_i, readval_i, target.c, long long,
+                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
+                                       "%X")) {
+                               return;
+                       }
+               }
+       }
+       pass(SIGNED_INT_READ_TEST_DESC_FMT_STR, src_i);
+
+       if (!src_ll)
+               nrbits_ll = 0;                          /* The number of bits needed to represent 0 is 0. */
+       if (src_ll & 0x8000000000000000ULL)
+               nrbits_ll = fls_u64(~src_ll) + 1;       /* Find least significant bit conveying sign */
+       else
+               nrbits_ll = fls_u64(src_ll) + 1;        /* Keep sign at 0 */
+
+       /* Read to signed long long readval output. */
+       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
+               for (l = nrbits_ll; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
+                       bt_bitfield_read(target.c, signed char, s, l, &readval_ll);
+                       if (check_result(src_ll, readval_ll, target.c, signed char,
+                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
+                       bt_bitfield_read(target.s, short, s, l, &readval_ll);
+                       if (check_result(src_ll, readval_ll, target.c, short,
+                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
+                       bt_bitfield_read(target.i, int, s, l, &readval_ll);
+                       if (check_result(src_ll, readval_ll, target.c, int,
+                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
+                       bt_bitfield_read(target.l, long, s, l, &readval_ll);
+                       if (check_result(src_ll, readval_ll, target.c, long,
+                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+
+                       init_byte_array(target.c, TEST_LEN, 0xFF);
+                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
+                       bt_bitfield_read(target.ll, long long, s, l, &readval_ll);
+                       if (check_result(src_ll, readval_ll, target.c, long long,
+                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
+                                       "%llX")) {
+                               return;
+                       }
+               }
+       }
+       pass(SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR, src_ll);
+}
+
+static
+void run_test_signed(int src_i, long long src_ll)
+{
+       run_test_signed_write(src_i, src_ll);
+       run_test_signed_read(src_i, src_ll);
+}
+
+static
+void run_test(void)
+{
+       int i;
+
+       plan_tests(NR_TESTS * 8 + 24);
+
+       srand(time(NULL));
+
+       run_test_unsigned(0, 0);
+       run_test_signed(0, 0);
+       run_test_unsigned(1, 1);
+       run_test_unsigned(~0U, ~0ULL);
+       run_test_signed(-1U, -1ULL);
+       run_test_signed(0x80000000U, 0x8000000000000000ULL);
+
+       for (i = 0; i < NR_TESTS; i++) {
+               unsigned int src_ui = rand();
+               unsigned long long src_ull = ((unsigned long long) (unsigned int) rand() << 32) |
+                               (unsigned long long) (unsigned int) rand();
+
+               run_test_unsigned(src_ui, src_ull);
+               run_test_signed((int) src_ui, (long long) src_ull);
+       }
+}
+
+static
+int print_encodings(unsigned long src, unsigned int shift, unsigned int len)
+{
+       union {
+               unsigned char c[8];
+               unsigned short s[4];
+               unsigned int i[2];
+               unsigned long l[2];
+               unsigned long long ll[1];
+       } target;
+       unsigned long long readval;
+
+       init_byte_array(target.c, 8, 0xFF);
+       bt_bitfield_write(target.c, unsigned char, shift, len, src);
+       printf("bytewise\n");
+       print_byte_array(target.c, 8);
+
+       init_byte_array(target.c, 8, 0xFF);
+       bt_bitfield_write(target.s, unsigned short, shift, len, src);
+       printf("shortwise\n");
+       print_byte_array(target.c, 8);
+
+       init_byte_array(target.c, 8, 0xFF);
+       bt_bitfield_write(target.i, unsigned int, shift, len, src);
+       printf("intwise\n");
+       print_byte_array(target.c, 8);
+
+       init_byte_array(target.c, 8, 0xFF);
+       bt_bitfield_write(target.l, unsigned long, shift, len, src);
+       printf("longwise\n");
+       print_byte_array(target.c, 8);
+
+       init_byte_array(target.c, 8, 0xFF);
+       bt_bitfield_write(target.ll, unsigned long long, shift, len, src);
+       printf("lluwise\n");
+       print_byte_array(target.c, 8);
+
+       bt_bitfield_read(target.c, unsigned char, shift, len, &readval);
+       printf("read: %llX\n", readval);
+       print_byte_array(target.c, 8);
+
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       if (argc > 1) {
+               /* Print encodings */
+               unsigned long src;
+               unsigned int shift, len;
+
+               src = atoi(argv[1]);
+               if (argc > 2)
+                       shift = atoi(argv[2]);
+               else
+                       shift = 12;
+               if (argc > 3)
+                       len = atoi(argv[3]);
+               else
+                       len = 40;
+               return print_encodings(src, shift, len);
+       }
+
+       /* Run tap-formatted tests */
+       run_test();
+       return exit_status();
+}
diff --git a/tests/bitfield/test_bitfield.c b/tests/bitfield/test_bitfield.c
deleted file mode 100644 (file)
index f0b4482..0000000
+++ /dev/null
@@ -1,754 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2010-2019 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * BabelTrace - bitfield test program
- */
-
-#include "compat/bitfield.h"
-#include <time.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <tap/tap.h>
-
-unsigned int glob;
-
-/*
- * This function is only declared to show the size of a bitfield write in
- * objdump.  The declaration is there to avoid a -Wmissing-prototypes warning.
- */
-void fct(void);
-void fct(void)
-{
-       bt_bitfield_write(&glob, unsigned int, 12, 15, 0x12345678);
-}
-
-/* Test array size, in bytes */
-#define TEST_LEN 128
-#define NR_TESTS 10
-#define SIGNED_INT_READ_TEST_DESC_FMT_STR "Writing and reading back 0x%X, signed int dest, varying read unit size"
-#define SIGNED_INT_WRITE_TEST_DESC_FMT_STR "Writing and reading back 0x%X, signed int source, varying write unit size"
-#define SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR "Writing and reading back 0x%llX, signed long long dest, varying read unit size"
-#define SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR "Writing and reading back 0x%llX, signed long long source, varying write unit size"
-#define UNSIGNED_INT_READ_TEST_DESC_FMT_STR "Writing and reading back 0x%X, unsigned int dest, varying read unit size"
-#define UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR "Writing and reading back 0x%X, unsigned int source, varying write unit size"
-#define UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR "Writing and reading back 0x%llX, unsigned long long dest, varying read unit size"
-#define UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR "Writing and reading back 0x%llX, unsigned long long source, varying write unit size"
-#define DIAG_FMT_STR(val_type_fmt) "Failed reading value written \"%s\"-wise, with start=%i" \
-       " and length=%i. Read 0x" val_type_fmt
-
-static
-unsigned int fls_u64(uint64_t x)
-{
-       unsigned int r = 64;
-
-       if (!x)
-               return 0;
-
-       if (!(x & 0xFFFFFFFF00000000ULL)) {
-               x <<= 32;
-               r -= 32;
-       }
-       if (!(x & 0xFFFF000000000000ULL)) {
-               x <<= 16;
-               r -= 16;
-       }
-       if (!(x & 0xFF00000000000000ULL)) {
-               x <<= 8;
-               r -= 8;
-       }
-       if (!(x & 0xF000000000000000ULL)) {
-               x <<= 4;
-               r -= 4;
-       }
-       if (!(x & 0xC000000000000000ULL)) {
-               x <<= 2;
-               r -= 2;
-       }
-       if (!(x & 0x8000000000000000ULL)) {
-               x <<= 1;
-               r -= 1;
-       }
-       return r;
-}
-
-static
-unsigned int fls_u32(uint32_t x)
-{
-       unsigned int r = 32;
-
-       if (!x)
-               return 0;
-       if (!(x & 0xFFFF0000U)) {
-               x <<= 16;
-               r -= 16;
-       }
-       if (!(x & 0xFF000000U)) {
-               x <<= 8;
-               r -= 8;
-       }
-       if (!(x & 0xF0000000U)) {
-               x <<= 4;
-               r -= 4;
-       }
-       if (!(x & 0xC0000000U)) {
-               x <<= 2;
-               r -= 2;
-       }
-       if (!(x & 0x80000000U)) {
-               x <<= 1;
-               r -= 1;
-       }
-       return r;
-}
-
-#define print_byte_array(c, len)       \
-do {                                   \
-       unsigned long i;                \
-                                       \
-       for (i = 0; i < (len); i++) {   \
-               printf("0x%X", (c)[i]); \
-               if (i != (len) - 1)     \
-                       printf(" ");    \
-       }                               \
-       printf("\n");                   \
-} while (0)
-
-#define init_byte_array(c, len, val)   \
-do {                                   \
-       unsigned long i;                \
-                                       \
-       for (i = 0; i < (len); i++)     \
-               (c)[i] = (val);         \
-} while (0)
-
-#define check_result(ref, val, buffer, typename, start, len,                   \
-               desc_fmt_str, val_type_fmt)                                     \
-({                                                                             \
-       if ((val) != (ref)) {                                                   \
-               fail(desc_fmt_str, ref);                                        \
-               diag(DIAG_FMT_STR(val_type_fmt), #typename, start, len, val);   \
-               printf("# ");                                                   \
-               print_byte_array(buffer, TEST_LEN);                             \
-       }                                                                       \
-       (val) != (ref);                                                         \
-})
-
-static
-void run_test_unsigned_write(unsigned int src_ui, unsigned long long src_ull)
-{
-       unsigned int nrbits_ui, nrbits_ull;
-       union {
-               unsigned char c[TEST_LEN];
-               unsigned short s[TEST_LEN/sizeof(unsigned short)];
-               unsigned int i[TEST_LEN/sizeof(unsigned int)];
-               unsigned long l[TEST_LEN/sizeof(unsigned long)];
-               unsigned long long ll[TEST_LEN/sizeof(unsigned long long)];
-       } target;
-       unsigned long long readval;
-       unsigned int s, l;
-
-       /* The number of bits needed to represent 0 is 0. */
-       nrbits_ui = fls_u32(src_ui);
-
-       /* Write from unsigned integer src input. */
-       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
-               for (l = nrbits_ui; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ui, readval, target.c, unsigned char,
-                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.s, unsigned short, s, l, src_ui);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ui, readval, target.c, unsigned short,
-                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.i, unsigned int, s, l, src_ui);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ui, readval, target.c, unsigned int,
-                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.l, unsigned long, s, l, src_ui);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ui, readval, target.c, unsigned long,
-                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.ll, unsigned long long, s, l, src_ui);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ui, readval, target.c, unsigned long long,
-                                       s, l, UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-               }
-       }
-       pass(UNSIGNED_INT_WRITE_TEST_DESC_FMT_STR, src_ui);
-
-       /* The number of bits needed to represent 0 is 0. */
-       nrbits_ull = fls_u64(src_ull);
-
-       /* Write from unsigned long long src input. */
-       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
-               for (l = nrbits_ull; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ull, readval, target.c, unsigned char,
-                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.s, unsigned short, s, l, src_ull);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ull, readval, target.c, unsigned short,
-                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.i, unsigned int, s, l, src_ull);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ull, readval, target.c, unsigned int,
-                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.l, unsigned long, s, l, src_ull);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ull, readval, target.c, unsigned long,
-                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.ll, unsigned long long, s, l, src_ull);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval);
-                       if (check_result(src_ull, readval, target.c, unsigned long long,
-                                       s, l, UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-               }
-       }
-       pass(UNSIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR, src_ull);
-}
-
-static
-void run_test_unsigned_read(unsigned int src_ui, unsigned long long src_ull)
-{
-       unsigned int nrbits_ui, nrbits_ull, readval_ui;
-       union {
-               unsigned char c[TEST_LEN];
-               unsigned short s[TEST_LEN/sizeof(unsigned short)];
-               unsigned int i[TEST_LEN/sizeof(unsigned int)];
-               unsigned long l[TEST_LEN/sizeof(unsigned long)];
-               unsigned long long ll[TEST_LEN/sizeof(unsigned long long)];
-       } target;
-       unsigned long long readval_ull;
-       unsigned int s, l;
-
-       /* The number of bits needed to represent 0 is 0. */
-       nrbits_ui = fls_u32(src_ui);
-
-       /* Read to unsigned integer readval output. */
-       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
-               for (l = nrbits_ui; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval_ui);
-                       if (check_result(src_ui, readval_ui, target.c, unsigned char,
-                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
-                       bt_bitfield_read(target.s, unsigned short, s, l, &readval_ui);
-                       if (check_result(src_ui, readval_ui, target.c, unsigned short,
-                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
-                       bt_bitfield_read(target.i, unsigned int, s, l, &readval_ui);
-                       if (check_result(src_ui, readval_ui, target.c, unsigned int,
-                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
-                       bt_bitfield_read(target.l, unsigned long, s, l, &readval_ui);
-                       if (check_result(src_ui, readval_ui, target.c, unsigned long,
-                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ui);
-                       bt_bitfield_read(target.ll, unsigned long long, s, l, &readval_ui);
-                       if (check_result(src_ui, readval_ui, target.c, unsigned long long,
-                                       s, l, UNSIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-               }
-       }
-       pass(UNSIGNED_INT_READ_TEST_DESC_FMT_STR, src_ui);
-
-       /* The number of bits needed to represent 0 is 0. */
-       nrbits_ull = fls_u64(src_ull);
-
-       /* Read to unsigned long long readval output. */
-       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
-               for (l = nrbits_ull; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
-                       bt_bitfield_read(target.c, unsigned char, s, l, &readval_ull);
-                       if (check_result(src_ull, readval_ull, target.c, unsigned char,
-                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
-                       bt_bitfield_read(target.s, unsigned short, s, l, &readval_ull);
-                       if (check_result(src_ull, readval_ull, target.c, unsigned short,
-                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
-                       bt_bitfield_read(target.i, unsigned int, s, l, &readval_ull);
-                       if (check_result(src_ull, readval_ull, target.c, unsigned int,
-                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
-                       bt_bitfield_read(target.l, unsigned long, s, l, &readval_ull);
-                       if (check_result(src_ull, readval_ull, target.c, unsigned long,
-                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, unsigned char, s, l, src_ull);
-                       bt_bitfield_read(target.ll, unsigned long long, s, l, &readval_ull);
-                       if (check_result(src_ull, readval_ull, target.c, unsigned long long,
-                                       s, l, UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-               }
-       }
-       pass(UNSIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR, src_ull);
-}
-
-static
-void run_test_unsigned(unsigned int src_ui, unsigned long long src_ull)
-{
-       run_test_unsigned_write(src_ui, src_ull);
-       run_test_unsigned_read(src_ui, src_ull);
-}
-
-static
-void run_test_signed_write(int src_i, long long src_ll)
-{
-       unsigned int nrbits_i, nrbits_ll;
-       union {
-               signed char c[TEST_LEN];
-               short s[TEST_LEN/sizeof(short)];
-               int i[TEST_LEN/sizeof(int)];
-               long l[TEST_LEN/sizeof(long)];
-               long long ll[TEST_LEN/sizeof(long long)];
-       } target;
-       long long readval;
-       unsigned int s, l;
-
-       if (!src_i)
-               nrbits_i = 0;                   /* The number of bits needed to represent 0 is 0. */
-       else if (src_i & 0x80000000U)
-               nrbits_i = fls_u32(~src_i) + 1; /* Find least significant bit conveying sign */
-       else
-               nrbits_i = fls_u32(src_i) + 1;  /* Keep sign at 0 */
-
-       /* Write from signed integer src input. */
-       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
-               for (l = nrbits_i; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.c, signed char, s, l, src_i);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_i, readval, target.c, signed char,
-                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.s, short, s, l, src_i);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_i, readval, target.c, short,
-                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.i, int, s, l, src_i);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_i, readval, target.c, int,
-                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.l, long, s, l, src_i);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_i, readval, target.c, long,
-                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.ll, long long, s, l, src_i);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_i, readval, target.c, long long,
-                                       s, l, SIGNED_INT_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-               }
-       }
-       pass(SIGNED_INT_WRITE_TEST_DESC_FMT_STR, src_i);
-
-       if (!src_ll)
-               nrbits_ll = 0;                          /* The number of bits needed to represent 0 is 0. */
-       else if (src_ll & 0x8000000000000000ULL)
-               nrbits_ll = fls_u64(~src_ll) + 1;       /* Find least significant bit conveying sign */
-       else
-               nrbits_ll = fls_u64(src_ll) + 1;        /* Keep sign at 0 */
-
-       /* Write from signed long long src input. */
-       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
-               for (l = nrbits_ll; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_ll, readval, target.c, signed char,
-                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.s, short, s, l, src_ll);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_ll, readval, target.c, short,
-                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.i, int, s, l, src_ll);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_ll, readval, target.c, int,
-                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.l, long, s, l, src_ll);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_ll, readval, target.c, long,
-                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0x0);
-                       bt_bitfield_write(target.ll, long long, s, l, src_ll);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval);
-                       if (check_result(src_ll, readval, target.c, long long,
-                                       s, l, SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-               }
-       }
-       pass(SIGNED_LONG_LONG_WRITE_TEST_DESC_FMT_STR, src_ll);
-}
-
-static
-void run_test_signed_read(int src_i, long long src_ll)
-{
-       unsigned int nrbits_i, nrbits_ll;
-       int readval_i;
-       union {
-               unsigned char c[TEST_LEN];
-               unsigned short s[TEST_LEN/sizeof(unsigned short)];
-               unsigned int i[TEST_LEN/sizeof(unsigned int)];
-               unsigned long l[TEST_LEN/sizeof(unsigned long)];
-               unsigned long long ll[TEST_LEN/sizeof(unsigned long long)];
-       } target;
-       long long readval_ll;
-       unsigned int s, l;
-
-       if (!src_i)
-               nrbits_i = 0;                   /* The number of bits needed to represent 0 is 0. */
-       else if (src_i & 0x80000000U)
-               nrbits_i = fls_u32(~src_i) + 1; /* Find least significant bit conveying sign */
-       else
-               nrbits_i = fls_u32(src_i) + 1;  /* Keep sign at 0 */
-
-       /* Read to signed integer readval output. */
-       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
-               for (l = nrbits_i; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_i);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval_i);
-                       if (check_result(src_i, readval_i, target.c, signed char,
-                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_i);
-                       bt_bitfield_read(target.s, short, s, l, &readval_i);
-                       if (check_result(src_i, readval_i, target.c, short,
-                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_i);
-                       bt_bitfield_read(target.i, int, s, l, &readval_i);
-                       if (check_result(src_i, readval_i, target.c, int,
-                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_i);
-                       bt_bitfield_read(target.l, long, s, l, &readval_i);
-                       if (check_result(src_i, readval_i, target.c, long,
-                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_i);
-                       bt_bitfield_read(target.ll, long long, s, l, &readval_i);
-                       if (check_result(src_i, readval_i, target.c, long long,
-                                       s, l, SIGNED_INT_READ_TEST_DESC_FMT_STR,
-                                       "%X")) {
-                               return;
-                       }
-               }
-       }
-       pass(SIGNED_INT_READ_TEST_DESC_FMT_STR, src_i);
-
-       if (!src_ll)
-               nrbits_ll = 0;                          /* The number of bits needed to represent 0 is 0. */
-       if (src_ll & 0x8000000000000000ULL)
-               nrbits_ll = fls_u64(~src_ll) + 1;       /* Find least significant bit conveying sign */
-       else
-               nrbits_ll = fls_u64(src_ll) + 1;        /* Keep sign at 0 */
-
-       /* Read to signed long long readval output. */
-       for (s = 0; s < CHAR_BIT * TEST_LEN; s++) {
-               for (l = nrbits_ll; l <= (CHAR_BIT * TEST_LEN) - s; l++) {
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
-                       bt_bitfield_read(target.c, signed char, s, l, &readval_ll);
-                       if (check_result(src_ll, readval_ll, target.c, signed char,
-                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
-                       bt_bitfield_read(target.s, short, s, l, &readval_ll);
-                       if (check_result(src_ll, readval_ll, target.c, short,
-                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
-                       bt_bitfield_read(target.i, int, s, l, &readval_ll);
-                       if (check_result(src_ll, readval_ll, target.c, int,
-                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
-                       bt_bitfield_read(target.l, long, s, l, &readval_ll);
-                       if (check_result(src_ll, readval_ll, target.c, long,
-                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-
-                       init_byte_array(target.c, TEST_LEN, 0xFF);
-                       bt_bitfield_write(target.c, signed char, s, l, src_ll);
-                       bt_bitfield_read(target.ll, long long, s, l, &readval_ll);
-                       if (check_result(src_ll, readval_ll, target.c, long long,
-                                       s, l, SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR,
-                                       "%llX")) {
-                               return;
-                       }
-               }
-       }
-       pass(SIGNED_LONG_LONG_READ_TEST_DESC_FMT_STR, src_ll);
-}
-
-static
-void run_test_signed(int src_i, long long src_ll)
-{
-       run_test_signed_write(src_i, src_ll);
-       run_test_signed_read(src_i, src_ll);
-}
-
-static
-void run_test(void)
-{
-       int i;
-
-       plan_tests(NR_TESTS * 8 + 24);
-
-       srand(time(NULL));
-
-       run_test_unsigned(0, 0);
-       run_test_signed(0, 0);
-       run_test_unsigned(1, 1);
-       run_test_unsigned(~0U, ~0ULL);
-       run_test_signed(-1U, -1ULL);
-       run_test_signed(0x80000000U, 0x8000000000000000ULL);
-
-       for (i = 0; i < NR_TESTS; i++) {
-               unsigned int src_ui = rand();
-               unsigned long long src_ull = ((unsigned long long) (unsigned int) rand() << 32) |
-                               (unsigned long long) (unsigned int) rand();
-
-               run_test_unsigned(src_ui, src_ull);
-               run_test_signed((int) src_ui, (long long) src_ull);
-       }
-}
-
-static
-int print_encodings(unsigned long src, unsigned int shift, unsigned int len)
-{
-       union {
-               unsigned char c[8];
-               unsigned short s[4];
-               unsigned int i[2];
-               unsigned long l[2];
-               unsigned long long ll[1];
-       } target;
-       unsigned long long readval;
-
-       init_byte_array(target.c, 8, 0xFF);
-       bt_bitfield_write(target.c, unsigned char, shift, len, src);
-       printf("bytewise\n");
-       print_byte_array(target.c, 8);
-
-       init_byte_array(target.c, 8, 0xFF);
-       bt_bitfield_write(target.s, unsigned short, shift, len, src);
-       printf("shortwise\n");
-       print_byte_array(target.c, 8);
-
-       init_byte_array(target.c, 8, 0xFF);
-       bt_bitfield_write(target.i, unsigned int, shift, len, src);
-       printf("intwise\n");
-       print_byte_array(target.c, 8);
-
-       init_byte_array(target.c, 8, 0xFF);
-       bt_bitfield_write(target.l, unsigned long, shift, len, src);
-       printf("longwise\n");
-       print_byte_array(target.c, 8);
-
-       init_byte_array(target.c, 8, 0xFF);
-       bt_bitfield_write(target.ll, unsigned long long, shift, len, src);
-       printf("lluwise\n");
-       print_byte_array(target.c, 8);
-
-       bt_bitfield_read(target.c, unsigned char, shift, len, &readval);
-       printf("read: %llX\n", readval);
-       print_byte_array(target.c, 8);
-
-       return 0;
-}
-
-int main(int argc, char **argv)
-{
-       if (argc > 1) {
-               /* Print encodings */
-               unsigned long src;
-               unsigned int shift, len;
-
-               src = atoi(argv[1]);
-               if (argc > 2)
-                       shift = atoi(argv[2]);
-               else
-                       shift = 12;
-               if (argc > 3)
-                       len = atoi(argv[3]);
-               else
-                       len = 40;
-               return print_encodings(src, shift, len);
-       }
-
-       /* Run tap-formated tests */
-       run_test();
-       return exit_status();
-}
diff --git a/tests/cli/convert/test-auto-source-discovery-grouping.sh b/tests/cli/convert/test-auto-source-discovery-grouping.sh
new file mode 100755 (executable)
index 0000000..75a4977
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+
+# Test the auto source disovery mechanism of the CLI.
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+SH_TAP=1 source "$UTILSSH"
+
+NUM_TESTS=3
+
+plan_tests $NUM_TESTS
+
+data_dir="${BT_TESTS_DATADIR}/auto-source-discovery/grouping"
+plugin_dir="${data_dir}"
+trace_dir="${data_dir}/traces"
+
+stdout_expected_file="${BT_TESTS_DATADIR}/cli/convert/auto-source-discovery-grouping.expect"
+stdout_actual_file=$(mktemp -t stdout-actual.XXXXXX)
+stderr_actual_file=$(mktemp -t actual-stderr.XXXXXX)
+
+bt_cli "$stdout_actual_file" "$stderr_actual_file" \
+       --plugin-path "${plugin_dir}" convert "ABCDE" "${trace_dir}" some_other_non_opt \
+       -c sink.text.details --params='with-metadata=false'
+ok "$?" "CLI runs successfully"
+
+# Check components and their inputs.
+bt_diff "$stdout_expected_file" "$stdout_actual_file"
+ok "$?" "expected components are instantiated with expected inputs"
+
+# Check that expected warning is printed.
+# shellcheck disable=SC2016
+bt_grep_ok \
+       'No trace was found based on input `some_other_non_opt`' \
+       "$stderr_actual_file" \
+       "warning is printed"
+
+rm -f "$stdout_actual_file"
+rm -f "$stderr_actual_file"
diff --git a/tests/cli/convert/test-auto-source-discovery-log-level.sh b/tests/cli/convert/test-auto-source-discovery-log-level.sh
new file mode 100755 (executable)
index 0000000..56df66f
--- /dev/null
@@ -0,0 +1,146 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+
+# Test how log level options are applied to sources auto-discovered by the
+# convert command.
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+SH_TAP=1 source "$UTILSSH"
+
+NUM_TESTS=4
+
+plan_tests $NUM_TESTS
+
+data_dir="${BT_TESTS_DATADIR}/auto-source-discovery/params-log-level"
+plugin_dir="${data_dir}"
+dir_a="${data_dir}/dir-a"
+dir_b="${data_dir}/dir-b"
+dir_ab="${data_dir}/dir-ab"
+
+expected_file=$(mktemp -t expected.XXXXXX)
+
+print_log_level=(--params 'what="log-level"')
+details_sink=("-c" "sink.text.details" "--params=with-metadata=false")
+
+debug=2
+trace=1
+
+# Apply log level to two components from one non-option argument.
+cat > "$expected_file" <<END
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceA: ${debug}
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceB: ${debug}
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+END
+
+bt_diff_cli "$expected_file" "/dev/null" \
+       --plugin-path "${plugin_dir}" convert \
+       "${dir_ab}" --log-level DEBUG "${print_log_level[@]}" \
+       "${details_sink[@]}"
+ok "$?" "apply log level to two components from one non-option argument"
+
+# Apply log level to two components from two distinct non-option arguments.
+cat > "$expected_file" <<END
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceA: ${debug}
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceB: ${trace}
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+END
+
+bt_diff_cli "$expected_file" "/dev/null" \
+       --plugin-path "${plugin_dir}" convert \
+       "${dir_a}" --log-level DEBUG "${print_log_level[@]}" "${dir_b}" --log-level TRACE "${print_log_level[@]}" \
+       "${details_sink[@]}"
+ok "$?" "apply log level to two non-option arguments"
+
+# Apply log level to one component coming from one non-option argument and one component coming from two non-option arguments (1).
+cat > "$expected_file" <<END
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceA: ${trace}
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceB: ${trace}
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+END
+
+bt_diff_cli "$expected_file" "/dev/null" \
+       --plugin-path "${plugin_dir}" convert \
+       "${dir_a}" --log-level DEBUG "${print_log_level[@]}" "${dir_ab}" --log-level TRACE "${print_log_level[@]}" \
+       "${details_sink[@]}"
+ok "$?" "apply log level to one component coming from one non-option argument and one component coming from two non-option arguments (1)"
+
+# Apply log level to one component coming from one non-option argument and one component coming from two non-option arguments (2).
+cat > "$expected_file" <<END
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceA: ${trace}
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceB: ${debug}
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+END
+
+bt_diff_cli "$expected_file" "/dev/null" \
+       --plugin-path "${plugin_dir}" convert \
+       "${dir_ab}" --log-level DEBUG "${print_log_level[@]}" "${dir_a}" --log-level TRACE "${print_log_level[@]}" \
+       "${details_sink[@]}"
+ok "$?" "apply log level to one component coming from one non-option argument and one component coming from two non-option arguments (2)"
+
+rm -f "$expected_file"
diff --git a/tests/cli/convert/test-auto-source-discovery-params.sh b/tests/cli/convert/test-auto-source-discovery-params.sh
new file mode 100755 (executable)
index 0000000..65ef536
--- /dev/null
@@ -0,0 +1,143 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+
+# Test how parameters are applied to sources auto-discovered by the convert
+# command.
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+SH_TAP=1 source "$UTILSSH"
+
+NUM_TESTS=4
+
+plan_tests $NUM_TESTS
+
+data_dir="${BT_TESTS_DATADIR}/auto-source-discovery/params-log-level"
+plugin_dir="${data_dir}"
+dir_a="${data_dir}/dir-a"
+dir_b="${data_dir}/dir-b"
+dir_ab="${data_dir}/dir-ab"
+
+expected_file=$(mktemp -t expected.XXXXXX)
+
+print_test_params=("--params" 'what="test-params"')
+details_sink=("-c" "sink.text.details" "--params=with-metadata=false")
+
+# Apply params to two components from one non-option argument.
+cat > "$expected_file" <<END
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceA: ('test-allo', 'madame')
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceB: ('test-allo', 'madame')
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+END
+
+bt_diff_cli "$expected_file" "/dev/null" \
+       --plugin-path "${plugin_dir}" convert \
+       "${dir_ab}" --params 'test-allo="madame"' "${print_test_params[@]}" \
+       "${details_sink[@]}"
+ok "$?" "apply params to two components from one non-option argument"
+
+# Apply params to two components from two distinct non-option arguments.
+cat > "$expected_file" <<END
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceA: ('test-allo', 'madame')
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceB: ('test-bonjour', 'monsieur')
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+END
+
+bt_diff_cli "$expected_file" "/dev/null" \
+       --plugin-path "${plugin_dir}" convert \
+       "${dir_a}" --params 'test-allo="madame"' "${print_test_params[@]}" "${dir_b}" --params 'test-bonjour="monsieur"' "${print_test_params[@]}" \
+       "${details_sink[@]}"
+ok "$?" "apply params to two non-option arguments"
+
+# Apply params to one component coming from one non-option argument and one component coming from two non-option arguments (1).
+cat > "$expected_file" <<END
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceA: ('test-allo', 'madame'), ('test-bonjour', 'monsieur')
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceB: ('test-bonjour', 'monsieur')
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+END
+
+bt_diff_cli "$expected_file" "/dev/null" \
+       --plugin-path "${plugin_dir}" convert \
+       "${dir_a}" --params 'test-allo="madame"' "${print_test_params[@]}" "${dir_ab}" --params 'test-bonjour="monsieur"' "${print_test_params[@]}" \
+       "${details_sink[@]}"
+ok "$?" "apply params to one component coming from one non-option argument and one component coming from two non-option arguments (1)"
+
+# Apply params to one component coming from one non-option argument and one component coming from two non-option arguments (2).
+cat > "$expected_file" <<END
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceA: ('test-bonjour', 'monsieur'), ('test-salut', 'les amis')
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: TestSourceB: ('test-bonjour', 'madame'), ('test-salut', 'les amis')
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+END
+
+bt_diff_cli "$expected_file" "/dev/null" \
+       --plugin-path "${plugin_dir}" convert \
+       "${dir_ab}" --params 'test-bonjour="madame",test-salut="les amis"' "${print_test_params[@]}" "${dir_a}" --params 'test-bonjour="monsieur"' "${print_test_params[@]}" \
+       "${details_sink[@]}"
+ok "$?" "apply params to one component coming from one non-option argument and one component coming from two non-option arguments (2)"
+
+rm -f "$expected_file"
diff --git a/tests/cli/convert/test-convert-args.sh b/tests/cli/convert/test-convert-args.sh
new file mode 100755 (executable)
index 0000000..ac4c2b5
--- /dev/null
@@ -0,0 +1,279 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+tmp_stdout=$(mktemp -t test-convert-args-stdout.XXXXXX)
+tmp_stderr=$(mktemp -t test-convert-args-stderr.XXXXXX)
+
+test_bt_convert_run_args() {
+       local what="$1"
+       local convert_args="$2"
+       local expected_run_args="$3"
+
+       local run_args
+
+       # Split argument string into array.
+       IFS=' ' read -ra convert_args_array <<< "$convert_args"
+
+       # Execute convert command.
+       bt_cli "${tmp_stdout}" "${tmp_stderr}" convert --run-args "${convert_args_array[@]}"
+       ok $? "${what}: success exit status"
+
+       run_args=$(cat "${tmp_stdout}")
+
+       # Verify output run args.
+       [ "$run_args" = "$expected_run_args" ]
+       ok $? "${what}: run arguments"
+}
+
+test_bt_convert_fails() {
+       local what="$1"
+       local convert_args="$2"
+       local expected_error_str="$3"
+
+       # Split argument string into array.
+       IFS=' ' read -ra convert_args_array <<< "$convert_args"
+
+       # Execute convert command.
+       bt_cli "${tmp_stdout}" "${tmp_stderr}" convert --run-args "${convert_args_array[@]}"
+       isnt "$?" 0 "failure exit status"
+
+       # Nothing should be printed on stdout.
+       bt_diff /dev/null "${tmp_stdout}"
+       ok $? "$what: nothing is printed on stdout"
+
+       # Check for expected error string in stderr.
+       bt_grep --quiet --fixed-strings -e "$expected_error_str" "$tmp_stderr"
+       local status=$?
+       ok "$status" "$what: expected error message"
+       if [ "$status" -ne 0 ]; then
+               diag "Expected error string '${expected_error_str}' not found in stderr:"
+               diag "$(cat "${tmp_stderr}")"
+       fi
+}
+
+path_to_trace="${BT_CTF_TRACES_PATH}/succeed/succeed1"
+path_to_trace2="${BT_CTF_TRACES_PATH}/succeed/succeed2"
+output_path="/output/path"
+
+if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
+       # Use Windows native paths for comparison because Unix
+       # paths are converted by the shell before they are passed
+       # to the native babeltrace2 binary.
+       path_to_trace=$(cygpath -m "$path_to_trace")
+       path_to_trace2=$(cygpath -m "$path_to_trace2")
+       output_path=$(cygpath -m "$output_path")
+fi
+
+plan_tests 161
+
+test_bt_convert_run_args 'path non-option arg' "$path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option args' "$path_to_trace $path_to_trace2" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\", \"${path_to_trace2}\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + named user source with --params' "$path_to_trace --component ZZ:source.another.source --params salut=yes" "--component ZZ:source.another.source --params salut=yes --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect ZZ:muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'unnamed user source' '--component source.salut.com' "--component source.salut.com:source.salut.com --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect 'source\.salut\.com:muxer' --connect muxer:pretty"
+test_bt_convert_run_args "path non-option arg + user source named \`auto-disc-source-ctf-fs\`" "--component auto-disc-source-ctf-fs:source.salut.com $path_to_trace" "--component auto-disc-source-ctf-fs:source.salut.com --component auto-disc-source-ctf-fs-0:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect auto-disc-source-ctf-fs-0:muxer --connect muxer:pretty"
+test_bt_convert_run_args "path non-option arg + user sink named \`pretty\`" "--component pretty:sink.my.sink $path_to_trace" "--component pretty:sink.my.sink --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args "path non-option arg + user filter named \`muxer\`" "--component muxer:filter.salut.com $path_to_trace" "--component muxer:filter.salut.com --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer-0:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer-0 --connect muxer-0:muxer --connect muxer:pretty"
+test_bt_convert_run_args "path non-option arg + --begin + user filter named \`trimmer\`" "$path_to_trace --component trimmer:filter.salut.com --begin=abc" "--component trimmer:filter.salut.com --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component trimmer-0:filter.utils.trimmer --params 'begin=\"abc\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:trimmer-0 --connect trimmer-0:trimmer --connect trimmer:pretty"
+test_bt_convert_run_args 'path non-option arg + --begin' "$path_to_trace --begin=123" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component trimmer:filter.utils.trimmer --params 'begin=\"123\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:trimmer --connect trimmer:pretty"
+test_bt_convert_run_args 'path non-option arg + --begin --end' "$path_to_trace --end=456 --begin 123" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component trimmer:filter.utils.trimmer --params 'end=\"456\"' --params 'begin=\"123\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:trimmer --connect trimmer:pretty"
+test_bt_convert_run_args 'path non-option arg + --timerange' "$path_to_trace --timerange=[abc,xyz]" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component trimmer:filter.utils.trimmer --params 'begin=\"abc\"' --params 'end=\"xyz\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:trimmer --connect trimmer:pretty"
+test_bt_convert_run_args 'path non-option arg + --clock-cycles' "$path_to_trace --clock-cycles" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params clock-cycles=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --clock-date' "$path_to_trace --clock-date" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params clock-date=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --clock-force-correlate' "$path_to_trace --clock-force-correlate" "--component auto-disc-source-ctf-fs:source.ctf.fs --params force-clock-class-origin-unix-epoch=yes --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --clock-gmt' "$path_to_trace --clock-gmt" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params clock-gmt=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --clock-offset' "$path_to_trace --clock-offset=15487" "--component auto-disc-source-ctf-fs:source.ctf.fs --params clock-class-offset-s=15487 --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --clock-offset-ns' "$path_to_trace --clock-offset-ns=326159487" "--component auto-disc-source-ctf-fs:source.ctf.fs --params clock-class-offset-ns=326159487 --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --clock-seconds' "$path_to_trace --clock-seconds" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params clock-seconds=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --color' "$path_to_trace --color=never" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params 'color=\"never\"' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --debug-info' "$path_to_trace --debug-info" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component debug-info:filter.lttng-utils.debug-info --connect auto-disc-source-ctf-fs:muxer --connect muxer:debug-info --connect debug-info:pretty"
+test_bt_convert_run_args 'path non-option arg + --debug-info-dir' "$path_to_trace --debug-info-dir=${output_path}" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component debug-info:filter.lttng-utils.debug-info --params 'debug-info-dir=\"${output_path}\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:debug-info --connect debug-info:pretty"
+test_bt_convert_run_args 'path non-option arg + --debug-info-target-prefix' "$path_to_trace --debug-info-target-prefix=${output_path}" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component debug-info:filter.lttng-utils.debug-info --params 'target-prefix=\"${output_path}\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:debug-info --connect debug-info:pretty"
+test_bt_convert_run_args 'path non-option arg + --debug-info-full-path' "$path_to_trace --debug-info-full-path" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component debug-info:filter.lttng-utils.debug-info --params full-path=yes --connect auto-disc-source-ctf-fs:muxer --connect muxer:debug-info --connect debug-info:pretty"
+test_bt_convert_run_args 'path non-option arg + --fields=trace:domain,loglevel' "--fields=trace:domain,loglevel $path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params field-trace:domain=yes,field-loglevel=yes,field-default=hide --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --fields=all' "--fields=all $path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params field-default=show --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --names=context,header' "--names=context,header $path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params name-context=yes,name-header=yes,name-default=hide --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --names=all' "--names=all $path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params name-default=show --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --no-delta' "$path_to_trace --no-delta" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params no-delta=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + --output' "$path_to_trace --output $output_path" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params 'path=\"$output_path\"' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + -i ctf' "$path_to_trace -i ctf" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'URL non-option arg + -i lttng-live' 'net://some-host/host/target/session -i lttng-live' "--component lttng-live:source.ctf.lttng-live --params 'inputs=[\"net://some-host/host/target/session\"]' --params 'session-not-found-action=\"end\"' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect lttng-live:muxer --connect muxer:pretty"
+test_bt_convert_run_args 'path non-option arg + -o dummy' "$path_to_trace -o dummy" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component dummy:sink.utils.dummy --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:dummy"
+test_bt_convert_run_args 'path non-option arg + -o ctf + --output' "$path_to_trace -o ctf --output $output_path" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component sink-ctf-fs:sink.ctf.fs --params 'path=\"$output_path\"' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:sink-ctf-fs"
+test_bt_convert_run_args 'path non-option arg + user sink with log level' "$path_to_trace -c sink.mein.sink -lW" "--component sink.mein.sink:sink.mein.sink --log-level W --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect 'muxer:sink\.mein\.sink'"
+
+test_bt_convert_fails \
+       'bad --component format (plugin only)' \
+       '--component salut' \
+       "Invalid format for --component option's argument:"
+
+test_bt_convert_fails \
+       'bad --component format (name and plugin only)' \
+       '--component name:salut' \
+       "Missing component class type (\`source\`, \`filter\`, or \`sink\`)."
+
+test_bt_convert_fails \
+       'bad --component format (name only)' \
+       '--component name:' \
+       "Missing component class type (\`source\`, \`filter\`, or \`sink\`)."
+
+test_bt_convert_fails \
+       'bad --component format (extra dot found)' \
+       '--component name:source.plugin.comp.cls' \
+       "Invalid format for --component option's argument:"
+
+test_bt_convert_fails \
+       'duplicate component name' \
+       '--component hello:sink.a.b --component hello:source.c.d' \
+       'Duplicate component instance name:'
+
+test_bt_convert_fails \
+       'unknown option' \
+       '--component hello:sink.a.b --salut' \
+       "Unknown option \`--salut\`"
+
+# The error string spans two lines in this error message, it's not convenient to
+# check for multiple lines, so we just check the first line.
+test_bt_convert_fails \
+       '--params without current component' \
+       '--params lol=23' \
+       "No current component (--component option) or non-option argument of which to"
+
+test_bt_convert_fails \
+       'duplicate --begin' \
+       '--begin abc --clock-seconds --begin cde' \
+       'At --begin option: --begin or --timerange option already specified'
+
+test_bt_convert_fails \
+       'duplicate --end' \
+       '--begin abc --end xyz --clock-seconds --end cde' \
+       'At --end option: --end or --timerange option already specified'
+
+test_bt_convert_fails \
+       '--begin and --timerange' \
+       '--begin abc --clock-seconds --timerange abc,def' \
+       'At --timerange option: --begin, --end, or --timerange option already specified'
+
+test_bt_convert_fails \
+       '--end and --timerange' \
+       '--end abc --clock-seconds --timerange abc,def' \
+       'At --timerange option: --begin, --end, or --timerange option already specified'
+
+test_bt_convert_fails \
+       'bad --timerange format (1)' \
+       '--timerange abc' \
+       "Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:"
+
+test_bt_convert_fails \
+       'bad --timerange format (2)' \
+       '--timerange abc,' \
+       "Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:"
+
+test_bt_convert_fails \
+       'bad --timerange format (3)' \
+       '--timerange ,cde' \
+       "Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:"
+
+test_bt_convert_fails \
+       'bad --fields format' \
+       '--fields salut' \
+       "Unknown field: \`salut\`."
+
+test_bt_convert_fails \
+       'bad --names format' \
+       '--names salut' \
+       "Unknown name: \`salut\`."
+
+test_bt_convert_fails \
+       'unknown -i' \
+       '-i lol' \
+       'Unknown legacy input format:'
+
+test_bt_convert_fails \
+       'duplicate -i' \
+       '-i lttng-live --clock-seconds --input-format=ctf' \
+       'Duplicate --input-format option.'
+
+test_bt_convert_fails \
+       'unknown -o' \
+       '-o lol' \
+       'Unknown legacy output format:'
+
+test_bt_convert_fails \
+       'duplicate -o' \
+       '-o dummy --clock-seconds --output-format=text' \
+       'Duplicate --output-format option.'
+
+test_bt_convert_fails \
+       '--run-args and --run-args-0' \
+       "$path_to_trace --run-args --run-args-0" \
+       'Cannot specify --run-args and --run-args-0.'
+
+test_bt_convert_fails \
+       '-o ctf-metadata without path' \
+       '-o ctf-metadata' \
+       '--output-format=ctf-metadata specified without a path.'
+
+test_bt_convert_fails \
+       '-i lttng-live and implicit source.ctf.fs' \
+       '-i lttng-live net://some-host/host/target/session --clock-offset=23' \
+       '--clock-offset specified, but no source.ctf.fs component instantiated.'
+
+test_bt_convert_fails \
+       'implicit source.ctf.fs without path' \
+       '--clock-offset=23' \
+       '--clock-offset specified, but no source.ctf.fs component instantiated.'
+
+test_bt_convert_fails \
+       'implicit source.ctf.lttng-live without URL' \
+       '-i lttng-live' \
+       "Missing URL for implicit \`source.ctf.lttng-live\` component."
+
+test_bt_convert_fails \
+       'no source' \
+       '-o text' \
+       'No source component.'
+
+test_bt_convert_fails \
+       '-o ctf without --output' \
+       'my-trace -o ctf' \
+       '--output-format=ctf specified without --output (trace output path).'
+
+# The error string spans two lines in this error message, it's not convenient to
+# check for multiple lines, so we just check the first line.
+test_bt_convert_fails \
+       '-o ctf + --output with implicit sink.text.pretty' \
+       "my-trace -o ctf --output $output_path --no-delta" \
+       'Ambiguous --output option: --output-format=ctf specified but another option'
+
+test_bt_convert_fails \
+       '--stream-intersection' \
+       "$path_to_trace --stream-intersection" \
+       'Cannot specify --stream-intersection with --run-args or --run-args-0.'
+
+test_bt_convert_fails \
+       'two sinks with -o dummy + --clock-seconds' \
+       "$path_to_trace -o dummy --clock-seconds" \
+       'More than one sink component specified.'
+
+test_bt_convert_fails \
+       'path non-option arg + user sink + -o text' \
+       "$path_to_trace --component=sink.abc.def -o text" \
+       'More than one sink component specified.'
+
+rm -f "${tmp_stdout}"
+rm -f "${tmp_stderr}"
diff --git a/tests/cli/convert/test_auto_source_discovery_grouping b/tests/cli/convert/test_auto_source_discovery_grouping
deleted file mode 100755 (executable)
index e476c96..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-# Test the auto source disovery mechanism of the CLI.
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-SH_TAP=1 source "$UTILSSH"
-
-NUM_TESTS=3
-
-plan_tests $NUM_TESTS
-
-data_dir="${BT_TESTS_DATADIR}/auto-source-discovery/grouping"
-plugin_dir="${data_dir}"
-trace_dir="${data_dir}/traces"
-
-stdout_expected_file="${BT_TESTS_DATADIR}/cli/convert/auto-source-discovery-grouping.expect"
-stdout_actual_file=$(mktemp -t stdout-actual.XXXXXX)
-stderr_actual_file=$(mktemp -t actual-stderr.XXXXXX)
-
-bt_cli "$stdout_actual_file" "$stderr_actual_file" \
-       --plugin-path "${plugin_dir}" convert "ABCDE" "${trace_dir}" some_other_non_opt \
-       -c sink.text.details --params='with-metadata=false'
-ok "$?" "CLI runs successfully"
-
-# Check components and their inputs.
-bt_diff "$stdout_expected_file" "$stdout_actual_file"
-ok "$?" "expected components are instantiated with expected inputs"
-
-# Check that expected warning is printed.
-# shellcheck disable=SC2016
-grep -q 'No trace was found based on input `some_other_non_opt`' "$stderr_actual_file"
-ok "$?" "warning is printed"
-
-rm -f "$stdout_actual_file"
-rm -f "$stderr_actual_file"
diff --git a/tests/cli/convert/test_auto_source_discovery_log_level b/tests/cli/convert/test_auto_source_discovery_log_level
deleted file mode 100755 (executable)
index 56df66f..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-# Test how log level options are applied to sources auto-discovered by the
-# convert command.
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-SH_TAP=1 source "$UTILSSH"
-
-NUM_TESTS=4
-
-plan_tests $NUM_TESTS
-
-data_dir="${BT_TESTS_DATADIR}/auto-source-discovery/params-log-level"
-plugin_dir="${data_dir}"
-dir_a="${data_dir}/dir-a"
-dir_b="${data_dir}/dir-b"
-dir_ab="${data_dir}/dir-ab"
-
-expected_file=$(mktemp -t expected.XXXXXX)
-
-print_log_level=(--params 'what="log-level"')
-details_sink=("-c" "sink.text.details" "--params=with-metadata=false")
-
-debug=2
-trace=1
-
-# Apply log level to two components from one non-option argument.
-cat > "$expected_file" <<END
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceA: ${debug}
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceB: ${debug}
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-END
-
-bt_diff_cli "$expected_file" "/dev/null" \
-       --plugin-path "${plugin_dir}" convert \
-       "${dir_ab}" --log-level DEBUG "${print_log_level[@]}" \
-       "${details_sink[@]}"
-ok "$?" "apply log level to two components from one non-option argument"
-
-# Apply log level to two components from two distinct non-option arguments.
-cat > "$expected_file" <<END
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceA: ${debug}
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceB: ${trace}
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-END
-
-bt_diff_cli "$expected_file" "/dev/null" \
-       --plugin-path "${plugin_dir}" convert \
-       "${dir_a}" --log-level DEBUG "${print_log_level[@]}" "${dir_b}" --log-level TRACE "${print_log_level[@]}" \
-       "${details_sink[@]}"
-ok "$?" "apply log level to two non-option arguments"
-
-# Apply log level to one component coming from one non-option argument and one component coming from two non-option arguments (1).
-cat > "$expected_file" <<END
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceA: ${trace}
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceB: ${trace}
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-END
-
-bt_diff_cli "$expected_file" "/dev/null" \
-       --plugin-path "${plugin_dir}" convert \
-       "${dir_a}" --log-level DEBUG "${print_log_level[@]}" "${dir_ab}" --log-level TRACE "${print_log_level[@]}" \
-       "${details_sink[@]}"
-ok "$?" "apply log level to one component coming from one non-option argument and one component coming from two non-option arguments (1)"
-
-# Apply log level to one component coming from one non-option argument and one component coming from two non-option arguments (2).
-cat > "$expected_file" <<END
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceA: ${trace}
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceB: ${debug}
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-END
-
-bt_diff_cli "$expected_file" "/dev/null" \
-       --plugin-path "${plugin_dir}" convert \
-       "${dir_ab}" --log-level DEBUG "${print_log_level[@]}" "${dir_a}" --log-level TRACE "${print_log_level[@]}" \
-       "${details_sink[@]}"
-ok "$?" "apply log level to one component coming from one non-option argument and one component coming from two non-option arguments (2)"
-
-rm -f "$expected_file"
diff --git a/tests/cli/convert/test_auto_source_discovery_params b/tests/cli/convert/test_auto_source_discovery_params
deleted file mode 100755 (executable)
index 65ef536..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-# Test how parameters are applied to sources auto-discovered by the convert
-# command.
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-SH_TAP=1 source "$UTILSSH"
-
-NUM_TESTS=4
-
-plan_tests $NUM_TESTS
-
-data_dir="${BT_TESTS_DATADIR}/auto-source-discovery/params-log-level"
-plugin_dir="${data_dir}"
-dir_a="${data_dir}/dir-a"
-dir_b="${data_dir}/dir-b"
-dir_ab="${data_dir}/dir-ab"
-
-expected_file=$(mktemp -t expected.XXXXXX)
-
-print_test_params=("--params" 'what="test-params"')
-details_sink=("-c" "sink.text.details" "--params=with-metadata=false")
-
-# Apply params to two components from one non-option argument.
-cat > "$expected_file" <<END
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceA: ('test-allo', 'madame')
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceB: ('test-allo', 'madame')
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-END
-
-bt_diff_cli "$expected_file" "/dev/null" \
-       --plugin-path "${plugin_dir}" convert \
-       "${dir_ab}" --params 'test-allo="madame"' "${print_test_params[@]}" \
-       "${details_sink[@]}"
-ok "$?" "apply params to two components from one non-option argument"
-
-# Apply params to two components from two distinct non-option arguments.
-cat > "$expected_file" <<END
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceA: ('test-allo', 'madame')
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceB: ('test-bonjour', 'monsieur')
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-END
-
-bt_diff_cli "$expected_file" "/dev/null" \
-       --plugin-path "${plugin_dir}" convert \
-       "${dir_a}" --params 'test-allo="madame"' "${print_test_params[@]}" "${dir_b}" --params 'test-bonjour="monsieur"' "${print_test_params[@]}" \
-       "${details_sink[@]}"
-ok "$?" "apply params to two non-option arguments"
-
-# Apply params to one component coming from one non-option argument and one component coming from two non-option arguments (1).
-cat > "$expected_file" <<END
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceA: ('test-allo', 'madame'), ('test-bonjour', 'monsieur')
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceB: ('test-bonjour', 'monsieur')
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-END
-
-bt_diff_cli "$expected_file" "/dev/null" \
-       --plugin-path "${plugin_dir}" convert \
-       "${dir_a}" --params 'test-allo="madame"' "${print_test_params[@]}" "${dir_ab}" --params 'test-bonjour="monsieur"' "${print_test_params[@]}" \
-       "${details_sink[@]}"
-ok "$?" "apply params to one component coming from one non-option argument and one component coming from two non-option arguments (1)"
-
-# Apply params to one component coming from one non-option argument and one component coming from two non-option arguments (2).
-cat > "$expected_file" <<END
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceA: ('test-bonjour', 'monsieur'), ('test-salut', 'les amis')
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: TestSourceB: ('test-bonjour', 'madame'), ('test-salut', 'les amis')
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-END
-
-bt_diff_cli "$expected_file" "/dev/null" \
-       --plugin-path "${plugin_dir}" convert \
-       "${dir_ab}" --params 'test-bonjour="madame",test-salut="les amis"' "${print_test_params[@]}" "${dir_a}" --params 'test-bonjour="monsieur"' "${print_test_params[@]}" \
-       "${details_sink[@]}"
-ok "$?" "apply params to one component coming from one non-option argument and one component coming from two non-option arguments (2)"
-
-rm -f "$expected_file"
diff --git a/tests/cli/convert/test_convert_args b/tests/cli/convert/test_convert_args
deleted file mode 100755 (executable)
index cd7aab2..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-tmp_stdout=$(mktemp -t test_convert_args_stdout.XXXXXX)
-tmp_stderr=$(mktemp -t test_convert_args_stderr.XXXXXX)
-
-test_bt_convert_run_args() {
-       local what="$1"
-       local convert_args="$2"
-       local expected_run_args="$3"
-
-       local run_args
-
-       # Split argument string into array.
-       IFS=' ' read -ra convert_args_array <<< "$convert_args"
-
-       # Execute convert command.
-       bt_cli "${tmp_stdout}" "${tmp_stderr}" convert --run-args "${convert_args_array[@]}"
-       ok $? "${what}: success exit status"
-
-       run_args=$(cat "${tmp_stdout}")
-
-       # Verify output run args.
-       [ "$run_args" = "$expected_run_args" ]
-       ok $? "${what}: run arguments"
-}
-
-test_bt_convert_fails() {
-       local what="$1"
-       local convert_args="$2"
-       local expected_error_str="$3"
-
-       # Split argument string into array.
-       IFS=' ' read -ra convert_args_array <<< "$convert_args"
-
-       # Execute convert command.
-       bt_cli "${tmp_stdout}" "${tmp_stderr}" convert --run-args "${convert_args_array[@]}"
-       isnt "$?" 0 "failure exit status"
-
-       # Nothing should be printed on stdout.
-       bt_diff /dev/null "${tmp_stdout}"
-       ok $? "$what: nothing is printed on stdout"
-
-       # Check for expected error string in stderr.
-       grep --quiet --fixed-strings -e "$expected_error_str" "$tmp_stderr"
-       local status=$?
-       ok "$status" "$what: expected error message"
-       if [ "$status" -ne 0 ]; then
-               diag "Expected error string '${expected_error_str}' not found in stderr:"
-               diag "$(cat "${tmp_stderr}")"
-       fi
-}
-
-path_to_trace="${BT_CTF_TRACES_PATH}/succeed/succeed1"
-path_to_trace2="${BT_CTF_TRACES_PATH}/succeed/succeed2"
-output_path="/output/path"
-
-if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
-       # Use Windows native paths for comparison because Unix
-       # paths are converted by the shell before they are passed
-       # to the native babeltrace2 binary.
-       path_to_trace=$(cygpath -m "$path_to_trace")
-       path_to_trace2=$(cygpath -m "$path_to_trace2")
-       output_path=$(cygpath -m "$output_path")
-fi
-
-plan_tests 161
-
-test_bt_convert_run_args 'path non-option arg' "$path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option args' "$path_to_trace $path_to_trace2" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\", \"${path_to_trace2}\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + named user source with --params' "$path_to_trace --component ZZ:source.another.source --params salut=yes" "--component ZZ:source.another.source --params salut=yes --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect ZZ:muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'unnamed user source' '--component source.salut.com' "--component source.salut.com:source.salut.com --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect 'source\.salut\.com:muxer' --connect muxer:pretty"
-test_bt_convert_run_args "path non-option arg + user source named \`auto-disc-source-ctf-fs\`" "--component auto-disc-source-ctf-fs:source.salut.com $path_to_trace" "--component auto-disc-source-ctf-fs:source.salut.com --component auto-disc-source-ctf-fs-0:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect auto-disc-source-ctf-fs-0:muxer --connect muxer:pretty"
-test_bt_convert_run_args "path non-option arg + user sink named \`pretty\`" "--component pretty:sink.my.sink $path_to_trace" "--component pretty:sink.my.sink --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args "path non-option arg + user filter named \`muxer\`" "--component muxer:filter.salut.com $path_to_trace" "--component muxer:filter.salut.com --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer-0:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer-0 --connect muxer-0:muxer --connect muxer:pretty"
-test_bt_convert_run_args "path non-option arg + --begin + user filter named \`trimmer\`" "$path_to_trace --component trimmer:filter.salut.com --begin=abc" "--component trimmer:filter.salut.com --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component trimmer-0:filter.utils.trimmer --params 'begin=\"abc\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:trimmer-0 --connect trimmer-0:trimmer --connect trimmer:pretty"
-test_bt_convert_run_args 'path non-option arg + --begin' "$path_to_trace --begin=123" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component trimmer:filter.utils.trimmer --params 'begin=\"123\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:trimmer --connect trimmer:pretty"
-test_bt_convert_run_args 'path non-option arg + --begin --end' "$path_to_trace --end=456 --begin 123" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component trimmer:filter.utils.trimmer --params 'end=\"456\"' --params 'begin=\"123\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:trimmer --connect trimmer:pretty"
-test_bt_convert_run_args 'path non-option arg + --timerange' "$path_to_trace --timerange=[abc,xyz]" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component trimmer:filter.utils.trimmer --params 'begin=\"abc\"' --params 'end=\"xyz\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:trimmer --connect trimmer:pretty"
-test_bt_convert_run_args 'path non-option arg + --clock-cycles' "$path_to_trace --clock-cycles" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params clock-cycles=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --clock-date' "$path_to_trace --clock-date" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params clock-date=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --clock-force-correlate' "$path_to_trace --clock-force-correlate" "--component auto-disc-source-ctf-fs:source.ctf.fs --params force-clock-class-origin-unix-epoch=yes --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --clock-gmt' "$path_to_trace --clock-gmt" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params clock-gmt=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --clock-offset' "$path_to_trace --clock-offset=15487" "--component auto-disc-source-ctf-fs:source.ctf.fs --params clock-class-offset-s=15487 --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --clock-offset-ns' "$path_to_trace --clock-offset-ns=326159487" "--component auto-disc-source-ctf-fs:source.ctf.fs --params clock-class-offset-ns=326159487 --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --clock-seconds' "$path_to_trace --clock-seconds" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params clock-seconds=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --color' "$path_to_trace --color=never" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params 'color=\"never\"' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --debug-info' "$path_to_trace --debug-info" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component debug-info:filter.lttng-utils.debug-info --connect auto-disc-source-ctf-fs:muxer --connect muxer:debug-info --connect debug-info:pretty"
-test_bt_convert_run_args 'path non-option arg + --debug-info-dir' "$path_to_trace --debug-info-dir=${output_path}" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component debug-info:filter.lttng-utils.debug-info --params 'debug-info-dir=\"${output_path}\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:debug-info --connect debug-info:pretty"
-test_bt_convert_run_args 'path non-option arg + --debug-info-target-prefix' "$path_to_trace --debug-info-target-prefix=${output_path}" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component debug-info:filter.lttng-utils.debug-info --params 'target-prefix=\"${output_path}\"' --connect auto-disc-source-ctf-fs:muxer --connect muxer:debug-info --connect debug-info:pretty"
-test_bt_convert_run_args 'path non-option arg + --debug-info-full-path' "$path_to_trace --debug-info-full-path" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --component debug-info:filter.lttng-utils.debug-info --params full-path=yes --connect auto-disc-source-ctf-fs:muxer --connect muxer:debug-info --connect debug-info:pretty"
-test_bt_convert_run_args 'path non-option arg + --fields=trace:domain,loglevel' "--fields=trace:domain,loglevel $path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params field-trace:domain=yes,field-loglevel=yes,field-default=hide --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --fields=all' "--fields=all $path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params field-default=show --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --names=context,header' "--names=context,header $path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params name-context=yes,name-header=yes,name-default=hide --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --names=all' "--names=all $path_to_trace" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params name-default=show --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --no-delta' "$path_to_trace --no-delta" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params no-delta=yes --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + --output' "$path_to_trace --output $output_path" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --params 'path=\"$output_path\"' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + -i ctf' "$path_to_trace -i ctf" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'URL non-option arg + -i lttng-live' 'net://some-host/host/target/session -i lttng-live' "--component lttng-live:source.ctf.lttng-live --params 'inputs=[\"net://some-host/host/target/session\"]' --params 'session-not-found-action=\"end\"' --component pretty:sink.text.pretty --component muxer:filter.utils.muxer --connect lttng-live:muxer --connect muxer:pretty"
-test_bt_convert_run_args 'path non-option arg + -o dummy' "$path_to_trace -o dummy" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component dummy:sink.utils.dummy --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:dummy"
-test_bt_convert_run_args 'path non-option arg + -o ctf + --output' "$path_to_trace -o ctf --output $output_path" "--component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component sink-ctf-fs:sink.ctf.fs --params 'path=\"$output_path\"' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect muxer:sink-ctf-fs"
-test_bt_convert_run_args 'path non-option arg + user sink with log level' "$path_to_trace -c sink.mein.sink -lW" "--component sink.mein.sink:sink.mein.sink --log-level W --component auto-disc-source-ctf-fs:source.ctf.fs --params 'inputs=[\"$path_to_trace\"]' --component muxer:filter.utils.muxer --connect auto-disc-source-ctf-fs:muxer --connect 'muxer:sink\.mein\.sink'"
-
-test_bt_convert_fails \
-       'bad --component format (plugin only)' \
-       '--component salut' \
-       "Invalid format for --component option's argument:"
-
-test_bt_convert_fails \
-       'bad --component format (name and plugin only)' \
-       '--component name:salut' \
-       "Missing component class type (\`source\`, \`filter\`, or \`sink\`)."
-
-test_bt_convert_fails \
-       'bad --component format (name only)' \
-       '--component name:' \
-       "Missing component class type (\`source\`, \`filter\`, or \`sink\`)."
-
-test_bt_convert_fails \
-       'bad --component format (extra dot found)' \
-       '--component name:source.plugin.comp.cls' \
-       "Invalid format for --component option's argument:"
-
-test_bt_convert_fails \
-       'duplicate component name' \
-       '--component hello:sink.a.b --component hello:source.c.d' \
-       'Duplicate component instance name:'
-
-test_bt_convert_fails \
-       'unknown option' \
-       '--component hello:sink.a.b --salut' \
-       "Unknown option \`--salut\`"
-
-# The error string spans two lines in this error message, it's not convenient to
-# check for multiple lines, so we just check the first line.
-test_bt_convert_fails \
-       '--params without current component' \
-       '--params lol=23' \
-       "No current component (--component option) or non-option argument of which to"
-
-test_bt_convert_fails \
-       'duplicate --begin' \
-       '--begin abc --clock-seconds --begin cde' \
-       'At --begin option: --begin or --timerange option already specified'
-
-test_bt_convert_fails \
-       'duplicate --end' \
-       '--begin abc --end xyz --clock-seconds --end cde' \
-       'At --end option: --end or --timerange option already specified'
-
-test_bt_convert_fails \
-       '--begin and --timerange' \
-       '--begin abc --clock-seconds --timerange abc,def' \
-       'At --timerange option: --begin, --end, or --timerange option already specified'
-
-test_bt_convert_fails \
-       '--end and --timerange' \
-       '--end abc --clock-seconds --timerange abc,def' \
-       'At --timerange option: --begin, --end, or --timerange option already specified'
-
-test_bt_convert_fails \
-       'bad --timerange format (1)' \
-       '--timerange abc' \
-       "Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:"
-
-test_bt_convert_fails \
-       'bad --timerange format (2)' \
-       '--timerange abc,' \
-       "Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:"
-
-test_bt_convert_fails \
-       'bad --timerange format (3)' \
-       '--timerange ,cde' \
-       "Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:"
-
-test_bt_convert_fails \
-       'bad --fields format' \
-       '--fields salut' \
-       "Unknown field: \`salut\`."
-
-test_bt_convert_fails \
-       'bad --names format' \
-       '--names salut' \
-       "Unknown name: \`salut\`."
-
-test_bt_convert_fails \
-       'unknown -i' \
-       '-i lol' \
-       'Unknown legacy input format:'
-
-test_bt_convert_fails \
-       'duplicate -i' \
-       '-i lttng-live --clock-seconds --input-format=ctf' \
-       'Duplicate --input-format option.'
-
-test_bt_convert_fails \
-       'unknown -o' \
-       '-o lol' \
-       'Unknown legacy output format:'
-
-test_bt_convert_fails \
-       'duplicate -o' \
-       '-o dummy --clock-seconds --output-format=text' \
-       'Duplicate --output-format option.'
-
-test_bt_convert_fails \
-       '--run-args and --run-args-0' \
-       "$path_to_trace --run-args --run-args-0" \
-       'Cannot specify --run-args and --run-args-0.'
-
-test_bt_convert_fails \
-       '-o ctf-metadata without path' \
-       '-o ctf-metadata' \
-       '--output-format=ctf-metadata specified without a path.'
-
-test_bt_convert_fails \
-       '-i lttng-live and implicit source.ctf.fs' \
-       '-i lttng-live net://some-host/host/target/session --clock-offset=23' \
-       '--clock-offset specified, but no source.ctf.fs component instantiated.'
-
-test_bt_convert_fails \
-       'implicit source.ctf.fs without path' \
-       '--clock-offset=23' \
-       '--clock-offset specified, but no source.ctf.fs component instantiated.'
-
-test_bt_convert_fails \
-       'implicit source.ctf.lttng-live without URL' \
-       '-i lttng-live' \
-       "Missing URL for implicit \`source.ctf.lttng-live\` component."
-
-test_bt_convert_fails \
-       'no source' \
-       '-o text' \
-       'No source component.'
-
-test_bt_convert_fails \
-       '-o ctf without --output' \
-       'my-trace -o ctf' \
-       '--output-format=ctf specified without --output (trace output path).'
-
-# The error string spans two lines in this error message, it's not convenient to
-# check for multiple lines, so we just check the first line.
-test_bt_convert_fails \
-       '-o ctf + --output with implicit sink.text.pretty' \
-       "my-trace -o ctf --output $output_path --no-delta" \
-       'Ambiguous --output option: --output-format=ctf specified but another option'
-
-test_bt_convert_fails \
-       '--stream-intersection' \
-       "$path_to_trace --stream-intersection" \
-       'Cannot specify --stream-intersection with --run-args or --run-args-0.'
-
-test_bt_convert_fails \
-       'two sinks with -o dummy + --clock-seconds' \
-       "$path_to_trace -o dummy --clock-seconds" \
-       'More than one sink component specified.'
-
-test_bt_convert_fails \
-       'path non-option arg + user sink + -o text' \
-       "$path_to_trace --component=sink.abc.def -o text" \
-       'More than one sink component specified.'
-
-rm -f "${tmp_stdout}"
-rm -f "${tmp_stderr}"
diff --git a/tests/cli/list-plugins/test-list-plugins.sh b/tests/cli/list-plugins/test-list-plugins.sh
new file mode 100755 (executable)
index 0000000..c6e7166
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 EfficiOS Inc.
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+SH_TAP=1 source "$UTILSSH"
+
+plan_tests 3
+
+data_dir="${BT_TESTS_DATADIR}/cli/list-plugins"
+plugin_dir="${data_dir}"
+
+stdout_file=$(mktemp -t test-cli-list-plugins-stdout.XXXXXX)
+stderr_file=$(mktemp -t test-cli-list-plugins-stderr.XXXXXX)
+grep_stdout_file=$(mktemp -t test-cli-list-plugins-grep-stdout.XXXXXX)
+py_plugin_expected_stdout_file=$(mktemp -t test-cli-list-plugins-expected-py-plugin-stdout.XXXXXX)
+
+# Run list-plugins.
+bt_cli "$stdout_file" "$stderr_file" \
+       --plugin-path "$plugin_dir" \
+       list-plugins
+ok "$?" "exit code is 0"
+
+# Extract the section about our custom this-is-a-plugin Python plugin.
+bt_grep --after-context=11 '^this-is-a-plugin:$' "${stdout_file}" > "${grep_stdout_file}"
+ok "$?" "entry for this-is-a-plugin is present"
+
+if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
+       platform_plugin_dir=$(cygpath -m "${plugin_dir}")
+else
+       platform_plugin_dir="${plugin_dir}"
+fi
+
+# Generate the expected output file for that plugin.
+cat <<- EOF > "${py_plugin_expected_stdout_file}"
+       this-is-a-plugin:
+         Path: ${platform_plugin_dir}/bt_plugin_list_plugins.py
+         Version: 1.2.3bob
+         Description: A plugin
+         Author: Jorge Mario Bergoglio
+         License: The license
+         Source component classes:
+           'source.this-is-a-plugin.ThisIsASource'
+         Filter component classes:
+           'filter.this-is-a-plugin.ThisIsAFilter'
+         Sink component classes:
+           'sink.this-is-a-plugin.ThisIsASink'
+EOF
+
+# Compare the entry for this-is-a-plugin with the expected version.
+bt_diff "${py_plugin_expected_stdout_file}" "${grep_stdout_file}"
+ok "$?" "entry for this-is-a-plugin is as expected"
+
+rm -f "${stdout_file}"
+rm -f "${stderr_file}"
+rm -f "${grep_stdout_file}"
+rm -f "${py_plugin_expected_stdout_file}"
diff --git a/tests/cli/list-plugins/test_list_plugins b/tests/cli/list-plugins/test_list_plugins
deleted file mode 100755 (executable)
index 595eb4d..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 EfficiOS Inc.
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-SH_TAP=1 source "$UTILSSH"
-
-plan_tests 3
-
-data_dir="${BT_TESTS_DATADIR}/cli/list-plugins"
-plugin_dir="${data_dir}"
-
-stdout_file=$(mktemp -t test_cli_list_plugins_stdout.XXXXXX)
-stderr_file=$(mktemp -t test_cli_list_plugins_stderr.XXXXXX)
-grep_stdout_file=$(mktemp -t test_cli_list_plugins_grep_stdout.XXXXXX)
-py_plugin_expected_stdout_file=$(mktemp -t test_cli_list_plugins_expected_py_plugin_stdout.XXXXXX)
-
-# Run list-plugins.
-bt_cli "$stdout_file" "$stderr_file" \
-       --plugin-path "$plugin_dir" \
-       list-plugins
-ok "$?" "exit code is 0"
-
-# Extract the section about our custom this-is-a-plugin Python plugin.
-grep --after-context=11 '^this-is-a-plugin:$' "${stdout_file}" > "${grep_stdout_file}"
-ok "$?" "entry for this-is-a-plugin is present"
-
-if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
-       platform_plugin_dir=$(cygpath -m "${plugin_dir}")
-else
-       platform_plugin_dir="${plugin_dir}"
-fi
-
-# Generate the expected output file for that plugin.
-cat <<- EOF > "${py_plugin_expected_stdout_file}"
-       this-is-a-plugin:
-         Path: ${platform_plugin_dir}/bt_plugin_list_plugins.py
-         Version: 1.2.3bob
-         Description: A plugin
-         Author: Jorge Mario Bergoglio
-         License: The license
-         Source component classes:
-           'source.this-is-a-plugin.ThisIsASource'
-         Filter component classes:
-           'filter.this-is-a-plugin.ThisIsAFilter'
-         Sink component classes:
-           'sink.this-is-a-plugin.ThisIsASink'
-EOF
-
-# Compare the entry for this-is-a-plugin with the expected version.
-bt_diff "${py_plugin_expected_stdout_file}" "${grep_stdout_file}"
-ok "$?" "entry for this-is-a-plugin is as expected"
-
-rm -f "${stdout_file}"
-rm -f "${stderr_file}"
-rm -f "${grep_stdout_file}"
-rm -f "${py_plugin_expected_stdout_file}"
diff --git a/tests/cli/params/test-params.sh b/tests/cli/params/test-params.sh
new file mode 100755 (executable)
index 0000000..22ef540
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+
+# Test how parameters are applied to sources auto-discovered by the convert
+# command.
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+SH_TAP=1 source "$UTILSSH"
+
+NUM_TESTS=9
+
+plan_tests $NUM_TESTS
+
+data_dir="${BT_TESTS_DATADIR}/cli/params"
+plugin_dir="${data_dir}"
+
+expected_file=$(mktemp -t expected.XXXXXX)
+
+function expect_success
+{
+       local test_name="$1"
+       local params_str="$2"
+       local expected_str="$3"
+
+       echo "$expected_str" > "$expected_file"
+
+       bt_diff_cli "$expected_file" /dev/null --plugin-path "$plugin_dir" -c "src.text.dmesg" \
+               -c "sink.params.SinkThatPrintsParams" --params "$params_str"
+       ok "$?" "$test_name"
+}
+
+expect_success 'null' 'a=null,b=nul,c=NULL' \
+       '{a=None, b=None, c=None}'
+expect_success 'bool' 'a=true,b=TRUE,c=yes,d=YES,e=false,f=FALSE,g=no,h=NO' \
+       '{a=True, b=True, c=True, d=True, e=False, f=False, g=False, h=False}'
+expect_success 'signed integer' 'a=0b110, b=022, c=22, d=0x22' \
+       '{a=6, b=18, c=22, d=34}'
+expect_success 'unsigned integer' 'a=+0b110, b=+022, c=+22, d=+0x22' \
+       '{a=6u, b=18u, c=22u, d=34u}'
+expect_success 'string' 'a="avril lavigne", b=patata, c="This\"is\\escaped"' \
+       '{a=avril lavigne, b=patata, c=This"is\escaped}'
+expect_success 'float' 'a=1.234, b=17., c=.28, d=-18.28' \
+       '{a=1.2340000, b=17.0000000, c=0.2800000, d=-18.2800000}'
+expect_success 'float scientific notation' 'a=10.5e6, b=10.5E6, c=10.5e-6, d=10.5E-6' \
+       '{a=10500000.0000000, b=10500000.0000000, c=0.0000105, d=0.0000105}'
+expect_success 'array' 'a=[1, [["hi",]]]' \
+       '{a=[1, [[hi]]]}'
+expect_success 'map' 'a=4,a={},b={salut="la gang",comment="ca va",oh={x=2}}' \
+       '{a={}, b={comment=ca va, oh={x=2}, salut=la gang}}'
+
+rm -f "$expected_file"
diff --git a/tests/cli/params/test_params b/tests/cli/params/test_params
deleted file mode 100755 (executable)
index c207532..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-# Test how parameters are applied to sources auto-discovered by the convert
-# command.
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-SH_TAP=1 source "$UTILSSH"
-
-NUM_TESTS=9
-
-plan_tests $NUM_TESTS
-
-data_dir="${BT_TESTS_DATADIR}/cli/params"
-plugin_dir="${data_dir}"
-
-expected_file=$(mktemp -t expected.XXXXXX)
-
-function expect_success
-{
-       local test_name="$1"
-       local params_str="$2"
-       local expected_str="$3"
-
-       echo "$expected_str" > "$expected_file"
-
-       bt_diff_cli "$expected_file" /dev/null --plugin-path "$plugin_dir" -c "src.text.dmesg" \
-               -c "sink.params.SinkThatPrintsParams" --params "$params_str"
-       ok "$?" "$test_name"
-}
-
-expect_success 'null' 'a=null,b=nul,c=NULL' \
-       '{a=None, b=None, c=None}'
-expect_success 'bool' 'a=true,b=TRUE,c=yes,d=YES,e=false,f=FALSE,g=no,h=NO' \
-       '{a=True, b=True, c=True, d=True, e=False, f=False, g=False, h=False}'
-expect_success 'signed integer' 'a=0b110, b=022, c=22, d=0x22' \
-       '{a=6, b=18, c=22, d=34}'
-expect_success 'unsigned integer' 'a=+0b110, b=+022, c=+22, d=+0x22' \
-       '{a=6u, b=18u, c=22u, d=34u}'
-expect_success 'string' 'a="avril lavigne", b=patata, c="This\"is\\escaped"' \
-       '{a=avril lavigne, b=patata, c=This"is\escaped}'
-expect_success 'float' 'a=1.234, b=17., c=.28, d=-18.28' \
-       '{a=1.234, b=17.0, c=0.28, d=-18.28}'
-expect_success 'float scientific notation' 'a=10.5e6, b=10.5E6, c=10.5e-6, d=10.5E-6' \
-       '{a=10500000.0, b=10500000.0, c=1.05e-05, d=1.05e-05}'
-expect_success 'array' 'a=[1, [["hi",]]]' \
-       '{a=[1, [[hi]]]}'
-expect_success 'map' 'a=4,a={},b={salut="la gang",comment="ca va",oh={x=2}}' \
-       '{a={}, b={comment=ca va, oh={x=2}, salut=la gang}}'
-
-rm -f "$expected_file"
diff --git a/tests/cli/query/test-query.sh b/tests/cli/query/test-query.sh
new file mode 100755 (executable)
index 0000000..ca9c001
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+SH_TAP=1 source "$UTILSSH"
+
+NUM_TESTS=15
+
+plan_tests $NUM_TESTS
+
+data_dir="${BT_TESTS_DATADIR}/cli/query"
+plugin_dir="${data_dir}"
+
+stdout_expected_file=$(mktemp -t test-cli-query-stdout-expected.XXXXXX)
+stdout_file=$(mktemp -t test-cli-query-stdout.XXXXXX)
+stderr_file=$(mktemp -t test-cli-query-stderr.XXXXXX)
+
+expect_success() {
+       local expected_str="$1"
+       shift 1
+       local args=("$@")
+
+       echo "$expected_str" > "$stdout_expected_file"
+
+       bt_diff_cli "$stdout_expected_file" /dev/null \
+               --plugin-path "$plugin_dir" \
+               query "src.query.SourceWithQueryThatPrintsParams" \
+               "${args[@]}"
+       ok "$?" "${args[*]}"
+}
+
+expect_failure() {
+       local expected_str="$1"
+       shift 1
+       local args=("$@")
+       local test_name="${args[*]}"
+
+       echo -n > "$stdout_expected_file"
+
+       bt_cli "$stdout_file" "$stderr_file" \
+               --plugin-path "$plugin_dir" \
+               query \
+               "${args[@]}"
+       isnt "$?" 0 "${test_name}: exit code is not 0"
+
+       bt_diff /dev/null "$stdout_file"
+       ok "$?" "${test_name}: nothing output on stout"
+
+       # Ensure that a CLI error stack is printed (and that babeltrace doesn't
+       # abort before that).
+       bt_grep_ok \
+               "^ERROR: " \
+               "${stderr_file}" \
+               "${test_name}: babeltrace produces an error stack"
+
+       bt_grep_ok \
+               "${expected_str}" \
+               "${stderr_file}" \
+               "${test_name}: expect \`${expected_str}\` error message on stderr"
+}
+
+expect_success 'the-object:{}' \
+       'the-object'
+expect_success "the-object:{a=2}" \
+       'the-object' -p 'a=2'
+
+# Check that -p parameters are processed in order.
+expect_success "the-object:{a=3, ben=kin, voyons=donc}" \
+       'the-object' -p 'a=2,ben=kin' -p 'voyons=donc,a=3'
+
+# Failure inside the component class' query method.
+expect_failure "ValueError: catastrophic failure" \
+       'src.query.SourceWithQueryThatPrintsParams' 'please-fail' '-p' 'a=2'
+
+# Non-existent component class.
+expect_failure 'Cannot find component class: plugin-name="query", comp-cls-name="NonExistentSource", comp-cls-type=SOURCE' \
+       'src.query.NonExistentSource' 'the-object' '-p' 'a=2'
+
+# Wrong parameter syntax.
+expect_failure "Invalid format for --params option's argument:" \
+       'src.query.SourceWithQueryThatPrintsParams' 'please-fail' '-p' 'a=3,'
+
+rm -f "$stdout_expected_file"
+rm -f "$stdout_file"
+rm -f "$stderr_file"
diff --git a/tests/cli/query/test_query b/tests/cli/query/test_query
deleted file mode 100755 (executable)
index e44d677..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-SH_TAP=1 source "$UTILSSH"
-
-NUM_TESTS=15
-
-plan_tests $NUM_TESTS
-
-data_dir="${BT_TESTS_DATADIR}/cli/query"
-plugin_dir="${data_dir}"
-
-stdout_expected_file=$(mktemp -t test_cli_query_stdout_xpected.XXXXXX)
-stdout_file=$(mktemp -t test_cli_query_stdout.XXXXXX)
-stderr_file=$(mktemp -t test_cli_query_stderr.XXXXXX)
-
-expect_success() {
-       local expected_str="$1"
-       shift 1
-       local args=("$@")
-
-       echo "$expected_str" > "$stdout_expected_file"
-
-       bt_diff_cli "$stdout_expected_file" /dev/null \
-               --plugin-path "$plugin_dir" \
-               query "src.query.SourceWithQueryThatPrintsParams" \
-               "${args[@]}"
-       ok "$?" "${args[*]}"
-}
-
-expect_failure() {
-       local expected_str="$1"
-       shift 1
-       local args=("$@")
-       local test_name="${args[*]}"
-
-       echo -n > "$stdout_expected_file"
-
-       bt_cli "$stdout_file" "$stderr_file" \
-               --plugin-path "$plugin_dir" \
-               query \
-               "${args[@]}"
-       isnt "$?" 0 "${test_name}: exit code is not 0"
-
-       bt_diff /dev/null "$stdout_file"
-       ok "$?" "${test_name}: nothing output on stout"
-
-       # Ensure that a CLI error stack is printed (and that babeltrace doesn't
-       # abort before that).
-       grep --silent "^ERROR: " "${stderr_file}"
-       ok $? "${test_name}: babeltrace produces an error stack"
-
-       grep --silent "${expected_str}" "${stderr_file}"
-       ok "$?" "${test_name}: expect \`${expected_str}\` error message on stderr"
-}
-
-expect_success 'the-object:{}' \
-       'the-object'
-expect_success "the-object:{a=2}" \
-       'the-object' -p 'a=2'
-
-# Check that -p parameters are processed in order.
-expect_success "the-object:{a=3, ben=kin, voyons=donc}" \
-       'the-object' -p 'a=2,ben=kin' -p 'voyons=donc,a=3'
-
-# Failure inside the component class' query method.
-expect_failure "ValueError: catastrophic failure" \
-       'src.query.SourceWithQueryThatPrintsParams' 'please-fail' '-p' 'a=2'
-
-# Non-existent component class.
-expect_failure 'Cannot find component class: plugin-name="query", comp-cls-name="NonExistentSource", comp-cls-type=SOURCE' \
-       'src.query.NonExistentSource' 'the-object' '-p' 'a=2'
-
-# Wrong parameter syntax.
-expect_failure "Invalid format for --params option's argument:" \
-       'src.query.SourceWithQueryThatPrintsParams' 'please-fail' '-p' 'a=3,'
-
-rm -f "$stdout_expected_file"
-rm -f "$stdout_file"
-rm -f "$stderr_file"
diff --git a/tests/cli/test-exit-status.sh b/tests/cli/test-exit-status.sh
new file mode 100755 (executable)
index 0000000..adfce3c
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 EfficiOS Inc.
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+data_dir="$BT_TESTS_DATADIR/cli/exit-status"
+source_name="src.test-exit-status.StatusSrc"
+
+test_interrupted_graph() {
+       local cli_args=("--plugin-path=$data_dir" "-c" "$source_name" "-p" "case=\"INTERRUPTED\"")
+       local actual_stdout
+       local actual_stderr
+
+       actual_stdout=$(mktemp -t test-cli-exit-status-stdout-actual.XXXXXX)
+       actual_stderr=$(mktemp -t test-cli-exit-status-stderr-actual.XXXXXX)
+
+       bt_cli "$actual_stdout" "$actual_stderr" "${cli_args[@]}"
+
+       is $? 2 "Interrupted graph exits with status 2"
+
+       bt_diff /dev/null "$actual_stdout"
+       ok $? "Interrupted graph gives no stdout"
+
+       bt_diff /dev/null "$actual_stderr"
+       ok $? "Interrupted graph gives no stderr"
+
+       rm -f "${actual_stdout}"
+       rm -f "${actual_stderr}"
+}
+
+test_error_graph() {
+       local cli_args=("--plugin-path=$data_dir" "-c" "$source_name" "-p" "case=\"ERROR\"")
+       local actual_stdout
+       local actual_stderr
+
+       actual_stdout=$(mktemp -t test-cli-exit-status-stdout-actual.XXXXXX)
+       actual_stderr=$(mktemp -t test-cli-exit-status-stderr-actual.XXXXXX)
+
+       bt_cli "$actual_stdout" "$actual_stderr" "${cli_args[@]}"
+
+       is $? 1 "Erroring graph exits with status 1"
+
+       bt_diff /dev/null "$actual_stdout"
+       ok $? "Erroring graph gives expected stdout"
+
+       like "$(cat "${actual_stderr}")" "TypeError: Raising type error" \
+               "Erroring graph gives expected error message"
+
+       rm -f "${actual_stdout}"
+       rm -f "${actual_stderr}"
+}
+
+test_stop_graph() {
+       local cli_args=("--plugin-path=$data_dir" "-c" "$source_name" "-p" "case=\"STOP\"")
+       local actual_stdout
+       local actual_stderr
+
+       actual_stdout=$(mktemp -t test-cli-exit-status-stdout-actual.XXXXXX)
+       actual_stderr=$(mktemp -t test-cli-exit-status-stderr-actual.XXXXXX)
+
+       bt_cli "$actual_stdout" "$actual_stderr" "${cli_args[@]}"
+
+       is $? 0 "Successful graph exits with status 0"
+
+       bt_diff /dev/null "$actual_stdout"
+       ok $? "Successful graph gives no stdout"
+
+       bt_diff /dev/null "$actual_stderr"
+       ok $? "Successful graph gives no stderr"
+
+       rm -f "${actual_stdout}"
+       rm -f "${actual_stderr}"
+}
+
+plan_tests 9
+
+test_interrupted_graph
+test_error_graph
+test_stop_graph
diff --git a/tests/cli/test-help.sh b/tests/cli/test-help.sh
new file mode 100755 (executable)
index 0000000..882f6c5
--- /dev/null
@@ -0,0 +1,117 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 EfficiOS Inc.
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+plan_tests 21
+
+stdout=$(mktemp -t test-help-stdout.XXXXXX)
+stderr=$(mktemp -t test-help-stderr.XXXXXX)
+
+# Return 0 if file "$1" exists and is empty, non-0 otherwise.
+
+is_empty()
+{
+       [[ -f "$1" && ! -s "$1" ]]
+}
+
+# Test with a working plugin name.
+bt_cli "${stdout}" "${stderr}" help ctf
+ok $? "help ctf plugin exit status"
+
+bt_grep_ok \
+       'Description: CTF input and output' \
+       "${stdout}" \
+       "help ctf plugin expected output"
+
+is_empty "${stderr}"
+ok $? "help ctf plugin produces no error"
+
+# Test with a working component class name.
+bt_cli "${stdout}" "${stderr}" help src.ctf.fs
+ok $? "help src.ctf.fs component class exit status"
+
+bt_grep_ok \
+       'Description: Read CTF traces from the file system.' \
+       "${stdout}" \
+       "help src.ctf.fs component class expected output"
+
+is_empty "${stderr}"
+ok $? "help src.ctf.fs component class produces no error"
+
+# Test without parameter.
+bt_cli "${stdout}" "${stderr}" help
+isnt $? 0 "help without parameter exit status"
+
+bt_grep_ok \
+       "Missing plugin name or component class descriptor." \
+       "${stderr}" \
+       "help without parameter produces expected error"
+
+is_empty "${stdout}"
+ok $? "help without parameter produces no output"
+
+# Test with too many parameters.
+bt_cli "${stdout}" "${stderr}" help ctf fs
+isnt $? 0  "help with too many parameters exit status"
+
+bt_grep_ok \
+       "Extraneous command-line argument specified to \`help\` command:" \
+       "${stderr}" \
+       "help with too many parameters produces expected error"
+
+is_empty "${stdout}"
+ok $? "help with too many parameters produces no output"
+
+# Test with unknown plugin name.
+bt_cli "${stdout}" "${stderr}" help zigotos
+isnt $? 0 "help with unknown plugin name"
+
+bt_grep_ok \
+       'Cannot find plugin: plugin-name="zigotos"' \
+       "${stderr}" \
+       "help with unknown plugin name produces expected error"
+
+is_empty "${stdout}"
+ok $? "help with unknown plugin name produces no output"
+
+# Test with unknown component class name (but known plugin).
+bt_cli "${stdout}" "${stderr}" help src.ctf.bob
+isnt $? 0 "help with unknown component class name"
+
+bt_grep_ok \
+       'Cannot find component class: plugin-name="ctf", comp-cls-name="bob", comp-cls-type=SOURCE' \
+       "${stderr}" \
+       "help with unknown component class name produces expected error"
+
+bt_grep_ok \
+       'Description: CTF input and output' \
+       "${stdout}" \
+       "help with unknown component class name prints plugin help"
+
+# Test with unknown component class plugin
+bt_cli "${stdout}" "${stderr}" help src.bob.fs
+isnt $? 0 "help with unknown component class plugin"
+
+bt_grep_ok \
+       'Cannot find plugin: plugin-name="bob"' \
+       "${stderr}" \
+       "help with unknown component class plugin produces expected error"
+
+is_empty "${stdout}"
+ok $? "help with unknown component class plugin produces no output"
+
+rm -f "${stdout}" "${stderr}"
diff --git a/tests/cli/test-intersection.sh b/tests/cli/test-intersection.sh
new file mode 100755 (executable)
index 0000000..18711c5
--- /dev/null
@@ -0,0 +1,86 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Julien Desfossez <jdesfossez@efficios.com>
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+plan_tests 20
+
+stdout=$(mktemp -t test-intersection-stdout.XXXXXX)
+stderr=$(mktemp -t test-intersection-stderr.XXXXXX)
+
+test_intersect() {
+       local trace="$1"
+       local totalevents="$2"
+       local intersect="$3"
+
+       local cnt
+
+       bt_cli "${stdout}" "/dev/null" "${trace}"
+       ok $? "run without --stream-intersection"
+
+       cnt=$(wc -l < "${stdout}")
+       test "${cnt// /}" = "$totalevents"
+       ok $? "$totalevents events in the whole trace"
+
+       bt_cli "${stdout}" "/dev/null" --stream-intersection "${trace}"
+       ok $? "run with --stream-intersection"
+
+       cnt=$(wc -l < "${stdout}")
+       test "${cnt// /}" = "$intersect"
+       ok $? "$intersect events in streams intersecting"
+}
+
+test_intersect_fails() {
+       local trace="$1"
+       local totalevents="$2"
+       local expected_error_message="$3"
+
+       bt_cli "${stdout}" "/dev/null" "${trace}"
+       ok $? "run without --stream-intersection"
+
+       cnt=$(wc -l < "${stdout}")
+       test "${cnt// /}" = "$totalevents"
+       ok $? "$totalevents events in the whole trace"
+
+       bt_cli "${stdout}" "${stderr}" --stream-intersection "${trace}"
+       isnt "$?" 0 "run with --stream-intersection fails"
+
+       bt_grep_ok \
+               "${expected_error_message}" \
+               "${stderr}" \
+               "stderr contains expected error message"
+}
+
+diag "Test the stream intersection feature"
+
+diag "2 streams offsetted with 3 packets intersecting"
+test_intersect "${BT_CTF_TRACES_PATH}/intersection/3eventsintersect" 8 3
+
+diag "2 streams offsetted with 3 packets intersecting (exchanged file names)"
+test_intersect "${BT_CTF_TRACES_PATH}/intersection/3eventsintersectreverse" 8 3
+
+diag "Only 1 stream"
+test_intersect "${BT_CTF_TRACES_PATH}/intersection/onestream" 3 3
+
+diag "No intersection between 2 streams"
+test_intersect_fails "${BT_CTF_TRACES_PATH}/intersection/nointersect" 6 \
+       "Trimming time range's beginning time is greater than end time: "
+
+diag "No stream at all"
+test_intersect_fails "${BT_CTF_TRACES_PATH}/intersection/nostream" 0 \
+       "Trace has no streams: "
+
+rm -f "${stdout}" "${stderr}"
diff --git a/tests/cli/test-output-ctf-metadata.sh b/tests/cli/test-output-ctf-metadata.sh
new file mode 100755 (executable)
index 0000000..f85fc05
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+NUM_TESTS=3
+
+plan_tests $NUM_TESTS
+
+tmp_metadata=$(mktemp)
+
+# Test a valid trace directory.
+"${BT_TESTS_BT2_BIN}" -o ctf-metadata "${BT_CTF_TRACES_PATH}/succeed/wk-heartbeat-u" > "$tmp_metadata"
+ok $? "Run babeltrace -o ctf-metadata with a valid trace directory, correct exit status"
+
+bt_diff "${BT_TESTS_DATADIR}/cli/test-output-ctf-metadata.ref" "$tmp_metadata"
+ok $? "Run babeltrace -o ctf-metadata with a valid trace directory, correct output"
+
+# Test an invalid trace directory.
+"${BT_TESTS_BT2_BIN}" -o ctf-metadata "${BT_CTF_TRACES_PATH}" >/dev/null 2>&1
+isnt $? 0 "Run babeltrace -o ctf-metadata with an invalid trace directory, expecting failure"
+
+rm -f "$tmp_metadata"
diff --git a/tests/cli/test-output-path-ctf-non-lttng-trace.sh b/tests/cli/test-output-path-ctf-non-lttng-trace.sh
new file mode 100755 (executable)
index 0000000..d589dbe
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) EfficiOS Inc.
+#
+
+# This test verifies that generic (non-LTTng) CTF traces are output with the
+# expected directory structure.
+#
+# Traces found when invoking
+#
+#   babeltrace2 in -c sink.ctf.fs -p 'path="out"'
+#
+# are expected to use the same directory structure relative to `out` as the
+# original traces had relative to `in`.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+plan_tests 3
+
+temp_input_dir=$(mktemp -t -d test-output-path-ctf-non-lttng-trace-input.XXXXXX)
+temp_output_dir=$(mktemp -t -d test-output-path-ctf-non-lttng-trace-output.XXXXXX)
+
+mkdir -p "${temp_input_dir}/a/b/c"
+cp -a "${BT_CTF_TRACES_PATH}/intersection/3eventsintersect" "${temp_input_dir}/a/b/c"
+
+mkdir -p "${temp_input_dir}/a/b/c"
+cp -a "${BT_CTF_TRACES_PATH}/intersection/3eventsintersectreverse" "${temp_input_dir}/a/b/c"
+
+mkdir -p "${temp_input_dir}/d/e/f"
+cp -a "${BT_CTF_TRACES_PATH}/intersection/nointersect" "${temp_input_dir}/d/e/f"
+
+bt_cli "/dev/null" "/dev/null" "${temp_input_dir}" -c sink.ctf.fs -p "path=\"${temp_output_dir}\""
+
+test -f "${temp_output_dir}/a/b/c/3eventsintersect/metadata"
+ok "$?" "3eventsintersect output trace exists"
+
+test -f "${temp_output_dir}/a/b/c/3eventsintersectreverse/metadata"
+ok "$?" "3eventsintersectreverse output trace exists"
+
+test -f "${temp_output_dir}/d/e/f/nointersect/metadata"
+ok "$?" "nointersect output trace exists"
+
+rm -rf "${temp_input_dir}" "${temp_output_dir}"
diff --git a/tests/cli/test-packet-seq-num.sh b/tests/cli/test-packet-seq-num.sh
new file mode 100755 (executable)
index 0000000..8c83805
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Julien Desfossez <jdesfossez@efficios.com>
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+NUM_TESTS=10
+
+plan_tests $NUM_TESTS
+
+test_no_lost() {
+       local trace=$1
+
+       "${BT_TESTS_BT2_BIN}" "$trace" >/dev/null 2>&1
+       ok $? "Trace parses"
+       "${BT_TESTS_BT2_BIN}" "$trace" 2>&1 >/dev/null | bt_grep "\[warning\] Tracer lost"
+       if test $? = 0; then
+               fail 1 "Should not find any lost events"
+       else
+               ok 0 "No events lost"
+       fi
+}
+
+test_lost() {
+       local trace=$1
+       local expectedcountstr=$2
+
+       "${BT_TESTS_BT2_BIN}" "$trace" >/dev/null 2>&1
+       ok $? "Trace parses"
+
+       # Convert warnings like:
+       # WARNING: Tracer discarded 2 trace packets between ....
+       # WARNING: Tracer discarded 3 trace packets between ....
+       # into "2,3" and make sure it matches the expected result
+       "${BT_TESTS_BT2_BIN}" "$trace" 2>&1 >/dev/null | bt_grep "WARNING: Tracer discarded" \
+               | cut -d" " -f4 | tr "\n" "," | "${BT_TESTS_SED_BIN}" "s/.$//" | \
+               bt_grep "$expectedcountstr" >/dev/null
+       ok $? "Lost events string matches $expectedcountstr"
+
+}
+
+diag "Test the packet_seq_num validation"
+
+diag "No packet lost"
+test_no_lost "${BT_CTF_TRACES_PATH}/packet-seq-num/no-lost"
+
+diag "No packet lost, packet_seq_num not starting at 0"
+test_no_lost "${BT_CTF_TRACES_PATH}/packet-seq-num/no-lost-not-starting-at-0"
+
+diag "1 stream, 2 packets lost before the last packet"
+test_lost "${BT_CTF_TRACES_PATH}/packet-seq-num/2-lost-before-last" "2"
+
+diag "2 streams, packets lost in one of them"
+test_lost "${BT_CTF_TRACES_PATH}/packet-seq-num/2-streams-lost-in-1" "2"
+
+diag "2 streams, packets lost in both"
+test_lost "${BT_CTF_TRACES_PATH}/packet-seq-num/2-streams-lost-in-2" "2,3,1"
diff --git a/tests/cli/test-trace-copy.sh b/tests/cli/test-trace-copy.sh
new file mode 100755 (executable)
index 0000000..5b9f9ad
--- /dev/null
@@ -0,0 +1,91 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2017 Julien Desfossez <jdesfossez@efficios.com>
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+clean_tmp() {
+       rm -rf "${out_path}" "${text_output1}" "${text_output2_intermediary}" "${text_output2}" "${stderr_file}"
+}
+
+SUCCESS_TRACES=("${BT_CTF_TRACES_PATH}/succeed/"*)
+
+# -4 because there are two empty traces that we skip
+NUM_TESTS=$((${#SUCCESS_TRACES[@]} * 3 - 4))
+
+plan_tests $NUM_TESTS
+
+for path in "${SUCCESS_TRACES[@]}"; do
+       out_path="$(mktemp -d)"
+       text_output1="$(mktemp)"
+       text_output2_intermediary="$(mktemp)"
+       text_output2="$(mktemp)"
+       stderr_file="$(mktemp)"
+       trace="$(basename "${path}")"
+       sort_cmd="cat" # by default do not sort the trace
+
+       bt_cli "${text_output1}" "/dev/null" --no-delta "${path}"
+       ret=$?
+       cnt="$(wc -l < "${text_output1}")"
+       if test "$ret" == 0 && test "${cnt// /}" == 0; then
+               pass "Empty trace ${trace}, nothing to copy"
+               clean_tmp
+               continue
+       fi
+
+       # If the trace has a timestamp (starts with [), check if there are
+       # duplicate timestamps in the output.
+       # If there are, we have to sort the text output to make sure it is
+       # always the same.
+       head -1 "${text_output1}" | bt_grep "^\[" >/dev/null
+       if test $? = 0; then
+               # shellcheck disable=SC2016
+               uniq_ts_cnt="$("${BT_TESTS_AWK_BIN}" '{ print $1 }' < "${text_output1}" | sort | uniq | wc -l)"
+               # Extract only the timestamp columns and compare the number of
+               # unique lines with the total number of lines to see if there
+               # are duplicate timestamps.
+               if test "${cnt// /}" != "${uniq_ts_cnt// /}"; then
+                       diag "Trace with non unique timestamps, sorting the output"
+                       sort_cmd="sort"
+                       tmp="$(mktemp)"
+                       sort "${text_output1}" > "$tmp"
+                       rm "${text_output1}"
+                       text_output1="$tmp"
+               fi
+       fi
+
+       bt_cli "/dev/null" "${stderr_file}" "${path}" --component sink.ctf.fs "--params=path=\"${out_path}\""
+       if ! ok $? "Copy trace ${trace} with ctf-fs sink"; then
+               diag "stderr:"
+               diag_file "${stderr_file}"
+       fi
+
+       bt_cli "/dev/null" "${stderr_file}" "${out_path}"
+       if ! ok $? "Read the new trace in ${out_path}"; then
+               diag "stderr:"
+               diag_file "${stderr_file}"
+       fi
+
+       if ! bt_cli "${text_output2_intermediary}" "${stderr_file}" --no-delta "${out_path}"; then
+               diag "stderr:"
+               diag_file "${stderr_file}"
+       fi
+
+       $sort_cmd "${text_output2_intermediary}" > "${text_output2}"
+       bt_diff "${text_output1}" "${text_output2}"
+       ok $? "Exact same content between the two traces"
+
+       clean_tmp
+done
diff --git a/tests/cli/test-trace-read.sh b/tests/cli/test-trace-read.sh
new file mode 100755 (executable)
index 0000000..474015e
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2013 Christian Babeux <christian.babeux@efficios.com>
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+SUCCESS_TRACES=("${BT_CTF_TRACES_PATH}/succeed/"*)
+FAIL_TRACES=("${BT_CTF_TRACES_PATH}/fail/"*)
+
+NUM_TESTS=$((${#SUCCESS_TRACES[@]} + ${#FAIL_TRACES[@]}))
+
+plan_tests $NUM_TESTS
+
+for path in "${SUCCESS_TRACES[@]}"; do
+       trace=$(basename "${path}")
+       "${BT_TESTS_BT2_BIN}" "${path}" > /dev/null 2>&1
+       ok $? "Run babeltrace2 with trace ${trace}"
+done
+
+for path in "${FAIL_TRACES[@]}"; do
+       trace=$(basename "${path}")
+       if "${BT_TESTS_BT2_BIN}" "${path}" > /dev/null 2>&1; then
+               fail "Run babeltrace2 with invalid trace ${trace}"
+       else
+               pass "Run babeltrace2 with invalid trace ${trace}"
+       fi
+done
diff --git a/tests/cli/test-trimmer.sh b/tests/cli/test-trimmer.sh
new file mode 100755 (executable)
index 0000000..4b415b4
--- /dev/null
@@ -0,0 +1,209 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2017 Julien Desfossez <jdesfossez@efficios.com>
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+TRACE_PATH="${BT_CTF_TRACES_PATH}/succeed/wk-heartbeat-u/"
+
+NUM_TESTS=118
+
+plan_tests $NUM_TESTS
+
+tmp_out=$(mktemp)
+tmp_err=$(mktemp)
+
+
+# Run Babeltrace with some command line arguments, verify exit status and
+# number of output events (i.e. number of output lines)
+#
+# Arguments:
+#
+#   $1: expected number of events
+#   $2: test description
+#   remaining arguments: command-line arguments to pass to Babeltrace
+
+function expect_success()
+{
+       local expected_num_events="$1"
+       local msg="$2"
+       shift 2
+
+       bt_cli "${tmp_out}" /dev/null "${TRACE_PATH}" "$@"
+       ok $? "trimmer: ${msg}: exit status"
+
+       num_events=$(wc -l < "${tmp_out}")
+       # Use bash parameter expansion to strip spaces added by BSD 'wc' on macOs and Solaris
+       is "${num_events// /}" "${expected_num_events}" "trimmer: ${msg}: number of events (${expected_num_events})"
+}
+
+# Run Babeltrace with some command line arguments, verify that the exit status
+# is not 0 and that the error message contains a given string.
+#
+# Arguments:
+#
+#   $1: a string expected to be found in the error message
+#   $2: test description
+#   remaining arguments: command-line arguments to pass to Babeltrace
+
+function expect_failure()
+{
+       local expected_err_string="$1"
+       local msg="$2"
+       shift 2
+
+       # We check the error message logged by the trimmer plugin, set the env
+       # var necessary for it to log errors.
+       BABELTRACE_FLT_UTILS_TRIMMER_LOG_LEVEL=E bt_cli "${tmp_out}" "${tmp_err}" "${TRACE_PATH}" "$@"
+       isnt $? 0 "trimmer: ${msg}: exit status"
+
+       num_events=$(wc -l < "${tmp_out}")
+       # Use bash parameter expansion to strip spaces added by BSD 'wc' on macOs and Solaris
+       is "${num_events// /}" 0 "trimmer: ${msg}: number of events (0)"
+
+       stderr="$(cat "${tmp_err}")"
+       # "like" doesn't like when the passed text is empty.
+       if [ -n "${stderr}" ]; then
+               like "${stderr}" "${expected_err_string}" "trimmer: ${msg}: error message"
+       else
+               fail "trimmer: ${msg}: error message"
+               diag "Nothing was output on stderr".
+       fi
+
+}
+
+expect_success 18 "--begin, GMT relative timestamps" \
+       --clock-gmt --begin 17:48:17.587029529
+expect_success 9 "--end, GMT relative timestamps" \
+       --clock-gmt --end 17:48:17.588680018
+expect_success 7 "--begin and --end, GMT relative timestamps" \
+       --clock-gmt --begin 17:48:17.587029529 --end 17:48:17.588680018
+expect_success 0 "--begin, out of range, GMT relative timestamps" \
+       --clock-gmt --begin 18:48:17.587029529
+expect_success 0 "--end, out of range, GMT relative timestamps" \
+       --clock-gmt --end 16:48:17.588680018
+
+expect_success 18 "--begin, GMT absolute timestamps" \
+       --clock-gmt --begin "2012-10-29 17:48:17.587029529"
+expect_success 9 "--end, GMT absolute timestamps" \
+       --clock-gmt --end "2012-10-29 17:48:17.588680018"
+expect_success 7 "--begin and --end, GMT absolute timestamps" \
+       --clock-gmt --begin "2012-10-29 17:48:17.587029529" --end "2012-10-29 17:48:17.588680018"
+expect_success 0 "--begin, out of range, GMT absolute timestamps" \
+       --clock-gmt --begin "2012-10-29 18:48:17.587029529"
+expect_success 0 "--begin, out of range, GMT absolute timestamps" \
+       --clock-gmt --end "2012-10-29 16:48:17.588680018"
+
+# Note here that the POSIX notation is a bit weird.
+# The libc documentation shed some light on this:
+#  The offset specifies the time value you must add to the local time to get a
+#  Coordinated Universal Time value. It has syntax like [+|-]hh[:mm[:ss]]. This
+#  is positive if the local time zone is west of the Prime Meridian and negative
+#  if it is east. The hour must be between 0 and 24, and the minute and seconds
+#  between 0 and 59. [1]
+#
+# This is why we use EST5 to simulate an effective UTC-5:00 time.
+#
+# [1] https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
+export TZ=EST5
+
+expect_success 18 "--begin, EST relative timestamps" \
+       --begin "12:48:17.587029529"
+expect_success 9 "--end, EST relative timestamps" \
+       --end "12:48:17.588680018"
+expect_success 7 "--begin and --end, EST relative timestamps" \
+       --begin "12:48:17.587029529" --end "12:48:17.588680018"
+expect_success 0 "--begin, out of range, EST relative timestamps" \
+       --begin "13:48:17.587029529"
+expect_success 0 "--end, out of range, EST relative timestamps" \
+       --end "11:48:17.588680018"
+
+expect_success 18 "--begin, EST absolute timestamps" \
+       --begin "2012-10-29 12:48:17.587029529"
+expect_success 9 "--end, EST absolute timestamps" \
+       --end "12:48:17.588680018"
+expect_success 7 "--begin and --end, EST absolute timestamps" \
+       --begin "2012-10-29 12:48:17.587029529" --end "2012-10-29 12:48:17.588680018"
+expect_success 0 "--begin, out of range, EST absolute timestamps" \
+       --begin "2012-10-29 13:48:17.587029529"
+expect_success 0 "--end, out of range, EST absolute timestamps" \
+       --end "2012-10-29 11:48:17.588680018"
+
+# Check various formats.
+#
+# We sometimes apply a clock offset to make the events of the trace span two
+# different seconds or minutes.
+
+expect_success 13 "date time format: partial nanosecond precision" \
+       --begin="2012-10-29 12:48:17.588"
+expect_success 11 "date time format: second precision" \
+       --clock-offset-ns=411282268 --begin="2012-10-29 12:48:18"
+expect_success 11 "date time format: minute precision" \
+       --clock-offset=42 --clock-offset-ns=411282268 --begin="2012-10-29 12:49"
+
+expect_success 11 "seconds from origin format: nanosecond precision" \
+       --begin="1351532897.588717732"
+expect_success 11 "seconds from origin format: partial nanosecond precision" \
+       --begin="1351532897.58871773"
+expect_success 11 "seconds from origin format: second precision" \
+       --clock-offset-ns=411282268 --begin="1351532898"
+
+expect_failure "Invalid date/time format" "date time format: too many nanosecond digits" \
+       --begin="2012-10-29 12:48:17.1231231231"
+expect_failure "Invalid date/time format" "date time format: missing nanoseconds" \
+       --begin="2012-10-29 12:48:17."
+expect_failure "Invalid date/time format" "date time format: seconds with too many digit" \
+       --begin="2012-10-29 12:48:123"
+expect_failure "Invalid date/time format" "date time format: seconds with missing digit" \
+       --begin="2012-10-29 12:48:1"
+expect_failure "Invalid date/time format" "date time format: minutes with too many digit" \
+       --begin="2012-10-29 12:489:17"
+expect_failure "Invalid date/time format" "date time format: minutes with missing digit" \
+       --begin="2012-10-29 12:4:17"
+expect_failure "Invalid date/time format" "date time format: hours with too many digit" \
+       --begin="2012-10-29 123:48:17"
+expect_failure "Invalid date/time format" "date time format: hours with missing digit" \
+       --begin="2012-10-29 2:48:17"
+expect_failure "Invalid date/time format" "date time format: missing seconds" \
+       --begin="2012-10-29 12:48:"
+expect_failure "Invalid date/time format" "date time format: missing minutes 1" \
+       --begin="2012-10-29 12:"
+expect_failure "Invalid date/time format" "date time format: missing minutes 2" \
+       --begin="2012-10-29 12"
+expect_failure "Invalid date/time format" "date time format: missing time" \
+       --begin="2012-10-29 "
+expect_failure "Invalid date/time format" "date time format: day with too many digit" \
+       --begin="2012-10-291"
+expect_failure "Invalid date/time format" "date time format: day with missing digit" \
+       --begin="2012-10-2"
+expect_failure "Invalid date/time format" "date time format: month with too many digit" \
+       --begin="2012-101-29"
+expect_failure "Invalid date/time format" "date time format: month with missing digit" \
+       --begin="2012-1-29"
+expect_failure "Invalid date/time format" "date time format: year with too many digits" \
+       --begin="20121-10-29"
+expect_failure "Invalid date/time format" "date time format: year with missing digits" \
+       --begin="12-10-29"
+expect_failure "Invalid date/time format" "date time format: missing day 1" \
+       --begin="2012-10-"
+expect_failure "Invalid date/time format" "date time format: missing day 2" \
+       --begin="2012-10"
+
+expect_failure "Invalid date/time format" "seconds from origin format: too many nanosecond digits" \
+       --begin="1351532898.1231231231"
+expect_failure "Invalid date/time format" "seconds from origin format: missing nanseconds" \
+       --begin="1351532898."
+
+rm "${tmp_out}" "${tmp_err}"
diff --git a/tests/cli/test_exit_status b/tests/cli/test_exit_status
deleted file mode 100755 (executable)
index 905a2eb..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 EfficiOS Inc.
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-data_dir="$BT_TESTS_DATADIR/cli/exit_status"
-source_name="src.test_exit_status.StatusSrc"
-
-test_interrupted_graph() {
-       local cli_args=("--plugin-path=$data_dir" "-c" "$source_name" "-p" "case=\"INTERRUPTED\"")
-       local actual_stdout
-       local actual_stderr
-
-       actual_stdout=$(mktemp -t test_cli_exit_status_stdout_actual.XXXXXX)
-       actual_stderr=$(mktemp -t test_cli_exit_status_stderr_actual.XXXXXX)
-
-       bt_cli "$actual_stdout" "$actual_stderr" "${cli_args[@]}"
-
-       is $? 2 "Interrupted graph exits with status 2"
-
-       bt_diff /dev/null "$actual_stdout"
-       ok $? "Interrupted graph gives no stdout"
-
-       bt_diff /dev/null "$actual_stderr"
-       ok $? "Interrupted graph gives no stderr"
-
-       rm -f "${actual_stdout}"
-       rm -f "${actual_stderr}"
-}
-
-test_error_graph() {
-       local cli_args=("--plugin-path=$data_dir" "-c" "$source_name" "-p" "case=\"ERROR\"")
-       local actual_stdout
-       local actual_stderr
-
-       actual_stdout=$(mktemp -t test_cli_exit_status_stdout_actual.XXXXXX)
-       actual_stderr=$(mktemp -t test_cli_exit_status_stderr_actual.XXXXXX)
-
-       bt_cli "$actual_stdout" "$actual_stderr" "${cli_args[@]}"
-
-       is $? 1 "Erroring graph exits with status 1"
-
-       bt_diff /dev/null "$actual_stdout"
-       ok $? "Erroring graph gives expected stdout"
-
-       like "$(cat "${actual_stderr}")" "TypeError: Raising type error" \
-               "Erroring graph gives expected error message"
-
-       rm -f "${actual_stdout}"
-       rm -f "${actual_stderr}"
-}
-
-test_stop_graph() {
-       local cli_args=("--plugin-path=$data_dir" "-c" "$source_name" "-p" "case=\"STOP\"")
-       local actual_stdout
-       local actual_stderr
-
-       actual_stdout=$(mktemp -t test_cli_exit_status_stdout_actual.XXXXXX)
-       actual_stderr=$(mktemp -t test_cli_exit_status_stderr_actual.XXXXXX)
-
-       bt_cli "$actual_stdout" "$actual_stderr" "${cli_args[@]}"
-
-       is $? 0 "Successful graph exits with status 0"
-
-       bt_diff /dev/null "$actual_stdout"
-       ok $? "Successful graph gives no stdout"
-
-       bt_diff /dev/null "$actual_stderr"
-       ok $? "Successful graph gives no stderr"
-
-       rm -f "${actual_stdout}"
-       rm -f "${actual_stderr}"
-}
-
-plan_tests 9
-
-test_interrupted_graph
-test_error_graph
-test_stop_graph
diff --git a/tests/cli/test_help b/tests/cli/test_help
deleted file mode 100755 (executable)
index dca6cfe..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 EfficiOS Inc.
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-plan_tests 21
-
-stdout=$(mktemp -t test_help_stdout.XXXXXX)
-stderr=$(mktemp -t test_help_stderr.XXXXXX)
-
-# Return 0 if file "$1" exists and is empty, non-0 otherwise.
-
-is_empty()
-{
-       [[ -f "$1" && ! -s "$1" ]]
-}
-
-# Test with a working plugin name.
-bt_cli "${stdout}" "${stderr}" help ctf
-ok $? "help ctf plugin exit status"
-
-grep --silent 'Description: CTF input and output' "${stdout}"
-ok $? "help ctf plugin expected output"
-
-is_empty "${stderr}"
-ok $? "help ctf plugin produces no error"
-
-# Test with a working component class name.
-bt_cli "${stdout}" "${stderr}" help src.ctf.fs
-ok $? "help src.ctf.fs component class exit status"
-
-grep --silent 'Description: Read CTF traces from the file system.' "${stdout}"
-ok $? "help src.ctf.fs component class expected output"
-
-is_empty "${stderr}"
-ok $? "help src.ctf.fs component class produces no error"
-
-# Test without parameter.
-bt_cli "${stdout}" "${stderr}" help
-isnt $? 0 "help without parameter exit status"
-
-grep --silent "Missing plugin name or component class descriptor." "${stderr}"
-ok $? "help without parameter produces expected error"
-
-is_empty "${stdout}"
-ok $? "help without parameter produces no output"
-
-# Test with too many parameters.
-bt_cli "${stdout}" "${stderr}" help ctf fs
-isnt $? 0  "help with too many parameters exit status"
-
-grep --silent "Extraneous command-line argument specified to \`help\` command:" "${stderr}"
-ok $? "help with too many parameters produces expected error"
-
-is_empty "${stdout}"
-ok $? "help with too many parameters produces no output"
-
-# Test with unknown plugin name.
-bt_cli "${stdout}" "${stderr}" help zigotos
-isnt $? 0 "help with unknown plugin name"
-
-grep --silent 'Cannot find plugin: plugin-name="zigotos"' "${stderr}"
-ok $? "help with unknown plugin name produces expected error"
-
-is_empty "${stdout}"
-ok $? "help with unknown plugin name produces no output"
-
-# Test with unknown component class name (but known plugin).
-bt_cli "${stdout}" "${stderr}" help src.ctf.bob
-isnt $? 0 "help with unknown component class name"
-
-grep --silent 'Cannot find component class: plugin-name="ctf", comp-cls-name="bob", comp-cls-type=SOURCE' "${stderr}"
-ok $? "help with unknown component class name produces expected error"
-
-grep --silent 'Description: CTF input and output' "${stdout}"
-ok $? "help with unknown component class name prints plugin help"
-
-# Test with unknown component class plugin
-bt_cli "${stdout}" "${stderr}" help src.bob.fs
-isnt $? 0 "help with unknown component class plugin"
-
-grep --silent 'Cannot find plugin: plugin-name="bob"' "${stderr}"
-ok $? "help with unknown component class plugin produces expected error"
-
-is_empty "${stdout}"
-ok $? "help with unknown component class plugin produces no output"
-
-rm -f "${stdout}" "${stderr}"
diff --git a/tests/cli/test_intersection b/tests/cli/test_intersection
deleted file mode 100755 (executable)
index df0f650..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Julien Desfossez <jdesfossez@efficios.com>
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-plan_tests 20
-
-stdout=$(mktemp -t test_intersection_stdout.XXXXXX)
-stderr=$(mktemp -t test_intersection_stderr.XXXXXX)
-
-test_intersect() {
-       local trace="$1"
-       local totalevents="$2"
-       local intersect="$3"
-
-       local cnt
-
-       bt_cli "${stdout}" "/dev/null" "${trace}"
-       ok $? "run without --stream-intersection"
-
-       cnt=$(wc -l < "${stdout}")
-       test "${cnt// /}" = "$totalevents"
-       ok $? "$totalevents events in the whole trace"
-
-       bt_cli "${stdout}" "/dev/null" --stream-intersection "${trace}"
-       ok $? "run with --stream-intersection"
-
-       cnt=$(wc -l < "${stdout}")
-       test "${cnt// /}" = "$intersect"
-       ok $? "$intersect events in streams intersecting"
-}
-
-test_intersect_fails() {
-       local trace="$1"
-       local totalevents="$2"
-       local expected_error_message="$3"
-
-       bt_cli "${stdout}" "/dev/null" "${trace}"
-       ok $? "run without --stream-intersection"
-
-       cnt=$(wc -l < "${stdout}")
-       test "${cnt// /}" = "$totalevents"
-       ok $? "$totalevents events in the whole trace"
-
-       bt_cli "${stdout}" "${stderr}" --stream-intersection "${trace}"
-       isnt "$?" 0 "run with --stream-intersection fails"
-
-       grep --silent "${expected_error_message}" "${stderr}"
-       ok $? "stderr contains expected error message"
-}
-
-diag "Test the stream intersection feature"
-
-diag "2 streams offsetted with 3 packets intersecting"
-test_intersect "${BT_CTF_TRACES_PATH}/intersection/3eventsintersect" 8 3
-
-diag "2 streams offsetted with 3 packets intersecting (exchanged file names)"
-test_intersect "${BT_CTF_TRACES_PATH}/intersection/3eventsintersectreverse" 8 3
-
-diag "Only 1 stream"
-test_intersect "${BT_CTF_TRACES_PATH}/intersection/onestream" 3 3
-
-diag "No intersection between 2 streams"
-test_intersect_fails "${BT_CTF_TRACES_PATH}/intersection/nointersect" 6 \
-       "Trimming time range's beginning time is greater than end time: "
-
-diag "No stream at all"
-test_intersect_fails "${BT_CTF_TRACES_PATH}/intersection/nostream" 0 \
-       "Trace has no streams: "
-
-rm -f "${stdout}" "${stderr}"
diff --git a/tests/cli/test_output_ctf_metadata b/tests/cli/test_output_ctf_metadata
deleted file mode 100755 (executable)
index 58969f8..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-NUM_TESTS=3
-
-plan_tests $NUM_TESTS
-
-tmp_metadata=$(mktemp)
-
-# Test a valid trace directory.
-"${BT_TESTS_BT2_BIN}" -o ctf-metadata "${BT_CTF_TRACES_PATH}/succeed/wk-heartbeat-u" > "$tmp_metadata"
-ok $? "Run babeltrace -o ctf-metadata with a valid trace directory, correct exit status"
-
-cmp -s "$tmp_metadata" "${BT_TESTS_DATADIR}/cli/test_output_ctf_metadata.ref"
-ok $? "Run babeltrace -o ctf-metadata with a valid trace directory, correct output"
-
-# Test an invalid trace directory.
-"${BT_TESTS_BT2_BIN}" -o ctf-metadata "${BT_CTF_TRACES_PATH}" >/dev/null 2>&1
-isnt $? 0 "Run babeltrace -o ctf-metadata with an invalid trace directory, expecting failure"
-
-rm -f "$tmp_metadata"
diff --git a/tests/cli/test_output_path_ctf_non_lttng_trace b/tests/cli/test_output_path_ctf_non_lttng_trace
deleted file mode 100755 (executable)
index fc0158b..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) EfficiOS Inc.
-#
-
-# This test verifies that generic (non-LTTng) CTF traces are output with the
-# expected directory structure.
-#
-# Traces found when invoking
-#
-#   babeltrace2 in -c sink.ctf.fs -p 'path="out"'
-#
-# are expected to use the same directory structure relative to `out` as the
-# original traces had relative to `in`.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-plan_tests 3
-
-temp_input_dir=$(mktemp -t -d test_output_path_ctf_non_lttng_trace_input.XXXXXX)
-temp_output_dir=$(mktemp -t -d test_output_path_ctf_non_lttng_trace_output.XXXXXX)
-
-mkdir -p "${temp_input_dir}/a/b/c"
-cp -a "${BT_CTF_TRACES_PATH}/intersection/3eventsintersect" "${temp_input_dir}/a/b/c"
-
-mkdir -p "${temp_input_dir}/a/b/c"
-cp -a "${BT_CTF_TRACES_PATH}/intersection/3eventsintersectreverse" "${temp_input_dir}/a/b/c"
-
-mkdir -p "${temp_input_dir}/d/e/f"
-cp -a "${BT_CTF_TRACES_PATH}/intersection/nointersect" "${temp_input_dir}/d/e/f"
-
-bt_cli "/dev/null" "/dev/null" "${temp_input_dir}" -c sink.ctf.fs -p "path=\"${temp_output_dir}\""
-
-test -f "${temp_output_dir}/a/b/c/3eventsintersect/metadata"
-ok "$?" "3eventsintersect output trace exists"
-
-test -f "${temp_output_dir}/a/b/c/3eventsintersectreverse/metadata"
-ok "$?" "3eventsintersectreverse output trace exists"
-
-test -f "${temp_output_dir}/d/e/f/nointersect/metadata"
-ok "$?" "nointersect output trace exists"
-
-rm -rf "${temp_input_dir}" "${temp_output_dir}"
diff --git a/tests/cli/test_packet_seq_num b/tests/cli/test_packet_seq_num
deleted file mode 100755 (executable)
index 341a9c2..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Julien Desfossez <jdesfossez@efficios.com>
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-NUM_TESTS=10
-
-plan_tests $NUM_TESTS
-
-test_no_lost() {
-       local trace=$1
-
-       "${BT_TESTS_BT2_BIN}" "$trace" >/dev/null 2>&1
-       ok $? "Trace parses"
-       "${BT_TESTS_BT2_BIN}" "$trace" 2>&1 >/dev/null | "${BT_TESTS_GREP_BIN}" "\[warning\] Tracer lost"
-       if test $? = 0; then
-               fail 1 "Should not find any lost events"
-       else
-               ok 0 "No events lost"
-       fi
-}
-
-test_lost() {
-       local trace=$1
-       local expectedcountstr=$2
-
-       "${BT_TESTS_BT2_BIN}" "$trace" >/dev/null 2>&1
-       ok $? "Trace parses"
-
-       # Convert warnings like:
-       # WARNING: Tracer discarded 2 trace packets between ....
-       # WARNING: Tracer discarded 3 trace packets between ....
-       # into "2,3" and make sure it matches the expected result
-       "${BT_TESTS_BT2_BIN}" "$trace" 2>&1 >/dev/null | "${BT_TESTS_GREP_BIN}" "WARNING: Tracer discarded" \
-               | cut -d" " -f4 | tr "\n" "," | "${BT_TESTS_SED_BIN}" "s/.$//" | \
-               "${BT_TESTS_GREP_BIN}" "$expectedcountstr" >/dev/null
-       ok $? "Lost events string matches $expectedcountstr"
-
-}
-
-diag "Test the packet_seq_num validation"
-
-diag "No packet lost"
-test_no_lost "${BT_CTF_TRACES_PATH}/packet_seq_num/no_lost"
-
-diag "No packet lost, packet_seq_num not starting at 0"
-test_no_lost "${BT_CTF_TRACES_PATH}/packet_seq_num/no_lost_not_starting_at_0"
-
-diag "1 stream, 2 packets lost before the last packet"
-test_lost "${BT_CTF_TRACES_PATH}/packet_seq_num/2_lost_before_last" "2"
-
-diag "2 streams, packets lost in one of them"
-test_lost "${BT_CTF_TRACES_PATH}/packet_seq_num/2_streams_lost_in_1" "2"
-
-diag "2 streams, packets lost in both"
-test_lost "${BT_CTF_TRACES_PATH}/packet_seq_num/2_streams_lost_in_2" "2,3,1"
diff --git a/tests/cli/test_trace_copy b/tests/cli/test_trace_copy
deleted file mode 100755 (executable)
index e8c9b2c..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2017 Julien Desfossez <jdesfossez@efficios.com>
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-clean_tmp() {
-       rm -rf "${out_path}" "${text_output1}" "${text_output2_intermediary}" "${text_output2}"
-}
-
-SUCCESS_TRACES=("${BT_CTF_TRACES_PATH}/succeed/"*)
-
-# -4 because there are two empty traces that we skip
-NUM_TESTS=$((${#SUCCESS_TRACES[@]} * 3 - 4))
-
-plan_tests $NUM_TESTS
-
-for path in "${SUCCESS_TRACES[@]}"; do
-       out_path="$(mktemp -d)"
-       text_output1="$(mktemp)"
-       text_output2_intermediary="$(mktemp)"
-       text_output2="$(mktemp)"
-       trace="$(basename "${path}")"
-       sort_cmd="cat" # by default do not sort the trace
-
-       bt_cli "${text_output1}" "/dev/null" --no-delta "${path}"
-       ret=$?
-       cnt="$(wc -l < "${text_output1}")"
-       if test "$ret" == 0 && test "${cnt// /}" == 0; then
-               pass "Empty trace ${trace}, nothing to copy"
-               clean_tmp
-               continue
-       fi
-
-       # If the trace has a timestamp (starts with [), check if there are
-       # duplicate timestamps in the output.
-       # If there are, we have to sort the text output to make sure it is
-       # always the same.
-       head -1 "${text_output1}" | "${BT_TESTS_GREP_BIN}" "^\[" >/dev/null
-       if test $? = 0; then
-               # shellcheck disable=SC2016
-               uniq_ts_cnt="$("${BT_TESTS_AWK_BIN}" '{ print $1 }' < "${text_output1}" | sort | uniq | wc -l)"
-               # Extract only the timestamp columns and compare the number of
-               # unique lines with the total number of lines to see if there
-               # are duplicate timestamps.
-               if test "${cnt// /}" != "${uniq_ts_cnt// /}"; then
-                       diag "Trace with non unique timestamps, sorting the output"
-                       sort_cmd="sort"
-                       tmp="$(mktemp)"
-                       sort "${text_output1}" > "$tmp"
-                       rm "${text_output1}"
-                       text_output1="$tmp"
-               fi
-       fi
-
-       bt_cli "/dev/null" "/dev/null" "${path}" --component sink.ctf.fs "--params=path=\"${out_path}\""
-       ok $? "Copy trace ${trace} with ctf-fs sink"
-
-       bt_cli "/dev/null" "/dev/null" "${out_path}"
-       ok $? "Read the new trace in ${out_path}"
-
-       bt_cli "${text_output2_intermediary}" "/dev/null" --no-delta "${out_path}"
-       $sort_cmd "${text_output2_intermediary}" > "${text_output2}"
-       cnt=$(diff "${text_output1}" "${text_output2}" | wc -l)
-       test "${cnt// /}" == 0
-       ok $? "Exact same content between the two traces"
-
-       clean_tmp
-done
diff --git a/tests/cli/test_trace_read b/tests/cli/test_trace_read
deleted file mode 100755 (executable)
index 474015e..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2013 Christian Babeux <christian.babeux@efficios.com>
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-SUCCESS_TRACES=("${BT_CTF_TRACES_PATH}/succeed/"*)
-FAIL_TRACES=("${BT_CTF_TRACES_PATH}/fail/"*)
-
-NUM_TESTS=$((${#SUCCESS_TRACES[@]} + ${#FAIL_TRACES[@]}))
-
-plan_tests $NUM_TESTS
-
-for path in "${SUCCESS_TRACES[@]}"; do
-       trace=$(basename "${path}")
-       "${BT_TESTS_BT2_BIN}" "${path}" > /dev/null 2>&1
-       ok $? "Run babeltrace2 with trace ${trace}"
-done
-
-for path in "${FAIL_TRACES[@]}"; do
-       trace=$(basename "${path}")
-       if "${BT_TESTS_BT2_BIN}" "${path}" > /dev/null 2>&1; then
-               fail "Run babeltrace2 with invalid trace ${trace}"
-       else
-               pass "Run babeltrace2 with invalid trace ${trace}"
-       fi
-done
diff --git a/tests/cli/test_trimmer b/tests/cli/test_trimmer
deleted file mode 100755 (executable)
index 4b415b4..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2017 Julien Desfossez <jdesfossez@efficios.com>
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-TRACE_PATH="${BT_CTF_TRACES_PATH}/succeed/wk-heartbeat-u/"
-
-NUM_TESTS=118
-
-plan_tests $NUM_TESTS
-
-tmp_out=$(mktemp)
-tmp_err=$(mktemp)
-
-
-# Run Babeltrace with some command line arguments, verify exit status and
-# number of output events (i.e. number of output lines)
-#
-# Arguments:
-#
-#   $1: expected number of events
-#   $2: test description
-#   remaining arguments: command-line arguments to pass to Babeltrace
-
-function expect_success()
-{
-       local expected_num_events="$1"
-       local msg="$2"
-       shift 2
-
-       bt_cli "${tmp_out}" /dev/null "${TRACE_PATH}" "$@"
-       ok $? "trimmer: ${msg}: exit status"
-
-       num_events=$(wc -l < "${tmp_out}")
-       # Use bash parameter expansion to strip spaces added by BSD 'wc' on macOs and Solaris
-       is "${num_events// /}" "${expected_num_events}" "trimmer: ${msg}: number of events (${expected_num_events})"
-}
-
-# Run Babeltrace with some command line arguments, verify that the exit status
-# is not 0 and that the error message contains a given string.
-#
-# Arguments:
-#
-#   $1: a string expected to be found in the error message
-#   $2: test description
-#   remaining arguments: command-line arguments to pass to Babeltrace
-
-function expect_failure()
-{
-       local expected_err_string="$1"
-       local msg="$2"
-       shift 2
-
-       # We check the error message logged by the trimmer plugin, set the env
-       # var necessary for it to log errors.
-       BABELTRACE_FLT_UTILS_TRIMMER_LOG_LEVEL=E bt_cli "${tmp_out}" "${tmp_err}" "${TRACE_PATH}" "$@"
-       isnt $? 0 "trimmer: ${msg}: exit status"
-
-       num_events=$(wc -l < "${tmp_out}")
-       # Use bash parameter expansion to strip spaces added by BSD 'wc' on macOs and Solaris
-       is "${num_events// /}" 0 "trimmer: ${msg}: number of events (0)"
-
-       stderr="$(cat "${tmp_err}")"
-       # "like" doesn't like when the passed text is empty.
-       if [ -n "${stderr}" ]; then
-               like "${stderr}" "${expected_err_string}" "trimmer: ${msg}: error message"
-       else
-               fail "trimmer: ${msg}: error message"
-               diag "Nothing was output on stderr".
-       fi
-
-}
-
-expect_success 18 "--begin, GMT relative timestamps" \
-       --clock-gmt --begin 17:48:17.587029529
-expect_success 9 "--end, GMT relative timestamps" \
-       --clock-gmt --end 17:48:17.588680018
-expect_success 7 "--begin and --end, GMT relative timestamps" \
-       --clock-gmt --begin 17:48:17.587029529 --end 17:48:17.588680018
-expect_success 0 "--begin, out of range, GMT relative timestamps" \
-       --clock-gmt --begin 18:48:17.587029529
-expect_success 0 "--end, out of range, GMT relative timestamps" \
-       --clock-gmt --end 16:48:17.588680018
-
-expect_success 18 "--begin, GMT absolute timestamps" \
-       --clock-gmt --begin "2012-10-29 17:48:17.587029529"
-expect_success 9 "--end, GMT absolute timestamps" \
-       --clock-gmt --end "2012-10-29 17:48:17.588680018"
-expect_success 7 "--begin and --end, GMT absolute timestamps" \
-       --clock-gmt --begin "2012-10-29 17:48:17.587029529" --end "2012-10-29 17:48:17.588680018"
-expect_success 0 "--begin, out of range, GMT absolute timestamps" \
-       --clock-gmt --begin "2012-10-29 18:48:17.587029529"
-expect_success 0 "--begin, out of range, GMT absolute timestamps" \
-       --clock-gmt --end "2012-10-29 16:48:17.588680018"
-
-# Note here that the POSIX notation is a bit weird.
-# The libc documentation shed some light on this:
-#  The offset specifies the time value you must add to the local time to get a
-#  Coordinated Universal Time value. It has syntax like [+|-]hh[:mm[:ss]]. This
-#  is positive if the local time zone is west of the Prime Meridian and negative
-#  if it is east. The hour must be between 0 and 24, and the minute and seconds
-#  between 0 and 59. [1]
-#
-# This is why we use EST5 to simulate an effective UTC-5:00 time.
-#
-# [1] https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html
-export TZ=EST5
-
-expect_success 18 "--begin, EST relative timestamps" \
-       --begin "12:48:17.587029529"
-expect_success 9 "--end, EST relative timestamps" \
-       --end "12:48:17.588680018"
-expect_success 7 "--begin and --end, EST relative timestamps" \
-       --begin "12:48:17.587029529" --end "12:48:17.588680018"
-expect_success 0 "--begin, out of range, EST relative timestamps" \
-       --begin "13:48:17.587029529"
-expect_success 0 "--end, out of range, EST relative timestamps" \
-       --end "11:48:17.588680018"
-
-expect_success 18 "--begin, EST absolute timestamps" \
-       --begin "2012-10-29 12:48:17.587029529"
-expect_success 9 "--end, EST absolute timestamps" \
-       --end "12:48:17.588680018"
-expect_success 7 "--begin and --end, EST absolute timestamps" \
-       --begin "2012-10-29 12:48:17.587029529" --end "2012-10-29 12:48:17.588680018"
-expect_success 0 "--begin, out of range, EST absolute timestamps" \
-       --begin "2012-10-29 13:48:17.587029529"
-expect_success 0 "--end, out of range, EST absolute timestamps" \
-       --end "2012-10-29 11:48:17.588680018"
-
-# Check various formats.
-#
-# We sometimes apply a clock offset to make the events of the trace span two
-# different seconds or minutes.
-
-expect_success 13 "date time format: partial nanosecond precision" \
-       --begin="2012-10-29 12:48:17.588"
-expect_success 11 "date time format: second precision" \
-       --clock-offset-ns=411282268 --begin="2012-10-29 12:48:18"
-expect_success 11 "date time format: minute precision" \
-       --clock-offset=42 --clock-offset-ns=411282268 --begin="2012-10-29 12:49"
-
-expect_success 11 "seconds from origin format: nanosecond precision" \
-       --begin="1351532897.588717732"
-expect_success 11 "seconds from origin format: partial nanosecond precision" \
-       --begin="1351532897.58871773"
-expect_success 11 "seconds from origin format: second precision" \
-       --clock-offset-ns=411282268 --begin="1351532898"
-
-expect_failure "Invalid date/time format" "date time format: too many nanosecond digits" \
-       --begin="2012-10-29 12:48:17.1231231231"
-expect_failure "Invalid date/time format" "date time format: missing nanoseconds" \
-       --begin="2012-10-29 12:48:17."
-expect_failure "Invalid date/time format" "date time format: seconds with too many digit" \
-       --begin="2012-10-29 12:48:123"
-expect_failure "Invalid date/time format" "date time format: seconds with missing digit" \
-       --begin="2012-10-29 12:48:1"
-expect_failure "Invalid date/time format" "date time format: minutes with too many digit" \
-       --begin="2012-10-29 12:489:17"
-expect_failure "Invalid date/time format" "date time format: minutes with missing digit" \
-       --begin="2012-10-29 12:4:17"
-expect_failure "Invalid date/time format" "date time format: hours with too many digit" \
-       --begin="2012-10-29 123:48:17"
-expect_failure "Invalid date/time format" "date time format: hours with missing digit" \
-       --begin="2012-10-29 2:48:17"
-expect_failure "Invalid date/time format" "date time format: missing seconds" \
-       --begin="2012-10-29 12:48:"
-expect_failure "Invalid date/time format" "date time format: missing minutes 1" \
-       --begin="2012-10-29 12:"
-expect_failure "Invalid date/time format" "date time format: missing minutes 2" \
-       --begin="2012-10-29 12"
-expect_failure "Invalid date/time format" "date time format: missing time" \
-       --begin="2012-10-29 "
-expect_failure "Invalid date/time format" "date time format: day with too many digit" \
-       --begin="2012-10-291"
-expect_failure "Invalid date/time format" "date time format: day with missing digit" \
-       --begin="2012-10-2"
-expect_failure "Invalid date/time format" "date time format: month with too many digit" \
-       --begin="2012-101-29"
-expect_failure "Invalid date/time format" "date time format: month with missing digit" \
-       --begin="2012-1-29"
-expect_failure "Invalid date/time format" "date time format: year with too many digits" \
-       --begin="20121-10-29"
-expect_failure "Invalid date/time format" "date time format: year with missing digits" \
-       --begin="12-10-29"
-expect_failure "Invalid date/time format" "date time format: missing day 1" \
-       --begin="2012-10-"
-expect_failure "Invalid date/time format" "date time format: missing day 2" \
-       --begin="2012-10"
-
-expect_failure "Invalid date/time format" "seconds from origin format: too many nanosecond digits" \
-       --begin="1351532898.1231231231"
-expect_failure "Invalid date/time format" "seconds from origin format: missing nanseconds" \
-       --begin="1351532898."
-
-rm "${tmp_out}" "${tmp_err}"
diff --git a/tests/cpp-common/test-c-string-view.cpp b/tests/cpp-common/test-c-string-view.cpp
new file mode 100644 (file)
index 0000000..70e624d
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2024 EfficiOS, Inc.
+ */
+
+#include <string>
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+
+#include "tap/tap.h"
+
+namespace {
+
+template <typename StrT>
+const char *asConstCharPtr(StrT&& val)
+{
+    return val.data();
+}
+
+const char *asConstCharPtr(const char * const val)
+{
+    return val;
+}
+
+const char *typeName(bt2c::CStringView)
+{
+    return "bt2c::CStringView";
+}
+
+const char *typeName(const char *)
+{
+    return "const char *";
+}
+
+const char *typeName(const std::string&)
+{
+    return "std::string";
+}
+
+template <typename Str1T, typename Str2T>
+void testEq(Str1T&& lhs, Str2T&& rhs)
+{
+    BT_ASSERT(asConstCharPtr(lhs) != asConstCharPtr(rhs));
+    ok(lhs == rhs, "`%s` == `%s`", typeName(lhs), typeName(rhs));
+}
+
+template <typename Str1T, typename Str2T>
+void testNe(Str1T&& lhs, Str2T&& rhs)
+{
+    BT_ASSERT(asConstCharPtr(lhs) != asConstCharPtr(rhs));
+    ok(lhs != rhs, "`%s` != `%s`", typeName(lhs), typeName(rhs));
+}
+
+void testEquality()
+{
+    const std::string foo1 = "foo", foo2 = "foo";
+    const std::string bar = "bar";
+
+    /* `CStringView` vs `CStringView` */
+    testEq(bt2c::CStringView {foo1}, bt2c::CStringView {foo2});
+    testNe(bt2c::CStringView {foo1}, bt2c::CStringView {bar});
+
+    /* `CStringView` vs `const char *` */
+    testEq(bt2c::CStringView {foo1}, foo2.c_str());
+    testNe(bt2c::CStringView {foo1}, bar.c_str());
+    testEq(foo1.c_str(), bt2c::CStringView {foo2});
+    testNe(foo1.c_str(), bt2c::CStringView {bar});
+
+    /* `StringView` vs `std::string` */
+    testEq(bt2c::CStringView {foo1}, foo2);
+    testNe(bt2c::CStringView {foo1}, bar);
+    testEq(foo1, bt2c::CStringView {foo2});
+    testNe(foo1, bt2c::CStringView {bar});
+}
+
+void testStartsWith()
+{
+    ok(bt2c::CStringView {"Moutarde choux"}.startsWith("Moutarde"),
+       "\"Moutarde Choux\" starts with \"Moutarde\"");
+    ok(!bt2c::CStringView {"Moutarde choux"}.startsWith("Choux"),
+       "\"Moutarde Choux\" does not start with \"Choux\"");
+    ok(bt2c::CStringView {"Moutarde choux"}.startsWith(""), "\"Moutarde Choux\" starts with \"\"");
+    ok(bt2c::CStringView {"Moutarde choux"}.startsWith("Moutarde choux"),
+       "\"Moutarde Choux\" starts with \"Moutarde choux\"");
+    ok(!bt2c::CStringView {"Moutarde"}.startsWith("Moutarde choux"),
+       "\"Moutarde\" does not start with \"Moutarde choux\"");
+    ok(bt2c::CStringView {""}.startsWith(""), "\"\" starts with \"\"");
+}
+
+} /* namespace */
+
+int main()
+{
+    plan_tests(16);
+    testEquality();
+    testStartsWith();
+    return exit_status();
+}
diff --git a/tests/cpp-common/test-uuid.cpp b/tests/cpp-common/test-uuid.cpp
new file mode 100644 (file)
index 0000000..a84ed38
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2024 EfficiOS, Inc.
+ */
+
+#include "cpp-common/bt2c/fmt.hpp"
+#include "cpp-common/bt2c/uuid.hpp"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "tap/tap.h"
+
+namespace {
+
+constexpr auto uuidStr = "c2281e4a-699b-4b78-903f-2f8407fe2b77";
+const bt2c::Uuid uuid {uuidStr};
+const bt2c::UuidView uuidView {uuid};
+
+void testFormatAs()
+{
+    const auto resUuid = fmt::to_string(uuid);
+    const auto resUuidView = fmt::to_string(uuidView);
+
+    ok(resUuid == uuidStr, "result of format_as() for `Uuid` is expected");
+    ok(resUuidView == uuidStr, "result of format_as() for `UuidView` is expected");
+}
+
+} /* namespace */
+
+int main()
+{
+    plan_tests(2);
+    testFormatAs();
+    return exit_status();
+}
index eb4f1124083ec2dd9f2a8ebdb732d1a6e3699e8c..4bd97d2612952cb87e1c6113d7a315e5c0a743e2 100644 (file)
@@ -2,15 +2,15 @@
 
 AM_CPPFLAGS += -I$(top_srcdir)/tests/utils
 
-noinst_PROGRAMS = ctf_writer
+noinst_PROGRAMS = ctf-writer
 
-ctf_writer_SOURCES = ctf_writer.c
+ctf_writer_SOURCES = ctf-writer.c
 ctf_writer_LDADD = \
        $(top_builddir)/tests/utils/tap/libtap.la \
        $(top_builddir)/tests/utils/libtestcommon.la \
        $(top_builddir)/src/ctf-writer/libbabeltrace2-ctf-writer.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/logging/liblogging.la
 
 
-dist_check_SCRIPTS = test_ctf_writer
+dist_check_SCRIPTS = test-ctf-writer.sh
diff --git a/tests/ctf-writer/ctf-writer.c b/tests/ctf-writer/ctf-writer.c
new file mode 100644 (file)
index 0000000..0e46404
--- /dev/null
@@ -0,0 +1,2067 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2013-2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ */
+
+#include <babeltrace2-ctf-writer/writer.h>
+#include <babeltrace2-ctf-writer/clock.h>
+#include <babeltrace2-ctf-writer/clock-class.h>
+#include <babeltrace2-ctf-writer/stream.h>
+#include <babeltrace2-ctf-writer/event.h>
+#include <babeltrace2-ctf-writer/event-types.h>
+#include <babeltrace2-ctf-writer/event-fields.h>
+#include <babeltrace2-ctf-writer/stream-class.h>
+#include <babeltrace2-ctf-writer/trace.h>
+#include <babeltrace2/babeltrace.h>
+#include <glib.h>
+#include <unistd.h>
+#include "compat/stdlib.h"
+#include <stdio.h>
+#include "compat/limits.h"
+#include "compat/stdio.h"
+#include <string.h>
+#include "common/assert.h"
+#include "common/uuid.h"
+#include <fcntl.h>
+#include "tap/tap.h"
+#include <math.h>
+#include <float.h>
+#include "common.h"
+
+#ifdef __FreeBSD__
+/* Required for WIFEXITED and WEXITSTATUS */
+#include <sys/wait.h>
+#endif
+
+#define METADATA_LINE_SIZE 512
+#define SEQUENCE_TEST_LENGTH 10
+#define ARRAY_TEST_LENGTH 5
+#define PACKET_RESIZE_TEST_DEF_LENGTH 100000
+
+#define DEFAULT_CLOCK_FREQ 1000000000
+#define DEFAULT_CLOCK_PRECISION 1
+#define DEFAULT_CLOCK_OFFSET 0
+#define DEFAULT_CLOCK_OFFSET_S 0
+#define DEFAULT_CLOCK_IS_ABSOLUTE 0
+#define DEFAULT_CLOCK_TIME 0
+#define DEFAULT_CLOCK_VALUE 0
+
+#define NR_TESTS 325
+
+struct bt_utsname {
+       char sysname[BABELTRACE_HOST_NAME_MAX];
+       char nodename[BABELTRACE_HOST_NAME_MAX];
+       char release[BABELTRACE_HOST_NAME_MAX];
+       char version[BABELTRACE_HOST_NAME_MAX];
+       char machine[BABELTRACE_HOST_NAME_MAX];
+};
+
+static int64_t current_time = 42;
+static unsigned int packet_resize_test_length = PACKET_RESIZE_TEST_DEF_LENGTH;
+
+/* Return 1 if uuids match, zero if different. */
+static
+int uuid_match(const uint8_t *uuid_a, const uint8_t *uuid_b)
+{
+       int ret = 0;
+       int i;
+
+       if (!uuid_a || !uuid_b) {
+               goto end;
+       }
+
+       for (i = 0; i < 16; i++) {
+               if (uuid_a[i] != uuid_b[i]) {
+                       goto end;
+               }
+       }
+
+       ret = 1;
+end:
+       return ret;
+}
+
+static
+void validate_trace(char *parser_path, char *trace_path)
+{
+       int ret = 0;
+       gint exit_status;
+       const char *argv[] = {parser_path, trace_path, "-o", "dummy", NULL};
+
+       if (!parser_path || !trace_path) {
+               ret = -1;
+               goto result;
+       }
+
+       if (!g_spawn_sync(NULL,
+                       (gchar **) argv,
+                       NULL,
+                       G_SPAWN_STDOUT_TO_DEV_NULL,
+                       NULL,
+                       NULL,
+                       NULL,
+                       NULL,
+                       &exit_status,
+                       NULL)) {
+               diag("Failed to spawn babeltrace.");
+               ret = -1;
+               goto result;
+       }
+
+       /* Replace by g_spawn_check_exit_status when we require glib >= 2.34 */
+#ifdef G_OS_UNIX
+       ret = WIFEXITED(exit_status) ? WEXITSTATUS(exit_status) : -1;
+#else
+       ret = exit_status;
+#endif
+
+       if (ret != 0) {
+               diag("Babeltrace returned an error.");
+               goto result;
+       }
+
+result:
+       ok(ret == 0, "Babeltrace could read the resulting trace");
+}
+
+static
+void append_simple_event(struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_stream *stream, struct bt_ctf_clock *clock)
+{
+       /* Create and add a simple event class */
+       struct bt_ctf_event_class *simple_event_class =
+               bt_ctf_event_class_create("Simple Event");
+       struct bt_ctf_field_type *uint_12_type =
+               bt_ctf_field_type_integer_create(12);
+       struct bt_ctf_field_type *int_64_type =
+               bt_ctf_field_type_integer_create(64);
+       struct bt_ctf_field_type *float_type =
+               bt_ctf_field_type_floating_point_create();
+       struct bt_ctf_field_type *enum_type;
+       struct bt_ctf_field_type *enum_type_unsigned =
+               bt_ctf_field_type_enumeration_create(uint_12_type);
+       struct bt_ctf_field_type *event_context_type =
+               bt_ctf_field_type_structure_create();
+       struct bt_ctf_field_type *event_payload_type = NULL;
+       struct bt_ctf_field_type *returned_type;
+       struct bt_ctf_event *simple_event;
+       struct bt_ctf_field *integer_field;
+       struct bt_ctf_field *float_field;
+       struct bt_ctf_field *enum_field;
+       struct bt_ctf_field *enum_field_unsigned;
+       struct bt_ctf_field *enum_container_field;
+       const char *mapping_name_test = "truie";
+       const double double_test_value = 3.1415;
+       struct bt_ctf_field *enum_container_field_unsigned;
+       const char *mapping_name_negative_test = "negative_value";
+       const char *ret_char;
+       double ret_double;
+       int64_t ret_range_start_int64_t, ret_range_end_int64_t;
+       uint64_t ret_range_start_uint64_t, ret_range_end_uint64_t;
+       struct bt_ctf_event_class *ret_event_class;
+       struct bt_ctf_field *packet_context;
+       struct bt_ctf_field *packet_context_field;
+       struct bt_ctf_field *stream_event_context;
+       struct bt_ctf_field *stream_event_context_field;
+       struct bt_ctf_field *event_context;
+       struct bt_ctf_field *event_context_field;
+       struct bt_ctf_field_type *ep_integer_field_type = NULL;
+       struct bt_ctf_field_type *ep_enum_field_type = NULL;
+       struct bt_ctf_field_type *ep_enum_field_unsigned_type = NULL;
+       struct bt_ctf_field_type_enumeration_mapping_iterator *iter = NULL;
+       int ret;
+
+       ok(uint_12_type, "Create an unsigned integer type");
+
+       ok(!bt_ctf_field_type_integer_set_signed(int_64_type, 1),
+               "Set signed 64 bit integer signedness to true");
+       ok(int_64_type, "Create a signed integer type");
+       enum_type = bt_ctf_field_type_enumeration_create(int_64_type);
+
+       returned_type = bt_ctf_field_type_enumeration_get_container_field_type(enum_type);
+       ok(returned_type == int_64_type, "bt_ctf_field_type_enumeration_get_container_field_type returns the right type");
+       ok(!bt_ctf_field_type_enumeration_create(enum_type),
+               "bt_ctf_field_enumeration_type_create rejects non-integer container field types");
+       bt_ctf_object_put_ref(returned_type);
+
+       bt_ctf_field_type_set_alignment(float_type, 32);
+       ok(bt_ctf_field_type_get_alignment(float_type) == 32,
+               "bt_ctf_field_type_get_alignment returns a correct value");
+
+       ok(bt_ctf_field_type_floating_point_set_exponent_digits(float_type, 11) == 0,
+               "Set a floating point type's exponent digit count");
+       ok(bt_ctf_field_type_floating_point_set_mantissa_digits(float_type, 53) == 0,
+               "Set a floating point type's mantissa digit count");
+
+       ok(bt_ctf_field_type_floating_point_get_exponent_digits(float_type) == 11,
+               "bt_ctf_field_type_floating_point_get_exponent_digits returns the correct value");
+       ok(bt_ctf_field_type_floating_point_get_mantissa_digits(float_type) == 53,
+               "bt_ctf_field_type_floating_point_get_mantissa_digits returns the correct value");
+
+       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type,
+               mapping_name_negative_test, -12345, 0) == 0,
+               "bt_ctf_field_type_enumeration_add_mapping accepts negative enumeration mappings");
+       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type,
+               "escaping; \"test\"", 1, 1) == 0,
+               "bt_ctf_field_type_enumeration_add_mapping accepts enumeration mapping strings containing quotes");
+       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type,
+               "\tanother \'escaping\'\n test\"", 2, 4) == 0,
+               "bt_ctf_field_type_enumeration_add_mapping accepts enumeration mapping strings containing special characters");
+       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type,
+               "event clock int float", 5, 22) == 0,
+               "Accept enumeration mapping strings containing reserved keywords");
+       bt_ctf_field_type_enumeration_add_mapping(enum_type, mapping_name_test,
+               42, 42);
+       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type, mapping_name_test,
+               43, 51) == 0, "bt_ctf_field_type_enumeration_add_mapping accepts duplicate mapping names");
+       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type, "something",
+               -500, -400) == 0, "bt_ctf_field_type_enumeration_add_mapping accepts overlapping enum entries");
+       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type, mapping_name_test,
+               -54, -55), "bt_ctf_field_type_enumeration_add_mapping rejects mapping where end < start");
+       bt_ctf_field_type_enumeration_add_mapping(enum_type, "another entry", -42000, -13000);
+
+       ok(bt_ctf_event_class_add_field(simple_event_class, enum_type,
+               "enum_field") == 0, "Add signed enumeration field to event");
+
+       ok(bt_ctf_field_type_enumeration_signed_get_mapping_by_index(enum_type, 0, NULL,
+               &ret_range_start_int64_t, &ret_range_end_int64_t) == 0,
+               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index handles a NULL string correctly");
+       ok(bt_ctf_field_type_enumeration_signed_get_mapping_by_index(enum_type, 0, &ret_char,
+               NULL, &ret_range_end_int64_t) == 0,
+               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index handles a NULL start correctly");
+       ok(bt_ctf_field_type_enumeration_signed_get_mapping_by_index(enum_type, 0, &ret_char,
+               &ret_range_start_int64_t, NULL) == 0,
+               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index handles a NULL end correctly");
+       /* Assumes entries are sorted by range_start values. */
+       ok(bt_ctf_field_type_enumeration_signed_get_mapping_by_index(enum_type, 6, &ret_char,
+               &ret_range_start_int64_t, &ret_range_end_int64_t) == 0,
+               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index returns a value");
+       ok(strcmp(ret_char, mapping_name_test) == 0,
+               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index returns a correct mapping name");
+       ok(ret_range_start_int64_t == 42,
+               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index returns a correct mapping start");
+       ok(ret_range_end_int64_t == 42,
+               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index returns a correct mapping end");
+
+       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned,
+               "escaping; \"test\"", 0, 0) == 0,
+               "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts enumeration mapping strings containing quotes");
+       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned,
+               "\tanother \'escaping\'\n test\"", 1, 4) == 0,
+               "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts enumeration mapping strings containing special characters");
+       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned,
+               "event clock int float", 5, 22) == 0,
+               "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts enumeration mapping strings containing reserved keywords");
+       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned, mapping_name_test,
+               42, 42) == 0, "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts single-value ranges");
+       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned, mapping_name_test,
+               43, 51) == 0, "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts duplicate mapping names");
+       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned, "something",
+               7, 8) == 0, "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts overlapping enum entries");
+       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned, mapping_name_test,
+               55, 54), "bt_ctf_field_type_enumeration_unsigned_add_mapping rejects mapping where end < start");
+       ok(bt_ctf_event_class_add_field(simple_event_class, enum_type_unsigned,
+               "enum_field_unsigned") == 0, "Add unsigned enumeration field to event");
+
+       ok(bt_ctf_field_type_enumeration_get_mapping_count(enum_type_unsigned) == 6,
+               "bt_ctf_field_type_enumeration_get_mapping_count returns the correct value");
+
+       ok(bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(enum_type_unsigned, 0, NULL,
+               &ret_range_start_uint64_t, &ret_range_end_uint64_t) == 0,
+               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index handles a NULL string correctly");
+       ok(bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(enum_type_unsigned, 0, &ret_char,
+               NULL, &ret_range_end_uint64_t) == 0,
+               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index handles a NULL start correctly");
+       ok(bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(enum_type_unsigned, 0, &ret_char,
+               &ret_range_start_uint64_t, NULL) == 0,
+               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index handles a NULL end correctly");
+       ok(bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(enum_type_unsigned, 4, &ret_char,
+               &ret_range_start_uint64_t, &ret_range_end_uint64_t) == 0,
+               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index returns a value");
+       ok(strcmp(ret_char, mapping_name_test) == 0,
+               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index returns a correct mapping name");
+       ok(ret_range_start_uint64_t == 42,
+               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index returns a correct mapping start");
+       ok(ret_range_end_uint64_t == 42,
+               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index returns a correct mapping end");
+
+       bt_ctf_event_class_add_field(simple_event_class, uint_12_type,
+               "integer_field");
+       bt_ctf_event_class_add_field(simple_event_class, float_type,
+               "float_field");
+
+       ret = bt_ctf_event_class_set_id(simple_event_class, 13);
+       BT_ASSERT(ret == 0);
+
+       /* Set an event context type which will contain a single integer. */
+       ok(!bt_ctf_field_type_structure_add_field(event_context_type, uint_12_type,
+               "event_specific_context"),
+               "Add event specific context field");
+
+       ok(bt_ctf_event_class_set_context_field_type(NULL, event_context_type) < 0,
+               "bt_ctf_event_class_set_context_field_type handles a NULL event class correctly");
+       ok(!bt_ctf_event_class_set_context_field_type(simple_event_class, event_context_type),
+               "Set an event class' context type successfully");
+       returned_type = bt_ctf_event_class_get_context_field_type(simple_event_class);
+       ok(returned_type == event_context_type,
+               "bt_ctf_event_class_get_context_field_type returns the appropriate type");
+       bt_ctf_object_put_ref(returned_type);
+
+       ok(!bt_ctf_stream_class_add_event_class(stream_class, simple_event_class),
+               "Adding simple event class to stream class");
+
+       /*
+        * bt_ctf_stream_class_add_event_class() copies the field types
+        * of simple_event_class, so we retrieve the new ones to create
+        * the appropriate fields.
+        */
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(event_context_type);
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(event_payload_type);
+       event_payload_type = bt_ctf_event_class_get_payload_field_type(
+               simple_event_class);
+       BT_ASSERT(event_payload_type);
+       event_context_type = bt_ctf_event_class_get_context_field_type(
+               simple_event_class);
+       BT_ASSERT(event_context_type);
+       ep_integer_field_type =
+               bt_ctf_field_type_structure_get_field_type_by_name(
+                       event_payload_type, "integer_field");
+       BT_ASSERT(ep_integer_field_type);
+       ep_enum_field_type =
+               bt_ctf_field_type_structure_get_field_type_by_name(
+                       event_payload_type, "enum_field");
+       BT_ASSERT(ep_enum_field_type);
+       ep_enum_field_unsigned_type =
+               bt_ctf_field_type_structure_get_field_type_by_name(
+                       event_payload_type, "enum_field_unsigned");
+       BT_ASSERT(ep_enum_field_unsigned_type);
+
+       ok(bt_ctf_stream_class_get_event_class_count(stream_class) == 1,
+               "bt_ctf_stream_class_get_event_class_count returns a correct number of event classes");
+       ret_event_class = bt_ctf_stream_class_get_event_class_by_index(stream_class, 0);
+       ok(ret_event_class == simple_event_class,
+               "bt_ctf_stream_class_get_event_class returns the correct event class");
+       bt_ctf_object_put_ref(ret_event_class);
+       ok(!bt_ctf_stream_class_get_event_class_by_id(stream_class, 2),
+               "bt_ctf_stream_class_get_event_class_by_id returns NULL when the requested ID doesn't exist");
+       ret_event_class =
+               bt_ctf_stream_class_get_event_class_by_id(stream_class, 13);
+       ok(ret_event_class == simple_event_class,
+               "bt_ctf_stream_class_get_event_class_by_id returns a correct event class");
+       bt_ctf_object_put_ref(ret_event_class);
+
+       simple_event = bt_ctf_event_create(simple_event_class);
+       ok(simple_event,
+               "Instantiate an event containing a single integer field");
+
+       integer_field = bt_ctf_field_create(ep_integer_field_type);
+       bt_ctf_field_integer_unsigned_set_value(integer_field, 42);
+       ok(bt_ctf_event_set_payload(simple_event, "integer_field",
+               integer_field) == 0, "Use bt_ctf_event_set_payload to set a manually allocated field");
+
+       float_field = bt_ctf_event_get_payload(simple_event, "float_field");
+       bt_ctf_field_floating_point_set_value(float_field, double_test_value);
+       ok(!bt_ctf_field_floating_point_get_value(float_field, &ret_double),
+               "bt_ctf_field_floating_point_get_value returns a double value");
+       ok(fabs(ret_double - double_test_value) <= DBL_EPSILON,
+               "bt_ctf_field_floating_point_get_value returns a correct value");
+
+       enum_field = bt_ctf_field_create(ep_enum_field_type);
+       BT_ASSERT(enum_field);
+
+       enum_container_field = bt_ctf_field_enumeration_get_container(enum_field);
+       ok(bt_ctf_field_integer_signed_set_value(
+               enum_container_field, -42) == 0,
+               "Set signed enumeration container value");
+       ret = bt_ctf_event_set_payload(simple_event, "enum_field", enum_field);
+       BT_ASSERT(!ret);
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(iter);
+
+       enum_field_unsigned = bt_ctf_field_create(ep_enum_field_unsigned_type);
+       BT_ASSERT(enum_field_unsigned);
+       enum_container_field_unsigned = bt_ctf_field_enumeration_get_container(
+               enum_field_unsigned);
+       ok(bt_ctf_field_integer_unsigned_set_value(
+               enum_container_field_unsigned, 42) == 0,
+               "Set unsigned enumeration container value");
+       ret = bt_ctf_event_set_payload(simple_event, "enum_field_unsigned",
+               enum_field_unsigned);
+       BT_ASSERT(!ret);
+
+       ok(bt_ctf_clock_set_time(clock, current_time) == 0, "Set clock time");
+
+       /* Populate stream event context */
+       stream_event_context =
+               bt_ctf_event_get_stream_event_context(simple_event);
+       BT_ASSERT(stream_event_context);
+       stream_event_context_field = bt_ctf_field_structure_get_field_by_name(
+               stream_event_context, "common_event_context");
+       bt_ctf_field_integer_unsigned_set_value(stream_event_context_field, 42);
+
+       /* Populate the event's context */
+       event_context = bt_ctf_event_get_context(simple_event);
+       ok(event_context,
+               "bt_ctf_event_get_context returns a field");
+       returned_type = bt_ctf_field_get_type(event_context);
+       ok(returned_type == event_context_type,
+               "bt_ctf_event_get_context returns a field of the appropriate type");
+       event_context_field = bt_ctf_field_structure_get_field_by_name(event_context,
+               "event_specific_context");
+       ok(!bt_ctf_field_integer_unsigned_set_value(event_context_field, 1234),
+               "Successfully set an event context's value");
+       ok(!bt_ctf_event_set_context(simple_event, event_context),
+               "Set an event context successfully");
+
+       ok(bt_ctf_stream_append_event(stream, simple_event) == 0,
+               "Append simple event to trace stream");
+
+       packet_context = bt_ctf_stream_get_packet_context(stream);
+       ok(packet_context,
+               "bt_ctf_stream_get_packet_context returns a packet context");
+
+       packet_context_field = bt_ctf_field_structure_get_field_by_name(packet_context,
+               "packet_size");
+       ok(packet_context_field,
+               "Packet context contains the default packet_size field.");
+       bt_ctf_object_put_ref(packet_context_field);
+       packet_context_field = bt_ctf_field_structure_get_field_by_name(packet_context,
+               "custom_packet_context_field");
+       ok(bt_ctf_field_integer_unsigned_set_value(packet_context_field, 8) == 0,
+               "Custom packet context field value successfully set.");
+
+       ok(bt_ctf_stream_set_packet_context(stream, packet_context) == 0,
+               "Successfully set a stream's packet context");
+
+       ok(bt_ctf_stream_flush(stream) == 0,
+               "Flush trace stream with one event");
+
+       bt_ctf_object_put_ref(simple_event_class);
+       bt_ctf_object_put_ref(simple_event);
+       bt_ctf_object_put_ref(uint_12_type);
+       bt_ctf_object_put_ref(int_64_type);
+       bt_ctf_object_put_ref(float_type);
+       bt_ctf_object_put_ref(enum_type);
+       bt_ctf_object_put_ref(enum_type_unsigned);
+       bt_ctf_object_put_ref(returned_type);
+       bt_ctf_object_put_ref(event_context_type);
+       bt_ctf_object_put_ref(integer_field);
+       bt_ctf_object_put_ref(float_field);
+       bt_ctf_object_put_ref(enum_field);
+       bt_ctf_object_put_ref(enum_field_unsigned);
+       bt_ctf_object_put_ref(enum_container_field);
+       bt_ctf_object_put_ref(enum_container_field_unsigned);
+       bt_ctf_object_put_ref(packet_context);
+       bt_ctf_object_put_ref(packet_context_field);
+       bt_ctf_object_put_ref(stream_event_context);
+       bt_ctf_object_put_ref(stream_event_context_field);
+       bt_ctf_object_put_ref(event_context);
+       bt_ctf_object_put_ref(event_context_field);
+       bt_ctf_object_put_ref(event_payload_type);
+       bt_ctf_object_put_ref(ep_integer_field_type);
+       bt_ctf_object_put_ref(ep_enum_field_type);
+       bt_ctf_object_put_ref(ep_enum_field_unsigned_type);
+       bt_ctf_object_put_ref(iter);
+}
+
+static
+void append_complex_event(struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_stream *stream, struct bt_ctf_clock *clock)
+{
+       int i;
+       struct event_class_attrs_counts ;
+       const char *complex_test_event_string = "Complex Test Event";
+       const char *test_string_1 = "Test ";
+       const char *test_string_2 = "string ";
+       const char *test_string_3 = "abcdefghi";
+       const char *test_string_4 = "abcd\0efg\0hi";
+       const char *test_string_cat = "Test string abcdeefg";
+       struct bt_ctf_field_type *uint_35_type =
+               bt_ctf_field_type_integer_create(35);
+       struct bt_ctf_field_type *int_16_type =
+               bt_ctf_field_type_integer_create(16);
+       struct bt_ctf_field_type *uint_3_type =
+               bt_ctf_field_type_integer_create(3);
+       struct bt_ctf_field_type *enum_variant_type =
+               bt_ctf_field_type_enumeration_create(uint_3_type);
+       struct bt_ctf_field_type *variant_type =
+               bt_ctf_field_type_variant_create(enum_variant_type,
+                       "variant_selector");
+       struct bt_ctf_field_type *string_type =
+               bt_ctf_field_type_string_create();
+       struct bt_ctf_field_type *sequence_type;
+       struct bt_ctf_field_type *array_type;
+       struct bt_ctf_field_type *inner_structure_type =
+               bt_ctf_field_type_structure_create();
+       struct bt_ctf_field_type *complex_structure_type =
+               bt_ctf_field_type_structure_create();
+       struct bt_ctf_field_type *ret_field_type;
+       struct bt_ctf_event_class *event_class;
+       struct bt_ctf_event *event;
+       struct bt_ctf_field *uint_35_field, *int_16_field, *a_string_field,
+               *inner_structure_field, *complex_structure_field,
+               *a_sequence_field, *enum_variant_field, *enum_container_field,
+               *variant_field, *an_array_field, *stream_event_ctx_field,
+               *stream_event_ctx_int_field;
+       uint64_t ret_unsigned_int;
+       int64_t ret_signed_int;
+       const char *ret_string;
+       struct bt_ctf_stream_class *ret_stream_class;
+       struct bt_ctf_event_class *ret_event_class;
+       struct bt_ctf_field *packet_context, *packet_context_field;
+
+       ok(bt_ctf_field_type_set_alignment(int_16_type, 0),
+               "bt_ctf_field_type_set_alignment handles 0-alignment correctly");
+       ok(bt_ctf_field_type_set_alignment(int_16_type, 3),
+               "bt_ctf_field_type_set_alignment handles wrong alignment correctly (3)");
+       ok(bt_ctf_field_type_set_alignment(int_16_type, 24),
+               "bt_ctf_field_type_set_alignment handles wrong alignment correctly (24)");
+       ok(!bt_ctf_field_type_set_alignment(int_16_type, 4),
+               "bt_ctf_field_type_set_alignment handles correct alignment correctly (4)");
+       ok(!bt_ctf_field_type_set_alignment(int_16_type, 32),
+               "Set alignment of signed 16 bit integer to 32");
+       ok(!bt_ctf_field_type_integer_set_signed(int_16_type, 1),
+               "Set integer signedness to true");
+       ok(!bt_ctf_field_type_integer_set_base(uint_35_type,
+               BT_CTF_INTEGER_BASE_HEXADECIMAL),
+               "Set signed 16 bit integer base to hexadecimal");
+
+       array_type = bt_ctf_field_type_array_create(int_16_type, ARRAY_TEST_LENGTH);
+       sequence_type = bt_ctf_field_type_sequence_create(int_16_type,
+               "seq_len");
+
+       ret_field_type = bt_ctf_field_type_array_get_element_field_type(
+               array_type);
+       ok(ret_field_type == int_16_type,
+               "bt_ctf_field_type_array_get_element_field_type returns the correct type");
+       bt_ctf_object_put_ref(ret_field_type);
+
+       ok(bt_ctf_field_type_array_get_length(array_type) == ARRAY_TEST_LENGTH,
+               "bt_ctf_field_type_array_get_length returns the correct length");
+
+       ok(bt_ctf_field_type_structure_add_field(inner_structure_type,
+               inner_structure_type, "yes"), "Cannot add self to structure");
+       ok(!bt_ctf_field_type_structure_add_field(inner_structure_type,
+               uint_35_type, "seq_len"), "Add seq_len field to inner structure");
+       ok(!bt_ctf_field_type_structure_add_field(inner_structure_type,
+               sequence_type, "a_sequence"), "Add a_sequence field to inner structure");
+       ok(!bt_ctf_field_type_structure_add_field(inner_structure_type,
+               array_type, "an_array"), "Add an_array field to inner structure");
+
+       bt_ctf_field_type_enumeration_add_mapping(enum_variant_type,
+               "UINT3_TYPE", 0, 0);
+       bt_ctf_field_type_enumeration_add_mapping(enum_variant_type,
+               "INT16_TYPE", 1, 1);
+       bt_ctf_field_type_enumeration_add_mapping(enum_variant_type,
+               "UINT35_TYPE", 2, 7);
+
+       ok(bt_ctf_field_type_variant_add_field(variant_type, uint_3_type,
+               "An unknown entry"), "Reject a variant field based on an unknown tag value");
+       ok(bt_ctf_field_type_variant_add_field(variant_type, uint_3_type,
+               "UINT3_TYPE") == 0, "Add a field to a variant");
+       ok(!bt_ctf_field_type_variant_add_field(variant_type, int_16_type,
+               "INT16_TYPE"), "Add INT16_TYPE field to variant");
+       ok(!bt_ctf_field_type_variant_add_field(variant_type, uint_35_type,
+               "UINT35_TYPE"), "Add UINT35_TYPE field to variant");
+
+       ret_field_type = bt_ctf_field_type_variant_get_tag_field_type(variant_type);
+       ok(ret_field_type == enum_variant_type,
+               "bt_ctf_field_type_variant_get_tag_field_type returns a correct tag type");
+       bt_ctf_object_put_ref(ret_field_type);
+
+       ret_string = bt_ctf_field_type_variant_get_tag_name(variant_type);
+       ok(ret_string ? strcmp(ret_string, "variant_selector") == 0 : 0,
+               "bt_ctf_field_type_variant_get_tag_name returns the correct variant tag name");
+       ret_field_type = bt_ctf_field_type_variant_get_field_type_by_name(
+               variant_type, "INT16_TYPE");
+       ok(ret_field_type == int_16_type,
+               "bt_ctf_field_type_variant_get_field_type_by_name returns a correct field type");
+       bt_ctf_object_put_ref(ret_field_type);
+
+       ok(bt_ctf_field_type_variant_get_field_count(variant_type) == 3,
+               "bt_ctf_field_type_variant_get_field_count returns the correct count");
+
+       ok(bt_ctf_field_type_variant_get_field_by_index(variant_type, NULL, &ret_field_type, 0) == 0,
+               "bt_ctf_field_type_variant_get_field handles a NULL field name correctly");
+       bt_ctf_object_put_ref(ret_field_type);
+       ok(bt_ctf_field_type_variant_get_field_by_index(variant_type, &ret_string, NULL, 0) == 0,
+               "bt_ctf_field_type_variant_get_field handles a NULL field type correctly");
+       ok(bt_ctf_field_type_variant_get_field_by_index(variant_type, &ret_string, &ret_field_type, 1) == 0,
+               "bt_ctf_field_type_variant_get_field returns a field");
+       ok(strcmp("INT16_TYPE", ret_string) == 0,
+               "bt_ctf_field_type_variant_get_field returns a correct field name");
+       ok(ret_field_type == int_16_type,
+               "bt_ctf_field_type_variant_get_field returns a correct field type");
+       bt_ctf_object_put_ref(ret_field_type);
+
+       ok(!bt_ctf_field_type_structure_add_field(complex_structure_type,
+               enum_variant_type, "variant_selector"),
+               "Add variant_selector field to complex structure");
+       ok(!bt_ctf_field_type_structure_add_field(complex_structure_type,
+               string_type, "string"), "Add `string` field to complex structure");
+       ok(!bt_ctf_field_type_structure_add_field(complex_structure_type,
+               variant_type, "variant_value"),
+               "Add variant_value field to complex structure");
+       ok(!bt_ctf_field_type_structure_add_field(complex_structure_type,
+               inner_structure_type, "inner_structure"),
+               "Add inner_structure field to complex structure");
+
+       event_class = bt_ctf_event_class_create(complex_test_event_string);
+       ok(event_class, "Create an event class");
+       ok(bt_ctf_event_class_add_field(event_class, uint_35_type, ""),
+               "Reject addition of a field with an empty name to an event");
+       ok(bt_ctf_event_class_add_field(event_class, NULL, "an_integer"),
+               "Reject addition of a field with a NULL type to an event");
+       ok(bt_ctf_event_class_add_field(event_class, uint_35_type,
+               "int"),
+               "Reject addition of a type with an illegal name to an event");
+       ok(bt_ctf_event_class_add_field(event_class, uint_35_type,
+               "uint_35") == 0,
+               "Add field of type unsigned integer to an event");
+       ok(bt_ctf_event_class_add_field(event_class, int_16_type,
+               "int_16") == 0, "Add field of type signed integer to an event");
+       ok(bt_ctf_event_class_add_field(event_class, complex_structure_type,
+               "complex_structure") == 0,
+               "Add composite structure to an event");
+
+       ret_string = bt_ctf_event_class_get_name(event_class);
+       ok(strcmp(ret_string, complex_test_event_string) == 0,
+               "bt_ctf_event_class_get_name returns a correct name");
+       ok(bt_ctf_event_class_get_id(event_class) < 0,
+               "bt_ctf_event_class_get_id returns a negative value when not set");
+       ok(bt_ctf_event_class_set_id(NULL, 42) < 0,
+               "bt_ctf_event_class_set_id handles NULL correctly");
+       ok(bt_ctf_event_class_set_id(event_class, 42) == 0,
+               "Set an event class' id");
+       ok(bt_ctf_event_class_get_id(event_class) == 42,
+               "bt_ctf_event_class_get_id returns the correct value");
+
+       /* Test event class attributes */
+       ok(bt_ctf_event_class_get_log_level(event_class) == BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED,
+               "event class has the expected initial log level");
+       ok(!bt_ctf_event_class_get_emf_uri(event_class),
+               "as expected, event class has no initial EMF URI");
+       ok(bt_ctf_event_class_set_log_level(NULL, BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO),
+               "bt_ctf_event_class_set_log_level handles a NULL event class correctly");
+       ok(bt_ctf_event_class_set_log_level(event_class, BT_CTF_EVENT_CLASS_LOG_LEVEL_UNKNOWN),
+               "bt_ctf_event_class_set_log_level handles an unknown log level correctly");
+       ok(!bt_ctf_event_class_set_log_level(event_class, BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO),
+               "bt_ctf_event_class_set_log_level succeeds with a valid log level");
+       ok(bt_ctf_event_class_get_log_level(event_class) == BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO,
+               "bt_ctf_event_class_get_log_level returns the expected log level");
+       ok(bt_ctf_event_class_set_emf_uri(NULL, "https://babeltrace.org/"),
+               "bt_ctf_event_class_set_emf_uri handles a NULL event class correctly");
+       ok(!bt_ctf_event_class_set_emf_uri(event_class, "https://babeltrace.org/"),
+               "bt_ctf_event_class_set_emf_uri succeeds with a valid EMF URI");
+       ok(strcmp(bt_ctf_event_class_get_emf_uri(event_class), "https://babeltrace.org/") == 0,
+               "bt_ctf_event_class_get_emf_uri returns the expected EMF URI");
+       ok(!bt_ctf_event_class_set_emf_uri(event_class, NULL),
+               "bt_ctf_event_class_set_emf_uri succeeds with NULL (to reset)");
+       ok(!bt_ctf_event_class_get_emf_uri(event_class),
+               "as expected, event class has no EMF URI after reset");
+
+       /* Add event class to the stream class */
+       ok(bt_ctf_stream_class_add_event_class(stream_class, NULL),
+               "Reject addition of NULL event class to a stream class");
+       ok(bt_ctf_stream_class_add_event_class(stream_class,
+               event_class) == 0, "Add an event class to stream class");
+
+       ret_stream_class = bt_ctf_event_class_get_stream_class(event_class);
+       ok(ret_stream_class == stream_class,
+               "bt_ctf_event_class_get_stream_class returns the correct stream class");
+       bt_ctf_object_put_ref(ret_stream_class);
+
+       ok(!bt_ctf_event_class_get_field_by_name(event_class, "truie"),
+               "bt_ctf_event_class_get_field_by_name handles an invalid field name correctly");
+       ret_field_type = bt_ctf_event_class_get_field_by_name(event_class,
+               "complex_structure");
+       bt_ctf_object_put_ref(ret_field_type);
+
+       event = bt_ctf_event_create(event_class);
+       ok(event, "Instantiate a complex event");
+
+       ret_event_class = bt_ctf_event_get_class(event);
+       ok(ret_event_class == event_class,
+               "bt_ctf_event_get_class returns the correct event class");
+       bt_ctf_object_put_ref(ret_event_class);
+
+       uint_35_field = bt_ctf_event_get_payload(event, "uint_35");
+       ok(uint_35_field, "Use bt_ctf_event_get_payload to get a field instance ");
+       bt_ctf_field_integer_unsigned_set_value(uint_35_field, 0x0DDF00D);
+       ok(bt_ctf_field_integer_unsigned_get_value(uint_35_field,
+               &ret_unsigned_int) == 0,
+               "bt_ctf_field_integer_unsigned_get_value succeeds after setting a value");
+       ok(ret_unsigned_int == 0x0DDF00D,
+               "bt_ctf_field_integer_unsigned_get_value returns the correct value");
+       bt_ctf_object_put_ref(uint_35_field);
+
+       int_16_field = bt_ctf_event_get_payload(event, "int_16");
+       bt_ctf_field_integer_signed_set_value(int_16_field, -12345);
+       ok(bt_ctf_field_integer_signed_get_value(int_16_field,
+               &ret_signed_int) == 0,
+               "bt_ctf_field_integer_signed_get_value succeeds after setting a value");
+       ok(ret_signed_int == -12345,
+               "bt_ctf_field_integer_signed_get_value returns the correct value");
+       bt_ctf_object_put_ref(int_16_field);
+
+       complex_structure_field = bt_ctf_event_get_payload(event,
+               "complex_structure");
+
+       inner_structure_field = bt_ctf_field_structure_get_field_by_index(
+               complex_structure_field, 3);
+       ret_field_type = bt_ctf_field_get_type(inner_structure_field);
+       bt_ctf_object_put_ref(inner_structure_field);
+       bt_ctf_object_put_ref(ret_field_type);
+
+       inner_structure_field = bt_ctf_field_structure_get_field_by_name(
+               complex_structure_field, "inner_structure");
+       a_string_field = bt_ctf_field_structure_get_field_by_name(
+               complex_structure_field, "string");
+       enum_variant_field = bt_ctf_field_structure_get_field_by_name(
+               complex_structure_field, "variant_selector");
+       variant_field = bt_ctf_field_structure_get_field_by_name(
+               complex_structure_field, "variant_value");
+       uint_35_field = bt_ctf_field_structure_get_field_by_name(
+               inner_structure_field, "seq_len");
+       a_sequence_field = bt_ctf_field_structure_get_field_by_name(
+               inner_structure_field, "a_sequence");
+       an_array_field = bt_ctf_field_structure_get_field_by_name(
+               inner_structure_field, "an_array");
+
+       enum_container_field = bt_ctf_field_enumeration_get_container(
+               enum_variant_field);
+       bt_ctf_field_integer_unsigned_set_value(enum_container_field, 1);
+       int_16_field = bt_ctf_field_variant_get_field(variant_field,
+               enum_variant_field);
+       bt_ctf_field_integer_signed_set_value(int_16_field, -200);
+       bt_ctf_object_put_ref(int_16_field);
+       bt_ctf_field_string_set_value(a_string_field,
+               test_string_1);
+       ok(!bt_ctf_field_string_append(a_string_field, test_string_2),
+               "bt_ctf_field_string_append succeeds");
+       ok(!bt_ctf_field_string_append_len(a_string_field, test_string_3, 5),
+               "bt_ctf_field_string_append_len succeeds (append 5 characters)");
+       ok(!bt_ctf_field_string_append_len(a_string_field, &test_string_4[5], 3),
+               "bt_ctf_field_string_append_len succeeds (append 0 characters)");
+       ok(!bt_ctf_field_string_append_len(a_string_field, test_string_3, 0),
+               "bt_ctf_field_string_append_len succeeds (append 0 characters)");
+
+       ret_string = bt_ctf_field_string_get_value(a_string_field);
+       ok(ret_string, "bt_ctf_field_string_get_value returns a string");
+       ok(ret_string ? strcmp(ret_string, test_string_cat) == 0 : 0,
+               "bt_ctf_field_string_get_value returns a correct value");
+       bt_ctf_field_integer_unsigned_set_value(uint_35_field,
+               SEQUENCE_TEST_LENGTH);
+
+       ret_field_type = bt_ctf_field_type_variant_get_field_type_from_tag(
+               variant_type, enum_variant_field);
+       ok(ret_field_type == int_16_type,
+               "bt_ctf_field_type_variant_get_field_type_from_tag returns the correct field type");
+
+       ok(bt_ctf_field_sequence_set_length(a_sequence_field,
+               uint_35_field) == 0, "Set a sequence field's length");
+
+       for (i = 0; i < SEQUENCE_TEST_LENGTH; i++) {
+               int_16_field = bt_ctf_field_sequence_get_field(
+                       a_sequence_field, i);
+               bt_ctf_field_integer_signed_set_value(int_16_field, 4 - i);
+               bt_ctf_object_put_ref(int_16_field);
+       }
+
+       for (i = 0; i < ARRAY_TEST_LENGTH; i++) {
+               int_16_field = bt_ctf_field_array_get_field(
+                       an_array_field, i);
+               bt_ctf_field_integer_signed_set_value(int_16_field, i);
+               bt_ctf_object_put_ref(int_16_field);
+       }
+
+       stream_event_ctx_field = bt_ctf_event_get_stream_event_context(event);
+       BT_ASSERT(stream_event_ctx_field);
+       stream_event_ctx_int_field = bt_ctf_field_structure_get_field_by_name(
+               stream_event_ctx_field, "common_event_context");
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_ctx_field);
+       bt_ctf_field_integer_unsigned_set_value(stream_event_ctx_int_field, 17);
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_ctx_int_field);
+
+       bt_ctf_clock_set_time(clock, ++current_time);
+       ok(bt_ctf_stream_append_event(stream, event) == 0,
+               "Append a complex event to a stream");
+
+       /*
+        * Populate the custom packet context field with a dummy value
+        * otherwise flush will fail.
+        */
+       packet_context = bt_ctf_stream_get_packet_context(stream);
+       packet_context_field = bt_ctf_field_structure_get_field_by_name(packet_context,
+               "custom_packet_context_field");
+       bt_ctf_field_integer_unsigned_set_value(packet_context_field, 1);
+
+       ok(bt_ctf_stream_flush(stream) == 0,
+               "Flush a stream containing a complex event");
+
+       bt_ctf_object_put_ref(uint_35_field);
+       bt_ctf_object_put_ref(a_string_field);
+       bt_ctf_object_put_ref(inner_structure_field);
+       bt_ctf_object_put_ref(complex_structure_field);
+       bt_ctf_object_put_ref(a_sequence_field);
+       bt_ctf_object_put_ref(an_array_field);
+       bt_ctf_object_put_ref(enum_variant_field);
+       bt_ctf_object_put_ref(enum_container_field);
+       bt_ctf_object_put_ref(variant_field);
+       bt_ctf_object_put_ref(packet_context_field);
+       bt_ctf_object_put_ref(packet_context);
+       bt_ctf_object_put_ref(uint_35_type);
+       bt_ctf_object_put_ref(int_16_type);
+       bt_ctf_object_put_ref(string_type);
+       bt_ctf_object_put_ref(sequence_type);
+       bt_ctf_object_put_ref(array_type);
+       bt_ctf_object_put_ref(inner_structure_type);
+       bt_ctf_object_put_ref(complex_structure_type);
+       bt_ctf_object_put_ref(uint_3_type);
+       bt_ctf_object_put_ref(enum_variant_type);
+       bt_ctf_object_put_ref(variant_type);
+       bt_ctf_object_put_ref(ret_field_type);
+       bt_ctf_object_put_ref(event_class);
+       bt_ctf_object_put_ref(event);
+}
+
+static
+void type_field_tests(void)
+{
+       struct bt_ctf_field *uint_12;
+       struct bt_ctf_field *int_16;
+       struct bt_ctf_field *string;
+       struct bt_ctf_field_type *composite_structure_type;
+       struct bt_ctf_field_type *structure_seq_type;
+       struct bt_ctf_field_type *string_type;
+       struct bt_ctf_field_type *sequence_type;
+       struct bt_ctf_field_type *uint_8_type;
+       struct bt_ctf_field_type *int_16_type;
+       struct bt_ctf_field_type *uint_12_type =
+               bt_ctf_field_type_integer_create(12);
+       struct bt_ctf_field_type *enumeration_type;
+       struct bt_ctf_field_type *returned_type;
+       const char *ret_string;
+
+       ok(uint_12_type, "Create an unsigned integer type");
+       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
+               BT_CTF_INTEGER_BASE_BINARY) == 0,
+               "Set integer type's base as binary");
+       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
+               BT_CTF_INTEGER_BASE_DECIMAL) == 0,
+               "Set integer type's base as decimal");
+       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
+               BT_CTF_INTEGER_BASE_UNKNOWN),
+               "Reject integer type's base set as unknown");
+       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
+               BT_CTF_INTEGER_BASE_OCTAL) == 0,
+               "Set integer type's base as octal");
+       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
+               BT_CTF_INTEGER_BASE_HEXADECIMAL) == 0,
+               "Set integer type's base as hexadecimal");
+       ok(bt_ctf_field_type_integer_set_base(uint_12_type, 457417),
+               "Reject unknown integer base value");
+       ok(bt_ctf_field_type_integer_set_signed(uint_12_type, 952835) == 0,
+               "Set integer type signedness to signed");
+       ok(bt_ctf_field_type_integer_set_signed(uint_12_type, 0) == 0,
+               "Set integer type signedness to unsigned");
+       ok(bt_ctf_field_type_integer_get_size(uint_12_type) == 12,
+               "bt_ctf_field_type_integer_get_size returns a correct value");
+       ok(bt_ctf_field_type_integer_get_signed(uint_12_type) == 0,
+               "bt_ctf_field_type_integer_get_signed returns a correct value for unsigned types");
+
+       ok(bt_ctf_field_type_set_byte_order(NULL,
+               BT_CTF_BYTE_ORDER_LITTLE_ENDIAN) < 0,
+               "bt_ctf_field_type_set_byte_order handles NULL correctly");
+       ok(bt_ctf_field_type_set_byte_order(uint_12_type,
+               (enum bt_ctf_byte_order) 42) < 0,
+               "bt_ctf_field_type_set_byte_order rejects invalid values");
+       ok(bt_ctf_field_type_set_byte_order(uint_12_type,
+               BT_CTF_BYTE_ORDER_LITTLE_ENDIAN) == 0,
+               "Set an integer's byte order to little endian");
+       ok(bt_ctf_field_type_set_byte_order(uint_12_type,
+               BT_CTF_BYTE_ORDER_BIG_ENDIAN) == 0,
+               "Set an integer's byte order to big endian");
+       ok(bt_ctf_field_type_get_byte_order(uint_12_type) ==
+               BT_CTF_BYTE_ORDER_BIG_ENDIAN,
+               "bt_ctf_field_type_get_byte_order returns a correct value");
+
+       ok(bt_ctf_field_type_get_type_id(uint_12_type) ==
+               BT_CTF_FIELD_TYPE_ID_INTEGER,
+               "bt_ctf_field_type_get_type_id returns a correct value with an integer type");
+
+       ok(bt_ctf_field_type_integer_get_base(uint_12_type) ==
+               BT_CTF_INTEGER_BASE_HEXADECIMAL,
+               "bt_ctf_field_type_integer_get_base returns a correct value");
+
+       ok(bt_ctf_field_type_integer_set_encoding(NULL,
+               BT_CTF_STRING_ENCODING_ASCII) < 0,
+               "bt_ctf_field_type_integer_set_encoding handles NULL correctly");
+       ok(bt_ctf_field_type_integer_set_encoding(uint_12_type,
+               (enum bt_ctf_string_encoding) 123) < 0,
+               "bt_ctf_field_type_integer_set_encoding handles invalid encodings correctly");
+       ok(bt_ctf_field_type_integer_set_encoding(uint_12_type,
+               BT_CTF_STRING_ENCODING_UTF8) == 0,
+               "Set integer type encoding to UTF8");
+       ok(bt_ctf_field_type_integer_get_encoding(uint_12_type) ==
+               BT_CTF_STRING_ENCODING_UTF8,
+               "bt_ctf_field_type_integer_get_encoding returns a correct value");
+
+       int_16_type = bt_ctf_field_type_integer_create(16);
+       BT_ASSERT(int_16_type);
+       ok(!bt_ctf_field_type_integer_set_signed(int_16_type, 1),
+               "Set signedness of 16 bit integer to true");
+       ok(bt_ctf_field_type_integer_get_signed(int_16_type) == 1,
+               "bt_ctf_field_type_integer_get_signed returns a correct value for signed types");
+       uint_8_type = bt_ctf_field_type_integer_create(8);
+       sequence_type =
+               bt_ctf_field_type_sequence_create(int_16_type, "seq_len");
+       ok(sequence_type, "Create a sequence of int16_t type");
+       ok(bt_ctf_field_type_get_type_id(sequence_type) ==
+               BT_CTF_FIELD_TYPE_ID_SEQUENCE,
+               "bt_ctf_field_type_get_type_id returns a correct value with a sequence type");
+
+       ret_string = bt_ctf_field_type_sequence_get_length_field_name(
+               sequence_type);
+       ok(strcmp(ret_string, "seq_len") == 0,
+               "bt_ctf_field_type_sequence_get_length_field_name returns the correct value");
+       returned_type = bt_ctf_field_type_sequence_get_element_field_type(
+               sequence_type);
+       ok(returned_type == int_16_type,
+               "bt_ctf_field_type_sequence_get_element_field_type returns the correct type");
+       bt_ctf_object_put_ref(returned_type);
+
+       string_type = bt_ctf_field_type_string_create();
+       ok(string_type, "Create a string type");
+       ok(bt_ctf_field_type_string_set_encoding(string_type,
+               BT_CTF_STRING_ENCODING_NONE),
+               "Reject invalid \"None\" string encoding");
+       ok(bt_ctf_field_type_string_set_encoding(string_type,
+               42),
+               "Reject invalid string encoding");
+       ok(bt_ctf_field_type_string_set_encoding(string_type,
+               BT_CTF_STRING_ENCODING_ASCII) == 0,
+               "Set string encoding to ASCII");
+
+       ok(bt_ctf_field_type_string_get_encoding(string_type) ==
+               BT_CTF_STRING_ENCODING_ASCII,
+               "bt_ctf_field_type_string_get_encoding returns the correct value");
+
+       structure_seq_type = bt_ctf_field_type_structure_create();
+       ok(bt_ctf_field_type_get_type_id(structure_seq_type) ==
+               BT_CTF_FIELD_TYPE_ID_STRUCT,
+               "bt_ctf_field_type_get_type_id returns a correct value with a structure type");
+       ok(structure_seq_type, "Create a structure type");
+       ok(bt_ctf_field_type_structure_add_field(structure_seq_type,
+               uint_8_type, "seq_len") == 0,
+               "Add a uint8_t type to a structure");
+       ok(bt_ctf_field_type_structure_add_field(structure_seq_type,
+               sequence_type, "a_sequence") == 0,
+               "Add a sequence type to a structure");
+
+       ok(bt_ctf_field_type_structure_get_field_count(structure_seq_type) == 2,
+               "bt_ctf_field_type_structure_get_field_count returns a correct value");
+
+       ok(bt_ctf_field_type_structure_get_field(structure_seq_type,
+               NULL, &returned_type, 1) == 0,
+               "bt_ctf_field_type_structure_get_field handles a NULL name correctly");
+       bt_ctf_object_put_ref(returned_type);
+       ok(bt_ctf_field_type_structure_get_field(structure_seq_type,
+               &ret_string, NULL, 1) == 0,
+               "bt_ctf_field_type_structure_get_field handles a NULL return type correctly");
+       ok(bt_ctf_field_type_structure_get_field(structure_seq_type,
+               &ret_string, &returned_type, 1) == 0,
+               "bt_ctf_field_type_structure_get_field returns a field");
+       ok(strcmp(ret_string, "a_sequence") == 0,
+               "bt_ctf_field_type_structure_get_field returns a correct field name");
+       ok(returned_type == sequence_type,
+               "bt_ctf_field_type_structure_get_field returns a correct field type");
+       bt_ctf_object_put_ref(returned_type);
+
+       returned_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               structure_seq_type, "a_sequence");
+       ok(returned_type == sequence_type,
+               "bt_ctf_field_type_structure_get_field_type_by_name returns the correct field type");
+       bt_ctf_object_put_ref(returned_type);
+
+       composite_structure_type = bt_ctf_field_type_structure_create();
+       ok(bt_ctf_field_type_structure_add_field(composite_structure_type,
+               string_type, "a_string") == 0,
+               "Add a string type to a structure");
+       ok(bt_ctf_field_type_structure_add_field(composite_structure_type,
+               structure_seq_type, "inner_structure") == 0,
+               "Add a structure type to a structure");
+
+       returned_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               structure_seq_type, "a_sequence");
+       ok(returned_type == sequence_type,
+               "bt_ctf_field_type_structure_get_field_type_by_name returns a correct type");
+       bt_ctf_object_put_ref(returned_type);
+
+       int_16 = bt_ctf_field_create(int_16_type);
+       ok(int_16, "Instantiate a signed 16-bit integer");
+       uint_12 = bt_ctf_field_create(uint_12_type);
+       ok(uint_12, "Instantiate an unsigned 12-bit integer");
+       returned_type = bt_ctf_field_get_type(int_16);
+       ok(returned_type == int_16_type,
+               "bt_ctf_field_get_type returns the correct type");
+
+       /* Can't modify types after instantiating them */
+       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
+               BT_CTF_INTEGER_BASE_DECIMAL),
+               "Check an integer type' base can't be modified after instantiation");
+       ok(bt_ctf_field_type_integer_set_signed(uint_12_type, 0),
+               "Check an integer type's signedness can't be modified after instantiation");
+
+       /* Check overflows are properly tested for */
+       ok(bt_ctf_field_integer_signed_set_value(int_16, -32768) == 0,
+               "Check -32768 is allowed for a signed 16-bit integer");
+       ok(bt_ctf_field_integer_signed_set_value(int_16, 32767) == 0,
+               "Check 32767 is allowed for a signed 16-bit integer");
+       ok(bt_ctf_field_integer_signed_set_value(int_16, -42) == 0,
+               "Check -42 is allowed for a signed 16-bit integer");
+
+       ok(bt_ctf_field_integer_unsigned_set_value(uint_12, 4095) == 0,
+               "Check 4095 is allowed for an unsigned 12-bit integer");
+       ok(bt_ctf_field_integer_unsigned_set_value(uint_12, 0) == 0,
+               "Check 0 is allowed for an unsigned 12-bit integer");
+
+       string = bt_ctf_field_create(string_type);
+       ok(string, "Instantiate a string field");
+       ok(bt_ctf_field_string_set_value(string, "A value") == 0,
+               "Set a string's value");
+
+       enumeration_type = bt_ctf_field_type_enumeration_create(uint_12_type);
+       ok(enumeration_type,
+               "Create an enumeration type with an unsigned 12-bit integer as container");
+
+       bt_ctf_object_put_ref(string);
+       bt_ctf_object_put_ref(uint_12);
+       bt_ctf_object_put_ref(int_16);
+       bt_ctf_object_put_ref(composite_structure_type);
+       bt_ctf_object_put_ref(structure_seq_type);
+       bt_ctf_object_put_ref(string_type);
+       bt_ctf_object_put_ref(sequence_type);
+       bt_ctf_object_put_ref(uint_8_type);
+       bt_ctf_object_put_ref(int_16_type);
+       bt_ctf_object_put_ref(uint_12_type);
+       bt_ctf_object_put_ref(enumeration_type);
+       bt_ctf_object_put_ref(returned_type);
+}
+
+static
+void packet_resize_test(struct bt_ctf_stream_class *stream_class,
+               struct bt_ctf_stream *stream, struct bt_ctf_clock *clock)
+{
+       /*
+        * Append enough events to force the underlying packet to be resized.
+        * Also tests that a new event can be declared after a stream has been
+        * instantiated and used/flushed.
+        */
+       int ret = 0;
+       int i;
+       struct bt_ctf_event_class *event_class = bt_ctf_event_class_create(
+               "Spammy_Event");
+       struct bt_ctf_field_type *integer_type =
+               bt_ctf_field_type_integer_create(17);
+       struct bt_ctf_field_type *string_type =
+               bt_ctf_field_type_string_create();
+       struct bt_ctf_event *event = NULL;
+       struct bt_ctf_field *ret_field = NULL;
+       struct bt_ctf_field_type *ret_field_type = NULL;
+       uint64_t ret_uint64;
+       int events_appended = 0;
+       struct bt_ctf_field *packet_context = NULL,
+               *packet_context_field = NULL, *stream_event_context = NULL;
+       struct bt_ctf_field_type *ep_field_1_type = NULL;
+       struct bt_ctf_field_type *ep_a_string_type = NULL;
+       struct bt_ctf_field_type *ep_type = NULL;
+
+       ret |= bt_ctf_event_class_add_field(event_class, integer_type,
+               "field_1");
+       ret |= bt_ctf_event_class_add_field(event_class, string_type,
+               "a_string");
+       ret |= bt_ctf_stream_class_add_event_class(stream_class, event_class);
+       ok(ret == 0, "Add a new event class to a stream class after writing an event");
+       if (ret) {
+               goto end;
+       }
+
+       /*
+        * bt_ctf_stream_class_add_event_class() copies the field types
+        * of event_class, so we retrieve the new ones to create the
+        * appropriate fields.
+        */
+       ep_type = bt_ctf_event_class_get_payload_field_type(event_class);
+       BT_ASSERT(ep_type);
+       ep_field_1_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               ep_type, "field_1");
+       BT_ASSERT(ep_field_1_type);
+       ep_a_string_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               ep_type, "a_string");
+       BT_ASSERT(ep_a_string_type);
+
+       event = bt_ctf_event_create(event_class);
+       ret_field = bt_ctf_event_get_payload(event, 0);
+       ret_field_type = bt_ctf_field_get_type(ret_field);
+       bt_ctf_object_put_ref(ret_field_type);
+       bt_ctf_object_put_ref(ret_field);
+       bt_ctf_object_put_ref(event);
+
+       for (i = 0; i < packet_resize_test_length; i++) {
+               event = bt_ctf_event_create(event_class);
+               struct bt_ctf_field *integer =
+                       bt_ctf_field_create(ep_field_1_type);
+               struct bt_ctf_field *string =
+                       bt_ctf_field_create(ep_a_string_type);
+
+               ret |= bt_ctf_clock_set_time(clock, ++current_time);
+               ret |= bt_ctf_field_integer_unsigned_set_value(integer, i);
+               ret |= bt_ctf_event_set_payload(event, "field_1",
+                       integer);
+               bt_ctf_object_put_ref(integer);
+               ret |= bt_ctf_field_string_set_value(string, "This is a test");
+               ret |= bt_ctf_event_set_payload(event, "a_string",
+                       string);
+               bt_ctf_object_put_ref(string);
+
+               /* Populate stream event context */
+               stream_event_context =
+                       bt_ctf_event_get_stream_event_context(event);
+               integer = bt_ctf_field_structure_get_field_by_name(stream_event_context,
+                       "common_event_context");
+               BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_context);
+               ret |= bt_ctf_field_integer_unsigned_set_value(integer,
+                       i % 42);
+               bt_ctf_object_put_ref(integer);
+
+               ret |= bt_ctf_stream_append_event(stream, event);
+               bt_ctf_object_put_ref(event);
+
+               if (ret) {
+                       break;
+               }
+       }
+
+       events_appended = !!(i == packet_resize_test_length);
+       ret = bt_ctf_stream_get_discarded_events_count(stream, &ret_uint64);
+       ok(ret == 0 && ret_uint64 == 0,
+               "bt_ctf_stream_get_discarded_events_count returns a correct number of discarded events when none were discarded");
+       bt_ctf_stream_append_discarded_events(stream, 1000);
+       ret = bt_ctf_stream_get_discarded_events_count(stream, &ret_uint64);
+       ok(ret == 0 && ret_uint64 == 1000,
+               "bt_ctf_stream_get_discarded_events_count returns a correct number of discarded events when some were discarded");
+
+end:
+       ok(events_appended, "Append 100 000 events to a stream");
+
+       /*
+        * Populate the custom packet context field with a dummy value
+        * otherwise flush will fail.
+        */
+       packet_context = bt_ctf_stream_get_packet_context(stream);
+       packet_context_field = bt_ctf_field_structure_get_field_by_name(packet_context,
+               "custom_packet_context_field");
+       bt_ctf_field_integer_unsigned_set_value(packet_context_field, 2);
+
+       ok(bt_ctf_stream_flush(stream) == 0,
+               "Flush a stream that forces a packet resize");
+       ret = bt_ctf_stream_get_discarded_events_count(stream, &ret_uint64);
+       ok(ret == 0 && ret_uint64 == 1000,
+               "bt_ctf_stream_get_discarded_events_count returns a correct number of discarded events after a flush");
+       bt_ctf_object_put_ref(integer_type);
+       bt_ctf_object_put_ref(string_type);
+       bt_ctf_object_put_ref(packet_context);
+       bt_ctf_object_put_ref(packet_context_field);
+       bt_ctf_object_put_ref(stream_event_context);
+       bt_ctf_object_put_ref(event_class);
+       bt_ctf_object_put_ref(ep_field_1_type);
+       bt_ctf_object_put_ref(ep_a_string_type);
+       bt_ctf_object_put_ref(ep_type);
+}
+
+static
+void test_empty_stream(struct bt_ctf_writer *writer)
+{
+       int ret = 0;
+       struct bt_ctf_trace *trace = NULL, *ret_trace = NULL;
+       struct bt_ctf_stream_class *stream_class = NULL;
+       struct bt_ctf_stream *stream = NULL;
+
+       trace = bt_ctf_writer_get_trace(writer);
+       if (!trace) {
+               diag("Failed to get trace from writer");
+               ret = -1;
+               goto end;
+       }
+
+       stream_class = bt_ctf_stream_class_create("empty_stream");
+       if (!stream_class) {
+               diag("Failed to create stream class");
+               ret = -1;
+               goto end;
+       }
+
+       ret = bt_ctf_stream_class_set_packet_context_type(stream_class, NULL);
+       BT_ASSERT(ret == 0);
+       ret = bt_ctf_stream_class_set_event_header_type(stream_class, NULL);
+       BT_ASSERT(ret == 0);
+
+       ok(!bt_ctf_stream_class_get_trace(stream_class),
+               "bt_ctf_stream_class_get_trace returns NULL when stream class is orphaned");
+
+       stream = bt_ctf_writer_create_stream(writer, stream_class);
+       if (!stream) {
+               diag("Failed to create writer stream");
+               ret = -1;
+               goto end;
+       }
+
+       ret_trace = bt_ctf_stream_class_get_trace(stream_class);
+       ok(ret_trace == trace,
+               "bt_ctf_stream_class_get_trace returns the correct trace after a stream has been created");
+end:
+       ok(ret == 0,
+               "Created a stream class with default attributes and an empty stream");
+       bt_ctf_object_put_ref(trace);
+       bt_ctf_object_put_ref(ret_trace);
+       bt_ctf_object_put_ref(stream);
+       bt_ctf_object_put_ref(stream_class);
+}
+
+static
+void test_custom_event_header_stream(struct bt_ctf_writer *writer,
+                       struct bt_ctf_clock *clock)
+{
+       int i, ret;
+       struct bt_ctf_stream_class *stream_class = NULL;
+       struct bt_ctf_stream *stream = NULL;
+       struct bt_ctf_field_type *integer_type = NULL,
+               *sequence_type = NULL, *event_header_type = NULL;
+       struct bt_ctf_field *integer = NULL, *sequence = NULL,
+               *event_header = NULL, *packet_header = NULL;
+       struct bt_ctf_event_class *event_class = NULL;
+       struct bt_ctf_event *event = NULL;
+
+       stream_class = bt_ctf_stream_class_create("custom_event_header_stream");
+       if (!stream_class) {
+               fail("Failed to create stream class");
+               goto end;
+       }
+
+       ret = bt_ctf_stream_class_set_clock(stream_class, clock);
+       if (ret) {
+               fail("Failed to set stream class clock");
+               goto end;
+       }
+
+       /*
+        * Customize event header to add an "seq_len" integer member
+        * which will be used as the length of a sequence in an event of this
+        * stream.
+        */
+       event_header_type = bt_ctf_stream_class_get_event_header_type(
+               stream_class);
+       if (!event_header_type) {
+               fail("Failed to get event header type");
+               goto end;
+       }
+
+       integer_type = bt_ctf_field_type_integer_create(13);
+       if (!integer_type) {
+               fail("Failed to create length integer type");
+               goto end;
+       }
+
+       ret = bt_ctf_field_type_structure_add_field(event_header_type,
+               integer_type, "seq_len");
+       if (ret) {
+               fail("Failed to add a new field to stream event header");
+               goto end;
+       }
+
+       event_class = bt_ctf_event_class_create("sequence_event");
+       if (!event_class) {
+               fail("Failed to create event class");
+               goto end;
+       }
+
+       /*
+        * This event's payload will contain a sequence which references
+        * stream.event.header.seq_len as its length field.
+        */
+       sequence_type = bt_ctf_field_type_sequence_create(integer_type,
+               "stream.event.header.seq_len");
+       if (!sequence_type) {
+               fail("Failed to create a sequence");
+               goto end;
+       }
+
+       ret = bt_ctf_event_class_add_field(event_class, sequence_type,
+               "some_sequence");
+       if (ret) {
+               fail("Failed to add a sequence to an event class");
+               goto end;
+       }
+
+       ret = bt_ctf_stream_class_add_event_class(stream_class, event_class);
+       if (ret) {
+               fail("Failed to add event class to stream class");
+               goto end;
+       }
+
+       stream = bt_ctf_writer_create_stream(writer, stream_class);
+       if (!stream) {
+               fail("Failed to create stream")
+               goto end;
+       }
+
+       /*
+        * We have defined a custom packet header field. We have to populate it
+        * explicitly.
+        */
+       packet_header = bt_ctf_stream_get_packet_header(stream);
+       if (!packet_header) {
+               fail("Failed to get stream packet header");
+               goto end;
+       }
+
+       integer = bt_ctf_field_structure_get_field_by_name(packet_header,
+               "custom_trace_packet_header_field");
+       if (!integer) {
+               fail("Failed to retrieve custom_trace_packet_header_field");
+               goto end;
+       }
+
+       ret = bt_ctf_field_integer_unsigned_set_value(integer, 3487);
+       if (ret) {
+               fail("Failed to set custom_trace_packet_header_field value");
+               goto end;
+       }
+       bt_ctf_object_put_ref(integer);
+
+       event = bt_ctf_event_create(event_class);
+       if (!event) {
+               fail("Failed to create event");
+               goto end;
+       }
+
+       event_header = bt_ctf_event_get_header(event);
+       if (!event_header) {
+               fail("Failed to get event header");
+               goto end;
+       }
+
+       integer = bt_ctf_field_structure_get_field_by_name(event_header,
+               "seq_len");
+       if (!integer) {
+               fail("Failed to get seq_len field from event header");
+               goto end;
+       }
+
+       ret = bt_ctf_field_integer_unsigned_set_value(integer, 2);
+       if (ret) {
+               fail("Failed to set seq_len value in event header");
+               goto end;
+       }
+
+       /* Populate both sequence integer fields */
+       sequence = bt_ctf_event_get_payload(event, "some_sequence");
+       if (!sequence) {
+               fail("Failed to retrieve sequence from event");
+               goto end;
+       }
+
+       ret = bt_ctf_field_sequence_set_length(sequence, integer);
+       if (ret) {
+               fail("Failed to set sequence length");
+               goto end;
+       }
+       bt_ctf_object_put_ref(integer);
+
+       for (i = 0; i < 2; i++) {
+               integer = bt_ctf_field_sequence_get_field(sequence, i);
+               if (ret) {
+                       fail("Failed to retrieve sequence element");
+                       goto end;
+               }
+
+               ret = bt_ctf_field_integer_unsigned_set_value(integer, i);
+               if (ret) {
+                       fail("Failed to set sequence element value");
+                       goto end;
+               }
+
+               bt_ctf_object_put_ref(integer);
+               integer = NULL;
+       }
+
+       ret = bt_ctf_stream_append_event(stream, event);
+       if (ret) {
+               fail("Failed to append event to stream");
+               goto end;
+       }
+
+       ret = bt_ctf_stream_flush(stream);
+       if (ret) {
+               fail("Failed to flush custom_event_header stream");
+       }
+end:
+       bt_ctf_object_put_ref(stream);
+       bt_ctf_object_put_ref(stream_class);
+       bt_ctf_object_put_ref(event_class);
+       bt_ctf_object_put_ref(event);
+       bt_ctf_object_put_ref(integer);
+       bt_ctf_object_put_ref(sequence);
+       bt_ctf_object_put_ref(event_header);
+       bt_ctf_object_put_ref(packet_header);
+       bt_ctf_object_put_ref(sequence_type);
+       bt_ctf_object_put_ref(integer_type);
+       bt_ctf_object_put_ref(event_header_type);
+}
+
+static
+void test_instantiate_event_before_stream(struct bt_ctf_writer *writer,
+               struct bt_ctf_clock *clock)
+{
+       int ret = 0;
+       struct bt_ctf_stream_class *stream_class = NULL;
+       struct bt_ctf_stream *stream = NULL,
+               *ret_stream = NULL;
+       struct bt_ctf_event_class *event_class = NULL;
+       struct bt_ctf_event *event = NULL;
+       struct bt_ctf_field_type *integer_type = NULL;
+       struct bt_ctf_field *payload_field = NULL;
+       struct bt_ctf_field *integer = NULL;
+
+       stream_class = bt_ctf_stream_class_create("event_before_stream_test");
+       if (!stream_class) {
+               diag("Failed to create stream class");
+               ret = -1;
+               goto end;
+       }
+
+       ret = bt_ctf_stream_class_set_clock(stream_class, clock);
+       if (ret) {
+               diag("Failed to set stream class clock");
+               goto end;
+       }
+
+       event_class = bt_ctf_event_class_create("some_event_class_name");
+       integer_type = bt_ctf_field_type_integer_create(32);
+       if (!integer_type) {
+               diag("Failed to create integer field type");
+               ret = -1;
+               goto end;
+       }
+
+       ret = bt_ctf_event_class_add_field(event_class, integer_type,
+               "integer_field");
+       if (ret) {
+               diag("Failed to add field to event class");
+               goto end;
+       }
+
+       ret = bt_ctf_stream_class_add_event_class(stream_class,
+               event_class);
+       if (ret) {
+               diag("Failed to add event class to stream class");
+       }
+
+       event = bt_ctf_event_create(event_class);
+       if (!event) {
+               diag("Failed to create event");
+               ret = -1;
+               goto end;
+       }
+
+       payload_field = bt_ctf_event_get_payload_field(event);
+       if (!payload_field) {
+               diag("Failed to get event's payload field");
+               ret = -1;
+               goto end;
+       }
+
+       integer = bt_ctf_field_structure_get_field_by_index(payload_field, 0);
+       if (!integer) {
+               diag("Failed to get integer field payload from event");
+               ret = -1;
+               goto end;
+       }
+
+       ret = bt_ctf_field_integer_unsigned_set_value(integer, 1234);
+       if (ret) {
+               diag("Failed to set integer field value");
+               goto end;
+       }
+
+       stream = bt_ctf_writer_create_stream(writer, stream_class);
+       if (!stream) {
+               diag("Failed to create writer stream");
+               ret = -1;
+               goto end;
+       }
+
+       ret = bt_ctf_stream_append_event(stream, event);
+       if (ret) {
+               diag("Failed to append event to stream");
+               goto end;
+       }
+
+       ret_stream = bt_ctf_event_get_stream(event);
+       ok(ret_stream == stream,
+               "bt_ctf_event_get_stream returns an event's stream after it has been appended");
+end:
+       ok(ret == 0,
+               "Create an event before instantiating its associated stream");
+       bt_ctf_object_put_ref(stream);
+       bt_ctf_object_put_ref(ret_stream);
+       bt_ctf_object_put_ref(stream_class);
+       bt_ctf_object_put_ref(event_class);
+       bt_ctf_object_put_ref(event);
+       bt_ctf_object_put_ref(integer_type);
+       bt_ctf_object_put_ref(integer);
+       bt_ctf_object_put_ref(payload_field);
+}
+
+static
+void append_existing_event_class(struct bt_ctf_stream_class *stream_class)
+{
+       int ret;
+       struct bt_ctf_event_class *event_class;
+
+       event_class = bt_ctf_event_class_create("Simple Event");
+       BT_ASSERT(event_class);
+       ok(bt_ctf_stream_class_add_event_class(stream_class, event_class) == 0,
+               "two event classes with the same name may cohabit within the same stream class");
+       bt_ctf_object_put_ref(event_class);
+
+       event_class = bt_ctf_event_class_create("different name, ok");
+       BT_ASSERT(event_class);
+       ret = bt_ctf_event_class_set_id(event_class, 13);
+       BT_ASSERT(ret == 0);
+       ok(bt_ctf_stream_class_add_event_class(stream_class, event_class),
+               "two event classes with the same ID cannot cohabit within the same stream class");
+       bt_ctf_object_put_ref(event_class);
+}
+
+static
+void test_clock_utils(void)
+{
+       int ret;
+       struct bt_ctf_clock *clock = NULL;
+
+       clock = bt_ctf_clock_create("water");
+       BT_ASSERT(clock);
+       ret = bt_ctf_clock_set_offset_s(clock, 1234);
+       BT_ASSERT(!ret);
+       ret = bt_ctf_clock_set_offset(clock, 1000);
+       BT_ASSERT(!ret);
+       ret = bt_ctf_clock_set_frequency(clock, 1000000000);
+       BT_ASSERT(!ret);
+       ret = bt_ctf_clock_set_frequency(clock, 1534);
+       BT_ASSERT(!ret);
+
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(clock);
+}
+
+int main(int argc, char **argv)
+{
+       const char *env_resize_length;
+       gchar *trace_path;
+       gchar *metadata_path;
+       const char *clock_name = "test_clock";
+       const char *clock_description = "This is a test clock";
+       const char *returned_clock_name;
+       const char *returned_clock_description;
+       const uint64_t frequency = 1123456789;
+       const int64_t offset_s = 13515309;
+       const int64_t offset = 1234567;
+       int64_t get_offset_s,
+               get_offset;
+       const uint64_t precision = 10;
+       const int is_absolute = 0xFF;
+       char *metadata_string;
+       struct bt_ctf_writer *writer;
+       struct bt_utsname name = {"GNU/Linux", "testhost", "4.4.0-87-generic",
+               "#110-Ubuntu SMP Tue Jul 18 12:55:35 UTC 2017", "x86_64"};
+       struct bt_ctf_clock *clock, *ret_clock;
+       struct bt_ctf_stream_class *stream_class, *ret_stream_class;
+       struct bt_ctf_stream *stream1;
+       struct bt_ctf_stream *stream;
+       const char *ret_string;
+       const uint8_t *ret_uuid;
+       bt_uuid_t tmp_uuid = { 0 };
+       struct bt_ctf_field_type *packet_context_type,
+               *packet_context_field_type,
+               *packet_header_type,
+               *packet_header_field_type,
+               *integer_type,
+               *stream_event_context_type,
+               *ret_field_type,
+               *event_header_field_type;
+       struct bt_ctf_field *packet_header, *packet_header_field;
+       struct bt_ctf_trace *trace;
+       int ret;
+
+       if (argc < 2) {
+               printf("Usage: tests-ctf-writer path_to_babeltrace\n");
+               return -1;
+       }
+
+       env_resize_length = getenv("PACKET_RESIZE_TEST_LENGTH");
+       if (env_resize_length) {
+               packet_resize_test_length =
+                       (unsigned int) atoi(env_resize_length);
+       }
+
+       plan_tests(NR_TESTS);
+
+       trace_path = g_build_filename(g_get_tmp_dir(), "ctfwriter_XXXXXX", NULL);
+       if (!bt_mkdtemp(trace_path)) {
+               perror("# perror");
+       }
+
+       metadata_path = g_build_filename(trace_path, "metadata", NULL);
+
+       writer = bt_ctf_writer_create(trace_path);
+       ok(writer, "bt_ctf_create succeeds in creating trace with path");
+
+       ok(!bt_ctf_writer_get_trace(NULL),
+               "bt_ctf_writer_get_trace correctly handles NULL");
+       trace = bt_ctf_writer_get_trace(writer);
+       ok(bt_ctf_trace_set_native_byte_order(trace, BT_CTF_BYTE_ORDER_NATIVE),
+               "Cannot set a trace's byte order to BT_CTF_BYTE_ORDER_NATIVE");
+       ok(bt_ctf_trace_set_native_byte_order(trace, BT_CTF_BYTE_ORDER_UNSPECIFIED),
+               "Cannot set a trace's byte order to BT_CTF_BYTE_ORDER_UNSPECIFIED");
+       ok(trace,
+               "bt_ctf_writer_get_trace returns a bt_ctf_trace object");
+       ok(bt_ctf_trace_set_native_byte_order(trace, BT_CTF_BYTE_ORDER_BIG_ENDIAN) == 0,
+               "Set a trace's byte order to big endian");
+       ok(bt_ctf_trace_get_native_byte_order(trace) == BT_CTF_BYTE_ORDER_BIG_ENDIAN,
+               "bt_ctf_trace_get_native_byte_order returns a correct endianness");
+
+       /* Add environment context to the trace */
+       ok(bt_ctf_writer_add_environment_field(writer, "host", name.nodename) == 0,
+               "Add host (%s) environment field to writer instance",
+               name.nodename);
+       ok(bt_ctf_writer_add_environment_field(NULL, "test_field",
+               "test_value"),
+               "bt_ctf_writer_add_environment_field error with NULL writer");
+       ok(bt_ctf_writer_add_environment_field(writer, NULL,
+               "test_value"),
+               "bt_ctf_writer_add_environment_field error with NULL field name");
+       ok(bt_ctf_writer_add_environment_field(writer, "test_field",
+               NULL),
+               "bt_ctf_writer_add_environment_field error with NULL field value");
+
+       /* Test bt_ctf_trace_set_environment_field_integer */
+       ok(bt_ctf_trace_set_environment_field_integer(NULL, "test_env_int",
+               -194875),
+               "bt_ctf_trace_set_environment_field_integer handles a NULL trace correctly");
+       ok(bt_ctf_trace_set_environment_field_integer(trace, NULL, -194875),
+               "bt_ctf_trace_set_environment_field_integer handles a NULL name correctly");
+       ok(!bt_ctf_trace_set_environment_field_integer(trace, "test_env_int",
+               -164973),
+               "bt_ctf_trace_set_environment_field_integer succeeds");
+
+       /* Test bt_ctf_trace_set_environment_field_string */
+       ok(bt_ctf_trace_set_environment_field_string(NULL, "test_env_str",
+               "yeah"),
+               "bt_ctf_trace_set_environment_field_string handles a NULL trace correctly");
+       ok(bt_ctf_trace_set_environment_field_string(trace, NULL, "yeah"),
+               "bt_ctf_trace_set_environment_field_string handles a NULL name correctly");
+       ok(bt_ctf_trace_set_environment_field_string(trace, "test_env_str",
+               NULL),
+               "bt_ctf_trace_set_environment_field_string handles a NULL value correctly");
+       ok(!bt_ctf_trace_set_environment_field_string(trace, "test_env_str",
+               "oh yeah"),
+               "bt_ctf_trace_set_environment_field_string succeeds");
+
+       /* Test environment field replacement */
+       ok(!bt_ctf_trace_set_environment_field_integer(trace, "test_env_int",
+               654321),
+               "bt_ctf_trace_set_environment_field_integer succeeds with an existing name");
+
+       ok(bt_ctf_writer_add_environment_field(writer, "sysname", name.sysname)
+               == 0, "Add sysname (%s) environment field to writer instance",
+               name.sysname);
+       ok(bt_ctf_writer_add_environment_field(writer, "nodename",
+               name.nodename) == 0,
+               "Add nodename (%s) environment field to writer instance",
+               name.nodename);
+       ok(bt_ctf_writer_add_environment_field(writer, "release", name.release)
+               == 0, "Add release (%s) environment field to writer instance",
+               name.release);
+       ok(bt_ctf_writer_add_environment_field(writer, "version", name.version)
+               == 0, "Add version (%s) environment field to writer instance",
+               name.version);
+       ok(bt_ctf_writer_add_environment_field(writer, "machine", name.machine)
+               == 0, "Add machine (%s) environment field to writer instance",
+               name.machine);
+
+       /* Define a clock and add it to the trace */
+       ok(!bt_ctf_clock_create("signed"),
+               "Illegal clock name rejected");
+       clock = bt_ctf_clock_create(clock_name);
+       ok(clock, "Clock created successfully");
+       returned_clock_name = bt_ctf_clock_get_name(clock);
+       ok(returned_clock_name, "bt_ctf_clock_get_name returns a clock name");
+       ok(returned_clock_name ? strcmp(returned_clock_name, clock_name) == 0 : 0,
+               "Returned clock name is valid");
+
+       returned_clock_description = bt_ctf_clock_get_description(clock);
+       ok(!returned_clock_description, "bt_ctf_clock_get_description returns NULL on an unset description");
+       ok(bt_ctf_clock_set_description(clock, clock_description) == 0,
+               "Clock description set successfully");
+
+       returned_clock_description = bt_ctf_clock_get_description(clock);
+       ok(returned_clock_description,
+               "bt_ctf_clock_get_description returns a description.");
+       ok(returned_clock_description ?
+               strcmp(returned_clock_description, clock_description) == 0 : 0,
+               "Returned clock description is valid");
+
+       ok(bt_ctf_clock_get_frequency(clock) == DEFAULT_CLOCK_FREQ,
+               "bt_ctf_clock_get_frequency returns the correct default frequency");
+       ok(bt_ctf_clock_set_frequency(clock, frequency) == 0,
+               "Set clock frequency");
+       ok(bt_ctf_clock_get_frequency(clock) == frequency,
+               "bt_ctf_clock_get_frequency returns the correct frequency once it is set");
+
+       ok(bt_ctf_clock_get_offset_s(clock, &get_offset_s) == 0,
+               "bt_ctf_clock_get_offset_s succeeds");
+       ok(get_offset_s == DEFAULT_CLOCK_OFFSET_S,
+               "bt_ctf_clock_get_offset_s returns the correct default offset (in seconds)");
+       ok(bt_ctf_clock_set_offset_s(clock, offset_s) == 0,
+               "Set clock offset (seconds)");
+       ok(bt_ctf_clock_get_offset_s(clock, &get_offset_s) == 0,
+               "bt_ctf_clock_get_offset_s succeeds");
+       ok(get_offset_s == offset_s,
+               "bt_ctf_clock_get_offset_s returns the correct default offset (in seconds) once it is set");
+
+       ok(bt_ctf_clock_get_offset(clock, &get_offset) == 0,
+               "bt_ctf_clock_get_offset succeeds");
+       ok(get_offset == DEFAULT_CLOCK_OFFSET,
+               "bt_ctf_clock_get_offset returns the correct default offset (in ticks)");
+       ok(bt_ctf_clock_set_offset(clock, offset) == 0, "Set clock offset");
+       ok(bt_ctf_clock_get_offset(clock, &get_offset) == 0,
+               "bt_ctf_clock_get_offset succeeds");
+       ok(get_offset == offset,
+               "bt_ctf_clock_get_offset returns the correct default offset (in ticks) once it is set");
+
+       ok(bt_ctf_clock_get_precision(clock) == DEFAULT_CLOCK_PRECISION,
+               "bt_ctf_clock_get_precision returns the correct default precision");
+       ok(bt_ctf_clock_set_precision(clock, precision) == 0,
+               "Set clock precision");
+       ok(bt_ctf_clock_get_precision(clock) == precision,
+               "bt_ctf_clock_get_precision returns the correct precision once it is set");
+
+       ok(bt_ctf_clock_get_is_absolute(clock) == DEFAULT_CLOCK_IS_ABSOLUTE,
+               "bt_ctf_clock_get_precision returns the correct default is_absolute attribute");
+       ok(bt_ctf_clock_set_is_absolute(clock, is_absolute) == 0,
+               "Set clock absolute property");
+       ok(bt_ctf_clock_get_is_absolute(clock) == !!is_absolute,
+               "bt_ctf_clock_get_precision returns the correct is_absolute attribute once it is set");
+       ok(bt_ctf_clock_set_time(clock, current_time) == 0,
+               "Set clock time");
+       ret_uuid = bt_ctf_clock_get_uuid(clock);
+       ok(ret_uuid,
+               "bt_ctf_clock_get_uuid returns a UUID");
+       if (ret_uuid) {
+               memcpy(tmp_uuid, ret_uuid, sizeof(tmp_uuid));
+               /* Slightly modify UUID */
+               tmp_uuid[sizeof(tmp_uuid) - 1]++;
+       }
+
+       ok(bt_ctf_clock_set_uuid(clock, tmp_uuid) == 0,
+               "bt_ctf_clock_set_uuid sets a new uuid successfully");
+       ret_uuid = bt_ctf_clock_get_uuid(clock);
+       ok(ret_uuid,
+               "bt_ctf_clock_get_uuid returns a UUID after setting a new one");
+       ok(uuid_match(ret_uuid, tmp_uuid),
+               "bt_ctf_clock_get_uuid returns the correct UUID after setting a new one");
+
+       /* Define a stream class */
+       stream_class = bt_ctf_stream_class_create("test_stream");
+       ret_string = bt_ctf_stream_class_get_name(stream_class);
+       ok(ret_string && strcmp(ret_string, "test_stream") == 0,
+               "bt_ctf_stream_class_get_name returns a correct stream class name");
+
+       ok(!bt_ctf_stream_class_get_clock(stream_class),
+               "bt_ctf_stream_class_get_clock returns NULL when a clock was not set");
+       ok(!bt_ctf_stream_class_get_clock(NULL),
+               "bt_ctf_stream_class_get_clock handles NULL correctly");
+
+       ok(stream_class, "Create stream class");
+       ok(bt_ctf_stream_class_set_clock(stream_class, clock) == 0,
+               "Set a stream class' clock");
+       ret_clock = bt_ctf_stream_class_get_clock(stream_class);
+       ok(ret_clock == clock,
+               "bt_ctf_stream_class_get_clock returns a correct clock");
+       bt_ctf_object_put_ref(ret_clock);
+
+       /* Test the event fields and event types APIs */
+       type_field_tests();
+
+       ok(bt_ctf_stream_class_get_id(stream_class) < 0,
+               "bt_ctf_stream_class_get_id returns an error when no id is set");
+       ok(bt_ctf_stream_class_set_id(NULL, 123) < 0,
+               "bt_ctf_stream_class_set_id handles NULL correctly");
+       ok(bt_ctf_stream_class_set_id(stream_class, 123) == 0,
+               "Set an stream class' id");
+       ok(bt_ctf_stream_class_get_id(stream_class) == 123,
+               "bt_ctf_stream_class_get_id returns the correct value");
+
+       /* Validate default event header fields */
+       ret_field_type = bt_ctf_stream_class_get_event_header_type(
+               stream_class);
+       ok(ret_field_type,
+               "bt_ctf_stream_class_get_event_header_type returns an event header type");
+       ok(bt_ctf_field_type_get_type_id(ret_field_type) == BT_CTF_FIELD_TYPE_ID_STRUCT,
+               "Default event header type is a structure");
+       event_header_field_type =
+               bt_ctf_field_type_structure_get_field_type_by_name(
+               ret_field_type, "id");
+       ok(event_header_field_type,
+               "Default event header type contains an \"id\" field");
+       ok(bt_ctf_field_type_get_type_id(
+               event_header_field_type) == BT_CTF_FIELD_TYPE_ID_INTEGER,
+               "Default event header \"id\" field is an integer");
+       bt_ctf_object_put_ref(event_header_field_type);
+       event_header_field_type =
+               bt_ctf_field_type_structure_get_field_type_by_name(
+               ret_field_type, "timestamp");
+       ok(event_header_field_type,
+               "Default event header type contains a \"timestamp\" field");
+       ok(bt_ctf_field_type_get_type_id(
+               event_header_field_type) == BT_CTF_FIELD_TYPE_ID_INTEGER,
+               "Default event header \"timestamp\" field is an integer");
+       bt_ctf_object_put_ref(event_header_field_type);
+       bt_ctf_object_put_ref(ret_field_type);
+
+       /* Add a custom trace packet header field */
+       packet_header_type = bt_ctf_trace_get_packet_header_field_type(trace);
+       ok(packet_header_type,
+               "bt_ctf_trace_get_packet_header_field_type returns a packet header");
+       ok(bt_ctf_field_type_get_type_id(packet_header_type) == BT_CTF_FIELD_TYPE_ID_STRUCT,
+               "bt_ctf_trace_get_packet_header_field_type returns a packet header of type struct");
+       ret_field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_header_type, "magic");
+       ok(ret_field_type, "Default packet header type contains a \"magic\" field");
+       bt_ctf_object_put_ref(ret_field_type);
+       ret_field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_header_type, "uuid");
+       ok(ret_field_type, "Default packet header type contains a \"uuid\" field");
+       bt_ctf_object_put_ref(ret_field_type);
+       ret_field_type = bt_ctf_field_type_structure_get_field_type_by_name(
+               packet_header_type, "stream_id");
+       ok(ret_field_type, "Default packet header type contains a \"stream_id\" field");
+       bt_ctf_object_put_ref(ret_field_type);
+
+       packet_header_field_type = bt_ctf_field_type_integer_create(22);
+       ok(!bt_ctf_field_type_structure_add_field(packet_header_type,
+               packet_header_field_type, "custom_trace_packet_header_field"),
+               "Added a custom trace packet header field successfully");
+
+       ok(bt_ctf_trace_set_packet_header_field_type(NULL, packet_header_type) < 0,
+               "bt_ctf_trace_set_packet_header_field_type handles a NULL trace correctly");
+       ok(!bt_ctf_trace_set_packet_header_field_type(trace, packet_header_type),
+               "Set a trace packet_header_type successfully");
+
+       /* Add a custom field to the stream class' packet context */
+       packet_context_type = bt_ctf_stream_class_get_packet_context_type(stream_class);
+       ok(packet_context_type,
+               "bt_ctf_stream_class_get_packet_context_type returns a packet context type.");
+       ok(bt_ctf_field_type_get_type_id(packet_context_type) == BT_CTF_FIELD_TYPE_ID_STRUCT,
+               "Packet context is a structure");
+
+       ok(bt_ctf_stream_class_set_packet_context_type(NULL, packet_context_type),
+               "bt_ctf_stream_class_set_packet_context_type handles a NULL stream class correctly");
+
+       integer_type = bt_ctf_field_type_integer_create(32);
+
+       ok(bt_ctf_stream_class_set_packet_context_type(stream_class,
+               integer_type) < 0,
+               "bt_ctf_stream_class_set_packet_context_type rejects a packet context that is not a structure");
+
+       /* Create a "uint5_t" equivalent custom packet context field */
+       packet_context_field_type = bt_ctf_field_type_integer_create(5);
+
+       ret = bt_ctf_field_type_structure_add_field(packet_context_type,
+               packet_context_field_type, "custom_packet_context_field");
+       ok(ret == 0, "Packet context field added successfully");
+
+       /* Define a stream event context containing a my_integer field. */
+       stream_event_context_type = bt_ctf_field_type_structure_create();
+       bt_ctf_field_type_structure_add_field(stream_event_context_type,
+               integer_type, "common_event_context");
+
+       ok(bt_ctf_stream_class_set_event_context_type(NULL,
+               stream_event_context_type) < 0,
+               "bt_ctf_stream_class_set_event_context_type handles a NULL stream_class correctly");
+       ok(bt_ctf_stream_class_set_event_context_type(stream_class,
+               integer_type) < 0,
+               "bt_ctf_stream_class_set_event_context_type validates that the event context os a structure");
+
+       ok(bt_ctf_stream_class_set_event_context_type(
+               stream_class, stream_event_context_type) == 0,
+               "Set a new stream event context type");
+
+       ret_field_type = bt_ctf_stream_class_get_event_context_type(
+               stream_class);
+       ok(ret_field_type == stream_event_context_type,
+               "bt_ctf_stream_class_get_event_context_type returns the correct field type.");
+       bt_ctf_object_put_ref(ret_field_type);
+
+
+       /* Instantiate a stream and append events */
+       ret = bt_ctf_writer_add_clock(writer, clock);
+       BT_ASSERT(ret == 0);
+
+       ok(bt_ctf_trace_get_stream_count(trace) == 0,
+               "bt_ctf_trace_get_stream_count() succeeds and returns the correct value (0)");
+       stream1 = bt_ctf_writer_create_stream(writer, stream_class);
+       ok(stream1, "Instantiate a stream class from writer");
+       ok(bt_ctf_trace_get_stream_count(trace) == 1,
+               "bt_ctf_trace_get_stream_count() succeeds and returns the correct value (1)");
+       stream = bt_ctf_trace_get_stream_by_index(trace, 0);
+       ok(stream == stream1,
+               "bt_ctf_trace_get_stream_by_index() succeeds and returns the correct value");
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(stream);
+
+       /*
+        * Creating a stream through a writer adds the given stream
+        * class to the writer's trace, thus registering the stream
+        * class's clock to the trace.
+        */
+
+       ret_stream_class = bt_ctf_stream_get_class(stream1);
+       ok(ret_stream_class,
+               "bt_ctf_stream_get_class returns a stream class");
+       ok(ret_stream_class == stream_class,
+               "Returned stream class is of the correct type");
+
+       /*
+        * Packet header, packet context, event header, and stream
+        * event context types were copied for the resolving
+        * process
+        */
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_header_type);
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_context_type);
+       BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_context_type);
+       packet_header_type = bt_ctf_trace_get_packet_header_field_type(trace);
+       BT_ASSERT(packet_header_type);
+       packet_context_type =
+               bt_ctf_stream_class_get_packet_context_type(stream_class);
+       BT_ASSERT(packet_context_type);
+       stream_event_context_type =
+               bt_ctf_stream_class_get_event_context_type(stream_class);
+       BT_ASSERT(stream_event_context_type);
+
+       /*
+        * Try to modify the packet context type after a stream has been
+        * created.
+        */
+       ret = bt_ctf_field_type_structure_add_field(packet_header_type,
+               packet_header_field_type, "should_fail");
+       ok(ret < 0,
+               "Trace packet header type can't be modified once a stream has been instantiated");
+
+       /*
+        * Try to modify the packet context type after a stream has been
+        * created.
+        */
+       ret = bt_ctf_field_type_structure_add_field(packet_context_type,
+               packet_context_field_type, "should_fail");
+       ok(ret < 0,
+               "Packet context type can't be modified once a stream has been instantiated");
+
+       /*
+        * Try to modify the stream event context type after a stream has been
+        * created.
+        */
+       ret = bt_ctf_field_type_structure_add_field(stream_event_context_type,
+               integer_type, "should_fail");
+       ok(ret < 0,
+               "Stream event context type can't be modified once a stream has been instantiated");
+
+       /* Should fail after instantiating a stream (frozen) */
+       ok(bt_ctf_stream_class_set_clock(stream_class, clock),
+               "Changes to a stream class that was already instantiated fail");
+
+       /* Populate the custom packet header field only once for all tests */
+       ok(!bt_ctf_stream_get_packet_header(NULL),
+               "bt_ctf_stream_get_packet_header handles NULL correctly");
+       packet_header = bt_ctf_stream_get_packet_header(stream1);
+       ok(packet_header,
+               "bt_ctf_stream_get_packet_header returns a packet header");
+       ret_field_type = bt_ctf_field_get_type(packet_header);
+       ok(ret_field_type == packet_header_type,
+               "Stream returns a packet header of the appropriate type");
+       bt_ctf_object_put_ref(ret_field_type);
+       packet_header_field = bt_ctf_field_structure_get_field_by_name(packet_header,
+               "custom_trace_packet_header_field");
+       ok(packet_header_field,
+               "Packet header structure contains a custom field with the appropriate name");
+       ret_field_type = bt_ctf_field_get_type(packet_header_field);
+       ok(!bt_ctf_field_integer_unsigned_set_value(packet_header_field,
+               54321), "Set custom packet header value successfully");
+       ok(bt_ctf_stream_set_packet_header(stream1, NULL) < 0,
+               "bt_ctf_stream_set_packet_header handles a NULL packet header correctly");
+       ok(bt_ctf_stream_set_packet_header(NULL, packet_header) < 0,
+               "bt_ctf_stream_set_packet_header handles a NULL stream correctly");
+       ok(bt_ctf_stream_set_packet_header(stream1, packet_header_field) < 0,
+               "bt_ctf_stream_set_packet_header rejects a packet header of the wrong type");
+       ok(!bt_ctf_stream_set_packet_header(stream1, packet_header),
+               "Successfully set a stream's packet header");
+
+       ok(bt_ctf_writer_add_environment_field(writer, "new_field", "test") == 0,
+               "Add environment field to writer after stream creation");
+
+       test_clock_utils();
+
+       test_instantiate_event_before_stream(writer, clock);
+
+       append_simple_event(stream_class, stream1, clock);
+
+       packet_resize_test(stream_class, stream1, clock);
+
+       append_complex_event(stream_class, stream1, clock);
+
+       append_existing_event_class(stream_class);
+
+       test_empty_stream(writer);
+
+       test_custom_event_header_stream(writer, clock);
+
+       metadata_string = bt_ctf_writer_get_metadata_string(writer);
+       ok(metadata_string, "Get metadata string");
+
+       bt_ctf_writer_flush_metadata(writer);
+
+       bt_ctf_object_put_ref(clock);
+       bt_ctf_object_put_ref(ret_stream_class);
+       bt_ctf_object_put_ref(writer);
+       bt_ctf_object_put_ref(stream1);
+       bt_ctf_object_put_ref(packet_context_type);
+       bt_ctf_object_put_ref(packet_context_field_type);
+       bt_ctf_object_put_ref(integer_type);
+       bt_ctf_object_put_ref(stream_event_context_type);
+       bt_ctf_object_put_ref(ret_field_type);
+       bt_ctf_object_put_ref(packet_header_type);
+       bt_ctf_object_put_ref(packet_header_field_type);
+       bt_ctf_object_put_ref(packet_header);
+       bt_ctf_object_put_ref(packet_header_field);
+       bt_ctf_object_put_ref(trace);
+       free(metadata_string);
+       bt_ctf_object_put_ref(stream_class);
+
+       validate_trace(argv[1], trace_path);
+
+       recursive_rmdir(trace_path);
+       g_free(trace_path);
+       g_free(metadata_path);
+
+       return exit_status();
+}
diff --git a/tests/ctf-writer/ctf_writer.c b/tests/ctf-writer/ctf_writer.c
deleted file mode 100644 (file)
index 0b979ac..0000000
+++ /dev/null
@@ -1,2067 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2013-2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- */
-
-#include <babeltrace2-ctf-writer/writer.h>
-#include <babeltrace2-ctf-writer/clock.h>
-#include <babeltrace2-ctf-writer/clock-class.h>
-#include <babeltrace2-ctf-writer/stream.h>
-#include <babeltrace2-ctf-writer/event.h>
-#include <babeltrace2-ctf-writer/event-types.h>
-#include <babeltrace2-ctf-writer/event-fields.h>
-#include <babeltrace2-ctf-writer/stream-class.h>
-#include <babeltrace2-ctf-writer/trace.h>
-#include <babeltrace2/babeltrace.h>
-#include <glib.h>
-#include <unistd.h>
-#include "compat/stdlib.h"
-#include <stdio.h>
-#include "compat/limits.h"
-#include "compat/stdio.h"
-#include <string.h>
-#include "common/assert.h"
-#include "common/uuid.h"
-#include <fcntl.h>
-#include "tap/tap.h"
-#include <math.h>
-#include <float.h>
-#include "common.h"
-
-#ifdef __FreeBSD__
-/* Required for WIFEXITED and WEXITSTATUS */
-#include <sys/wait.h>
-#endif
-
-#define METADATA_LINE_SIZE 512
-#define SEQUENCE_TEST_LENGTH 10
-#define ARRAY_TEST_LENGTH 5
-#define PACKET_RESIZE_TEST_DEF_LENGTH 100000
-
-#define DEFAULT_CLOCK_FREQ 1000000000
-#define DEFAULT_CLOCK_PRECISION 1
-#define DEFAULT_CLOCK_OFFSET 0
-#define DEFAULT_CLOCK_OFFSET_S 0
-#define DEFAULT_CLOCK_IS_ABSOLUTE 0
-#define DEFAULT_CLOCK_TIME 0
-#define DEFAULT_CLOCK_VALUE 0
-
-#define NR_TESTS 325
-
-struct bt_utsname {
-       char sysname[BABELTRACE_HOST_NAME_MAX];
-       char nodename[BABELTRACE_HOST_NAME_MAX];
-       char release[BABELTRACE_HOST_NAME_MAX];
-       char version[BABELTRACE_HOST_NAME_MAX];
-       char machine[BABELTRACE_HOST_NAME_MAX];
-};
-
-static int64_t current_time = 42;
-static unsigned int packet_resize_test_length = PACKET_RESIZE_TEST_DEF_LENGTH;
-
-/* Return 1 if uuids match, zero if different. */
-static
-int uuid_match(const uint8_t *uuid_a, const uint8_t *uuid_b)
-{
-       int ret = 0;
-       int i;
-
-       if (!uuid_a || !uuid_b) {
-               goto end;
-       }
-
-       for (i = 0; i < 16; i++) {
-               if (uuid_a[i] != uuid_b[i]) {
-                       goto end;
-               }
-       }
-
-       ret = 1;
-end:
-       return ret;
-}
-
-static
-void validate_trace(char *parser_path, char *trace_path)
-{
-       int ret = 0;
-       gint exit_status;
-       const char *argv[] = {parser_path, trace_path, "-o", "dummy", NULL};
-
-       if (!parser_path || !trace_path) {
-               ret = -1;
-               goto result;
-       }
-
-       if (!g_spawn_sync(NULL,
-                       (gchar **) argv,
-                       NULL,
-                       G_SPAWN_STDOUT_TO_DEV_NULL,
-                       NULL,
-                       NULL,
-                       NULL,
-                       NULL,
-                       &exit_status,
-                       NULL)) {
-               diag("Failed to spawn babeltrace.");
-               ret = -1;
-               goto result;
-       }
-
-       /* Replace by g_spawn_check_exit_status when we require glib >= 2.34 */
-#ifdef G_OS_UNIX
-       ret = WIFEXITED(exit_status) ? WEXITSTATUS(exit_status) : -1;
-#else
-       ret = exit_status;
-#endif
-
-       if (ret != 0) {
-               diag("Babeltrace returned an error.");
-               goto result;
-       }
-
-result:
-       ok(ret == 0, "Babeltrace could read the resulting trace");
-}
-
-static
-void append_simple_event(struct bt_ctf_stream_class *stream_class,
-               struct bt_ctf_stream *stream, struct bt_ctf_clock *clock)
-{
-       /* Create and add a simple event class */
-       struct bt_ctf_event_class *simple_event_class =
-               bt_ctf_event_class_create("Simple Event");
-       struct bt_ctf_field_type *uint_12_type =
-               bt_ctf_field_type_integer_create(12);
-       struct bt_ctf_field_type *int_64_type =
-               bt_ctf_field_type_integer_create(64);
-       struct bt_ctf_field_type *float_type =
-               bt_ctf_field_type_floating_point_create();
-       struct bt_ctf_field_type *enum_type;
-       struct bt_ctf_field_type *enum_type_unsigned =
-               bt_ctf_field_type_enumeration_create(uint_12_type);
-       struct bt_ctf_field_type *event_context_type =
-               bt_ctf_field_type_structure_create();
-       struct bt_ctf_field_type *event_payload_type = NULL;
-       struct bt_ctf_field_type *returned_type;
-       struct bt_ctf_event *simple_event;
-       struct bt_ctf_field *integer_field;
-       struct bt_ctf_field *float_field;
-       struct bt_ctf_field *enum_field;
-       struct bt_ctf_field *enum_field_unsigned;
-       struct bt_ctf_field *enum_container_field;
-       const char *mapping_name_test = "truie";
-       const double double_test_value = 3.1415;
-       struct bt_ctf_field *enum_container_field_unsigned;
-       const char *mapping_name_negative_test = "negative_value";
-       const char *ret_char;
-       double ret_double;
-       int64_t ret_range_start_int64_t, ret_range_end_int64_t;
-       uint64_t ret_range_start_uint64_t, ret_range_end_uint64_t;
-       struct bt_ctf_event_class *ret_event_class;
-       struct bt_ctf_field *packet_context;
-       struct bt_ctf_field *packet_context_field;
-       struct bt_ctf_field *stream_event_context;
-       struct bt_ctf_field *stream_event_context_field;
-       struct bt_ctf_field *event_context;
-       struct bt_ctf_field *event_context_field;
-       struct bt_ctf_field_type *ep_integer_field_type = NULL;
-       struct bt_ctf_field_type *ep_enum_field_type = NULL;
-       struct bt_ctf_field_type *ep_enum_field_unsigned_type = NULL;
-       struct bt_ctf_field_type_enumeration_mapping_iterator *iter = NULL;
-       int ret;
-
-       ok(uint_12_type, "Create an unsigned integer type");
-
-       ok(!bt_ctf_field_type_integer_set_signed(int_64_type, 1),
-               "Set signed 64 bit integer signedness to true");
-       ok(int_64_type, "Create a signed integer type");
-       enum_type = bt_ctf_field_type_enumeration_create(int_64_type);
-
-       returned_type = bt_ctf_field_type_enumeration_get_container_field_type(enum_type);
-       ok(returned_type == int_64_type, "bt_ctf_field_type_enumeration_get_container_field_type returns the right type");
-       ok(!bt_ctf_field_type_enumeration_create(enum_type),
-               "bt_ctf_field_enumeration_type_create rejects non-integer container field types");
-       bt_ctf_object_put_ref(returned_type);
-
-       bt_ctf_field_type_set_alignment(float_type, 32);
-       ok(bt_ctf_field_type_get_alignment(float_type) == 32,
-               "bt_ctf_field_type_get_alignment returns a correct value");
-
-       ok(bt_ctf_field_type_floating_point_set_exponent_digits(float_type, 11) == 0,
-               "Set a floating point type's exponent digit count");
-       ok(bt_ctf_field_type_floating_point_set_mantissa_digits(float_type, 53) == 0,
-               "Set a floating point type's mantissa digit count");
-
-       ok(bt_ctf_field_type_floating_point_get_exponent_digits(float_type) == 11,
-               "bt_ctf_field_type_floating_point_get_exponent_digits returns the correct value");
-       ok(bt_ctf_field_type_floating_point_get_mantissa_digits(float_type) == 53,
-               "bt_ctf_field_type_floating_point_get_mantissa_digits returns the correct value");
-
-       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type,
-               mapping_name_negative_test, -12345, 0) == 0,
-               "bt_ctf_field_type_enumeration_add_mapping accepts negative enumeration mappings");
-       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type,
-               "escaping; \"test\"", 1, 1) == 0,
-               "bt_ctf_field_type_enumeration_add_mapping accepts enumeration mapping strings containing quotes");
-       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type,
-               "\tanother \'escaping\'\n test\"", 2, 4) == 0,
-               "bt_ctf_field_type_enumeration_add_mapping accepts enumeration mapping strings containing special characters");
-       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type,
-               "event clock int float", 5, 22) == 0,
-               "Accept enumeration mapping strings containing reserved keywords");
-       bt_ctf_field_type_enumeration_add_mapping(enum_type, mapping_name_test,
-               42, 42);
-       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type, mapping_name_test,
-               43, 51) == 0, "bt_ctf_field_type_enumeration_add_mapping accepts duplicate mapping names");
-       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type, "something",
-               -500, -400) == 0, "bt_ctf_field_type_enumeration_add_mapping accepts overlapping enum entries");
-       ok(bt_ctf_field_type_enumeration_add_mapping(enum_type, mapping_name_test,
-               -54, -55), "bt_ctf_field_type_enumeration_add_mapping rejects mapping where end < start");
-       bt_ctf_field_type_enumeration_add_mapping(enum_type, "another entry", -42000, -13000);
-
-       ok(bt_ctf_event_class_add_field(simple_event_class, enum_type,
-               "enum_field") == 0, "Add signed enumeration field to event");
-
-       ok(bt_ctf_field_type_enumeration_signed_get_mapping_by_index(enum_type, 0, NULL,
-               &ret_range_start_int64_t, &ret_range_end_int64_t) == 0,
-               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index handles a NULL string correctly");
-       ok(bt_ctf_field_type_enumeration_signed_get_mapping_by_index(enum_type, 0, &ret_char,
-               NULL, &ret_range_end_int64_t) == 0,
-               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index handles a NULL start correctly");
-       ok(bt_ctf_field_type_enumeration_signed_get_mapping_by_index(enum_type, 0, &ret_char,
-               &ret_range_start_int64_t, NULL) == 0,
-               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index handles a NULL end correctly");
-       /* Assumes entries are sorted by range_start values. */
-       ok(bt_ctf_field_type_enumeration_signed_get_mapping_by_index(enum_type, 6, &ret_char,
-               &ret_range_start_int64_t, &ret_range_end_int64_t) == 0,
-               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index returns a value");
-       ok(strcmp(ret_char, mapping_name_test) == 0,
-               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index returns a correct mapping name");
-       ok(ret_range_start_int64_t == 42,
-               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index returns a correct mapping start");
-       ok(ret_range_end_int64_t == 42,
-               "bt_ctf_field_type_enumeration_signed_get_mapping_by_index returns a correct mapping end");
-
-       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned,
-               "escaping; \"test\"", 0, 0) == 0,
-               "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts enumeration mapping strings containing quotes");
-       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned,
-               "\tanother \'escaping\'\n test\"", 1, 4) == 0,
-               "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts enumeration mapping strings containing special characters");
-       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned,
-               "event clock int float", 5, 22) == 0,
-               "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts enumeration mapping strings containing reserved keywords");
-       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned, mapping_name_test,
-               42, 42) == 0, "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts single-value ranges");
-       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned, mapping_name_test,
-               43, 51) == 0, "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts duplicate mapping names");
-       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned, "something",
-               7, 8) == 0, "bt_ctf_field_type_enumeration_unsigned_add_mapping accepts overlapping enum entries");
-       ok(bt_ctf_field_type_enumeration_unsigned_add_mapping(enum_type_unsigned, mapping_name_test,
-               55, 54), "bt_ctf_field_type_enumeration_unsigned_add_mapping rejects mapping where end < start");
-       ok(bt_ctf_event_class_add_field(simple_event_class, enum_type_unsigned,
-               "enum_field_unsigned") == 0, "Add unsigned enumeration field to event");
-
-       ok(bt_ctf_field_type_enumeration_get_mapping_count(enum_type_unsigned) == 6,
-               "bt_ctf_field_type_enumeration_get_mapping_count returns the correct value");
-
-       ok(bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(enum_type_unsigned, 0, NULL,
-               &ret_range_start_uint64_t, &ret_range_end_uint64_t) == 0,
-               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index handles a NULL string correctly");
-       ok(bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(enum_type_unsigned, 0, &ret_char,
-               NULL, &ret_range_end_uint64_t) == 0,
-               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index handles a NULL start correctly");
-       ok(bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(enum_type_unsigned, 0, &ret_char,
-               &ret_range_start_uint64_t, NULL) == 0,
-               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index handles a NULL end correctly");
-       ok(bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index(enum_type_unsigned, 4, &ret_char,
-               &ret_range_start_uint64_t, &ret_range_end_uint64_t) == 0,
-               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index returns a value");
-       ok(strcmp(ret_char, mapping_name_test) == 0,
-               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index returns a correct mapping name");
-       ok(ret_range_start_uint64_t == 42,
-               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index returns a correct mapping start");
-       ok(ret_range_end_uint64_t == 42,
-               "bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index returns a correct mapping end");
-
-       bt_ctf_event_class_add_field(simple_event_class, uint_12_type,
-               "integer_field");
-       bt_ctf_event_class_add_field(simple_event_class, float_type,
-               "float_field");
-
-       ret = bt_ctf_event_class_set_id(simple_event_class, 13);
-       BT_ASSERT(ret == 0);
-
-       /* Set an event context type which will contain a single integer. */
-       ok(!bt_ctf_field_type_structure_add_field(event_context_type, uint_12_type,
-               "event_specific_context"),
-               "Add event specific context field");
-
-       ok(bt_ctf_event_class_set_context_field_type(NULL, event_context_type) < 0,
-               "bt_ctf_event_class_set_context_field_type handles a NULL event class correctly");
-       ok(!bt_ctf_event_class_set_context_field_type(simple_event_class, event_context_type),
-               "Set an event class' context type successfully");
-       returned_type = bt_ctf_event_class_get_context_field_type(simple_event_class);
-       ok(returned_type == event_context_type,
-               "bt_ctf_event_class_get_context_field_type returns the appropriate type");
-       bt_ctf_object_put_ref(returned_type);
-
-       ok(!bt_ctf_stream_class_add_event_class(stream_class, simple_event_class),
-               "Adding simple event class to stream class");
-
-       /*
-        * bt_ctf_stream_class_add_event_class() copies the field types
-        * of simple_event_class, so we retrieve the new ones to create
-        * the appropriate fields.
-        */
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(event_context_type);
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(event_payload_type);
-       event_payload_type = bt_ctf_event_class_get_payload_field_type(
-               simple_event_class);
-       BT_ASSERT(event_payload_type);
-       event_context_type = bt_ctf_event_class_get_context_field_type(
-               simple_event_class);
-       BT_ASSERT(event_context_type);
-       ep_integer_field_type =
-               bt_ctf_field_type_structure_get_field_type_by_name(
-                       event_payload_type, "integer_field");
-       BT_ASSERT(ep_integer_field_type);
-       ep_enum_field_type =
-               bt_ctf_field_type_structure_get_field_type_by_name(
-                       event_payload_type, "enum_field");
-       BT_ASSERT(ep_enum_field_type);
-       ep_enum_field_unsigned_type =
-               bt_ctf_field_type_structure_get_field_type_by_name(
-                       event_payload_type, "enum_field_unsigned");
-       BT_ASSERT(ep_enum_field_unsigned_type);
-
-       ok(bt_ctf_stream_class_get_event_class_count(stream_class) == 1,
-               "bt_ctf_stream_class_get_event_class_count returns a correct number of event classes");
-       ret_event_class = bt_ctf_stream_class_get_event_class_by_index(stream_class, 0);
-       ok(ret_event_class == simple_event_class,
-               "bt_ctf_stream_class_get_event_class returns the correct event class");
-       bt_ctf_object_put_ref(ret_event_class);
-       ok(!bt_ctf_stream_class_get_event_class_by_id(stream_class, 2),
-               "bt_ctf_stream_class_get_event_class_by_id returns NULL when the requested ID doesn't exist");
-       ret_event_class =
-               bt_ctf_stream_class_get_event_class_by_id(stream_class, 13);
-       ok(ret_event_class == simple_event_class,
-               "bt_ctf_stream_class_get_event_class_by_id returns a correct event class");
-       bt_ctf_object_put_ref(ret_event_class);
-
-       simple_event = bt_ctf_event_create(simple_event_class);
-       ok(simple_event,
-               "Instantiate an event containing a single integer field");
-
-       integer_field = bt_ctf_field_create(ep_integer_field_type);
-       bt_ctf_field_integer_unsigned_set_value(integer_field, 42);
-       ok(bt_ctf_event_set_payload(simple_event, "integer_field",
-               integer_field) == 0, "Use bt_ctf_event_set_payload to set a manually allocated field");
-
-       float_field = bt_ctf_event_get_payload(simple_event, "float_field");
-       bt_ctf_field_floating_point_set_value(float_field, double_test_value);
-       ok(!bt_ctf_field_floating_point_get_value(float_field, &ret_double),
-               "bt_ctf_field_floating_point_get_value returns a double value");
-       ok(fabs(ret_double - double_test_value) <= DBL_EPSILON,
-               "bt_ctf_field_floating_point_get_value returns a correct value");
-
-       enum_field = bt_ctf_field_create(ep_enum_field_type);
-       BT_ASSERT(enum_field);
-
-       enum_container_field = bt_ctf_field_enumeration_get_container(enum_field);
-       ok(bt_ctf_field_integer_signed_set_value(
-               enum_container_field, -42) == 0,
-               "Set signed enumeration container value");
-       ret = bt_ctf_event_set_payload(simple_event, "enum_field", enum_field);
-       BT_ASSERT(!ret);
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(iter);
-
-       enum_field_unsigned = bt_ctf_field_create(ep_enum_field_unsigned_type);
-       BT_ASSERT(enum_field_unsigned);
-       enum_container_field_unsigned = bt_ctf_field_enumeration_get_container(
-               enum_field_unsigned);
-       ok(bt_ctf_field_integer_unsigned_set_value(
-               enum_container_field_unsigned, 42) == 0,
-               "Set unsigned enumeration container value");
-       ret = bt_ctf_event_set_payload(simple_event, "enum_field_unsigned",
-               enum_field_unsigned);
-       BT_ASSERT(!ret);
-
-       ok(bt_ctf_clock_set_time(clock, current_time) == 0, "Set clock time");
-
-       /* Populate stream event context */
-       stream_event_context =
-               bt_ctf_event_get_stream_event_context(simple_event);
-       BT_ASSERT(stream_event_context);
-       stream_event_context_field = bt_ctf_field_structure_get_field_by_name(
-               stream_event_context, "common_event_context");
-       bt_ctf_field_integer_unsigned_set_value(stream_event_context_field, 42);
-
-       /* Populate the event's context */
-       event_context = bt_ctf_event_get_context(simple_event);
-       ok(event_context,
-               "bt_ctf_event_get_context returns a field");
-       returned_type = bt_ctf_field_get_type(event_context);
-       ok(returned_type == event_context_type,
-               "bt_ctf_event_get_context returns a field of the appropriate type");
-       event_context_field = bt_ctf_field_structure_get_field_by_name(event_context,
-               "event_specific_context");
-       ok(!bt_ctf_field_integer_unsigned_set_value(event_context_field, 1234),
-               "Successfully set an event context's value");
-       ok(!bt_ctf_event_set_context(simple_event, event_context),
-               "Set an event context successfully");
-
-       ok(bt_ctf_stream_append_event(stream, simple_event) == 0,
-               "Append simple event to trace stream");
-
-       packet_context = bt_ctf_stream_get_packet_context(stream);
-       ok(packet_context,
-               "bt_ctf_stream_get_packet_context returns a packet context");
-
-       packet_context_field = bt_ctf_field_structure_get_field_by_name(packet_context,
-               "packet_size");
-       ok(packet_context_field,
-               "Packet context contains the default packet_size field.");
-       bt_ctf_object_put_ref(packet_context_field);
-       packet_context_field = bt_ctf_field_structure_get_field_by_name(packet_context,
-               "custom_packet_context_field");
-       ok(bt_ctf_field_integer_unsigned_set_value(packet_context_field, 8) == 0,
-               "Custom packet context field value successfully set.");
-
-       ok(bt_ctf_stream_set_packet_context(stream, packet_context) == 0,
-               "Successfully set a stream's packet context");
-
-       ok(bt_ctf_stream_flush(stream) == 0,
-               "Flush trace stream with one event");
-
-       bt_ctf_object_put_ref(simple_event_class);
-       bt_ctf_object_put_ref(simple_event);
-       bt_ctf_object_put_ref(uint_12_type);
-       bt_ctf_object_put_ref(int_64_type);
-       bt_ctf_object_put_ref(float_type);
-       bt_ctf_object_put_ref(enum_type);
-       bt_ctf_object_put_ref(enum_type_unsigned);
-       bt_ctf_object_put_ref(returned_type);
-       bt_ctf_object_put_ref(event_context_type);
-       bt_ctf_object_put_ref(integer_field);
-       bt_ctf_object_put_ref(float_field);
-       bt_ctf_object_put_ref(enum_field);
-       bt_ctf_object_put_ref(enum_field_unsigned);
-       bt_ctf_object_put_ref(enum_container_field);
-       bt_ctf_object_put_ref(enum_container_field_unsigned);
-       bt_ctf_object_put_ref(packet_context);
-       bt_ctf_object_put_ref(packet_context_field);
-       bt_ctf_object_put_ref(stream_event_context);
-       bt_ctf_object_put_ref(stream_event_context_field);
-       bt_ctf_object_put_ref(event_context);
-       bt_ctf_object_put_ref(event_context_field);
-       bt_ctf_object_put_ref(event_payload_type);
-       bt_ctf_object_put_ref(ep_integer_field_type);
-       bt_ctf_object_put_ref(ep_enum_field_type);
-       bt_ctf_object_put_ref(ep_enum_field_unsigned_type);
-       bt_ctf_object_put_ref(iter);
-}
-
-static
-void append_complex_event(struct bt_ctf_stream_class *stream_class,
-               struct bt_ctf_stream *stream, struct bt_ctf_clock *clock)
-{
-       int i;
-       struct event_class_attrs_counts ;
-       const char *complex_test_event_string = "Complex Test Event";
-       const char *test_string_1 = "Test ";
-       const char *test_string_2 = "string ";
-       const char *test_string_3 = "abcdefghi";
-       const char *test_string_4 = "abcd\0efg\0hi";
-       const char *test_string_cat = "Test string abcdeefg";
-       struct bt_ctf_field_type *uint_35_type =
-               bt_ctf_field_type_integer_create(35);
-       struct bt_ctf_field_type *int_16_type =
-               bt_ctf_field_type_integer_create(16);
-       struct bt_ctf_field_type *uint_3_type =
-               bt_ctf_field_type_integer_create(3);
-       struct bt_ctf_field_type *enum_variant_type =
-               bt_ctf_field_type_enumeration_create(uint_3_type);
-       struct bt_ctf_field_type *variant_type =
-               bt_ctf_field_type_variant_create(enum_variant_type,
-                       "variant_selector");
-       struct bt_ctf_field_type *string_type =
-               bt_ctf_field_type_string_create();
-       struct bt_ctf_field_type *sequence_type;
-       struct bt_ctf_field_type *array_type;
-       struct bt_ctf_field_type *inner_structure_type =
-               bt_ctf_field_type_structure_create();
-       struct bt_ctf_field_type *complex_structure_type =
-               bt_ctf_field_type_structure_create();
-       struct bt_ctf_field_type *ret_field_type;
-       struct bt_ctf_event_class *event_class;
-       struct bt_ctf_event *event;
-       struct bt_ctf_field *uint_35_field, *int_16_field, *a_string_field,
-               *inner_structure_field, *complex_structure_field,
-               *a_sequence_field, *enum_variant_field, *enum_container_field,
-               *variant_field, *an_array_field, *stream_event_ctx_field,
-               *stream_event_ctx_int_field;
-       uint64_t ret_unsigned_int;
-       int64_t ret_signed_int;
-       const char *ret_string;
-       struct bt_ctf_stream_class *ret_stream_class;
-       struct bt_ctf_event_class *ret_event_class;
-       struct bt_ctf_field *packet_context, *packet_context_field;
-
-       ok(bt_ctf_field_type_set_alignment(int_16_type, 0),
-               "bt_ctf_field_type_set_alignment handles 0-alignment correctly");
-       ok(bt_ctf_field_type_set_alignment(int_16_type, 3),
-               "bt_ctf_field_type_set_alignment handles wrong alignment correctly (3)");
-       ok(bt_ctf_field_type_set_alignment(int_16_type, 24),
-               "bt_ctf_field_type_set_alignment handles wrong alignment correctly (24)");
-       ok(!bt_ctf_field_type_set_alignment(int_16_type, 4),
-               "bt_ctf_field_type_set_alignment handles correct alignment correctly (4)");
-       ok(!bt_ctf_field_type_set_alignment(int_16_type, 32),
-               "Set alignment of signed 16 bit integer to 32");
-       ok(!bt_ctf_field_type_integer_set_signed(int_16_type, 1),
-               "Set integer signedness to true");
-       ok(!bt_ctf_field_type_integer_set_base(uint_35_type,
-               BT_CTF_INTEGER_BASE_HEXADECIMAL),
-               "Set signed 16 bit integer base to hexadecimal");
-
-       array_type = bt_ctf_field_type_array_create(int_16_type, ARRAY_TEST_LENGTH);
-       sequence_type = bt_ctf_field_type_sequence_create(int_16_type,
-               "seq_len");
-
-       ret_field_type = bt_ctf_field_type_array_get_element_field_type(
-               array_type);
-       ok(ret_field_type == int_16_type,
-               "bt_ctf_field_type_array_get_element_field_type returns the correct type");
-       bt_ctf_object_put_ref(ret_field_type);
-
-       ok(bt_ctf_field_type_array_get_length(array_type) == ARRAY_TEST_LENGTH,
-               "bt_ctf_field_type_array_get_length returns the correct length");
-
-       ok(bt_ctf_field_type_structure_add_field(inner_structure_type,
-               inner_structure_type, "yes"), "Cannot add self to structure");
-       ok(!bt_ctf_field_type_structure_add_field(inner_structure_type,
-               uint_35_type, "seq_len"), "Add seq_len field to inner structure");
-       ok(!bt_ctf_field_type_structure_add_field(inner_structure_type,
-               sequence_type, "a_sequence"), "Add a_sequence field to inner structure");
-       ok(!bt_ctf_field_type_structure_add_field(inner_structure_type,
-               array_type, "an_array"), "Add an_array field to inner structure");
-
-       bt_ctf_field_type_enumeration_add_mapping(enum_variant_type,
-               "UINT3_TYPE", 0, 0);
-       bt_ctf_field_type_enumeration_add_mapping(enum_variant_type,
-               "INT16_TYPE", 1, 1);
-       bt_ctf_field_type_enumeration_add_mapping(enum_variant_type,
-               "UINT35_TYPE", 2, 7);
-
-       ok(bt_ctf_field_type_variant_add_field(variant_type, uint_3_type,
-               "An unknown entry"), "Reject a variant field based on an unknown tag value");
-       ok(bt_ctf_field_type_variant_add_field(variant_type, uint_3_type,
-               "UINT3_TYPE") == 0, "Add a field to a variant");
-       ok(!bt_ctf_field_type_variant_add_field(variant_type, int_16_type,
-               "INT16_TYPE"), "Add INT16_TYPE field to variant");
-       ok(!bt_ctf_field_type_variant_add_field(variant_type, uint_35_type,
-               "UINT35_TYPE"), "Add UINT35_TYPE field to variant");
-
-       ret_field_type = bt_ctf_field_type_variant_get_tag_field_type(variant_type);
-       ok(ret_field_type == enum_variant_type,
-               "bt_ctf_field_type_variant_get_tag_field_type returns a correct tag type");
-       bt_ctf_object_put_ref(ret_field_type);
-
-       ret_string = bt_ctf_field_type_variant_get_tag_name(variant_type);
-       ok(ret_string ? strcmp(ret_string, "variant_selector") == 0 : 0,
-               "bt_ctf_field_type_variant_get_tag_name returns the correct variant tag name");
-       ret_field_type = bt_ctf_field_type_variant_get_field_type_by_name(
-               variant_type, "INT16_TYPE");
-       ok(ret_field_type == int_16_type,
-               "bt_ctf_field_type_variant_get_field_type_by_name returns a correct field type");
-       bt_ctf_object_put_ref(ret_field_type);
-
-       ok(bt_ctf_field_type_variant_get_field_count(variant_type) == 3,
-               "bt_ctf_field_type_variant_get_field_count returns the correct count");
-
-       ok(bt_ctf_field_type_variant_get_field_by_index(variant_type, NULL, &ret_field_type, 0) == 0,
-               "bt_ctf_field_type_variant_get_field handles a NULL field name correctly");
-       bt_ctf_object_put_ref(ret_field_type);
-       ok(bt_ctf_field_type_variant_get_field_by_index(variant_type, &ret_string, NULL, 0) == 0,
-               "bt_ctf_field_type_variant_get_field handles a NULL field type correctly");
-       ok(bt_ctf_field_type_variant_get_field_by_index(variant_type, &ret_string, &ret_field_type, 1) == 0,
-               "bt_ctf_field_type_variant_get_field returns a field");
-       ok(strcmp("INT16_TYPE", ret_string) == 0,
-               "bt_ctf_field_type_variant_get_field returns a correct field name");
-       ok(ret_field_type == int_16_type,
-               "bt_ctf_field_type_variant_get_field returns a correct field type");
-       bt_ctf_object_put_ref(ret_field_type);
-
-       ok(!bt_ctf_field_type_structure_add_field(complex_structure_type,
-               enum_variant_type, "variant_selector"),
-               "Add variant_selector field to complex structure");
-       ok(!bt_ctf_field_type_structure_add_field(complex_structure_type,
-               string_type, "string"), "Add `string` field to complex structure");
-       ok(!bt_ctf_field_type_structure_add_field(complex_structure_type,
-               variant_type, "variant_value"),
-               "Add variant_value field to complex structure");
-       ok(!bt_ctf_field_type_structure_add_field(complex_structure_type,
-               inner_structure_type, "inner_structure"),
-               "Add inner_structure field to complex structure");
-
-       event_class = bt_ctf_event_class_create(complex_test_event_string);
-       ok(event_class, "Create an event class");
-       ok(bt_ctf_event_class_add_field(event_class, uint_35_type, ""),
-               "Reject addition of a field with an empty name to an event");
-       ok(bt_ctf_event_class_add_field(event_class, NULL, "an_integer"),
-               "Reject addition of a field with a NULL type to an event");
-       ok(bt_ctf_event_class_add_field(event_class, uint_35_type,
-               "int"),
-               "Reject addition of a type with an illegal name to an event");
-       ok(bt_ctf_event_class_add_field(event_class, uint_35_type,
-               "uint_35") == 0,
-               "Add field of type unsigned integer to an event");
-       ok(bt_ctf_event_class_add_field(event_class, int_16_type,
-               "int_16") == 0, "Add field of type signed integer to an event");
-       ok(bt_ctf_event_class_add_field(event_class, complex_structure_type,
-               "complex_structure") == 0,
-               "Add composite structure to an event");
-
-       ret_string = bt_ctf_event_class_get_name(event_class);
-       ok(strcmp(ret_string, complex_test_event_string) == 0,
-               "bt_ctf_event_class_get_name returns a correct name");
-       ok(bt_ctf_event_class_get_id(event_class) < 0,
-               "bt_ctf_event_class_get_id returns a negative value when not set");
-       ok(bt_ctf_event_class_set_id(NULL, 42) < 0,
-               "bt_ctf_event_class_set_id handles NULL correctly");
-       ok(bt_ctf_event_class_set_id(event_class, 42) == 0,
-               "Set an event class' id");
-       ok(bt_ctf_event_class_get_id(event_class) == 42,
-               "bt_ctf_event_class_get_id returns the correct value");
-
-       /* Test event class attributes */
-       ok(bt_ctf_event_class_get_log_level(event_class) == BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED,
-               "event class has the expected initial log level");
-       ok(!bt_ctf_event_class_get_emf_uri(event_class),
-               "as expected, event class has no initial EMF URI");
-       ok(bt_ctf_event_class_set_log_level(NULL, BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO),
-               "bt_ctf_event_class_set_log_level handles a NULL event class correctly");
-       ok(bt_ctf_event_class_set_log_level(event_class, BT_CTF_EVENT_CLASS_LOG_LEVEL_UNKNOWN),
-               "bt_ctf_event_class_set_log_level handles an unknown log level correctly");
-       ok(!bt_ctf_event_class_set_log_level(event_class, BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO),
-               "bt_ctf_event_class_set_log_level succeeds with a valid log level");
-       ok(bt_ctf_event_class_get_log_level(event_class) == BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO,
-               "bt_ctf_event_class_get_log_level returns the expected log level");
-       ok(bt_ctf_event_class_set_emf_uri(NULL, "https://babeltrace.org/"),
-               "bt_ctf_event_class_set_emf_uri handles a NULL event class correctly");
-       ok(!bt_ctf_event_class_set_emf_uri(event_class, "https://babeltrace.org/"),
-               "bt_ctf_event_class_set_emf_uri succeeds with a valid EMF URI");
-       ok(strcmp(bt_ctf_event_class_get_emf_uri(event_class), "https://babeltrace.org/") == 0,
-               "bt_ctf_event_class_get_emf_uri returns the expected EMF URI");
-       ok(!bt_ctf_event_class_set_emf_uri(event_class, NULL),
-               "bt_ctf_event_class_set_emf_uri succeeds with NULL (to reset)");
-       ok(!bt_ctf_event_class_get_emf_uri(event_class),
-               "as expected, event class has no EMF URI after reset");
-
-       /* Add event class to the stream class */
-       ok(bt_ctf_stream_class_add_event_class(stream_class, NULL),
-               "Reject addition of NULL event class to a stream class");
-       ok(bt_ctf_stream_class_add_event_class(stream_class,
-               event_class) == 0, "Add an event class to stream class");
-
-       ret_stream_class = bt_ctf_event_class_get_stream_class(event_class);
-       ok(ret_stream_class == stream_class,
-               "bt_ctf_event_class_get_stream_class returns the correct stream class");
-       bt_ctf_object_put_ref(ret_stream_class);
-
-       ok(!bt_ctf_event_class_get_field_by_name(event_class, "truie"),
-               "bt_ctf_event_class_get_field_by_name handles an invalid field name correctly");
-       ret_field_type = bt_ctf_event_class_get_field_by_name(event_class,
-               "complex_structure");
-       bt_ctf_object_put_ref(ret_field_type);
-
-       event = bt_ctf_event_create(event_class);
-       ok(event, "Instanciate a complex event");
-
-       ret_event_class = bt_ctf_event_get_class(event);
-       ok(ret_event_class == event_class,
-               "bt_ctf_event_get_class returns the correct event class");
-       bt_ctf_object_put_ref(ret_event_class);
-
-       uint_35_field = bt_ctf_event_get_payload(event, "uint_35");
-       ok(uint_35_field, "Use bt_ctf_event_get_payload to get a field instance ");
-       bt_ctf_field_integer_unsigned_set_value(uint_35_field, 0x0DDF00D);
-       ok(bt_ctf_field_integer_unsigned_get_value(uint_35_field,
-               &ret_unsigned_int) == 0,
-               "bt_ctf_field_integer_unsigned_get_value succeeds after setting a value");
-       ok(ret_unsigned_int == 0x0DDF00D,
-               "bt_ctf_field_integer_unsigned_get_value returns the correct value");
-       bt_ctf_object_put_ref(uint_35_field);
-
-       int_16_field = bt_ctf_event_get_payload(event, "int_16");
-       bt_ctf_field_integer_signed_set_value(int_16_field, -12345);
-       ok(bt_ctf_field_integer_signed_get_value(int_16_field,
-               &ret_signed_int) == 0,
-               "bt_ctf_field_integer_signed_get_value succeeds after setting a value");
-       ok(ret_signed_int == -12345,
-               "bt_ctf_field_integer_signed_get_value returns the correct value");
-       bt_ctf_object_put_ref(int_16_field);
-
-       complex_structure_field = bt_ctf_event_get_payload(event,
-               "complex_structure");
-
-       inner_structure_field = bt_ctf_field_structure_get_field_by_index(
-               complex_structure_field, 3);
-       ret_field_type = bt_ctf_field_get_type(inner_structure_field);
-       bt_ctf_object_put_ref(inner_structure_field);
-       bt_ctf_object_put_ref(ret_field_type);
-
-       inner_structure_field = bt_ctf_field_structure_get_field_by_name(
-               complex_structure_field, "inner_structure");
-       a_string_field = bt_ctf_field_structure_get_field_by_name(
-               complex_structure_field, "string");
-       enum_variant_field = bt_ctf_field_structure_get_field_by_name(
-               complex_structure_field, "variant_selector");
-       variant_field = bt_ctf_field_structure_get_field_by_name(
-               complex_structure_field, "variant_value");
-       uint_35_field = bt_ctf_field_structure_get_field_by_name(
-               inner_structure_field, "seq_len");
-       a_sequence_field = bt_ctf_field_structure_get_field_by_name(
-               inner_structure_field, "a_sequence");
-       an_array_field = bt_ctf_field_structure_get_field_by_name(
-               inner_structure_field, "an_array");
-
-       enum_container_field = bt_ctf_field_enumeration_get_container(
-               enum_variant_field);
-       bt_ctf_field_integer_unsigned_set_value(enum_container_field, 1);
-       int_16_field = bt_ctf_field_variant_get_field(variant_field,
-               enum_variant_field);
-       bt_ctf_field_integer_signed_set_value(int_16_field, -200);
-       bt_ctf_object_put_ref(int_16_field);
-       bt_ctf_field_string_set_value(a_string_field,
-               test_string_1);
-       ok(!bt_ctf_field_string_append(a_string_field, test_string_2),
-               "bt_ctf_field_string_append succeeds");
-       ok(!bt_ctf_field_string_append_len(a_string_field, test_string_3, 5),
-               "bt_ctf_field_string_append_len succeeds (append 5 characters)");
-       ok(!bt_ctf_field_string_append_len(a_string_field, &test_string_4[5], 3),
-               "bt_ctf_field_string_append_len succeeds (append 0 characters)");
-       ok(!bt_ctf_field_string_append_len(a_string_field, test_string_3, 0),
-               "bt_ctf_field_string_append_len succeeds (append 0 characters)");
-
-       ret_string = bt_ctf_field_string_get_value(a_string_field);
-       ok(ret_string, "bt_ctf_field_string_get_value returns a string");
-       ok(ret_string ? strcmp(ret_string, test_string_cat) == 0 : 0,
-               "bt_ctf_field_string_get_value returns a correct value");
-       bt_ctf_field_integer_unsigned_set_value(uint_35_field,
-               SEQUENCE_TEST_LENGTH);
-
-       ret_field_type = bt_ctf_field_type_variant_get_field_type_from_tag(
-               variant_type, enum_variant_field);
-       ok(ret_field_type == int_16_type,
-               "bt_ctf_field_type_variant_get_field_type_from_tag returns the correct field type");
-
-       ok(bt_ctf_field_sequence_set_length(a_sequence_field,
-               uint_35_field) == 0, "Set a sequence field's length");
-
-       for (i = 0; i < SEQUENCE_TEST_LENGTH; i++) {
-               int_16_field = bt_ctf_field_sequence_get_field(
-                       a_sequence_field, i);
-               bt_ctf_field_integer_signed_set_value(int_16_field, 4 - i);
-               bt_ctf_object_put_ref(int_16_field);
-       }
-
-       for (i = 0; i < ARRAY_TEST_LENGTH; i++) {
-               int_16_field = bt_ctf_field_array_get_field(
-                       an_array_field, i);
-               bt_ctf_field_integer_signed_set_value(int_16_field, i);
-               bt_ctf_object_put_ref(int_16_field);
-       }
-
-       stream_event_ctx_field = bt_ctf_event_get_stream_event_context(event);
-       BT_ASSERT(stream_event_ctx_field);
-       stream_event_ctx_int_field = bt_ctf_field_structure_get_field_by_name(
-               stream_event_ctx_field, "common_event_context");
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_ctx_field);
-       bt_ctf_field_integer_unsigned_set_value(stream_event_ctx_int_field, 17);
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_ctx_int_field);
-
-       bt_ctf_clock_set_time(clock, ++current_time);
-       ok(bt_ctf_stream_append_event(stream, event) == 0,
-               "Append a complex event to a stream");
-
-       /*
-        * Populate the custom packet context field with a dummy value
-        * otherwise flush will fail.
-        */
-       packet_context = bt_ctf_stream_get_packet_context(stream);
-       packet_context_field = bt_ctf_field_structure_get_field_by_name(packet_context,
-               "custom_packet_context_field");
-       bt_ctf_field_integer_unsigned_set_value(packet_context_field, 1);
-
-       ok(bt_ctf_stream_flush(stream) == 0,
-               "Flush a stream containing a complex event");
-
-       bt_ctf_object_put_ref(uint_35_field);
-       bt_ctf_object_put_ref(a_string_field);
-       bt_ctf_object_put_ref(inner_structure_field);
-       bt_ctf_object_put_ref(complex_structure_field);
-       bt_ctf_object_put_ref(a_sequence_field);
-       bt_ctf_object_put_ref(an_array_field);
-       bt_ctf_object_put_ref(enum_variant_field);
-       bt_ctf_object_put_ref(enum_container_field);
-       bt_ctf_object_put_ref(variant_field);
-       bt_ctf_object_put_ref(packet_context_field);
-       bt_ctf_object_put_ref(packet_context);
-       bt_ctf_object_put_ref(uint_35_type);
-       bt_ctf_object_put_ref(int_16_type);
-       bt_ctf_object_put_ref(string_type);
-       bt_ctf_object_put_ref(sequence_type);
-       bt_ctf_object_put_ref(array_type);
-       bt_ctf_object_put_ref(inner_structure_type);
-       bt_ctf_object_put_ref(complex_structure_type);
-       bt_ctf_object_put_ref(uint_3_type);
-       bt_ctf_object_put_ref(enum_variant_type);
-       bt_ctf_object_put_ref(variant_type);
-       bt_ctf_object_put_ref(ret_field_type);
-       bt_ctf_object_put_ref(event_class);
-       bt_ctf_object_put_ref(event);
-}
-
-static
-void type_field_tests(void)
-{
-       struct bt_ctf_field *uint_12;
-       struct bt_ctf_field *int_16;
-       struct bt_ctf_field *string;
-       struct bt_ctf_field_type *composite_structure_type;
-       struct bt_ctf_field_type *structure_seq_type;
-       struct bt_ctf_field_type *string_type;
-       struct bt_ctf_field_type *sequence_type;
-       struct bt_ctf_field_type *uint_8_type;
-       struct bt_ctf_field_type *int_16_type;
-       struct bt_ctf_field_type *uint_12_type =
-               bt_ctf_field_type_integer_create(12);
-       struct bt_ctf_field_type *enumeration_type;
-       struct bt_ctf_field_type *returned_type;
-       const char *ret_string;
-
-       ok(uint_12_type, "Create an unsigned integer type");
-       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
-               BT_CTF_INTEGER_BASE_BINARY) == 0,
-               "Set integer type's base as binary");
-       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
-               BT_CTF_INTEGER_BASE_DECIMAL) == 0,
-               "Set integer type's base as decimal");
-       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
-               BT_CTF_INTEGER_BASE_UNKNOWN),
-               "Reject integer type's base set as unknown");
-       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
-               BT_CTF_INTEGER_BASE_OCTAL) == 0,
-               "Set integer type's base as octal");
-       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
-               BT_CTF_INTEGER_BASE_HEXADECIMAL) == 0,
-               "Set integer type's base as hexadecimal");
-       ok(bt_ctf_field_type_integer_set_base(uint_12_type, 457417),
-               "Reject unknown integer base value");
-       ok(bt_ctf_field_type_integer_set_signed(uint_12_type, 952835) == 0,
-               "Set integer type signedness to signed");
-       ok(bt_ctf_field_type_integer_set_signed(uint_12_type, 0) == 0,
-               "Set integer type signedness to unsigned");
-       ok(bt_ctf_field_type_integer_get_size(uint_12_type) == 12,
-               "bt_ctf_field_type_integer_get_size returns a correct value");
-       ok(bt_ctf_field_type_integer_get_signed(uint_12_type) == 0,
-               "bt_ctf_field_type_integer_get_signed returns a correct value for unsigned types");
-
-       ok(bt_ctf_field_type_set_byte_order(NULL,
-               BT_CTF_BYTE_ORDER_LITTLE_ENDIAN) < 0,
-               "bt_ctf_field_type_set_byte_order handles NULL correctly");
-       ok(bt_ctf_field_type_set_byte_order(uint_12_type,
-               (enum bt_ctf_byte_order) 42) < 0,
-               "bt_ctf_field_type_set_byte_order rejects invalid values");
-       ok(bt_ctf_field_type_set_byte_order(uint_12_type,
-               BT_CTF_BYTE_ORDER_LITTLE_ENDIAN) == 0,
-               "Set an integer's byte order to little endian");
-       ok(bt_ctf_field_type_set_byte_order(uint_12_type,
-               BT_CTF_BYTE_ORDER_BIG_ENDIAN) == 0,
-               "Set an integer's byte order to big endian");
-       ok(bt_ctf_field_type_get_byte_order(uint_12_type) ==
-               BT_CTF_BYTE_ORDER_BIG_ENDIAN,
-               "bt_ctf_field_type_get_byte_order returns a correct value");
-
-       ok(bt_ctf_field_type_get_type_id(uint_12_type) ==
-               BT_CTF_FIELD_TYPE_ID_INTEGER,
-               "bt_ctf_field_type_get_type_id returns a correct value with an integer type");
-
-       ok(bt_ctf_field_type_integer_get_base(uint_12_type) ==
-               BT_CTF_INTEGER_BASE_HEXADECIMAL,
-               "bt_ctf_field_type_integer_get_base returns a correct value");
-
-       ok(bt_ctf_field_type_integer_set_encoding(NULL,
-               BT_CTF_STRING_ENCODING_ASCII) < 0,
-               "bt_ctf_field_type_integer_set_encoding handles NULL correctly");
-       ok(bt_ctf_field_type_integer_set_encoding(uint_12_type,
-               (enum bt_ctf_string_encoding) 123) < 0,
-               "bt_ctf_field_type_integer_set_encoding handles invalid encodings correctly");
-       ok(bt_ctf_field_type_integer_set_encoding(uint_12_type,
-               BT_CTF_STRING_ENCODING_UTF8) == 0,
-               "Set integer type encoding to UTF8");
-       ok(bt_ctf_field_type_integer_get_encoding(uint_12_type) ==
-               BT_CTF_STRING_ENCODING_UTF8,
-               "bt_ctf_field_type_integer_get_encoding returns a correct value");
-
-       int_16_type = bt_ctf_field_type_integer_create(16);
-       BT_ASSERT(int_16_type);
-       ok(!bt_ctf_field_type_integer_set_signed(int_16_type, 1),
-               "Set signedness of 16 bit integer to true");
-       ok(bt_ctf_field_type_integer_get_signed(int_16_type) == 1,
-               "bt_ctf_field_type_integer_get_signed returns a correct value for signed types");
-       uint_8_type = bt_ctf_field_type_integer_create(8);
-       sequence_type =
-               bt_ctf_field_type_sequence_create(int_16_type, "seq_len");
-       ok(sequence_type, "Create a sequence of int16_t type");
-       ok(bt_ctf_field_type_get_type_id(sequence_type) ==
-               BT_CTF_FIELD_TYPE_ID_SEQUENCE,
-               "bt_ctf_field_type_get_type_id returns a correct value with a sequence type");
-
-       ret_string = bt_ctf_field_type_sequence_get_length_field_name(
-               sequence_type);
-       ok(strcmp(ret_string, "seq_len") == 0,
-               "bt_ctf_field_type_sequence_get_length_field_name returns the correct value");
-       returned_type = bt_ctf_field_type_sequence_get_element_field_type(
-               sequence_type);
-       ok(returned_type == int_16_type,
-               "bt_ctf_field_type_sequence_get_element_field_type returns the correct type");
-       bt_ctf_object_put_ref(returned_type);
-
-       string_type = bt_ctf_field_type_string_create();
-       ok(string_type, "Create a string type");
-       ok(bt_ctf_field_type_string_set_encoding(string_type,
-               BT_CTF_STRING_ENCODING_NONE),
-               "Reject invalid \"None\" string encoding");
-       ok(bt_ctf_field_type_string_set_encoding(string_type,
-               42),
-               "Reject invalid string encoding");
-       ok(bt_ctf_field_type_string_set_encoding(string_type,
-               BT_CTF_STRING_ENCODING_ASCII) == 0,
-               "Set string encoding to ASCII");
-
-       ok(bt_ctf_field_type_string_get_encoding(string_type) ==
-               BT_CTF_STRING_ENCODING_ASCII,
-               "bt_ctf_field_type_string_get_encoding returns the correct value");
-
-       structure_seq_type = bt_ctf_field_type_structure_create();
-       ok(bt_ctf_field_type_get_type_id(structure_seq_type) ==
-               BT_CTF_FIELD_TYPE_ID_STRUCT,
-               "bt_ctf_field_type_get_type_id returns a correct value with a structure type");
-       ok(structure_seq_type, "Create a structure type");
-       ok(bt_ctf_field_type_structure_add_field(structure_seq_type,
-               uint_8_type, "seq_len") == 0,
-               "Add a uint8_t type to a structure");
-       ok(bt_ctf_field_type_structure_add_field(structure_seq_type,
-               sequence_type, "a_sequence") == 0,
-               "Add a sequence type to a structure");
-
-       ok(bt_ctf_field_type_structure_get_field_count(structure_seq_type) == 2,
-               "bt_ctf_field_type_structure_get_field_count returns a correct value");
-
-       ok(bt_ctf_field_type_structure_get_field(structure_seq_type,
-               NULL, &returned_type, 1) == 0,
-               "bt_ctf_field_type_structure_get_field handles a NULL name correctly");
-       bt_ctf_object_put_ref(returned_type);
-       ok(bt_ctf_field_type_structure_get_field(structure_seq_type,
-               &ret_string, NULL, 1) == 0,
-               "bt_ctf_field_type_structure_get_field handles a NULL return type correctly");
-       ok(bt_ctf_field_type_structure_get_field(structure_seq_type,
-               &ret_string, &returned_type, 1) == 0,
-               "bt_ctf_field_type_structure_get_field returns a field");
-       ok(strcmp(ret_string, "a_sequence") == 0,
-               "bt_ctf_field_type_structure_get_field returns a correct field name");
-       ok(returned_type == sequence_type,
-               "bt_ctf_field_type_structure_get_field returns a correct field type");
-       bt_ctf_object_put_ref(returned_type);
-
-       returned_type = bt_ctf_field_type_structure_get_field_type_by_name(
-               structure_seq_type, "a_sequence");
-       ok(returned_type == sequence_type,
-               "bt_ctf_field_type_structure_get_field_type_by_name returns the correct field type");
-       bt_ctf_object_put_ref(returned_type);
-
-       composite_structure_type = bt_ctf_field_type_structure_create();
-       ok(bt_ctf_field_type_structure_add_field(composite_structure_type,
-               string_type, "a_string") == 0,
-               "Add a string type to a structure");
-       ok(bt_ctf_field_type_structure_add_field(composite_structure_type,
-               structure_seq_type, "inner_structure") == 0,
-               "Add a structure type to a structure");
-
-       returned_type = bt_ctf_field_type_structure_get_field_type_by_name(
-               structure_seq_type, "a_sequence");
-       ok(returned_type == sequence_type,
-               "bt_ctf_field_type_structure_get_field_type_by_name returns a correct type");
-       bt_ctf_object_put_ref(returned_type);
-
-       int_16 = bt_ctf_field_create(int_16_type);
-       ok(int_16, "Instanciate a signed 16-bit integer");
-       uint_12 = bt_ctf_field_create(uint_12_type);
-       ok(uint_12, "Instanciate an unsigned 12-bit integer");
-       returned_type = bt_ctf_field_get_type(int_16);
-       ok(returned_type == int_16_type,
-               "bt_ctf_field_get_type returns the correct type");
-
-       /* Can't modify types after instanciating them */
-       ok(bt_ctf_field_type_integer_set_base(uint_12_type,
-               BT_CTF_INTEGER_BASE_DECIMAL),
-               "Check an integer type' base can't be modified after instanciation");
-       ok(bt_ctf_field_type_integer_set_signed(uint_12_type, 0),
-               "Check an integer type's signedness can't be modified after instanciation");
-
-       /* Check overflows are properly tested for */
-       ok(bt_ctf_field_integer_signed_set_value(int_16, -32768) == 0,
-               "Check -32768 is allowed for a signed 16-bit integer");
-       ok(bt_ctf_field_integer_signed_set_value(int_16, 32767) == 0,
-               "Check 32767 is allowed for a signed 16-bit integer");
-       ok(bt_ctf_field_integer_signed_set_value(int_16, -42) == 0,
-               "Check -42 is allowed for a signed 16-bit integer");
-
-       ok(bt_ctf_field_integer_unsigned_set_value(uint_12, 4095) == 0,
-               "Check 4095 is allowed for an unsigned 12-bit integer");
-       ok(bt_ctf_field_integer_unsigned_set_value(uint_12, 0) == 0,
-               "Check 0 is allowed for an unsigned 12-bit integer");
-
-       string = bt_ctf_field_create(string_type);
-       ok(string, "Instanciate a string field");
-       ok(bt_ctf_field_string_set_value(string, "A value") == 0,
-               "Set a string's value");
-
-       enumeration_type = bt_ctf_field_type_enumeration_create(uint_12_type);
-       ok(enumeration_type,
-               "Create an enumeration type with an unsigned 12-bit integer as container");
-
-       bt_ctf_object_put_ref(string);
-       bt_ctf_object_put_ref(uint_12);
-       bt_ctf_object_put_ref(int_16);
-       bt_ctf_object_put_ref(composite_structure_type);
-       bt_ctf_object_put_ref(structure_seq_type);
-       bt_ctf_object_put_ref(string_type);
-       bt_ctf_object_put_ref(sequence_type);
-       bt_ctf_object_put_ref(uint_8_type);
-       bt_ctf_object_put_ref(int_16_type);
-       bt_ctf_object_put_ref(uint_12_type);
-       bt_ctf_object_put_ref(enumeration_type);
-       bt_ctf_object_put_ref(returned_type);
-}
-
-static
-void packet_resize_test(struct bt_ctf_stream_class *stream_class,
-               struct bt_ctf_stream *stream, struct bt_ctf_clock *clock)
-{
-       /*
-        * Append enough events to force the underlying packet to be resized.
-        * Also tests that a new event can be declared after a stream has been
-        * instantiated and used/flushed.
-        */
-       int ret = 0;
-       int i;
-       struct bt_ctf_event_class *event_class = bt_ctf_event_class_create(
-               "Spammy_Event");
-       struct bt_ctf_field_type *integer_type =
-               bt_ctf_field_type_integer_create(17);
-       struct bt_ctf_field_type *string_type =
-               bt_ctf_field_type_string_create();
-       struct bt_ctf_event *event = NULL;
-       struct bt_ctf_field *ret_field = NULL;
-       struct bt_ctf_field_type *ret_field_type = NULL;
-       uint64_t ret_uint64;
-       int events_appended = 0;
-       struct bt_ctf_field *packet_context = NULL,
-               *packet_context_field = NULL, *stream_event_context = NULL;
-       struct bt_ctf_field_type *ep_field_1_type = NULL;
-       struct bt_ctf_field_type *ep_a_string_type = NULL;
-       struct bt_ctf_field_type *ep_type = NULL;
-
-       ret |= bt_ctf_event_class_add_field(event_class, integer_type,
-               "field_1");
-       ret |= bt_ctf_event_class_add_field(event_class, string_type,
-               "a_string");
-       ret |= bt_ctf_stream_class_add_event_class(stream_class, event_class);
-       ok(ret == 0, "Add a new event class to a stream class after writing an event");
-       if (ret) {
-               goto end;
-       }
-
-       /*
-        * bt_ctf_stream_class_add_event_class() copies the field types
-        * of event_class, so we retrieve the new ones to create the
-        * appropriate fields.
-        */
-       ep_type = bt_ctf_event_class_get_payload_field_type(event_class);
-       BT_ASSERT(ep_type);
-       ep_field_1_type = bt_ctf_field_type_structure_get_field_type_by_name(
-               ep_type, "field_1");
-       BT_ASSERT(ep_field_1_type);
-       ep_a_string_type = bt_ctf_field_type_structure_get_field_type_by_name(
-               ep_type, "a_string");
-       BT_ASSERT(ep_a_string_type);
-
-       event = bt_ctf_event_create(event_class);
-       ret_field = bt_ctf_event_get_payload(event, 0);
-       ret_field_type = bt_ctf_field_get_type(ret_field);
-       bt_ctf_object_put_ref(ret_field_type);
-       bt_ctf_object_put_ref(ret_field);
-       bt_ctf_object_put_ref(event);
-
-       for (i = 0; i < packet_resize_test_length; i++) {
-               event = bt_ctf_event_create(event_class);
-               struct bt_ctf_field *integer =
-                       bt_ctf_field_create(ep_field_1_type);
-               struct bt_ctf_field *string =
-                       bt_ctf_field_create(ep_a_string_type);
-
-               ret |= bt_ctf_clock_set_time(clock, ++current_time);
-               ret |= bt_ctf_field_integer_unsigned_set_value(integer, i);
-               ret |= bt_ctf_event_set_payload(event, "field_1",
-                       integer);
-               bt_ctf_object_put_ref(integer);
-               ret |= bt_ctf_field_string_set_value(string, "This is a test");
-               ret |= bt_ctf_event_set_payload(event, "a_string",
-                       string);
-               bt_ctf_object_put_ref(string);
-
-               /* Populate stream event context */
-               stream_event_context =
-                       bt_ctf_event_get_stream_event_context(event);
-               integer = bt_ctf_field_structure_get_field_by_name(stream_event_context,
-                       "common_event_context");
-               BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_context);
-               ret |= bt_ctf_field_integer_unsigned_set_value(integer,
-                       i % 42);
-               bt_ctf_object_put_ref(integer);
-
-               ret |= bt_ctf_stream_append_event(stream, event);
-               bt_ctf_object_put_ref(event);
-
-               if (ret) {
-                       break;
-               }
-       }
-
-       events_appended = !!(i == packet_resize_test_length);
-       ret = bt_ctf_stream_get_discarded_events_count(stream, &ret_uint64);
-       ok(ret == 0 && ret_uint64 == 0,
-               "bt_ctf_stream_get_discarded_events_count returns a correct number of discarded events when none were discarded");
-       bt_ctf_stream_append_discarded_events(stream, 1000);
-       ret = bt_ctf_stream_get_discarded_events_count(stream, &ret_uint64);
-       ok(ret == 0 && ret_uint64 == 1000,
-               "bt_ctf_stream_get_discarded_events_count returns a correct number of discarded events when some were discarded");
-
-end:
-       ok(events_appended, "Append 100 000 events to a stream");
-
-       /*
-        * Populate the custom packet context field with a dummy value
-        * otherwise flush will fail.
-        */
-       packet_context = bt_ctf_stream_get_packet_context(stream);
-       packet_context_field = bt_ctf_field_structure_get_field_by_name(packet_context,
-               "custom_packet_context_field");
-       bt_ctf_field_integer_unsigned_set_value(packet_context_field, 2);
-
-       ok(bt_ctf_stream_flush(stream) == 0,
-               "Flush a stream that forces a packet resize");
-       ret = bt_ctf_stream_get_discarded_events_count(stream, &ret_uint64);
-       ok(ret == 0 && ret_uint64 == 1000,
-               "bt_ctf_stream_get_discarded_events_count returns a correct number of discarded events after a flush");
-       bt_ctf_object_put_ref(integer_type);
-       bt_ctf_object_put_ref(string_type);
-       bt_ctf_object_put_ref(packet_context);
-       bt_ctf_object_put_ref(packet_context_field);
-       bt_ctf_object_put_ref(stream_event_context);
-       bt_ctf_object_put_ref(event_class);
-       bt_ctf_object_put_ref(ep_field_1_type);
-       bt_ctf_object_put_ref(ep_a_string_type);
-       bt_ctf_object_put_ref(ep_type);
-}
-
-static
-void test_empty_stream(struct bt_ctf_writer *writer)
-{
-       int ret = 0;
-       struct bt_ctf_trace *trace = NULL, *ret_trace = NULL;
-       struct bt_ctf_stream_class *stream_class = NULL;
-       struct bt_ctf_stream *stream = NULL;
-
-       trace = bt_ctf_writer_get_trace(writer);
-       if (!trace) {
-               diag("Failed to get trace from writer");
-               ret = -1;
-               goto end;
-       }
-
-       stream_class = bt_ctf_stream_class_create("empty_stream");
-       if (!stream_class) {
-               diag("Failed to create stream class");
-               ret = -1;
-               goto end;
-       }
-
-       ret = bt_ctf_stream_class_set_packet_context_type(stream_class, NULL);
-       BT_ASSERT(ret == 0);
-       ret = bt_ctf_stream_class_set_event_header_type(stream_class, NULL);
-       BT_ASSERT(ret == 0);
-
-       ok(!bt_ctf_stream_class_get_trace(stream_class),
-               "bt_ctf_stream_class_get_trace returns NULL when stream class is orphaned");
-
-       stream = bt_ctf_writer_create_stream(writer, stream_class);
-       if (!stream) {
-               diag("Failed to create writer stream");
-               ret = -1;
-               goto end;
-       }
-
-       ret_trace = bt_ctf_stream_class_get_trace(stream_class);
-       ok(ret_trace == trace,
-               "bt_ctf_stream_class_get_trace returns the correct trace after a stream has been created");
-end:
-       ok(ret == 0,
-               "Created a stream class with default attributes and an empty stream");
-       bt_ctf_object_put_ref(trace);
-       bt_ctf_object_put_ref(ret_trace);
-       bt_ctf_object_put_ref(stream);
-       bt_ctf_object_put_ref(stream_class);
-}
-
-static
-void test_custom_event_header_stream(struct bt_ctf_writer *writer,
-                       struct bt_ctf_clock *clock)
-{
-       int i, ret;
-       struct bt_ctf_stream_class *stream_class = NULL;
-       struct bt_ctf_stream *stream = NULL;
-       struct bt_ctf_field_type *integer_type = NULL,
-               *sequence_type = NULL, *event_header_type = NULL;
-       struct bt_ctf_field *integer = NULL, *sequence = NULL,
-               *event_header = NULL, *packet_header = NULL;
-       struct bt_ctf_event_class *event_class = NULL;
-       struct bt_ctf_event *event = NULL;
-
-       stream_class = bt_ctf_stream_class_create("custom_event_header_stream");
-       if (!stream_class) {
-               fail("Failed to create stream class");
-               goto end;
-       }
-
-       ret = bt_ctf_stream_class_set_clock(stream_class, clock);
-       if (ret) {
-               fail("Failed to set stream class clock");
-               goto end;
-       }
-
-       /*
-        * Customize event header to add an "seq_len" integer member
-        * which will be used as the length of a sequence in an event of this
-        * stream.
-        */
-       event_header_type = bt_ctf_stream_class_get_event_header_type(
-               stream_class);
-       if (!event_header_type) {
-               fail("Failed to get event header type");
-               goto end;
-       }
-
-       integer_type = bt_ctf_field_type_integer_create(13);
-       if (!integer_type) {
-               fail("Failed to create length integer type");
-               goto end;
-       }
-
-       ret = bt_ctf_field_type_structure_add_field(event_header_type,
-               integer_type, "seq_len");
-       if (ret) {
-               fail("Failed to add a new field to stream event header");
-               goto end;
-       }
-
-       event_class = bt_ctf_event_class_create("sequence_event");
-       if (!event_class) {
-               fail("Failed to create event class");
-               goto end;
-       }
-
-       /*
-        * This event's payload will contain a sequence which references
-        * stream.event.header.seq_len as its length field.
-        */
-       sequence_type = bt_ctf_field_type_sequence_create(integer_type,
-               "stream.event.header.seq_len");
-       if (!sequence_type) {
-               fail("Failed to create a sequence");
-               goto end;
-       }
-
-       ret = bt_ctf_event_class_add_field(event_class, sequence_type,
-               "some_sequence");
-       if (ret) {
-               fail("Failed to add a sequence to an event class");
-               goto end;
-       }
-
-       ret = bt_ctf_stream_class_add_event_class(stream_class, event_class);
-       if (ret) {
-               fail("Failed to add event class to stream class");
-               goto end;
-       }
-
-       stream = bt_ctf_writer_create_stream(writer, stream_class);
-       if (!stream) {
-               fail("Failed to create stream")
-               goto end;
-       }
-
-       /*
-        * We have defined a custom packet header field. We have to populate it
-        * explicitly.
-        */
-       packet_header = bt_ctf_stream_get_packet_header(stream);
-       if (!packet_header) {
-               fail("Failed to get stream packet header");
-               goto end;
-       }
-
-       integer = bt_ctf_field_structure_get_field_by_name(packet_header,
-               "custom_trace_packet_header_field");
-       if (!integer) {
-               fail("Failed to retrieve custom_trace_packet_header_field");
-               goto end;
-       }
-
-       ret = bt_ctf_field_integer_unsigned_set_value(integer, 3487);
-       if (ret) {
-               fail("Failed to set custom_trace_packet_header_field value");
-               goto end;
-       }
-       bt_ctf_object_put_ref(integer);
-
-       event = bt_ctf_event_create(event_class);
-       if (!event) {
-               fail("Failed to create event");
-               goto end;
-       }
-
-       event_header = bt_ctf_event_get_header(event);
-       if (!event_header) {
-               fail("Failed to get event header");
-               goto end;
-       }
-
-       integer = bt_ctf_field_structure_get_field_by_name(event_header,
-               "seq_len");
-       if (!integer) {
-               fail("Failed to get seq_len field from event header");
-               goto end;
-       }
-
-       ret = bt_ctf_field_integer_unsigned_set_value(integer, 2);
-       if (ret) {
-               fail("Failed to set seq_len value in event header");
-               goto end;
-       }
-
-       /* Populate both sequence integer fields */
-       sequence = bt_ctf_event_get_payload(event, "some_sequence");
-       if (!sequence) {
-               fail("Failed to retrieve sequence from event");
-               goto end;
-       }
-
-       ret = bt_ctf_field_sequence_set_length(sequence, integer);
-       if (ret) {
-               fail("Failed to set sequence length");
-               goto end;
-       }
-       bt_ctf_object_put_ref(integer);
-
-       for (i = 0; i < 2; i++) {
-               integer = bt_ctf_field_sequence_get_field(sequence, i);
-               if (ret) {
-                       fail("Failed to retrieve sequence element");
-                       goto end;
-               }
-
-               ret = bt_ctf_field_integer_unsigned_set_value(integer, i);
-               if (ret) {
-                       fail("Failed to set sequence element value");
-                       goto end;
-               }
-
-               bt_ctf_object_put_ref(integer);
-               integer = NULL;
-       }
-
-       ret = bt_ctf_stream_append_event(stream, event);
-       if (ret) {
-               fail("Failed to append event to stream");
-               goto end;
-       }
-
-       ret = bt_ctf_stream_flush(stream);
-       if (ret) {
-               fail("Failed to flush custom_event_header stream");
-       }
-end:
-       bt_ctf_object_put_ref(stream);
-       bt_ctf_object_put_ref(stream_class);
-       bt_ctf_object_put_ref(event_class);
-       bt_ctf_object_put_ref(event);
-       bt_ctf_object_put_ref(integer);
-       bt_ctf_object_put_ref(sequence);
-       bt_ctf_object_put_ref(event_header);
-       bt_ctf_object_put_ref(packet_header);
-       bt_ctf_object_put_ref(sequence_type);
-       bt_ctf_object_put_ref(integer_type);
-       bt_ctf_object_put_ref(event_header_type);
-}
-
-static
-void test_instanciate_event_before_stream(struct bt_ctf_writer *writer,
-               struct bt_ctf_clock *clock)
-{
-       int ret = 0;
-       struct bt_ctf_stream_class *stream_class = NULL;
-       struct bt_ctf_stream *stream = NULL,
-               *ret_stream = NULL;
-       struct bt_ctf_event_class *event_class = NULL;
-       struct bt_ctf_event *event = NULL;
-       struct bt_ctf_field_type *integer_type = NULL;
-       struct bt_ctf_field *payload_field = NULL;
-       struct bt_ctf_field *integer = NULL;
-
-       stream_class = bt_ctf_stream_class_create("event_before_stream_test");
-       if (!stream_class) {
-               diag("Failed to create stream class");
-               ret = -1;
-               goto end;
-       }
-
-       ret = bt_ctf_stream_class_set_clock(stream_class, clock);
-       if (ret) {
-               diag("Failed to set stream class clock");
-               goto end;
-       }
-
-       event_class = bt_ctf_event_class_create("some_event_class_name");
-       integer_type = bt_ctf_field_type_integer_create(32);
-       if (!integer_type) {
-               diag("Failed to create integer field type");
-               ret = -1;
-               goto end;
-       }
-
-       ret = bt_ctf_event_class_add_field(event_class, integer_type,
-               "integer_field");
-       if (ret) {
-               diag("Failed to add field to event class");
-               goto end;
-       }
-
-       ret = bt_ctf_stream_class_add_event_class(stream_class,
-               event_class);
-       if (ret) {
-               diag("Failed to add event class to stream class");
-       }
-
-       event = bt_ctf_event_create(event_class);
-       if (!event) {
-               diag("Failed to create event");
-               ret = -1;
-               goto end;
-       }
-
-       payload_field = bt_ctf_event_get_payload_field(event);
-       if (!payload_field) {
-               diag("Failed to get event's payload field");
-               ret = -1;
-               goto end;
-       }
-
-       integer = bt_ctf_field_structure_get_field_by_index(payload_field, 0);
-       if (!integer) {
-               diag("Failed to get integer field payload from event");
-               ret = -1;
-               goto end;
-       }
-
-       ret = bt_ctf_field_integer_unsigned_set_value(integer, 1234);
-       if (ret) {
-               diag("Failed to set integer field value");
-               goto end;
-       }
-
-       stream = bt_ctf_writer_create_stream(writer, stream_class);
-       if (!stream) {
-               diag("Failed to create writer stream");
-               ret = -1;
-               goto end;
-       }
-
-       ret = bt_ctf_stream_append_event(stream, event);
-       if (ret) {
-               diag("Failed to append event to stream");
-               goto end;
-       }
-
-       ret_stream = bt_ctf_event_get_stream(event);
-       ok(ret_stream == stream,
-               "bt_ctf_event_get_stream returns an event's stream after it has been appended");
-end:
-       ok(ret == 0,
-               "Create an event before instanciating its associated stream");
-       bt_ctf_object_put_ref(stream);
-       bt_ctf_object_put_ref(ret_stream);
-       bt_ctf_object_put_ref(stream_class);
-       bt_ctf_object_put_ref(event_class);
-       bt_ctf_object_put_ref(event);
-       bt_ctf_object_put_ref(integer_type);
-       bt_ctf_object_put_ref(integer);
-       bt_ctf_object_put_ref(payload_field);
-}
-
-static
-void append_existing_event_class(struct bt_ctf_stream_class *stream_class)
-{
-       int ret;
-       struct bt_ctf_event_class *event_class;
-
-       event_class = bt_ctf_event_class_create("Simple Event");
-       BT_ASSERT(event_class);
-       ok(bt_ctf_stream_class_add_event_class(stream_class, event_class) == 0,
-               "two event classes with the same name may cohabit within the same stream class");
-       bt_ctf_object_put_ref(event_class);
-
-       event_class = bt_ctf_event_class_create("different name, ok");
-       BT_ASSERT(event_class);
-       ret = bt_ctf_event_class_set_id(event_class, 13);
-       BT_ASSERT(ret == 0);
-       ok(bt_ctf_stream_class_add_event_class(stream_class, event_class),
-               "two event classes with the same ID cannot cohabit within the same stream class");
-       bt_ctf_object_put_ref(event_class);
-}
-
-static
-void test_clock_utils(void)
-{
-       int ret;
-       struct bt_ctf_clock *clock = NULL;
-
-       clock = bt_ctf_clock_create("water");
-       BT_ASSERT(clock);
-       ret = bt_ctf_clock_set_offset_s(clock, 1234);
-       BT_ASSERT(!ret);
-       ret = bt_ctf_clock_set_offset(clock, 1000);
-       BT_ASSERT(!ret);
-       ret = bt_ctf_clock_set_frequency(clock, 1000000000);
-       BT_ASSERT(!ret);
-       ret = bt_ctf_clock_set_frequency(clock, 1534);
-       BT_ASSERT(!ret);
-
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(clock);
-}
-
-int main(int argc, char **argv)
-{
-       const char *env_resize_length;
-       gchar *trace_path;
-       gchar *metadata_path;
-       const char *clock_name = "test_clock";
-       const char *clock_description = "This is a test clock";
-       const char *returned_clock_name;
-       const char *returned_clock_description;
-       const uint64_t frequency = 1123456789;
-       const int64_t offset_s = 13515309;
-       const int64_t offset = 1234567;
-       int64_t get_offset_s,
-               get_offset;
-       const uint64_t precision = 10;
-       const int is_absolute = 0xFF;
-       char *metadata_string;
-       struct bt_ctf_writer *writer;
-       struct bt_utsname name = {"GNU/Linux", "testhost", "4.4.0-87-generic",
-               "#110-Ubuntu SMP Tue Jul 18 12:55:35 UTC 2017", "x86_64"};
-       struct bt_ctf_clock *clock, *ret_clock;
-       struct bt_ctf_stream_class *stream_class, *ret_stream_class;
-       struct bt_ctf_stream *stream1;
-       struct bt_ctf_stream *stream;
-       const char *ret_string;
-       const uint8_t *ret_uuid;
-       bt_uuid_t tmp_uuid = { 0 };
-       struct bt_ctf_field_type *packet_context_type,
-               *packet_context_field_type,
-               *packet_header_type,
-               *packet_header_field_type,
-               *integer_type,
-               *stream_event_context_type,
-               *ret_field_type,
-               *event_header_field_type;
-       struct bt_ctf_field *packet_header, *packet_header_field;
-       struct bt_ctf_trace *trace;
-       int ret;
-
-       if (argc < 2) {
-               printf("Usage: tests-ctf-writer path_to_babeltrace\n");
-               return -1;
-       }
-
-       env_resize_length = getenv("PACKET_RESIZE_TEST_LENGTH");
-       if (env_resize_length) {
-               packet_resize_test_length =
-                       (unsigned int) atoi(env_resize_length);
-       }
-
-       plan_tests(NR_TESTS);
-
-       trace_path = g_build_filename(g_get_tmp_dir(), "ctfwriter_XXXXXX", NULL);
-       if (!bt_mkdtemp(trace_path)) {
-               perror("# perror");
-       }
-
-       metadata_path = g_build_filename(trace_path, "metadata", NULL);
-
-       writer = bt_ctf_writer_create(trace_path);
-       ok(writer, "bt_ctf_create succeeds in creating trace with path");
-
-       ok(!bt_ctf_writer_get_trace(NULL),
-               "bt_ctf_writer_get_trace correctly handles NULL");
-       trace = bt_ctf_writer_get_trace(writer);
-       ok(bt_ctf_trace_set_native_byte_order(trace, BT_CTF_BYTE_ORDER_NATIVE),
-               "Cannot set a trace's byte order to BT_CTF_BYTE_ORDER_NATIVE");
-       ok(bt_ctf_trace_set_native_byte_order(trace, BT_CTF_BYTE_ORDER_UNSPECIFIED),
-               "Cannot set a trace's byte order to BT_CTF_BYTE_ORDER_UNSPECIFIED");
-       ok(trace,
-               "bt_ctf_writer_get_trace returns a bt_ctf_trace object");
-       ok(bt_ctf_trace_set_native_byte_order(trace, BT_CTF_BYTE_ORDER_BIG_ENDIAN) == 0,
-               "Set a trace's byte order to big endian");
-       ok(bt_ctf_trace_get_native_byte_order(trace) == BT_CTF_BYTE_ORDER_BIG_ENDIAN,
-               "bt_ctf_trace_get_native_byte_order returns a correct endianness");
-
-       /* Add environment context to the trace */
-       ok(bt_ctf_writer_add_environment_field(writer, "host", name.nodename) == 0,
-               "Add host (%s) environment field to writer instance",
-               name.nodename);
-       ok(bt_ctf_writer_add_environment_field(NULL, "test_field",
-               "test_value"),
-               "bt_ctf_writer_add_environment_field error with NULL writer");
-       ok(bt_ctf_writer_add_environment_field(writer, NULL,
-               "test_value"),
-               "bt_ctf_writer_add_environment_field error with NULL field name");
-       ok(bt_ctf_writer_add_environment_field(writer, "test_field",
-               NULL),
-               "bt_ctf_writer_add_environment_field error with NULL field value");
-
-       /* Test bt_ctf_trace_set_environment_field_integer */
-       ok(bt_ctf_trace_set_environment_field_integer(NULL, "test_env_int",
-               -194875),
-               "bt_ctf_trace_set_environment_field_integer handles a NULL trace correctly");
-       ok(bt_ctf_trace_set_environment_field_integer(trace, NULL, -194875),
-               "bt_ctf_trace_set_environment_field_integer handles a NULL name correctly");
-       ok(!bt_ctf_trace_set_environment_field_integer(trace, "test_env_int",
-               -164973),
-               "bt_ctf_trace_set_environment_field_integer succeeds");
-
-       /* Test bt_ctf_trace_set_environment_field_string */
-       ok(bt_ctf_trace_set_environment_field_string(NULL, "test_env_str",
-               "yeah"),
-               "bt_ctf_trace_set_environment_field_string handles a NULL trace correctly");
-       ok(bt_ctf_trace_set_environment_field_string(trace, NULL, "yeah"),
-               "bt_ctf_trace_set_environment_field_string handles a NULL name correctly");
-       ok(bt_ctf_trace_set_environment_field_string(trace, "test_env_str",
-               NULL),
-               "bt_ctf_trace_set_environment_field_string handles a NULL value correctly");
-       ok(!bt_ctf_trace_set_environment_field_string(trace, "test_env_str",
-               "oh yeah"),
-               "bt_ctf_trace_set_environment_field_string succeeds");
-
-       /* Test environment field replacement */
-       ok(!bt_ctf_trace_set_environment_field_integer(trace, "test_env_int",
-               654321),
-               "bt_ctf_trace_set_environment_field_integer succeeds with an existing name");
-
-       ok(bt_ctf_writer_add_environment_field(writer, "sysname", name.sysname)
-               == 0, "Add sysname (%s) environment field to writer instance",
-               name.sysname);
-       ok(bt_ctf_writer_add_environment_field(writer, "nodename",
-               name.nodename) == 0,
-               "Add nodename (%s) environment field to writer instance",
-               name.nodename);
-       ok(bt_ctf_writer_add_environment_field(writer, "release", name.release)
-               == 0, "Add release (%s) environment field to writer instance",
-               name.release);
-       ok(bt_ctf_writer_add_environment_field(writer, "version", name.version)
-               == 0, "Add version (%s) environment field to writer instance",
-               name.version);
-       ok(bt_ctf_writer_add_environment_field(writer, "machine", name.machine)
-               == 0, "Add machine (%s) environment field to writer istance",
-               name.machine);
-
-       /* Define a clock and add it to the trace */
-       ok(!bt_ctf_clock_create("signed"),
-               "Illegal clock name rejected");
-       clock = bt_ctf_clock_create(clock_name);
-       ok(clock, "Clock created sucessfully");
-       returned_clock_name = bt_ctf_clock_get_name(clock);
-       ok(returned_clock_name, "bt_ctf_clock_get_name returns a clock name");
-       ok(returned_clock_name ? strcmp(returned_clock_name, clock_name) == 0 : 0,
-               "Returned clock name is valid");
-
-       returned_clock_description = bt_ctf_clock_get_description(clock);
-       ok(!returned_clock_description, "bt_ctf_clock_get_description returns NULL on an unset description");
-       ok(bt_ctf_clock_set_description(clock, clock_description) == 0,
-               "Clock description set successfully");
-
-       returned_clock_description = bt_ctf_clock_get_description(clock);
-       ok(returned_clock_description,
-               "bt_ctf_clock_get_description returns a description.");
-       ok(returned_clock_description ?
-               strcmp(returned_clock_description, clock_description) == 0 : 0,
-               "Returned clock description is valid");
-
-       ok(bt_ctf_clock_get_frequency(clock) == DEFAULT_CLOCK_FREQ,
-               "bt_ctf_clock_get_frequency returns the correct default frequency");
-       ok(bt_ctf_clock_set_frequency(clock, frequency) == 0,
-               "Set clock frequency");
-       ok(bt_ctf_clock_get_frequency(clock) == frequency,
-               "bt_ctf_clock_get_frequency returns the correct frequency once it is set");
-
-       ok(bt_ctf_clock_get_offset_s(clock, &get_offset_s) == 0,
-               "bt_ctf_clock_get_offset_s succeeds");
-       ok(get_offset_s == DEFAULT_CLOCK_OFFSET_S,
-               "bt_ctf_clock_get_offset_s returns the correct default offset (in seconds)");
-       ok(bt_ctf_clock_set_offset_s(clock, offset_s) == 0,
-               "Set clock offset (seconds)");
-       ok(bt_ctf_clock_get_offset_s(clock, &get_offset_s) == 0,
-               "bt_ctf_clock_get_offset_s succeeds");
-       ok(get_offset_s == offset_s,
-               "bt_ctf_clock_get_offset_s returns the correct default offset (in seconds) once it is set");
-
-       ok(bt_ctf_clock_get_offset(clock, &get_offset) == 0,
-               "bt_ctf_clock_get_offset succeeds");
-       ok(get_offset == DEFAULT_CLOCK_OFFSET,
-               "bt_ctf_clock_get_offset returns the correct default offset (in ticks)");
-       ok(bt_ctf_clock_set_offset(clock, offset) == 0, "Set clock offset");
-       ok(bt_ctf_clock_get_offset(clock, &get_offset) == 0,
-               "bt_ctf_clock_get_offset succeeds");
-       ok(get_offset == offset,
-               "bt_ctf_clock_get_offset returns the correct default offset (in ticks) once it is set");
-
-       ok(bt_ctf_clock_get_precision(clock) == DEFAULT_CLOCK_PRECISION,
-               "bt_ctf_clock_get_precision returns the correct default precision");
-       ok(bt_ctf_clock_set_precision(clock, precision) == 0,
-               "Set clock precision");
-       ok(bt_ctf_clock_get_precision(clock) == precision,
-               "bt_ctf_clock_get_precision returns the correct precision once it is set");
-
-       ok(bt_ctf_clock_get_is_absolute(clock) == DEFAULT_CLOCK_IS_ABSOLUTE,
-               "bt_ctf_clock_get_precision returns the correct default is_absolute attribute");
-       ok(bt_ctf_clock_set_is_absolute(clock, is_absolute) == 0,
-               "Set clock absolute property");
-       ok(bt_ctf_clock_get_is_absolute(clock) == !!is_absolute,
-               "bt_ctf_clock_get_precision returns the correct is_absolute attribute once it is set");
-       ok(bt_ctf_clock_set_time(clock, current_time) == 0,
-               "Set clock time");
-       ret_uuid = bt_ctf_clock_get_uuid(clock);
-       ok(ret_uuid,
-               "bt_ctf_clock_get_uuid returns a UUID");
-       if (ret_uuid) {
-               memcpy(tmp_uuid, ret_uuid, sizeof(tmp_uuid));
-               /* Slightly modify UUID */
-               tmp_uuid[sizeof(tmp_uuid) - 1]++;
-       }
-
-       ok(bt_ctf_clock_set_uuid(clock, tmp_uuid) == 0,
-               "bt_ctf_clock_set_uuid sets a new uuid successfully");
-       ret_uuid = bt_ctf_clock_get_uuid(clock);
-       ok(ret_uuid,
-               "bt_ctf_clock_get_uuid returns a UUID after setting a new one");
-       ok(uuid_match(ret_uuid, tmp_uuid),
-               "bt_ctf_clock_get_uuid returns the correct UUID after setting a new one");
-
-       /* Define a stream class */
-       stream_class = bt_ctf_stream_class_create("test_stream");
-       ret_string = bt_ctf_stream_class_get_name(stream_class);
-       ok(ret_string && strcmp(ret_string, "test_stream") == 0,
-               "bt_ctf_stream_class_get_name returns a correct stream class name");
-
-       ok(!bt_ctf_stream_class_get_clock(stream_class),
-               "bt_ctf_stream_class_get_clock returns NULL when a clock was not set");
-       ok(!bt_ctf_stream_class_get_clock(NULL),
-               "bt_ctf_stream_class_get_clock handles NULL correctly");
-
-       ok(stream_class, "Create stream class");
-       ok(bt_ctf_stream_class_set_clock(stream_class, clock) == 0,
-               "Set a stream class' clock");
-       ret_clock = bt_ctf_stream_class_get_clock(stream_class);
-       ok(ret_clock == clock,
-               "bt_ctf_stream_class_get_clock returns a correct clock");
-       bt_ctf_object_put_ref(ret_clock);
-
-       /* Test the event fields and event types APIs */
-       type_field_tests();
-
-       ok(bt_ctf_stream_class_get_id(stream_class) < 0,
-               "bt_ctf_stream_class_get_id returns an error when no id is set");
-       ok(bt_ctf_stream_class_set_id(NULL, 123) < 0,
-               "bt_ctf_stream_class_set_id handles NULL correctly");
-       ok(bt_ctf_stream_class_set_id(stream_class, 123) == 0,
-               "Set an stream class' id");
-       ok(bt_ctf_stream_class_get_id(stream_class) == 123,
-               "bt_ctf_stream_class_get_id returns the correct value");
-
-       /* Validate default event header fields */
-       ret_field_type = bt_ctf_stream_class_get_event_header_type(
-               stream_class);
-       ok(ret_field_type,
-               "bt_ctf_stream_class_get_event_header_type returns an event header type");
-       ok(bt_ctf_field_type_get_type_id(ret_field_type) == BT_CTF_FIELD_TYPE_ID_STRUCT,
-               "Default event header type is a structure");
-       event_header_field_type =
-               bt_ctf_field_type_structure_get_field_type_by_name(
-               ret_field_type, "id");
-       ok(event_header_field_type,
-               "Default event header type contains an \"id\" field");
-       ok(bt_ctf_field_type_get_type_id(
-               event_header_field_type) == BT_CTF_FIELD_TYPE_ID_INTEGER,
-               "Default event header \"id\" field is an integer");
-       bt_ctf_object_put_ref(event_header_field_type);
-       event_header_field_type =
-               bt_ctf_field_type_structure_get_field_type_by_name(
-               ret_field_type, "timestamp");
-       ok(event_header_field_type,
-               "Default event header type contains a \"timestamp\" field");
-       ok(bt_ctf_field_type_get_type_id(
-               event_header_field_type) == BT_CTF_FIELD_TYPE_ID_INTEGER,
-               "Default event header \"timestamp\" field is an integer");
-       bt_ctf_object_put_ref(event_header_field_type);
-       bt_ctf_object_put_ref(ret_field_type);
-
-       /* Add a custom trace packet header field */
-       packet_header_type = bt_ctf_trace_get_packet_header_field_type(trace);
-       ok(packet_header_type,
-               "bt_ctf_trace_get_packet_header_field_type returns a packet header");
-       ok(bt_ctf_field_type_get_type_id(packet_header_type) == BT_CTF_FIELD_TYPE_ID_STRUCT,
-               "bt_ctf_trace_get_packet_header_field_type returns a packet header of type struct");
-       ret_field_type = bt_ctf_field_type_structure_get_field_type_by_name(
-               packet_header_type, "magic");
-       ok(ret_field_type, "Default packet header type contains a \"magic\" field");
-       bt_ctf_object_put_ref(ret_field_type);
-       ret_field_type = bt_ctf_field_type_structure_get_field_type_by_name(
-               packet_header_type, "uuid");
-       ok(ret_field_type, "Default packet header type contains a \"uuid\" field");
-       bt_ctf_object_put_ref(ret_field_type);
-       ret_field_type = bt_ctf_field_type_structure_get_field_type_by_name(
-               packet_header_type, "stream_id");
-       ok(ret_field_type, "Default packet header type contains a \"stream_id\" field");
-       bt_ctf_object_put_ref(ret_field_type);
-
-       packet_header_field_type = bt_ctf_field_type_integer_create(22);
-       ok(!bt_ctf_field_type_structure_add_field(packet_header_type,
-               packet_header_field_type, "custom_trace_packet_header_field"),
-               "Added a custom trace packet header field successfully");
-
-       ok(bt_ctf_trace_set_packet_header_field_type(NULL, packet_header_type) < 0,
-               "bt_ctf_trace_set_packet_header_field_type handles a NULL trace correctly");
-       ok(!bt_ctf_trace_set_packet_header_field_type(trace, packet_header_type),
-               "Set a trace packet_header_type successfully");
-
-       /* Add a custom field to the stream class' packet context */
-       packet_context_type = bt_ctf_stream_class_get_packet_context_type(stream_class);
-       ok(packet_context_type,
-               "bt_ctf_stream_class_get_packet_context_type returns a packet context type.");
-       ok(bt_ctf_field_type_get_type_id(packet_context_type) == BT_CTF_FIELD_TYPE_ID_STRUCT,
-               "Packet context is a structure");
-
-       ok(bt_ctf_stream_class_set_packet_context_type(NULL, packet_context_type),
-               "bt_ctf_stream_class_set_packet_context_type handles a NULL stream class correctly");
-
-       integer_type = bt_ctf_field_type_integer_create(32);
-
-       ok(bt_ctf_stream_class_set_packet_context_type(stream_class,
-               integer_type) < 0,
-               "bt_ctf_stream_class_set_packet_context_type rejects a packet context that is not a structure");
-
-       /* Create a "uint5_t" equivalent custom packet context field */
-       packet_context_field_type = bt_ctf_field_type_integer_create(5);
-
-       ret = bt_ctf_field_type_structure_add_field(packet_context_type,
-               packet_context_field_type, "custom_packet_context_field");
-       ok(ret == 0, "Packet context field added successfully");
-
-       /* Define a stream event context containing a my_integer field. */
-       stream_event_context_type = bt_ctf_field_type_structure_create();
-       bt_ctf_field_type_structure_add_field(stream_event_context_type,
-               integer_type, "common_event_context");
-
-       ok(bt_ctf_stream_class_set_event_context_type(NULL,
-               stream_event_context_type) < 0,
-               "bt_ctf_stream_class_set_event_context_type handles a NULL stream_class correctly");
-       ok(bt_ctf_stream_class_set_event_context_type(stream_class,
-               integer_type) < 0,
-               "bt_ctf_stream_class_set_event_context_type validates that the event context os a structure");
-
-       ok(bt_ctf_stream_class_set_event_context_type(
-               stream_class, stream_event_context_type) == 0,
-               "Set a new stream event context type");
-
-       ret_field_type = bt_ctf_stream_class_get_event_context_type(
-               stream_class);
-       ok(ret_field_type == stream_event_context_type,
-               "bt_ctf_stream_class_get_event_context_type returns the correct field type.");
-       bt_ctf_object_put_ref(ret_field_type);
-
-
-       /* Instantiate a stream and append events */
-       ret = bt_ctf_writer_add_clock(writer, clock);
-       BT_ASSERT(ret == 0);
-
-       ok(bt_ctf_trace_get_stream_count(trace) == 0,
-               "bt_ctf_trace_get_stream_count() succeeds and returns the correct value (0)");
-       stream1 = bt_ctf_writer_create_stream(writer, stream_class);
-       ok(stream1, "Instanciate a stream class from writer");
-       ok(bt_ctf_trace_get_stream_count(trace) == 1,
-               "bt_ctf_trace_get_stream_count() succeeds and returns the correct value (1)");
-       stream = bt_ctf_trace_get_stream_by_index(trace, 0);
-       ok(stream == stream1,
-               "bt_ctf_trace_get_stream_by_index() succeeds and returns the correct value");
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(stream);
-
-       /*
-        * Creating a stream through a writer adds the given stream
-        * class to the writer's trace, thus registering the stream
-        * class's clock to the trace.
-        */
-
-       ret_stream_class = bt_ctf_stream_get_class(stream1);
-       ok(ret_stream_class,
-               "bt_ctf_stream_get_class returns a stream class");
-       ok(ret_stream_class == stream_class,
-               "Returned stream class is of the correct type");
-
-       /*
-        * Packet header, packet context, event header, and stream
-        * event context types were copied for the resolving
-        * process
-        */
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_header_type);
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_context_type);
-       BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_context_type);
-       packet_header_type = bt_ctf_trace_get_packet_header_field_type(trace);
-       BT_ASSERT(packet_header_type);
-       packet_context_type =
-               bt_ctf_stream_class_get_packet_context_type(stream_class);
-       BT_ASSERT(packet_context_type);
-       stream_event_context_type =
-               bt_ctf_stream_class_get_event_context_type(stream_class);
-       BT_ASSERT(stream_event_context_type);
-
-       /*
-        * Try to modify the packet context type after a stream has been
-        * created.
-        */
-       ret = bt_ctf_field_type_structure_add_field(packet_header_type,
-               packet_header_field_type, "should_fail");
-       ok(ret < 0,
-               "Trace packet header type can't be modified once a stream has been instanciated");
-
-       /*
-        * Try to modify the packet context type after a stream has been
-        * created.
-        */
-       ret = bt_ctf_field_type_structure_add_field(packet_context_type,
-               packet_context_field_type, "should_fail");
-       ok(ret < 0,
-               "Packet context type can't be modified once a stream has been instanciated");
-
-       /*
-        * Try to modify the stream event context type after a stream has been
-        * created.
-        */
-       ret = bt_ctf_field_type_structure_add_field(stream_event_context_type,
-               integer_type, "should_fail");
-       ok(ret < 0,
-               "Stream event context type can't be modified once a stream has been instanciated");
-
-       /* Should fail after instanciating a stream (frozen) */
-       ok(bt_ctf_stream_class_set_clock(stream_class, clock),
-               "Changes to a stream class that was already instantiated fail");
-
-       /* Populate the custom packet header field only once for all tests */
-       ok(!bt_ctf_stream_get_packet_header(NULL),
-               "bt_ctf_stream_get_packet_header handles NULL correctly");
-       packet_header = bt_ctf_stream_get_packet_header(stream1);
-       ok(packet_header,
-               "bt_ctf_stream_get_packet_header returns a packet header");
-       ret_field_type = bt_ctf_field_get_type(packet_header);
-       ok(ret_field_type == packet_header_type,
-               "Stream returns a packet header of the appropriate type");
-       bt_ctf_object_put_ref(ret_field_type);
-       packet_header_field = bt_ctf_field_structure_get_field_by_name(packet_header,
-               "custom_trace_packet_header_field");
-       ok(packet_header_field,
-               "Packet header structure contains a custom field with the appropriate name");
-       ret_field_type = bt_ctf_field_get_type(packet_header_field);
-       ok(!bt_ctf_field_integer_unsigned_set_value(packet_header_field,
-               54321), "Set custom packet header value successfully");
-       ok(bt_ctf_stream_set_packet_header(stream1, NULL) < 0,
-               "bt_ctf_stream_set_packet_header handles a NULL packet header correctly");
-       ok(bt_ctf_stream_set_packet_header(NULL, packet_header) < 0,
-               "bt_ctf_stream_set_packet_header handles a NULL stream correctly");
-       ok(bt_ctf_stream_set_packet_header(stream1, packet_header_field) < 0,
-               "bt_ctf_stream_set_packet_header rejects a packet header of the wrong type");
-       ok(!bt_ctf_stream_set_packet_header(stream1, packet_header),
-               "Successfully set a stream's packet header");
-
-       ok(bt_ctf_writer_add_environment_field(writer, "new_field", "test") == 0,
-               "Add environment field to writer after stream creation");
-
-       test_clock_utils();
-
-       test_instanciate_event_before_stream(writer, clock);
-
-       append_simple_event(stream_class, stream1, clock);
-
-       packet_resize_test(stream_class, stream1, clock);
-
-       append_complex_event(stream_class, stream1, clock);
-
-       append_existing_event_class(stream_class);
-
-       test_empty_stream(writer);
-
-       test_custom_event_header_stream(writer, clock);
-
-       metadata_string = bt_ctf_writer_get_metadata_string(writer);
-       ok(metadata_string, "Get metadata string");
-
-       bt_ctf_writer_flush_metadata(writer);
-
-       bt_ctf_object_put_ref(clock);
-       bt_ctf_object_put_ref(ret_stream_class);
-       bt_ctf_object_put_ref(writer);
-       bt_ctf_object_put_ref(stream1);
-       bt_ctf_object_put_ref(packet_context_type);
-       bt_ctf_object_put_ref(packet_context_field_type);
-       bt_ctf_object_put_ref(integer_type);
-       bt_ctf_object_put_ref(stream_event_context_type);
-       bt_ctf_object_put_ref(ret_field_type);
-       bt_ctf_object_put_ref(packet_header_type);
-       bt_ctf_object_put_ref(packet_header_field_type);
-       bt_ctf_object_put_ref(packet_header);
-       bt_ctf_object_put_ref(packet_header_field);
-       bt_ctf_object_put_ref(trace);
-       free(metadata_string);
-       bt_ctf_object_put_ref(stream_class);
-
-       validate_trace(argv[1], trace_path);
-
-       recursive_rmdir(trace_path);
-       g_free(trace_path);
-       g_free(metadata_path);
-
-       return exit_status();
-}
diff --git a/tests/ctf-writer/test-ctf-writer.sh b/tests/ctf-writer/test-ctf-writer.sh
new file mode 100755 (executable)
index 0000000..38a4192
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2013 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+"${BT_TESTS_BUILDDIR}/ctf-writer/ctf-writer" "$BT_TESTS_BT2_BIN"
diff --git a/tests/ctf-writer/test_ctf_writer b/tests/ctf-writer/test_ctf_writer
deleted file mode 100755 (executable)
index 7616b69..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2013 Jérémie Galarneau <jeremie.galarneau@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-"${BT_TESTS_BUILDDIR}/ctf-writer/ctf_writer" "$BT_TESTS_BT2_BIN"
index 208f53813a356455d80361de375f91d29ceb5279..2bf0843b79abedc60580bc0ad6f9c682dbb44b31 100644 (file)
@@ -3,9 +3,10 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import bt2
 import os
 
+import bt2
+
 # Source component classes in this file recognize and group inputs in
 # various ways.  One stream is created by each component, with a name
 # derived from the component class and the inputs.  This is then checked
index 91d0c74c1b8d54de60220a236a07c063f077079f..ddb6508a56140c72bcdab4724f4f6a594e773e05 100644 (file)
@@ -3,9 +3,10 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import bt2
 import os
 
+import bt2
+
 # This file defines source component classes to help verify the parameters an
 # log levels passed to components.  Each component creates one stream, with a
 # name derived from either:
@@ -30,7 +31,7 @@ class TestIter(bt2._UserMessageIterator):
             log_level = self._component.logging_level
             stream_name = "{}: {}".format(comp_cls_name, log_level)
         elif params["what"] == "python-obj":
-            assert type(obj) == str or obj is None
+            assert type(obj) is str or obj is None
             stream_name = "{}: {}".format(comp_cls_name, obj)
         else:
             assert False
diff --git a/tests/data/cli/exit-status/bt_plugin_test_cli_exit_status.py b/tests/data/cli/exit-status/bt_plugin_test_cli_exit_status.py
new file mode 100644 (file)
index 0000000..37f48ba
--- /dev/null
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 EfficiOS Inc.
+#
+
+import os
+import time
+import signal
+
+import bt2
+
+bt2.register_plugin(__name__, "test-exit-status")
+
+
+class StatusIter(bt2._UserMessageIterator):
+    def __init__(self, config, output_port):
+        self.case = output_port.user_data["case"]
+
+    def __next__(self):
+        if self.case == "STOP":
+            raise bt2.Stop()
+        if self.case == "INTERRUPTED":
+            os.kill(os.getpid(), signal.SIGINT)
+
+            # Wait until the graph is in the interrupted state.
+            timeout_s = 10
+            for _ in range(timeout_s * 10):
+                if self._is_interrupted:
+                    raise bt2.TryAgain()
+
+                time.sleep(0.1)
+
+            raise Exception(
+                "{} was not interrupted after {} seconds".format(
+                    self.__class__.__name__, timeout_s
+                )
+            )
+
+        elif self.case == "ERROR":
+            raise TypeError("Raising type error")
+        else:
+            raise ValueError("Invalid parameter")
+
+
+@bt2.plugin_component_class
+class StatusSrc(bt2._UserSourceComponent, message_iterator_class=StatusIter):
+    def __init__(self, config, params, obj):
+        self._add_output_port("out", {"case": params["case"]})
diff --git a/tests/data/cli/exit_status/bt_plugin_test_cli_exit_status.py b/tests/data/cli/exit_status/bt_plugin_test_cli_exit_status.py
deleted file mode 100644 (file)
index d651e25..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 EfficiOS Inc.
-#
-
-import bt2
-import signal
-import os
-import time
-
-bt2.register_plugin(__name__, "test_exit_status")
-
-
-class StatusIter(bt2._UserMessageIterator):
-    def __init__(self, config, output_port):
-        self.case = output_port.user_data["case"]
-
-    def __next__(self):
-        if self.case == "STOP":
-            raise bt2.Stop()
-        if self.case == "INTERRUPTED":
-            os.kill(os.getpid(), signal.SIGINT)
-
-            # Wait until the graph is in the interrupted state.
-            timeout_s = 10
-            for _ in range(timeout_s * 10):
-                if self._is_interrupted:
-                    raise bt2.TryAgain()
-
-                time.sleep(0.1)
-
-            raise Exception(
-                "{} was not interrupted after {} seconds".format(
-                    self.__class__.__name__, timeout_s
-                )
-            )
-
-        elif self.case == "ERROR":
-            raise TypeError("Raising type error")
-        else:
-            raise ValueError("Invalid parameter")
-
-
-@bt2.plugin_component_class
-class StatusSrc(bt2._UserSourceComponent, message_iterator_class=StatusIter):
-    def __init__(self, config, params, obj):
-        self._add_output_port("out", {"case": params["case"]})
diff --git a/tests/data/cli/test-output-ctf-metadata.ref b/tests/data/cli/test-output-ctf-metadata.ref
new file mode 100644 (file)
index 0000000..01dbf3f
--- /dev/null
@@ -0,0 +1,111 @@
+/* CTF 1.8 */
+
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
+typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
+typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
+typealias integer { size = 5; align = 1; signed = false; } := uint5_t;
+typealias integer { size = 27; align = 1; signed = false; } := uint27_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "624b19d9-19cd-4eae-bab8-8342e1b96a5d";
+       byte_order = le;
+       packet.header := struct {
+               uint32_t magic;
+               uint8_t  uuid[16];
+               uint32_t stream_id;
+       };
+};
+
+env {
+       vpid = 3208;
+       procname = "wk-heartbeat";
+       domain = "ust";
+       tracer_name = "lttng-ust";
+       tracer_major = 2;
+       tracer_minor = 0;
+       tracer_patchlevel = 2;
+};
+
+clock {
+       name = monotonic;
+       uuid = "c19b5ac9-b8e6-4f78-be95-a605d04e34c6";
+       description = "Monotonic Clock";
+       freq = 1000000000; /* Frequency, in Hz */
+       /* clock value offset from Epoch is: offset * (1/freq) */
+       offset = 1351530929945824323;
+};
+
+typealias integer {
+       size = 27; align = 1; signed = false;
+       map = clock.monotonic.value;
+} := uint27_clock_monotonic_t;
+
+typealias integer {
+       size = 32; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint32_clock_monotonic_t;
+
+typealias integer {
+       size = 64; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint64_clock_monotonic_t;
+
+struct packet_context {
+       uint64_clock_monotonic_t timestamp_begin;
+       uint64_clock_monotonic_t timestamp_end;
+       uint32_t events_discarded;
+       uint32_t content_size;
+       uint32_t packet_size;
+       uint32_t cpu_id;
+};
+
+struct event_header_compact {
+       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
+       variant <id> {
+               struct {
+                       uint27_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+struct event_header_large {
+       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
+       variant <id> {
+               struct {
+                       uint32_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+stream {
+       id = 0;
+       event.header := struct event_header_compact;
+       packet.context := struct packet_context;
+       event.context := struct {
+               integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _vtid;
+               integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _vpid;
+       };
+};
+
+event {
+       name = "heartbeat:msg";
+       id = 0;
+       stream_id = 0;
+       loglevel = 13;
+       fields := struct {
+               string _msg;
+       };
+};
+
+
diff --git a/tests/data/cli/test_output_ctf_metadata.ref b/tests/data/cli/test_output_ctf_metadata.ref
deleted file mode 100644 (file)
index 01dbf3f..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/* CTF 1.8 */
-
-typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
-typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
-typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
-typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
-typealias integer { size = 5; align = 1; signed = false; } := uint5_t;
-typealias integer { size = 27; align = 1; signed = false; } := uint27_t;
-
-trace {
-       major = 1;
-       minor = 8;
-       uuid = "624b19d9-19cd-4eae-bab8-8342e1b96a5d";
-       byte_order = le;
-       packet.header := struct {
-               uint32_t magic;
-               uint8_t  uuid[16];
-               uint32_t stream_id;
-       };
-};
-
-env {
-       vpid = 3208;
-       procname = "wk-heartbeat";
-       domain = "ust";
-       tracer_name = "lttng-ust";
-       tracer_major = 2;
-       tracer_minor = 0;
-       tracer_patchlevel = 2;
-};
-
-clock {
-       name = monotonic;
-       uuid = "c19b5ac9-b8e6-4f78-be95-a605d04e34c6";
-       description = "Monotonic Clock";
-       freq = 1000000000; /* Frequency, in Hz */
-       /* clock value offset from Epoch is: offset * (1/freq) */
-       offset = 1351530929945824323;
-};
-
-typealias integer {
-       size = 27; align = 1; signed = false;
-       map = clock.monotonic.value;
-} := uint27_clock_monotonic_t;
-
-typealias integer {
-       size = 32; align = 8; signed = false;
-       map = clock.monotonic.value;
-} := uint32_clock_monotonic_t;
-
-typealias integer {
-       size = 64; align = 8; signed = false;
-       map = clock.monotonic.value;
-} := uint64_clock_monotonic_t;
-
-struct packet_context {
-       uint64_clock_monotonic_t timestamp_begin;
-       uint64_clock_monotonic_t timestamp_end;
-       uint32_t events_discarded;
-       uint32_t content_size;
-       uint32_t packet_size;
-       uint32_t cpu_id;
-};
-
-struct event_header_compact {
-       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
-       variant <id> {
-               struct {
-                       uint27_clock_monotonic_t timestamp;
-               } compact;
-               struct {
-                       uint32_t id;
-                       uint64_clock_monotonic_t timestamp;
-               } extended;
-       } v;
-} align(8);
-
-struct event_header_large {
-       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
-       variant <id> {
-               struct {
-                       uint32_clock_monotonic_t timestamp;
-               } compact;
-               struct {
-                       uint32_t id;
-                       uint64_clock_monotonic_t timestamp;
-               } extended;
-       } v;
-} align(8);
-
-stream {
-       id = 0;
-       event.header := struct event_header_compact;
-       packet.context := struct packet_context;
-       event.context := struct {
-               integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _vtid;
-               integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _vpid;
-       };
-};
-
-event {
-       name = "heartbeat:msg";
-       id = 0;
-       stream_id = 0;
-       loglevel = 13;
-       fields := struct {
-               string _msg;
-       };
-};
-
-
diff --git a/tests/data/ctf-traces/fail/invalid-sequence-length-field-class/metadata b/tests/data/ctf-traces/fail/invalid-sequence-length-field-class/metadata
new file mode 100644 (file)
index 0000000..d56b8c8
--- /dev/null
@@ -0,0 +1,25 @@
+/* CTF 1.8 */
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       byte_order = le;
+};
+
+stream {
+       event.header := struct {
+               uint8_t id;
+       };
+};
+
+event {
+       name = ze_event;
+       id = 1;
+       fields := struct {
+               struct {
+                       uint8_t hello;
+               } len;
+               uint8_t x[len];
+       };
+};
diff --git a/tests/data/ctf-traces/fail/invalid-variant-selector-field-class/metadata b/tests/data/ctf-traces/fail/invalid-variant-selector-field-class/metadata
new file mode 100644 (file)
index 0000000..3cfa342
--- /dev/null
@@ -0,0 +1,28 @@
+/* CTF 1.8 */
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       byte_order = le;
+};
+
+stream {
+       event.header := struct {
+               uint8_t id;
+       };
+};
+
+event {
+       name = ze_event;
+       id = 1;
+       fields := struct {
+               struct {
+                       uint8_t hello;
+               } selector;
+               variant<selector> {
+                       uint8_t a;
+                       uint8_t b;
+               } v;
+       };
+};
diff --git a/tests/data/ctf-traces/live/new-streams/first-trace.mctf b/tests/data/ctf-traces/live/new-streams/first-trace.mctf
new file mode 100644 (file)
index 0000000..c3db828
--- /dev/null
@@ -0,0 +1,90 @@
+--- metadata
+/* CTF 1.8 */
+
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       byte_order = le;
+};
+
+clock {
+       name = cycle_counter_test;
+       uuid = "5b59e7db-5e49-418a-9adf-e1adfdf571c4";
+       freq = 1000000000;
+};
+
+typealias integer { size = 8; align = 8; signed = false; map = clock.cycle_counter_test.value; } := cc_count_t;
+
+struct packet_context {
+       cc_count_t timestamp_begin;
+       cc_count_t timestamp_end;
+       uint8_t content_size;
+       uint8_t packet_size;
+};
+
+struct event_header {
+       uint8_t id;
+       cc_count_t timestamp;
+};
+
+stream {
+       event.header := struct event_header;
+       packet.context := struct packet_context;
+};
+
+event {
+       name = "first_trace_event";
+       id = 0;
+       fields := struct {
+               uint8_t value;
+       };
+};
+
+--- first_trace_stream_0
+!macro packet(ts_beg, event_id)
+  <beg>
+  [         ts_beg : 8] # timestamp begin
+  [     ts_beg + 1 : 8] # timestamp end
+  [8 * (end - beg) : 8] # content size in bits
+  [8 * (end - beg) : 8] # packet size in bits
+
+  [       event_id : 8] # event id
+  [         ts_beg : 8] # timestamp
+  [             42 : 8] # value field
+  <end>
+!end
+
+{ p1_s0_ts = 10 }
+{ p2_s0_ts = 20 }
+
+<p1_s0>
+m:packet(p1_s0_ts, 0)
+<p1_s0_end>
+
+<p2_s0>
+m:packet(p2_s0_ts, 0)
+<p2_s0_end>
+
+--- index/first_trace_stream_0.idx
+!be
+
+[0xC1F1DCC1 : 32] # Magic number
+[         1 : 32] # Major
+[         0 : 32] # Minor
+[        56 : 32] # Index entry size (56 bytes)
+
+# Packet 1
+!macro entry(beg_label, end_label, ts_beg)
+  [                  beg_label : 64] # offset in bytes
+  [8 * (end_label - beg_label) : 64] # total size in bits
+  [8 * (end_label - beg_label) : 64] # content size in bits
+  [                     ts_beg : 64] # timestamp begin
+  [                 ts_beg + 1 : 64] # timestamp end
+  [                          0 : 64] # events discarded
+  [                          0 : 64] # stream class id
+!end
+
+m:entry(p1_s0, p1_s0_end, p1_s0_ts)
+m:entry(p2_s0, p2_s0_end, p2_s0_ts)
diff --git a/tests/data/ctf-traces/live/new-streams/second-trace.mctf b/tests/data/ctf-traces/live/new-streams/second-trace.mctf
new file mode 100644 (file)
index 0000000..969e0be
--- /dev/null
@@ -0,0 +1,90 @@
+--- metadata
+/* CTF 1.8 */
+
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       byte_order = le;
+};
+
+clock {
+       name = cycle_counter_test;
+       uuid = "5b59e7db-5e49-418a-9adf-e1adfdf571c4";
+       freq = 1000000000;
+};
+
+typealias integer { size = 8; align = 8; signed = false; map = clock.cycle_counter_test.value; } := cc_count_t;
+
+struct packet_context {
+       cc_count_t timestamp_begin;
+       cc_count_t timestamp_end;
+       uint8_t content_size;
+       uint8_t packet_size;
+};
+
+struct event_header {
+       uint8_t id;
+       cc_count_t timestamp;
+};
+
+stream {
+       event.header := struct event_header;
+       packet.context := struct packet_context;
+};
+
+event {
+       name = "second_trace_event";
+       id = 0;
+       fields := struct {
+               uint8_t value;
+       };
+};
+
+--- second_trace_stream_0
+!macro packet(ts_beg, event_id)
+  <beg>
+  [         ts_beg : 8] # timestamp begin
+  [     ts_beg + 1 : 8] # timestamp end
+  [8 * (end - beg) : 8] # content size in bits
+  [8 * (end - beg) : 8] # packet size in bits
+
+  [       event_id : 8] # event id
+  [         ts_beg : 8] # timestamp
+  [            123 : 8] # value field
+  <end>
+!end
+
+{ p1_s0_ts = 110 }
+{ p2_s0_ts = 120 }
+
+<p1_s0>
+m:packet(p1_s0_ts, 0)
+<p1_s0_end>
+
+<p2_s0>
+m:packet(p2_s0_ts, 0)
+<p2_s0_end>
+
+--- index/second_trace_stream_0.idx
+!be
+
+[0xC1F1DCC1 : 32] # Magic number
+[         1 : 32] # Major
+[         0 : 32] # Minor
+[        56 : 32] # Index entry size (56 bytes)
+
+# Packet 1
+!macro entry(beg_label, end_label, ts_beg)
+  [                  beg_label : 64] # offset in bytes
+  [8 * (end_label - beg_label) : 64] # total size in bits
+  [8 * (end_label - beg_label) : 64] # content size in bits
+  [                     ts_beg : 64] # timestamp begin
+  [                 ts_beg + 1 : 64] # timestamp end
+  [                          0 : 64] # events discarded
+  [                          0 : 64] # stream class id
+!end
+
+m:entry(p1_s0, p1_s0_end, p1_s0_ts)
+m:entry(p2_s0, p2_s0_end, p2_s0_ts)
diff --git a/tests/data/ctf-traces/live/split-metadata/channel0_0 b/tests/data/ctf-traces/live/split-metadata/channel0_0
new file mode 100644 (file)
index 0000000..c8e9ddb
Binary files /dev/null and b/tests/data/ctf-traces/live/split-metadata/channel0_0 differ
diff --git a/tests/data/ctf-traces/live/split-metadata/index/channel0_0.idx b/tests/data/ctf-traces/live/split-metadata/index/channel0_0.idx
new file mode 100644 (file)
index 0000000..399ec45
Binary files /dev/null and b/tests/data/ctf-traces/live/split-metadata/index/channel0_0.idx differ
diff --git a/tests/data/ctf-traces/live/split-metadata/metadata b/tests/data/ctf-traces/live/split-metadata/metadata
new file mode 100644 (file)
index 0000000..b61a4e1
--- /dev/null
@@ -0,0 +1,119 @@
+/* CTF 1.8 */
+
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
+typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
+typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
+typealias integer { size = 64; align = 8; signed = false; } := unsigned long;
+typealias integer { size = 5; align = 1; signed = false; } := uint5_t;
+typealias integer { size = 27; align = 1; signed = false; } := uint27_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "0339cd08-892d-404c-9291-64c1a8a74c81";
+       byte_order = le;
+       packet.header := struct {
+               uint32_t magic;
+               uint8_t  uuid[16];
+               uint32_t stream_id;
+               uint64_t stream_instance_id;
+       };
+};
+
+env {
+       domain = "ust";
+       tracer_name = "lttng-ust";
+       tracer_major = 2;
+       tracer_minor = 12;
+       tracer_buffering_scheme = "uid";
+       tracer_buffering_id = 1000;
+       architecture_bit_width = 64;
+       trace_name = "barney_descontie";
+       trace_creation_datetime = "20200715T174253-0400";
+       hostname = "raton";
+};
+
+clock {
+       name = "monotonic";
+       uuid = "81a04b89-9028-4d3e-a28d-5fbd53a8eb9d";
+       description = "Monotonic Clock";
+       freq = 1000000000; /* Frequency, in Hz */
+       /* clock value offset from Epoch is: offset * (1/freq) */
+       offset = 1594406328768346378;
+};
+
+typealias integer {
+       size = 27; align = 1; signed = false;
+       map = clock.monotonic.value;
+} := uint27_clock_monotonic_t;
+
+typealias integer {
+       size = 32; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint32_clock_monotonic_t;
+
+typealias integer {
+       size = 64; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint64_clock_monotonic_t;
+
+struct packet_context {
+       uint64_clock_monotonic_t timestamp_begin;
+       uint64_clock_monotonic_t timestamp_end;
+       uint64_t content_size;
+       uint64_t packet_size;
+       uint64_t packet_seq_num;
+       unsigned long events_discarded;
+       uint32_t cpu_id;
+};
+
+struct event_header_compact {
+       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
+       variant <id> {
+               struct {
+                       uint27_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+struct event_header_large {
+       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
+       variant <id> {
+               struct {
+                       uint32_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+stream {
+       id = 0;
+       event.header := struct event_header_large;
+       packet.context := struct packet_context;
+};
+
+event {
+       name = "my_app:signe_de_pia$$e";
+       id = 0;
+       stream_id = 0;
+       loglevel = 13;
+       fields := struct {
+       };
+};
+
+event {
+       name = "my_app:signe_de_pia$$e_2";
+       id = 1;
+       stream_id = 0;
+       loglevel = 13;
+       fields := struct {
+       };
+};
diff --git a/tests/data/ctf-traces/live/split_metadata/channel0_0 b/tests/data/ctf-traces/live/split_metadata/channel0_0
deleted file mode 100644 (file)
index c8e9ddb..0000000
Binary files a/tests/data/ctf-traces/live/split_metadata/channel0_0 and /dev/null differ
diff --git a/tests/data/ctf-traces/live/split_metadata/index/channel0_0.idx b/tests/data/ctf-traces/live/split_metadata/index/channel0_0.idx
deleted file mode 100644 (file)
index 399ec45..0000000
Binary files a/tests/data/ctf-traces/live/split_metadata/index/channel0_0.idx and /dev/null differ
diff --git a/tests/data/ctf-traces/live/split_metadata/metadata b/tests/data/ctf-traces/live/split_metadata/metadata
deleted file mode 100644 (file)
index b61a4e1..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/* CTF 1.8 */
-
-typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
-typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
-typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
-typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
-typealias integer { size = 64; align = 8; signed = false; } := unsigned long;
-typealias integer { size = 5; align = 1; signed = false; } := uint5_t;
-typealias integer { size = 27; align = 1; signed = false; } := uint27_t;
-
-trace {
-       major = 1;
-       minor = 8;
-       uuid = "0339cd08-892d-404c-9291-64c1a8a74c81";
-       byte_order = le;
-       packet.header := struct {
-               uint32_t magic;
-               uint8_t  uuid[16];
-               uint32_t stream_id;
-               uint64_t stream_instance_id;
-       };
-};
-
-env {
-       domain = "ust";
-       tracer_name = "lttng-ust";
-       tracer_major = 2;
-       tracer_minor = 12;
-       tracer_buffering_scheme = "uid";
-       tracer_buffering_id = 1000;
-       architecture_bit_width = 64;
-       trace_name = "barney_descontie";
-       trace_creation_datetime = "20200715T174253-0400";
-       hostname = "raton";
-};
-
-clock {
-       name = "monotonic";
-       uuid = "81a04b89-9028-4d3e-a28d-5fbd53a8eb9d";
-       description = "Monotonic Clock";
-       freq = 1000000000; /* Frequency, in Hz */
-       /* clock value offset from Epoch is: offset * (1/freq) */
-       offset = 1594406328768346378;
-};
-
-typealias integer {
-       size = 27; align = 1; signed = false;
-       map = clock.monotonic.value;
-} := uint27_clock_monotonic_t;
-
-typealias integer {
-       size = 32; align = 8; signed = false;
-       map = clock.monotonic.value;
-} := uint32_clock_monotonic_t;
-
-typealias integer {
-       size = 64; align = 8; signed = false;
-       map = clock.monotonic.value;
-} := uint64_clock_monotonic_t;
-
-struct packet_context {
-       uint64_clock_monotonic_t timestamp_begin;
-       uint64_clock_monotonic_t timestamp_end;
-       uint64_t content_size;
-       uint64_t packet_size;
-       uint64_t packet_seq_num;
-       unsigned long events_discarded;
-       uint32_t cpu_id;
-};
-
-struct event_header_compact {
-       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
-       variant <id> {
-               struct {
-                       uint27_clock_monotonic_t timestamp;
-               } compact;
-               struct {
-                       uint32_t id;
-                       uint64_clock_monotonic_t timestamp;
-               } extended;
-       } v;
-} align(8);
-
-struct event_header_large {
-       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
-       variant <id> {
-               struct {
-                       uint32_clock_monotonic_t timestamp;
-               } compact;
-               struct {
-                       uint32_t id;
-                       uint64_clock_monotonic_t timestamp;
-               } extended;
-       } v;
-} align(8);
-
-stream {
-       id = 0;
-       event.header := struct event_header_large;
-       packet.context := struct packet_context;
-};
-
-event {
-       name = "my_app:signe_de_pia$$e";
-       id = 0;
-       stream_id = 0;
-       loglevel = 13;
-       fields := struct {
-       };
-};
-
-event {
-       name = "my_app:signe_de_pia$$e_2";
-       id = 1;
-       stream_id = 0;
-       loglevel = 13;
-       fields := struct {
-       };
-};
diff --git a/tests/data/ctf-traces/live/stored-values.mctf b/tests/data/ctf-traces/live/stored-values.mctf
new file mode 100644 (file)
index 0000000..8d19835
--- /dev/null
@@ -0,0 +1,91 @@
+--- metadata
+/* CTF 1.8 */
+
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       byte_order = le;
+};
+
+struct packet_context {
+       uint8_t timestamp_begin;
+       uint8_t timestamp_end;
+       uint8_t content_size;
+       uint8_t packet_size;
+};
+
+struct event_header {
+       uint8_t id;
+       uint8_t timestamp;
+};
+
+stream {
+       event.header := struct event_header;
+       packet.context := struct packet_context;
+};
+
+event {
+       name = "event1";
+       id = 1;
+       fields := struct {
+               uint8_t len;
+               uint8_t seq[len];
+       };
+};
+
+event {
+       name = "event2";
+       id = 2;
+       fields := struct {
+               uint8_t len;
+               uint8_t seq[len];
+       };
+};
+
+--- channel0_0
+!macro packet(ts_beg, event_id)
+  <beg>
+  [         ts_beg : 8] # timestamp begin
+  [     ts_beg + 1 : 8] # timestamp end
+  [8 * (end - beg) : 8] # content size in bits
+  [8 * (end - beg) : 8] # packet size in bits
+
+  [       event_id : 8] # event id
+  [         ts_beg : 8] # timestamp
+  [              0 : 8] # len field
+  <end>
+!end
+
+{ p1_ts = 10 }
+{ p2_ts = 20 }
+
+<p1>
+m:packet(p1_ts, 1)
+<p1_end>
+
+<p2>
+m:packet(p2_ts, 2)
+<p2_end>
+
+--- index/channel0_0.idx
+!be
+
+[0xC1F1DCC1 : 32] # Magic number
+[         1 : 32] # Major
+[         0 : 32] # Minor
+[        56 : 32] # Index entry size (56 bytes)
+
+!macro entry(beg_label, end_label, ts_beg)
+  [                  beg_label : 64] # offset in bytes
+  [8 * (end_label - beg_label) : 64] # total size in bits
+  [8 * (end_label - beg_label) : 64] # content size in bits
+  [                     ts_beg : 64] # timestamp begin
+  [                 ts_beg + 1 : 64] # timestamp end
+  [                          0 : 64] # events discarded
+  [                          0 : 64] # stream class id
+!end
+
+m:entry(p1, p1_end, p1_ts)
+m:entry(p2, p2_end, p2_ts)
diff --git a/tests/data/ctf-traces/packet-seq-num/2-lost-before-last/metadata b/tests/data/ctf-traces/packet-seq-num/2-lost-before-last/metadata
new file mode 100644 (file)
index 0000000..36e26cd
--- /dev/null
@@ -0,0 +1,58 @@
+/* CTF 1.8 */
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "ad21eeaa-fab9-4692-aab8-ebd68c7feb17";
+       byte_order = be;
+       packet.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
+               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
+       } align(8);
+};
+
+env {
+       host = "sinkpad";
+};
+
+clock {
+       name = test_clock;
+       uuid = "d336520f-985d-481e-8e35-d99328655354";
+       description = "This is a test clock";
+       freq = 1000000000;
+       precision = 10;
+       offset_s = 13515309;
+       offset = 0;
+       absolute = TRUE;
+};
+
+stream {
+       id = 0;
+       event.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
+       } align(8);
+
+       packet.context := struct {
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
+               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
+       } align(8);
+};
+
+event {
+       id = 0;
+       name = "dummy_event";
+       stream_id = 0;
+       fields := struct {
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
+       } align(1);
+};
+
diff --git a/tests/data/ctf-traces/packet-seq-num/2-lost-before-last/test_stream_0 b/tests/data/ctf-traces/packet-seq-num/2-lost-before-last/test_stream_0
new file mode 100644 (file)
index 0000000..50d36a4
Binary files /dev/null and b/tests/data/ctf-traces/packet-seq-num/2-lost-before-last/test_stream_0 differ
diff --git a/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/metadata b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/metadata
new file mode 100644 (file)
index 0000000..525405e
--- /dev/null
@@ -0,0 +1,58 @@
+/* CTF 1.8 */
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "61db8e6b-2069-40e4-84ed-bc15f42181f0";
+       byte_order = be;
+       packet.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
+               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
+       } align(8);
+};
+
+env {
+       host = "sinkpad";
+};
+
+clock {
+       name = test_clock;
+       uuid = "a0a8c252-db03-4f36-a148-80a0a3c4edff";
+       description = "This is a test clock";
+       freq = 1000000000;
+       precision = 10;
+       offset_s = 13515309;
+       offset = 0;
+       absolute = TRUE;
+};
+
+stream {
+       id = 0;
+       event.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
+       } align(8);
+
+       packet.context := struct {
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
+               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
+       } align(8);
+};
+
+event {
+       id = 0;
+       name = "dummy_event";
+       stream_id = 0;
+       fields := struct {
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
+       } align(1);
+};
+
diff --git a/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/test_stream_0 b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/test_stream_0
new file mode 100644 (file)
index 0000000..1b362cd
Binary files /dev/null and b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/test_stream_0 differ
diff --git a/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/test_stream_1 b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/test_stream_1
new file mode 100644 (file)
index 0000000..d5114d9
Binary files /dev/null and b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-1/test_stream_1 differ
diff --git a/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/metadata b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/metadata
new file mode 100644 (file)
index 0000000..680d910
--- /dev/null
@@ -0,0 +1,58 @@
+/* CTF 1.8 */
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "eb5045f7-b471-488e-b963-0221ddf423a7";
+       byte_order = be;
+       packet.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
+               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
+       } align(8);
+};
+
+env {
+       host = "sinkpad";
+};
+
+clock {
+       name = test_clock;
+       uuid = "2447a359-1e57-448f-96ef-3c324327047c";
+       description = "This is a test clock";
+       freq = 1000000000;
+       precision = 10;
+       offset_s = 13515309;
+       offset = 0;
+       absolute = TRUE;
+};
+
+stream {
+       id = 0;
+       event.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
+       } align(8);
+
+       packet.context := struct {
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
+               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
+       } align(8);
+};
+
+event {
+       id = 0;
+       name = "dummy_event";
+       stream_id = 0;
+       fields := struct {
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
+       } align(1);
+};
+
diff --git a/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/test_stream_0 b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/test_stream_0
new file mode 100644 (file)
index 0000000..b0beb6b
Binary files /dev/null and b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/test_stream_0 differ
diff --git a/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/test_stream_1 b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/test_stream_1
new file mode 100644 (file)
index 0000000..1ad8320
Binary files /dev/null and b/tests/data/ctf-traces/packet-seq-num/2-streams-lost-in-2/test_stream_1 differ
diff --git a/tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/index/stream_0.idx b/tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/index/stream_0.idx
new file mode 100644 (file)
index 0000000..e06d92f
Binary files /dev/null and b/tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/index/stream_0.idx differ
diff --git a/tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/metadata b/tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/metadata
new file mode 100644 (file)
index 0000000..37a3030
--- /dev/null
@@ -0,0 +1,48 @@
+/* CTF 1.8 */
+
+/* This was generated by a Babeltrace `sink.ctf.fs` component. */
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "1c810767-575e-4c4e-afa1-5d3e15081cb9";
+       byte_order = le;
+       packet.header := struct {
+               integer { size = 32; align = 8; base = x; } magic;
+               integer { size = 8; align = 8; } uuid[16];
+               integer { size = 64; align = 8; } stream_id;
+               integer { size = 64; align = 8; } stream_instance_id;
+       } align(8);
+};
+
+clock {
+       name = default;
+       freq = 1000000000;
+       precision = 0;
+       offset_s = 0;
+       offset = 0;
+       absolute = true;
+};
+
+stream {
+       id = 0;
+       packet.context := struct {
+               integer { size = 64; align = 8; } packet_size;
+               integer { size = 64; align = 8; } content_size;
+               integer { size = 64; align = 8; map = clock.default.value; } timestamp_begin;
+               integer { size = 64; align = 8; map = clock.default.value; } timestamp_end;
+               integer { size = 64; align = 8; } packet_seq_num;
+       } align(8);
+
+       event.header := struct {
+               integer { size = 64; align = 8; } id;
+               integer { size = 64; align = 8; map = clock.default.value; } timestamp;
+       } align(8);
+};
+
+event {
+       name = "my-event";
+       stream_id = 0;
+       id = 0;
+};
+
diff --git a/tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/stream_0 b/tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/stream_0
new file mode 100644 (file)
index 0000000..fdfe9e1
Binary files /dev/null and b/tests/data/ctf-traces/packet-seq-num/7-lost-between-2-with-index/stream_0 differ
diff --git a/tests/data/ctf-traces/packet-seq-num/no-lost-not-starting-at-0/metadata b/tests/data/ctf-traces/packet-seq-num/no-lost-not-starting-at-0/metadata
new file mode 100644 (file)
index 0000000..c8f5783
--- /dev/null
@@ -0,0 +1,58 @@
+/* CTF 1.8 */
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "0bef2d78-5020-4b09-b520-64480ef5c0e6";
+       byte_order = be;
+       packet.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
+               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
+       } align(8);
+};
+
+env {
+       host = "sinkpad";
+};
+
+clock {
+       name = test_clock;
+       uuid = "ae130a0d-e10b-49cb-8b2d-64beaa23814c";
+       description = "This is a test clock";
+       freq = 1000000000;
+       precision = 10;
+       offset_s = 13932323;
+       offset = 0;
+       absolute = TRUE;
+};
+
+stream {
+       id = 0;
+       event.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
+       } align(8);
+
+       packet.context := struct {
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
+               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
+       } align(8);
+};
+
+event {
+       id = 0;
+       name = "dummy_event";
+       stream_id = 0;
+       fields := struct {
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
+       } align(1);
+};
+
diff --git a/tests/data/ctf-traces/packet-seq-num/no-lost-not-starting-at-0/test_stream_0 b/tests/data/ctf-traces/packet-seq-num/no-lost-not-starting-at-0/test_stream_0
new file mode 100644 (file)
index 0000000..f9639a3
Binary files /dev/null and b/tests/data/ctf-traces/packet-seq-num/no-lost-not-starting-at-0/test_stream_0 differ
diff --git a/tests/data/ctf-traces/packet-seq-num/no-lost/metadata b/tests/data/ctf-traces/packet-seq-num/no-lost/metadata
new file mode 100644 (file)
index 0000000..d562061
--- /dev/null
@@ -0,0 +1,58 @@
+/* CTF 1.8 */
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "b7d90429-287f-45ff-897c-3db7c5ab8b5a";
+       byte_order = be;
+       packet.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
+               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
+       } align(8);
+};
+
+env {
+       host = "sinkpad";
+};
+
+clock {
+       name = test_clock;
+       uuid = "004fa3e8-48aa-453a-8be8-9d30ead9ac66";
+       description = "This is a test clock";
+       freq = 1000000000;
+       precision = 10;
+       offset_s = 13515309;
+       offset = 0;
+       absolute = TRUE;
+};
+
+stream {
+       id = 0;
+       event.header := struct {
+               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
+       } align(8);
+
+       packet.context := struct {
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
+               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
+               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
+       } align(8);
+};
+
+event {
+       id = 0;
+       name = "dummy_event";
+       stream_id = 0;
+       fields := struct {
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
+               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
+       } align(1);
+};
+
diff --git a/tests/data/ctf-traces/packet-seq-num/no-lost/test_stream_0 b/tests/data/ctf-traces/packet-seq-num/no-lost/test_stream_0
new file mode 100644 (file)
index 0000000..15821fb
Binary files /dev/null and b/tests/data/ctf-traces/packet-seq-num/no-lost/test_stream_0 differ
diff --git a/tests/data/ctf-traces/packet_seq_num/2_lost_before_last/metadata b/tests/data/ctf-traces/packet_seq_num/2_lost_before_last/metadata
deleted file mode 100644 (file)
index 36e26cd..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* CTF 1.8 */
-
-trace {
-       major = 1;
-       minor = 8;
-       uuid = "ad21eeaa-fab9-4692-aab8-ebd68c7feb17";
-       byte_order = be;
-       packet.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
-               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
-       } align(8);
-};
-
-env {
-       host = "sinkpad";
-};
-
-clock {
-       name = test_clock;
-       uuid = "d336520f-985d-481e-8e35-d99328655354";
-       description = "This is a test clock";
-       freq = 1000000000;
-       precision = 10;
-       offset_s = 13515309;
-       offset = 0;
-       absolute = TRUE;
-};
-
-stream {
-       id = 0;
-       event.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
-       } align(8);
-
-       packet.context := struct {
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
-               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
-       } align(8);
-};
-
-event {
-       id = 0;
-       name = "dummy_event";
-       stream_id = 0;
-       fields := struct {
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
-       } align(1);
-};
-
diff --git a/tests/data/ctf-traces/packet_seq_num/2_lost_before_last/test_stream_0 b/tests/data/ctf-traces/packet_seq_num/2_lost_before_last/test_stream_0
deleted file mode 100644 (file)
index 50d36a4..0000000
Binary files a/tests/data/ctf-traces/packet_seq_num/2_lost_before_last/test_stream_0 and /dev/null differ
diff --git a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/metadata b/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/metadata
deleted file mode 100644 (file)
index 525405e..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* CTF 1.8 */
-
-trace {
-       major = 1;
-       minor = 8;
-       uuid = "61db8e6b-2069-40e4-84ed-bc15f42181f0";
-       byte_order = be;
-       packet.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
-               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
-       } align(8);
-};
-
-env {
-       host = "sinkpad";
-};
-
-clock {
-       name = test_clock;
-       uuid = "a0a8c252-db03-4f36-a148-80a0a3c4edff";
-       description = "This is a test clock";
-       freq = 1000000000;
-       precision = 10;
-       offset_s = 13515309;
-       offset = 0;
-       absolute = TRUE;
-};
-
-stream {
-       id = 0;
-       event.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
-       } align(8);
-
-       packet.context := struct {
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
-               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
-       } align(8);
-};
-
-event {
-       id = 0;
-       name = "dummy_event";
-       stream_id = 0;
-       fields := struct {
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
-       } align(1);
-};
-
diff --git a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/test_stream_0 b/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/test_stream_0
deleted file mode 100644 (file)
index 1b362cd..0000000
Binary files a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/test_stream_0 and /dev/null differ
diff --git a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/test_stream_1 b/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/test_stream_1
deleted file mode 100644 (file)
index d5114d9..0000000
Binary files a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_1/test_stream_1 and /dev/null differ
diff --git a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/metadata b/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/metadata
deleted file mode 100644 (file)
index 680d910..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* CTF 1.8 */
-
-trace {
-       major = 1;
-       minor = 8;
-       uuid = "eb5045f7-b471-488e-b963-0221ddf423a7";
-       byte_order = be;
-       packet.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
-               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
-       } align(8);
-};
-
-env {
-       host = "sinkpad";
-};
-
-clock {
-       name = test_clock;
-       uuid = "2447a359-1e57-448f-96ef-3c324327047c";
-       description = "This is a test clock";
-       freq = 1000000000;
-       precision = 10;
-       offset_s = 13515309;
-       offset = 0;
-       absolute = TRUE;
-};
-
-stream {
-       id = 0;
-       event.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
-       } align(8);
-
-       packet.context := struct {
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
-               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
-       } align(8);
-};
-
-event {
-       id = 0;
-       name = "dummy_event";
-       stream_id = 0;
-       fields := struct {
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
-       } align(1);
-};
-
diff --git a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/test_stream_0 b/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/test_stream_0
deleted file mode 100644 (file)
index b0beb6b..0000000
Binary files a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/test_stream_0 and /dev/null differ
diff --git a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/test_stream_1 b/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/test_stream_1
deleted file mode 100644 (file)
index 1ad8320..0000000
Binary files a/tests/data/ctf-traces/packet_seq_num/2_streams_lost_in_2/test_stream_1 and /dev/null differ
diff --git a/tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/index/stream_0.idx b/tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/index/stream_0.idx
deleted file mode 100644 (file)
index e06d92f..0000000
Binary files a/tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/index/stream_0.idx and /dev/null differ
diff --git a/tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/metadata b/tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/metadata
deleted file mode 100644 (file)
index 37a3030..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/* CTF 1.8 */
-
-/* This was generated by a Babeltrace `sink.ctf.fs` component. */
-
-trace {
-       major = 1;
-       minor = 8;
-       uuid = "1c810767-575e-4c4e-afa1-5d3e15081cb9";
-       byte_order = le;
-       packet.header := struct {
-               integer { size = 32; align = 8; base = x; } magic;
-               integer { size = 8; align = 8; } uuid[16];
-               integer { size = 64; align = 8; } stream_id;
-               integer { size = 64; align = 8; } stream_instance_id;
-       } align(8);
-};
-
-clock {
-       name = default;
-       freq = 1000000000;
-       precision = 0;
-       offset_s = 0;
-       offset = 0;
-       absolute = true;
-};
-
-stream {
-       id = 0;
-       packet.context := struct {
-               integer { size = 64; align = 8; } packet_size;
-               integer { size = 64; align = 8; } content_size;
-               integer { size = 64; align = 8; map = clock.default.value; } timestamp_begin;
-               integer { size = 64; align = 8; map = clock.default.value; } timestamp_end;
-               integer { size = 64; align = 8; } packet_seq_num;
-       } align(8);
-
-       event.header := struct {
-               integer { size = 64; align = 8; } id;
-               integer { size = 64; align = 8; map = clock.default.value; } timestamp;
-       } align(8);
-};
-
-event {
-       name = "my-event";
-       stream_id = 0;
-       id = 0;
-};
-
diff --git a/tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/stream_0 b/tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/stream_0
deleted file mode 100644 (file)
index fdfe9e1..0000000
Binary files a/tests/data/ctf-traces/packet_seq_num/7_lost_between_2_with_index/stream_0 and /dev/null differ
diff --git a/tests/data/ctf-traces/packet_seq_num/no_lost/metadata b/tests/data/ctf-traces/packet_seq_num/no_lost/metadata
deleted file mode 100644 (file)
index d562061..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* CTF 1.8 */
-
-trace {
-       major = 1;
-       minor = 8;
-       uuid = "b7d90429-287f-45ff-897c-3db7c5ab8b5a";
-       byte_order = be;
-       packet.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
-               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
-       } align(8);
-};
-
-env {
-       host = "sinkpad";
-};
-
-clock {
-       name = test_clock;
-       uuid = "004fa3e8-48aa-453a-8be8-9d30ead9ac66";
-       description = "This is a test clock";
-       freq = 1000000000;
-       precision = 10;
-       offset_s = 13515309;
-       offset = 0;
-       absolute = TRUE;
-};
-
-stream {
-       id = 0;
-       event.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
-       } align(8);
-
-       packet.context := struct {
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
-               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
-       } align(8);
-};
-
-event {
-       id = 0;
-       name = "dummy_event";
-       stream_id = 0;
-       fields := struct {
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
-       } align(1);
-};
-
diff --git a/tests/data/ctf-traces/packet_seq_num/no_lost/test_stream_0 b/tests/data/ctf-traces/packet_seq_num/no_lost/test_stream_0
deleted file mode 100644 (file)
index 15821fb..0000000
Binary files a/tests/data/ctf-traces/packet_seq_num/no_lost/test_stream_0 and /dev/null differ
diff --git a/tests/data/ctf-traces/packet_seq_num/no_lost_not_starting_at_0/metadata b/tests/data/ctf-traces/packet_seq_num/no_lost_not_starting_at_0/metadata
deleted file mode 100644 (file)
index c8f5783..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* CTF 1.8 */
-
-trace {
-       major = 1;
-       minor = 8;
-       uuid = "0bef2d78-5020-4b09-b520-64480ef5c0e6";
-       byte_order = be;
-       packet.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } magic;
-               integer { size = 8; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } uuid[16];
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } stream_id;
-       } align(8);
-};
-
-env {
-       host = "sinkpad";
-};
-
-clock {
-       name = test_clock;
-       uuid = "ae130a0d-e10b-49cb-8b2d-64beaa23814c";
-       description = "This is a test clock";
-       freq = 1000000000;
-       precision = 10;
-       offset_s = 13932323;
-       offset = 0;
-       absolute = TRUE;
-};
-
-stream {
-       id = 0;
-       event.header := struct {
-               integer { size = 32; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } id;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; map = clock.test_clock.value; } timestamp;
-       } align(8);
-
-       packet.context := struct {
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_begin;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } timestamp_end;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } content_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } packet_size;
-               integer { size = 64; align = 8; signed = false; encoding = none; base = decimal; byte_order = be; } events_discarded;
-               integer { size = 64; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_seq_num;
-       } align(8);
-};
-
-event {
-       id = 0;
-       name = "dummy_event";
-       stream_id = 0;
-       fields := struct {
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } dummy_value;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } tracefile_id;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_begin;
-               integer { size = 32; align = 1; signed = false; encoding = none; base = decimal; byte_order = be; } packet_end;
-       } align(1);
-};
-
diff --git a/tests/data/ctf-traces/packet_seq_num/no_lost_not_starting_at_0/test_stream_0 b/tests/data/ctf-traces/packet_seq_num/no_lost_not_starting_at_0/test_stream_0
deleted file mode 100644 (file)
index f9639a3..0000000
Binary files a/tests/data/ctf-traces/packet_seq_num/no_lost_not_starting_at_0/test_stream_0 and /dev/null differ
diff --git a/tests/data/ctf-traces/succeed/crlf-metadata/channel0_0 b/tests/data/ctf-traces/succeed/crlf-metadata/channel0_0
new file mode 100644 (file)
index 0000000..af54261
Binary files /dev/null and b/tests/data/ctf-traces/succeed/crlf-metadata/channel0_0 differ
diff --git a/tests/data/ctf-traces/succeed/crlf-metadata/metadata b/tests/data/ctf-traces/succeed/crlf-metadata/metadata
new file mode 100644 (file)
index 0000000..3c1438f
--- /dev/null
@@ -0,0 +1,114 @@
+/* CTF 1.8 */\r
+\r
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\r
+typealias integer { size = 16; align = 8; signed = false; } := uint16_t;\r
+typealias integer { size = 32; align = 8; signed = false; } := uint32_t;\r
+typealias integer { size = 64; align = 8; signed = false; } := uint64_t;\r
+typealias integer { size = 64; align = 8; signed = false; } := unsigned long;\r
+typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\r
+typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\r
+\r
+trace {\r
+       major = 1;\r
+       minor = 8;\r
+       uuid = "ddb15f3f-a235-444e-9d1b-f131648b5bf1";\r
+       byte_order = le;\r
+       packet.header := struct {\r
+               uint32_t magic;\r
+               uint8_t  uuid[16];\r
+               uint32_t stream_id;\r
+               uint64_t stream_instance_id;\r
+       };\r
+};\r
+\r
+env {\r
+       domain = "ust";\r
+       tracer_name = "lttng-ust";\r
+       tracer_major = 2;\r
+       tracer_minor = 13;\r
+       tracer_buffering_scheme = "uid";\r
+       tracer_buffering_id = 1000;\r
+       architecture_bit_width = 64;\r
+       trace_name = "crlf-metadata";\r
+       trace_creation_datetime = "20231023T193151+0000";\r
+       hostname = "line-endings";\r
+};\r
+\r
+clock {\r
+       name = "monotonic";\r
+       uuid = "d0367630-5511-4855-a390-afe86f9b4545";\r
+       description = "Monotonic Clock";\r
+       freq = 1000000000; /* Frequency, in Hz */\r
+       /* clock value offset from Epoch is: offset * (1/freq) */\r
+       offset = 1698076473717549015;\r
+};\r
+\r
+typealias integer {\r
+       size = 27; align = 1; signed = false;\r
+       map = clock.monotonic.value;\r
+} := uint27_clock_monotonic_t;\r
+\r
+typealias integer {\r
+       size = 32; align = 8; signed = false;\r
+       map = clock.monotonic.value;\r
+} := uint32_clock_monotonic_t;\r
+\r
+typealias integer {\r
+       size = 64; align = 8; signed = false;\r
+       map = clock.monotonic.value;\r
+} := uint64_clock_monotonic_t;\r
+\r
+struct packet_context {\r
+       uint64_clock_monotonic_t timestamp_begin;\r
+       uint64_clock_monotonic_t timestamp_end;\r
+       uint64_t content_size;\r
+       uint64_t packet_size;\r
+       uint64_t packet_seq_num;\r
+       unsigned long events_discarded;\r
+       uint32_t cpu_id;\r
+};\r
+\r
+struct event_header_compact {\r
+       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\r
+       variant <id> {\r
+               struct {\r
+                       uint27_clock_monotonic_t timestamp;\r
+               } compact;\r
+               struct {\r
+                       uint32_t id;\r
+                       uint64_clock_monotonic_t timestamp;\r
+               } extended;\r
+       } v;\r
+} align(8);\r
+\r
+struct event_header_large {\r
+       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\r
+       variant <id> {\r
+               struct {\r
+                       uint32_clock_monotonic_t timestamp;\r
+               } compact;\r
+               struct {\r
+                       uint32_t id;\r
+                       uint64_clock_monotonic_t timestamp;\r
+               } extended;\r
+       } v;\r
+} align(8);\r
+\r
+stream {\r
+       id = 0;\r
+       event.header := struct event_header_large;\r
+       packet.context := struct packet_context;\r
+};\r
+\r
+event {\r
+       name = "lttng_ust_tracef:event";\r
+       id = 0;\r
+       stream_id = 0;\r
+       loglevel = 14;\r
+       fields := struct {\r
+               integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } __msg_length;\r
+               integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _msg[ __msg_length ];\r
+       };\r
+};\r
+\r
+\r
index 35b979b20c24d1e165f9fd8f3707de247996bbd3..1186c4bc765e43f5a7ec17d3c04361a631b55422 100644 (file)
Binary files a/tests/data/ctf-traces/succeed/debug-info/channel0_0 and b/tests/data/ctf-traces/succeed/debug-info/channel0_0 differ
diff --git a/tests/data/ctf-traces/succeed/lf-metadata/channel0_0 b/tests/data/ctf-traces/succeed/lf-metadata/channel0_0
new file mode 100644 (file)
index 0000000..82dc458
Binary files /dev/null and b/tests/data/ctf-traces/succeed/lf-metadata/channel0_0 differ
diff --git a/tests/data/ctf-traces/succeed/lf-metadata/metadata b/tests/data/ctf-traces/succeed/lf-metadata/metadata
new file mode 100644 (file)
index 0000000..af907ff
--- /dev/null
@@ -0,0 +1,114 @@
+/* CTF 1.8 */
+
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
+typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
+typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
+typealias integer { size = 64; align = 8; signed = false; } := unsigned long;
+typealias integer { size = 5; align = 1; signed = false; } := uint5_t;
+typealias integer { size = 27; align = 1; signed = false; } := uint27_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "8c609d38-ed7e-49c3-81df-33cf268062b7";
+       byte_order = le;
+       packet.header := struct {
+               uint32_t magic;
+               uint8_t  uuid[16];
+               uint32_t stream_id;
+               uint64_t stream_instance_id;
+       };
+};
+
+env {
+       domain = "ust";
+       tracer_name = "lttng-ust";
+       tracer_major = 2;
+       tracer_minor = 13;
+       tracer_buffering_scheme = "uid";
+       tracer_buffering_id = 1000;
+       architecture_bit_width = 64;
+       trace_name = "lf-metadata";
+       trace_creation_datetime = "20231023T185853+0000";
+       hostname = "line-endings";
+};
+
+clock {
+       name = "monotonic";
+       uuid = "d0367630-5511-4855-a390-afe86f9b4545";
+       description = "Monotonic Clock";
+       freq = 1000000000; /* Frequency, in Hz */
+       /* clock value offset from Epoch is: offset * (1/freq) */
+       offset = 1698076473717549018;
+};
+
+typealias integer {
+       size = 27; align = 1; signed = false;
+       map = clock.monotonic.value;
+} := uint27_clock_monotonic_t;
+
+typealias integer {
+       size = 32; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint32_clock_monotonic_t;
+
+typealias integer {
+       size = 64; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint64_clock_monotonic_t;
+
+struct packet_context {
+       uint64_clock_monotonic_t timestamp_begin;
+       uint64_clock_monotonic_t timestamp_end;
+       uint64_t content_size;
+       uint64_t packet_size;
+       uint64_t packet_seq_num;
+       unsigned long events_discarded;
+       uint32_t cpu_id;
+};
+
+struct event_header_compact {
+       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
+       variant <id> {
+               struct {
+                       uint27_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+struct event_header_large {
+       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
+       variant <id> {
+               struct {
+                       uint32_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+stream {
+       id = 0;
+       event.header := struct event_header_large;
+       packet.context := struct packet_context;
+};
+
+event {
+       name = "lttng_ust_tracef:event";
+       id = 0;
+       stream_id = 0;
+       loglevel = 14;
+       fields := struct {
+               integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } __msg_length;
+               integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _msg[ __msg_length ];
+       };
+};
+
+
index 2820805270ad6499a37c8801b6463d3f18f78b35..d51a7231a2cd49446e4929ab2116c9b9ff55ae09 100644 (file)
@@ -4,10 +4,10 @@ BUILD_ID=$(BUILD_ID_PREFIX)$(BUILD_ID_SUFFIX)
 
 BUILD_DIR ?= build
 
-OBJS=$(BUILD_DIR)/dwarf_full/libhello_so \
-     $(BUILD_DIR)/elf_only/libhello_so \
-     $(BUILD_DIR)/build_id/libhello_so \
-     $(BUILD_DIR)/debug_link/libhello_so
+OBJS=$(BUILD_DIR)/dwarf-full/libhello-so \
+     $(BUILD_DIR)/elf-only/libhello-so \
+     $(BUILD_DIR)/build-id/libhello-so \
+     $(BUILD_DIR)/debug-link/libhello-so
 
 all: $(OBJS)
 
@@ -15,30 +15,30 @@ all: $(OBJS)
        $(CC) -gdwarf -fdebug-prefix-map=$(CURDIR)=. -fPIC -c -I. -o $@ $<
 
 # Master copy: ELF with DWARF and build-id
-$(BUILD_DIR)/dwarf_full/libhello_so: tp.o libhello.o
+$(BUILD_DIR)/dwarf-full/libhello-so: tp.o libhello.o
        mkdir -p $(@D)
        $(CC) -shared -gdwarf -llttng-ust -ldl -Wl,-soname,libhello.so -Wl,--build-id=0x$(BUILD_ID) -o $@ $^
 
 # ELF only, no debug symbols, no build-d
-$(BUILD_DIR)/elf_only/libhello_so: $(BUILD_DIR)/dwarf_full/libhello_so
+$(BUILD_DIR)/elf-only/libhello-so: $(BUILD_DIR)/dwarf-full/libhello-so
        mkdir -p $(@D)
        objcopy -g $< $@.tmp
        objcopy --remove-section=.note.gnu.build-id $@.tmp
        mv $@.tmp $@
 
 # ELF with external build-id DWARF
-$(BUILD_DIR)/build_id/libhello_so: $(BUILD_DIR)/dwarf_full/libhello_so
+$(BUILD_DIR)/build-id/libhello-so: $(BUILD_DIR)/dwarf-full/libhello-so
        mkdir -p $(@D)/.build-id/$(BUILD_ID_PREFIX)
        objcopy --only-keep-debug $< $(@D)/.build-id/$(BUILD_ID_PREFIX)/$(BUILD_ID_SUFFIX).debug
        objcopy -g $< $@
 
 # ELF with external debug link DWARF
-$(BUILD_DIR)/debug_link/libhello_so: $(BUILD_DIR)/dwarf_full/libhello_so
+$(BUILD_DIR)/debug-link/libhello-so: $(BUILD_DIR)/dwarf-full/libhello-so
        mkdir -p $(@D)
        objcopy --remove-section=.note.gnu.build-id $< $@.tmp
-       objcopy --only-keep-debug $@.tmp $(@D)/libhello_so.debug
+       objcopy --only-keep-debug $@.tmp $(@D)/libhello-so.debug
        objcopy -g $@.tmp
-       cd $(@D) && objcopy --add-gnu-debuglink=libhello_so.debug $(@F).tmp
+       cd $(@D) && objcopy --add-gnu-debuglink=libhello-so.debug $(@F).tmp
        mv $@.tmp $@
 
 clean:
index 6d20c5cb2c787bfbb2165b1bab81b49a5bed9ebc..38045d1675dea9b6b2586f462e90fd11f925df85 100644 (file)
@@ -8,12 +8,12 @@ files used to generate them.
 
 The generated files are:
 
-* `ARCH/dwarf_full/libhello_so` (ELF and DWARF)
-* `ARCH/elf_only/libhello_so` (ELF only)
-* `ARCH/build_id/libhello_so` (ELF with separate DWARF via build ID)
-* `ARCH/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug` (DWARF for build ID)
-* `ARCH/debug_link/libhello_so` (ELF with separate DWARF via debug link)
-* `ARCH/debug_link/libhello_so.debug` (DWARF for debug link)
+* `ARCH/dwarf-full/libhello-so` (ELF and DWARF)
+* `ARCH/elf-only/libhello-so` (ELF only)
+* `ARCH/build-id/libhello-so` (ELF with separate DWARF via build ID)
+* `ARCH/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug` (DWARF for build ID)
+* `ARCH/debug_link/libhello-so` (ELF with separate DWARF via debug link)
+* `ARCH/debug_link/libhello-so.debug` (DWARF for debug link)
 
 We use a suffix of "_so" instead of ".so" since some distributions
 build systems will consider ".so" files as artifacts from a previous
@@ -40,28 +40,28 @@ To regenerate them, you can use the included Makefile or follow these steps:
     $ build_id_prefix=cd
     $ build_id_suffix=d98cdd87f7fe64c13b6daad553987eafd40cbb
     $ build_id="$build_id_prefix$build_id_suffix"
-    $ mkdir dwarf_full
-    $ gcc -shared -g -llttng-ust -ldl -Wl,-soname,libhello.so -Wl,--build-id=0x$build_id -o dwarf_full/libhello_so tp.o libhello.o
+    $ mkdir dwarf-full
+    $ gcc -shared -g -llttng-ust -ldl -Wl,-soname,libhello.so -Wl,--build-id=0x$build_id -o dwarf-full/libhello-so tp.o libhello.o
 
 ## ELF only
 
-    $ mkdir elf_only
-    $ objcopy -g dwarf_full/libhello_so elf_only/libhello_so
-    $ objcopy --remove-section=.note.gnu.build-id elf_only/libhello_so
+    $ mkdir elf-only
+    $ objcopy -g dwarf-full/libhello-so elf-only/libhello-so
+    $ objcopy --remove-section=.note.gnu.build-id elf-only/libhello-so
 
 ## ELF and DWARF with Build ID
 
-    $ mkdir -p build_id/.build-id/$build_id_prefix
-    $ objcopy --only-keep-debug dwarf_full/libhello_so build_id/.build-id/$build_id_prefix/$build_id_suffix.debug
-    $ objcopy -g dwarf_full/libhello_so build_id/libhello_so
+    $ mkdir -p build-id/.build-id/$build_id_prefix
+    $ objcopy --only-keep-debug dwarf-full/libhello-so build-id/.build-id/$build_id_prefix/$build_id_suffix.debug
+    $ objcopy -g dwarf-full/libhello-so build-id/libhello-so
 
 ##  ELF and DWARF with Debug Link
 
     $ mkdir debug_link
-    $ objcopy --remove-section=.note.gnu.build-id dwarf_full/libhello_so debug_link/libhello_so
-    $ objcopy --only-keep-debug debug_link/libhello_so debug_link/libhello_so.debug
-    $ objcopy -g debug_link/libhello_so
-    $ cd debug_link && objcopy --add-gnu-debuglink=libhello_so.debug libhello_so && cd ..
+    $ objcopy --remove-section=.note.gnu.build-id dwarf-full/libhello-so debug_link/libhello-so
+    $ objcopy --only-keep-debug debug_link/libhello-so debug_link/libhello-so.debug
+    $ objcopy -g debug_link/libhello-so
+    $ cd debug_link && objcopy --add-gnu-debuglink=libhello-so.debug libhello-so && cd ..
 
 
 Test program
@@ -72,25 +72,25 @@ to do the debug-info resolution. You can generate such trace by following these
 steps:
 
 1. Compile the example binary:
-    $ ln -s x86_64-linux-gnu/dwarf_full/libhello_so libhello.so
+    $ ln -s x86-64-linux-gnu/dwarf-full/libhello-so libhello.so
     $ gcc -I. -o debug_info_app main.c -L. -lhello -llttng-ust -ldl -Wl,--rpath=.
 
 2. In order to have paths to binary and shared objects that are not relative
    to the file system they were built on, we used a simple trick of copying
    the following files to the root directory ('/') like this:
 
-    $ sudo cp x86_64-linux-gnu/dwarf_full/libhello_so /libhello_so
+    $ sudo cp x86-64-linux-gnu/dwarf-full/libhello-so /libhello-so
     $ sudo cp ./debug_info_app /
 
-3. Create symbolic link to the `/libhello_so` file with the `/libhello.so` name.
-    $ sudo ln -s /libhello_so /libhello.so
+3. Create symbolic link to the `/libhello-so` file with the `/libhello.so` name.
+    $ sudo ln -s /libhello-so /libhello.so
 
 4. Create the LTTng tracing session using the following commands:
     $ cd /
     $ sudo lttng create
     $ sudo lttng add-context -u -t vpid -t ip
     $ sudo lttng enable-event -u my_provider:my_first_tracepoint
-    $ sudo lttng enable-event -u lttng_ust_statedump:bin_info --filter='path=="/libhello_so"'
+    $ sudo lttng enable-event -u lttng_ust_statedump:bin_info --filter='path=="/libhello-so"'
     $ sudo lttng enable-event -u lttng_ust_statedump:bin_info --filter='path=="[linux-vdso.so.1]"'
     $ sudo lttng start
     $ sudo /debug_info_app
@@ -99,10 +99,10 @@ steps:
 5. Copy the resulting trace back into the Babeltrace repository.
 
 When running babeltrace with the `--debug-info-target-prefix` option or
-`target-prefix` component paramater set to the directory containing the right
-`libhello_so` file. In the example used above, the `libhello_so` file is in the
-`x86_64-linux-gnu/dwarf_full/` directory.
+`target-prefix` component parameter set to the directory containing the right
+`libhello-so` file. In the example used above, the `libhello-so` file is in the
+`x86-64-linux-gnu/dwarf-full/` directory.
 In the printed trace, the `my_provider:my_first_tracepoint` events should
 contain information similar to this:
 
-    debug_info = { bin = "libhello_so+0x2349", func = "foo+0xd2", src = "libhello.c:35" }
+    debug_info = { bin = "libhello-so+0x2349", func = "foo+0xd2", src = "libhello.c:35" }
index cc9bc53b934679b22bef2d86b45361f38a6b9afb..295e674e9629c25eeec31f02edcbd768f76f55b8 100644 (file)
@@ -3,10 +3,11 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
-import bt2
 import math
 
-bt2.register_plugin(__name__, "test_debug_info")
+import bt2
+
+bt2.register_plugin(__name__, "test-debug-info")
 
 
 class CompleteIter(bt2._UserMessageIterator):
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug
new file mode 100755 (executable)
index 0000000..ba371f5
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build-id/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build-id/libhello-so
new file mode 100755 (executable)
index 0000000..218e668
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build-id/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug
deleted file mode 100755 (executable)
index ba371f5..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build_id/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build_id/libhello_so
deleted file mode 100755 (executable)
index 218e668..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/build_id/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug-link/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug-link/libhello-so
new file mode 100755 (executable)
index 0000000..2b76ed5
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug-link/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug-link/libhello-so.debug b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug-link/libhello-so.debug
new file mode 100755 (executable)
index 0000000..1ede770
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug-link/libhello-so.debug differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug_link/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug_link/libhello_so
deleted file mode 100755 (executable)
index 4dcfdc5..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug_link/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug_link/libhello_so.debug b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug_link/libhello_so.debug
deleted file mode 100755 (executable)
index 1ede770..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/debug_link/libhello_so.debug and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/dwarf-full/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/dwarf-full/libhello-so
new file mode 100755 (executable)
index 0000000..08390c8
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/dwarf-full/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/dwarf_full/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/dwarf_full/libhello_so
deleted file mode 100755 (executable)
index 08390c8..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/dwarf_full/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/elf-only/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/elf-only/libhello-so
new file mode 100755 (executable)
index 0000000..84c31e2
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/elf-only/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/elf_only/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/elf_only/libhello_so
deleted file mode 100755 (executable)
index 84c31e2..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/i386-linux-gnu/elf_only/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug
new file mode 100755 (executable)
index 0000000..6fa1499
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build-id/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build-id/libhello-so
new file mode 100755 (executable)
index 0000000..49c6249
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build-id/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug
deleted file mode 100755 (executable)
index 6fa1499..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build_id/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build_id/libhello_so
deleted file mode 100755 (executable)
index 49c6249..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/build_id/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug-link/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug-link/libhello-so
new file mode 100755 (executable)
index 0000000..b3ac626
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug-link/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug-link/libhello-so.debug b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug-link/libhello-so.debug
new file mode 100755 (executable)
index 0000000..a95eed2
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug-link/libhello-so.debug differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug_link/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug_link/libhello_so
deleted file mode 100755 (executable)
index 23efdc8..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug_link/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug_link/libhello_so.debug b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug_link/libhello_so.debug
deleted file mode 100755 (executable)
index a95eed2..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/debug_link/libhello_so.debug and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/dwarf-full/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/dwarf-full/libhello-so
new file mode 100755 (executable)
index 0000000..ef39d51
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/dwarf-full/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/dwarf_full/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/dwarf_full/libhello_so
deleted file mode 100755 (executable)
index ef39d51..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/dwarf_full/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/elf-only/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/elf-only/libhello-so
new file mode 100755 (executable)
index 0000000..d1e7322
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/elf-only/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/elf_only/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/elf_only/libhello_so
deleted file mode 100755 (executable)
index d1e7322..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc-linux-gnu/elf_only/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug
new file mode 100755 (executable)
index 0000000..b419832
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build-id/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build-id/libhello-so
new file mode 100755 (executable)
index 0000000..f740eb0
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build-id/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug
deleted file mode 100755 (executable)
index b419832..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build_id/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build_id/libhello_so
deleted file mode 100755 (executable)
index f740eb0..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/build_id/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug-link/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug-link/libhello-so
new file mode 100755 (executable)
index 0000000..1d08451
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug-link/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug-link/libhello-so.debug b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug-link/libhello-so.debug
new file mode 100755 (executable)
index 0000000..86364a3
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug-link/libhello-so.debug differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug_link/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug_link/libhello_so
deleted file mode 100755 (executable)
index 01e0c6b..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug_link/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug_link/libhello_so.debug b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug_link/libhello_so.debug
deleted file mode 100755 (executable)
index 86364a3..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/debug_link/libhello_so.debug and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/dwarf-full/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/dwarf-full/libhello-so
new file mode 100755 (executable)
index 0000000..3874f11
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/dwarf-full/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/dwarf_full/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/dwarf_full/libhello_so
deleted file mode 100755 (executable)
index 3874f11..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/dwarf_full/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/elf-only/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/elf-only/libhello-so
new file mode 100755 (executable)
index 0000000..067b472
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/elf-only/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/elf_only/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/elf_only/libhello_so
deleted file mode 100755 (executable)
index 067b472..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/powerpc64le-linux-gnu/elf_only/libhello_so and /dev/null differ
index 047ef8f960fdff51f7565d1c1b882625365940c5..e800e9c4bf680a44117ff921235ccd9cca9b4941 100644 (file)
@@ -89,7 +89,7 @@ Event `lttng_ust_statedump:bin_info` (Class ID 0):
   Payload:
     baddr: 0x7f09:b7f9:8000
     memsz: 2,114,208
-    path: /libhello_so
+    path: /libhello-so
     is_pic: 1
     has_build_id: 1
     has_debug_link: 0
@@ -101,7 +101,7 @@ Event `my_provider:my_first_tracepoint` (Class ID 1):
     vpid: 9746
     ip: 0x7f09:b7f9:a349
     debug_info:
-      bin: libhello_so+0x2349
+      bin: libhello-so+0x2349
       func: foo+0xd2
       src: libhello.c:35
   Payload:
@@ -115,7 +115,7 @@ Event `my_provider:my_first_tracepoint` (Class ID 1):
     vpid: 9746
     ip: 0x7f09:b7f9:a448
     debug_info:
-      bin: libhello_so+0x2448
+      bin: libhello-so+0x2448
       func: bar+0xd2
       src: libhello.c:41
   Payload:
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug
new file mode 100755 (executable)
index 0000000..41e2cfb
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/build-id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/build-id/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/build-id/libhello-so
new file mode 100755 (executable)
index 0000000..0edfd79
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/build-id/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/debug-link/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/debug-link/libhello-so
new file mode 100755 (executable)
index 0000000..d3cd342
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/debug-link/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/debug-link/libhello-so.debug b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/debug-link/libhello-so.debug
new file mode 100755 (executable)
index 0000000..082f513
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/debug-link/libhello-so.debug differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/dwarf-full/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/dwarf-full/libhello-so
new file mode 100755 (executable)
index 0000000..f8d3e41
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/dwarf-full/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/elf-only/libhello-so b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/elf-only/libhello-so
new file mode 100755 (executable)
index 0000000..bffdaab
Binary files /dev/null and b/tests/data/plugins/flt.lttng-utils.debug-info/x86-64-linux-gnu/elf-only/libhello-so differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug b/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug
deleted file mode 100755 (executable)
index 41e2cfb..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/build_id/.build-id/cd/d98cdd87f7fe64c13b6daad553987eafd40cbb.debug and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/build_id/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/build_id/libhello_so
deleted file mode 100755 (executable)
index 0edfd79..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/build_id/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/debug_link/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/debug_link/libhello_so
deleted file mode 100755 (executable)
index 0c12601..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/debug_link/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/debug_link/libhello_so.debug b/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/debug_link/libhello_so.debug
deleted file mode 100755 (executable)
index 082f513..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/debug_link/libhello_so.debug and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/dwarf_full/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/dwarf_full/libhello_so
deleted file mode 100755 (executable)
index f8d3e41..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/dwarf_full/libhello_so and /dev/null differ
diff --git a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/elf_only/libhello_so b/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/elf_only/libhello_so
deleted file mode 100755 (executable)
index bffdaab..0000000
Binary files a/tests/data/plugins/flt.lttng-utils.debug-info/x86_64-linux-gnu/elf_only/libhello_so and /dev/null differ
index 8ac1f1b7456300c7820b0f838b81384d0547a72b..4026433ff9d568dfd88a6164847573cb2c73124c 100644 (file)
@@ -368,7 +368,7 @@ class DiffEventClassName:
         stream = trace.create_stream(sc, 0)
 
         # Use event class name length as timestamp so that both stream
-        # beginning message are not at the same time. This test is targetting
+        # beginning message are not at the same time. This test is targeting
         # event message.
         sb_msg = msg_iter._create_stream_beginning_message(stream, len(ec.name))
         ev_msg = msg_iter._create_event_message(ec, stream, 50)
@@ -405,7 +405,7 @@ class DiffEventClassId:
         stream = trace.create_stream(sc, 0)
 
         # Use event class id as timestamp so that both stream beginning message
-        # are not at the same time. This test is targetting event message.
+        # are not at the same time. This test is targeting event message.
         sb_msg = msg_iter._create_stream_beginning_message(stream, ec.id)
         ev_msg = msg_iter._create_event_message(ec, stream, 50)
         se_msg = msg_iter._create_stream_end_message(stream, iter_id * 193)
@@ -432,18 +432,18 @@ class DiffInactivityMsgCs:
 
 
 TEST_CASES = {
-    "diff_trace_name": DiffTraceName,
-    "diff_event_class_name": DiffEventClassName,
-    "diff_event_class_id": DiffEventClassId,
-    "diff_stream_name": DiffStreamName,
-    "diff_stream_no_name": DiffStreamNoName,
-    "diff_stream_id": DiffStreamId,
-    "diff_stream_class_id": DiffStreamClassId,
-    "diff_stream_class_name": DiffStreamClassName,
-    "diff_stream_class_no_name": DiffStreamClassNoName,
-    "diff_inactivity_msg_cs": DiffInactivityMsgCs,
-    "basic_timestamp_ordering": BasicTimestampOrdering,
-    "multi_iter_ordering": MultiIterOrdering,
+    "diff-trace-name": DiffTraceName,
+    "diff-event-class-name": DiffEventClassName,
+    "diff-event-class-id": DiffEventClassId,
+    "diff-stream-name": DiffStreamName,
+    "diff-stream-no-name": DiffStreamNoName,
+    "diff-stream-id": DiffStreamId,
+    "diff-stream-class-id": DiffStreamClassId,
+    "diff-stream-class-name": DiffStreamClassName,
+    "diff-stream-class-no-name": DiffStreamClassNoName,
+    "diff-inactivity-msg-cs": DiffInactivityMsgCs,
+    "basic-timestamp-ordering": BasicTimestampOrdering,
+    "multi-iter-ordering": MultiIterOrdering,
 }
 
 bt2.register_plugin(__name__, "test-muxer")
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/basic-timestamp-ordering.expect b/tests/data/plugins/flt.utils.muxer/succeed/basic-timestamp-ordering.expect
new file mode 100644 (file)
index 0000000..e0cb89a
--- /dev/null
@@ -0,0 +1,29 @@
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[4 cycles, 4,000,000,000 ns from origin]
+{Trace 1, Stream class ID 2, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 2)
+
+[120 cycles, 120,000,000,000 ns from origin]
+{Trace 2, Stream class ID 1, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 1)
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 2, Stream class ID 1, Stream ID 0}
+Stream end
+
+[579 cycles, 579,000,000,000 ns from origin]
+{Trace 1, Stream class ID 2, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/basic_timestamp_ordering.expect b/tests/data/plugins/flt.utils.muxer/succeed/basic_timestamp_ordering.expect
deleted file mode 100644 (file)
index e0cb89a..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[4 cycles, 4,000,000,000 ns from origin]
-{Trace 1, Stream class ID 2, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 2)
-
-[120 cycles, 120,000,000,000 ns from origin]
-{Trace 2, Stream class ID 1, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 1)
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 2, Stream class ID 1, Stream ID 0}
-Stream end
-
-[579 cycles, 579,000,000,000 ns from origin]
-{Trace 1, Stream class ID 2, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-event-class-id.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-event-class-id.expect
new file mode 100644 (file)
index 0000000..bff55c9
--- /dev/null
@@ -0,0 +1,27 @@
+[1 cycles, 1,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[2 cycles, 2,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[50 cycles, 50,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event (Class ID 1):
+
+[50 cycles, 50,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Event (Class ID 2):
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-event-class-name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-event-class-name.expect
new file mode 100644 (file)
index 0000000..3ea1340
--- /dev/null
@@ -0,0 +1,27 @@
+[4 cycles, 4,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[8 cycles, 8,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[50 cycles, 50,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Event `Gatineau` (Class ID 0):
+
+[50 cycles, 50,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `Hull` (Class ID 0):
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-inactivity-msg-cs.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-inactivity-msg-cs.expect
new file mode 100644 (file)
index 0000000..c018a3e
--- /dev/null
@@ -0,0 +1,19 @@
+[0 cycles, 0 ns from origin]
+Message iterator inactivity:
+  Clock class:
+    Name: Chicoutimi
+    Frequency (Hz): 1
+    Precision (cycles): 0
+    Offset (s): 0
+    Offset (cycles): 0
+    Origin is Unix epoch: Yes
+
+[0 cycles, 0 ns from origin]
+Message iterator inactivity:
+  Clock class:
+    Name: La Baie
+    Frequency (Hz): 1
+    Precision (cycles): 0
+    Offset (s): 0
+    Offset (cycles): 0
+    Origin is Unix epoch: Yes
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-class-id.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-class-id.expect
new file mode 100644 (file)
index 0000000..b96f250
--- /dev/null
@@ -0,0 +1,19 @@
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 18, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 18)
+
+[0 cycles, 0 ns from origin]
+{Trace 1, Stream class ID 23, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 23)
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 0, Stream class ID 18, Stream ID 0}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 1, Stream class ID 23, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-class-name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-class-name.expect
new file mode 100644 (file)
index 0000000..6e8e857
--- /dev/null
@@ -0,0 +1,21 @@
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Class name: one
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[0 cycles, 0 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Class name: two
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-class-no-name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-class-no-name.expect
new file mode 100644 (file)
index 0000000..b87875d
--- /dev/null
@@ -0,0 +1,20 @@
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[0 cycles, 0 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Class name: one
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-id.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-id.expect
new file mode 100644 (file)
index 0000000..46a6981
--- /dev/null
@@ -0,0 +1,19 @@
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 18}
+Stream beginning:
+  Trace:
+    Stream (ID 18, Class ID 0)
+
+[0 cycles, 0 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 23}
+Stream beginning:
+  Trace:
+    Stream (ID 23, Class ID 0)
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 18}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 23}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-name.expect
new file mode 100644 (file)
index 0000000..86c025c
--- /dev/null
@@ -0,0 +1,21 @@
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: gascon
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[0 cycles, 0 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: port-daniel
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-no-name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-stream-no-name.expect
new file mode 100644 (file)
index 0000000..6e9e43d
--- /dev/null
@@ -0,0 +1,20 @@
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[0 cycles, 0 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: one
+  Trace:
+    Stream (ID 0, Class ID 0)
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff-trace-name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff-trace-name.expect
new file mode 100644 (file)
index 0000000..128ac95
--- /dev/null
@@ -0,0 +1,19 @@
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace `noranda`:
+    Stream (ID 0, Class ID 0)
+
+[0 cycles, 0 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace `rouyn`:
+    Stream (ID 0, Class ID 0)
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 0}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_event_class_id.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_event_class_id.expect
deleted file mode 100644 (file)
index bff55c9..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-[1 cycles, 1,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[2 cycles, 2,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[50 cycles, 50,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Event (Class ID 1):
-
-[50 cycles, 50,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Event (Class ID 2):
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_event_class_name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_event_class_name.expect
deleted file mode 100644 (file)
index 3ea1340..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-[4 cycles, 4,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[8 cycles, 8,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[50 cycles, 50,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Event `Gatineau` (Class ID 0):
-
-[50 cycles, 50,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Event `Hull` (Class ID 0):
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_inactivity_msg_cs.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_inactivity_msg_cs.expect
deleted file mode 100644 (file)
index c018a3e..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-[0 cycles, 0 ns from origin]
-Message iterator inactivity:
-  Clock class:
-    Name: Chicoutimi
-    Frequency (Hz): 1
-    Precision (cycles): 0
-    Offset (s): 0
-    Offset (cycles): 0
-    Origin is Unix epoch: Yes
-
-[0 cycles, 0 ns from origin]
-Message iterator inactivity:
-  Clock class:
-    Name: La Baie
-    Frequency (Hz): 1
-    Precision (cycles): 0
-    Offset (s): 0
-    Offset (cycles): 0
-    Origin is Unix epoch: Yes
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_class_id.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_class_id.expect
deleted file mode 100644 (file)
index b96f250..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 18, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 18)
-
-[0 cycles, 0 ns from origin]
-{Trace 1, Stream class ID 23, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 23)
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 0, Stream class ID 18, Stream ID 0}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 1, Stream class ID 23, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_class_name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_class_name.expect
deleted file mode 100644 (file)
index 6e8e857..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Class name: one
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[0 cycles, 0 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Class name: two
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_class_no_name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_class_no_name.expect
deleted file mode 100644 (file)
index b87875d..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[0 cycles, 0 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Class name: one
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_id.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_id.expect
deleted file mode 100644 (file)
index 46a6981..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 18}
-Stream beginning:
-  Trace:
-    Stream (ID 18, Class ID 0)
-
-[0 cycles, 0 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 23}
-Stream beginning:
-  Trace:
-    Stream (ID 23, Class ID 0)
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 18}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 23}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_name.expect
deleted file mode 100644 (file)
index 86c025c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: gascon
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[0 cycles, 0 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: port-daniel
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_no_name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_stream_no_name.expect
deleted file mode 100644 (file)
index 6e9e43d..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[0 cycles, 0 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: one
-  Trace:
-    Stream (ID 0, Class ID 0)
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/diff_trace_name.expect b/tests/data/plugins/flt.utils.muxer/succeed/diff_trace_name.expect
deleted file mode 100644 (file)
index 128ac95..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace `noranda`:
-    Stream (ID 0, Class ID 0)
-
-[0 cycles, 0 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace `rouyn`:
-    Stream (ID 0, Class ID 0)
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 0}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/multi-iter-ordering.expect b/tests/data/plugins/flt.utils.muxer/succeed/multi-iter-ordering.expect
new file mode 100644 (file)
index 0000000..bce10d4
--- /dev/null
@@ -0,0 +1,55 @@
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Stream beginning:
+  Trace `hello`:
+    Stream (ID 1, Class ID 0)
+
+[1 cycles, 1,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 1}
+Stream beginning:
+  Trace `meow`:
+    Stream (ID 1, Class ID 0)
+
+[3 cycles, 3,000,000,000 ns from origin]
+{Trace 2, Stream class ID 1, Stream ID 1}
+Stream beginning:
+  Trace `meow`:
+    Stream (ID 1, Class ID 1)
+
+[25 cycles, 25,000,000,000 ns from origin]
+{Trace 3, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Trace `hello`:
+    Stream (ID 0, Class ID 0)
+
+[25 cycles, 25,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Event `saumon atlantique` (Class ID 0):
+
+[25 cycles, 25,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 1}
+Packet beginning
+
+[25 cycles, 25,000,000,000 ns from origin]
+{Trace 2, Stream class ID 1, Stream ID 1}
+Event `bar rayé` (Class ID 0):
+
+[158 cycles, 158,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 1}
+Packet end
+
+[193 cycles, 193,000,000,000 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Stream end
+
+[386 cycles, 386,000,000,000 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 1}
+Stream end
+
+[579 cycles, 579,000,000,000 ns from origin]
+{Trace 3, Stream class ID 0, Stream ID 0}
+Stream end
+
+[772 cycles, 772,000,000,000 ns from origin]
+{Trace 2, Stream class ID 1, Stream ID 1}
+Stream end
diff --git a/tests/data/plugins/flt.utils.muxer/succeed/multi_iter_ordering.expect b/tests/data/plugins/flt.utils.muxer/succeed/multi_iter_ordering.expect
deleted file mode 100644 (file)
index bce10d4..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 1}
-Stream beginning:
-  Trace `hello`:
-    Stream (ID 1, Class ID 0)
-
-[1 cycles, 1,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 1}
-Stream beginning:
-  Trace `meow`:
-    Stream (ID 1, Class ID 0)
-
-[3 cycles, 3,000,000,000 ns from origin]
-{Trace 2, Stream class ID 1, Stream ID 1}
-Stream beginning:
-  Trace `meow`:
-    Stream (ID 1, Class ID 1)
-
-[25 cycles, 25,000,000,000 ns from origin]
-{Trace 3, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Trace `hello`:
-    Stream (ID 0, Class ID 0)
-
-[25 cycles, 25,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 1}
-Event `saumon atlantique` (Class ID 0):
-
-[25 cycles, 25,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 1}
-Packet beginning
-
-[25 cycles, 25,000,000,000 ns from origin]
-{Trace 2, Stream class ID 1, Stream ID 1}
-Event `bar rayé` (Class ID 0):
-
-[158 cycles, 158,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 1}
-Packet end
-
-[193 cycles, 193,000,000,000 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 1}
-Stream end
-
-[386 cycles, 386,000,000,000 ns from origin]
-{Trace 1, Stream class ID 0, Stream ID 1}
-Stream end
-
-[579 cycles, 579,000,000,000 ns from origin]
-{Trace 3, Stream class ID 0, Stream ID 0}
-Stream end
-
-[772 cycles, 772,000,000,000 ns from origin]
-{Trace 2, Stream class ID 1, Stream ID 1}
-Stream end
diff --git a/tests/data/plugins/src.ctf.fs/field/bt_plugin_test_text.py b/tests/data/plugins/src.ctf.fs/field/bt_plugin_test_text.py
new file mode 100644 (file)
index 0000000..e177798
--- /dev/null
@@ -0,0 +1,154 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright (c) 2023 Olivier Dion <odion@efficios.com>
+# Copyright (c) 2024 Philippe Proulx <pproulx@efficios.com>
+
+import bt2
+
+_array_elem = object()
+
+
+# Recursively prints the contents of `field` with the indentation level
+# `indent` and the introduction `intro`.
+#
+# `intro` is one of:
+#
+# `None`:
+#     No introduction (root field).
+#
+# A string:
+#     Structure field member name.
+#
+# `_array_elem`:
+#     `field` is an array field element.
+def _print_field(intro, field, indent=0):
+    indent_str = " " * indent * 2
+    intro_str = ""
+
+    if intro is _array_elem:
+        intro_str = "- "
+    elif intro is not None:
+        intro_str = "{}: ".format(intro)
+
+    if isinstance(field, bt2._StringFieldConst):
+        print('{}{}"{}"'.format(indent_str, intro_str, field))
+    elif isinstance(field, bt2._StructureFieldConst):
+        print(indent_str + intro_str, end="")
+
+        if len(field) == 0:
+            # Special case for an empty structure field
+            print("{}")
+        else:
+            if intro is _array_elem:
+                # Structure field is an array field element itself:
+                # print the first element first, and then print the
+                # remaining ones indented.
+                #
+                # Example:
+                #
+                #     - meow: "mix"
+                #       montant: -23.599312
+                #       bateau: "jacques"
+                sub_field_names = list(field)
+                _print_field(sub_field_names[0], field[sub_field_names[0]], 0)
+
+                for sub_field_name in sub_field_names[1:]:
+                    _print_field(sub_field_name, field[sub_field_name], indent + 1)
+            else:
+                add_indent = 0
+
+                if intro is not None:
+                    # Structure field has a name (already printed at
+                    # this point): print a newline, and then print all
+                    # the members indented (one more level):
+                    #
+                    # Example:
+                    #
+                    #     struct_name:
+                    #       meow: "mix"
+                    #       montant: -23.599312
+                    #       bateau: "jacques"
+                    add_indent = 1
+                    print()
+
+                for sub_field_name in field:
+                    _print_field(
+                        sub_field_name,
+                        field[sub_field_name],
+                        indent + add_indent,
+                    )
+    elif isinstance(field, bt2._ArrayFieldConst):
+        add_indent = 0
+
+        if intro is not None:
+            # Array field has an intro: print it, then print a newline,
+            # and then print all the elements indented (one more level).
+            #
+            # Example 1 (parent is an structure field):
+            #
+            #     array_name:
+            #       - -17
+            #       - "salut"
+            #       - 23
+            #
+            # Example 2 (parent is an array field):
+            #
+            #     -
+            #       - -17
+            #       - "salut"
+            #       - 23
+            add_indent = 1
+            print(indent_str + intro_str.rstrip())
+
+        for sub_field in field:
+            _print_field(_array_elem, sub_field, indent + add_indent)
+    elif isinstance(field.cls, bt2._IntegerFieldClassConst):
+        # Honor the preferred display base
+        base = field.cls.preferred_display_base
+        print(indent_str + intro_str, end="")
+
+        if base == 10:
+            print(int(field))
+        elif base == 16:
+            print(hex(field))
+        elif base == 8:
+            print(oct(field))
+        elif base == 2:
+            print(bin(field))
+    elif isinstance(field, bt2._BitArrayFieldConst):
+        print(indent_str + intro_str + bin(field))
+    elif isinstance(field, bt2._BoolFieldConst):
+        print(indent_str + intro_str + ("yes" if field else "no"))
+    elif isinstance(field, bt2._RealFieldConst):
+        print("{}{}{:.6f}".format(indent_str, intro_str, float(field)))
+    elif isinstance(field, bt2._OptionFieldConst):
+        if field.has_field:
+            _print_field(intro, field.field, indent)
+        else:
+            # Special case for an option field without a field
+            print("{}{}~".format(indent_str, intro_str))
+    elif isinstance(field, bt2._VariantFieldConst):
+        _print_field(intro, field.selected_option, indent)
+    else:
+        print(indent_str + intro_str + field)
+
+
+@bt2.plugin_component_class
+class _SingleSinkComponent(bt2._UserSinkComponent, name="single"):
+    def __init__(self, config, params, obj):
+        self._input = self._add_input_port("input")
+        self._field_name = str(params.get("field-name", "root"))
+
+    def _user_graph_is_configured(self):
+        self._it = self._create_message_iterator(self._input)
+
+    def _user_consume(self):
+        msg = next(self._it)
+
+        if type(msg) is bt2._EventMessageConst:
+            assert self._field_name in msg.event.payload_field
+            assert len(msg.event.payload_field) == 1
+            _print_field(None, msg.event.payload_field[self._field_name])
+
+
+bt2.register_plugin(__name__, "test-text")
diff --git a/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-fixed-len-uint-32-be.mp b/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-fixed-len-uint-32-be.mp
new file mode 100644 (file)
index 0000000..4e72ded
--- /dev/null
@@ -0,0 +1,8 @@
+---
+u32be
+
+---
+[3187239923:32be]
+
+---
+3187239923
diff --git a/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-fixed-len-uint-32-le.mp b/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-fixed-len-uint-32-le.mp
new file mode 100644 (file)
index 0000000..53364d1
--- /dev/null
@@ -0,0 +1,8 @@
+---
+u32le
+
+---
+[3187239923:32le]
+
+---
+3187239923
diff --git a/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-static-len-array-of-struct.mp b/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-static-len-array-of-struct.mp
new file mode 100644 (file)
index 0000000..4ed8683
--- /dev/null
@@ -0,0 +1,42 @@
+---
+struct {
+  struct {
+    u8 a;
+    nt_str b;
+  } x[3];
+} @[2]
+
+---
+01              # `a`
+"salut\0"       # `b`
+
+02              # `a`
+"patente\0"     # `b`
+
+03              # `a`
+"Quebec\0"      # `b`
+
+04              # `a`
+"chez nous\0"   # `b`
+
+05              # `a`
+"aidez-moi\0"   # `b`
+
+06              # `a`
+"rasseye\0"     # `b`
+
+---
+- x:
+  - a: 1
+    b: "salut"
+  - a: 2
+    b: "patente"
+  - a: 3
+    b: "Quebec"
+- x:
+  - a: 4
+    b: "chez nous"
+  - a: 5
+    b: "aidez-moi"
+  - a: 6
+    b: "rasseye"
diff --git a/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-struct-empty.mp b/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-struct-empty.mp
new file mode 100644 (file)
index 0000000..2665098
--- /dev/null
@@ -0,0 +1,17 @@
+---
+struct {
+  u8 a;
+
+  struct {
+  } b;
+
+  u8 c;
+}
+---
+01  # `a`
+02  # `c`
+
+---
+a: 1
+b: {}
+c: 2
diff --git a/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-struct.mp b/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-struct.mp
new file mode 100644 (file)
index 0000000..07ad223
--- /dev/null
@@ -0,0 +1,16 @@
+---
+struct {
+  i16 meow;
+  nt_str mix;
+  flt32 cat;
+}
+
+---
+[-1717:16]        # `meow`
+"rapidement!\0"   # `mix`
+[2.897771955:32]  # `cat`
+
+---
+meow: -1717
+mix: "rapidement!"
+cat: 2.897772
diff --git a/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-variant.mp b/tests/data/plugins/src.ctf.fs/field/ctf-1/pass-variant.mp
new file mode 100644 (file)
index 0000000..3b01754
--- /dev/null
@@ -0,0 +1,24 @@
+---
+struct {
+  enum : u8 {
+    MEOW,
+    MIX,
+  } tag;
+
+  variant <tag> {
+    u16 MEOW;
+    nt_str MIX;
+  } var;
+} @[2]
+
+---
+00                # `tag`
+[1995:16]         # `var`
+
+01                # `tag`
+"hello there!\0"  # `var`
+---
+- tag: 0
+  var: 1995
+- tag: 1
+  var: "hello there!"
diff --git a/tests/data/plugins/src.ctf.fs/field/data_from_mp.py b/tests/data/plugins/src.ctf.fs/field/data_from_mp.py
new file mode 100644 (file)
index 0000000..2a1a170
--- /dev/null
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2023 EfficiOS Inc.
+#
+# pyright: strict, reportTypeCommentUsage=false
+
+import os
+import string
+import argparse
+
+import normand
+import moultipart
+
+
+def _make_ctf_1_metadata(payload_fc: str):
+    payload_fc = payload_fc.strip()
+
+    if "@" in payload_fc:
+        payload_fc = payload_fc.replace("@", "root")
+    else:
+        payload_fc += " root"
+
+    return string.Template(
+        """\
+/* CTF 1.8 */
+
+trace {
+    major = 1;
+    minor = 8;
+    byte_order = le;
+};
+
+typealias integer { size = 8; } := u8;
+typealias integer { size = 16; } := u16;
+typealias integer { size = 32; } := u32;
+typealias integer { size = 64; } := u64;
+typealias integer { size = 8; byte_order = le; } := u8le;
+typealias integer { size = 16; byte_order = le; } := u16le;
+typealias integer { size = 32; byte_order = le; } := u32le;
+typealias integer { size = 64; byte_order = le; } := u64le;
+typealias integer { size = 8; byte_order = be; } := u8be;
+typealias integer { size = 16; byte_order = be; } := u16be;
+typealias integer { size = 32; byte_order = be; } := u32be;
+typealias integer { size = 64; byte_order = be; } := u64be;
+typealias integer { signed = true; size = 8; } := i8;
+typealias integer { signed = true; size = 16; } := i16;
+typealias integer { signed = true; size = 32; } := i32;
+typealias integer { signed = true; size = 64; } := i64;
+typealias integer { signed = true; size = 8; byte_order = le; } := i8le;
+typealias integer { signed = true; size = 16; byte_order = le; } := i16le;
+typealias integer { signed = true; size = 32; byte_order = le; } := i32le;
+typealias integer { signed = true; size = 64; byte_order = le; } := i64le;
+typealias integer { signed = true; size = 8; byte_order = be; } := i8be;
+typealias integer { signed = true; size = 16; byte_order = be; } := i16be;
+typealias integer { signed = true; size = 32; byte_order = be; } := i32be;
+typealias integer { signed = true; size = 64; byte_order = be; } := i64be;
+typealias floating_point { exp_dig = 8; mant_dig = 24; } := flt32;
+typealias floating_point { exp_dig = 11; mant_dig = 53; } := flt64;
+typealias floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; } := flt32le;
+typealias floating_point { exp_dig = 11; mant_dig = 53; byte_order = le; } := flt64le;
+typealias floating_point { exp_dig = 8; mant_dig = 24; byte_order = be; } := flt32be;
+typealias floating_point { exp_dig = 11; mant_dig = 53; byte_order = be; } := flt64be;
+typealias string { encoding = UTF8; } := nt_str;
+
+event {
+    name = the_event;
+    fields := struct {
+        ${payload_fc};
+    };
+};
+"""
+    ).substitute(payload_fc=payload_fc)
+
+
+def _make_ctf_1_data(normand_text: str):
+    # Default to little-endian because that's also the TSDL default in
+    # _make_ctf_1_metadata() above.
+    return normand.parse("!le\n" + normand_text).data
+
+
+def _create_files_from_mp(mp_path: str, output_dir: str):
+    trace_dir = os.path.join(output_dir, "trace")
+    expect_path = os.path.join(output_dir, "expect")
+    metadata_path = os.path.join(trace_dir, "metadata")
+    data_path = os.path.join(trace_dir, "data")
+    os.makedirs(trace_dir, exist_ok=True)
+
+    with open(mp_path, "r") as f:
+        parts = moultipart.parse(f)
+
+    with open(metadata_path, "w") as f:
+        f.write(_make_ctf_1_metadata(parts[0].content))
+
+    with open(data_path, "wb") as f:
+        f.write(_make_ctf_1_data(parts[1].content))
+
+    with open(expect_path, "w") as f:
+        f.write(parts[2].content)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "mp_path", metavar="MP-PATH", help="moultipart document to process"
+    )
+    parser.add_argument(
+        "output_dir",
+        metavar="OUTPUT-DIR",
+        help="output directory for the CTF trace and expectation file",
+    )
+    args = parser.parse_args()
+    _create_files_from_mp(args.mp_path, args.output_dir)
diff --git a/tests/data/plugins/src.ctf.fs/query/metadata-info-crlf-metadata.expect b/tests/data/plugins/src.ctf.fs/query/metadata-info-crlf-metadata.expect
new file mode 100644 (file)
index 0000000..ad49502
--- /dev/null
@@ -0,0 +1,116 @@
+is-packetized: no
+text: /* CTF 1.8 */
+
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
+typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
+typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
+typealias integer { size = 64; align = 8; signed = false; } := unsigned long;
+typealias integer { size = 5; align = 1; signed = false; } := uint5_t;
+typealias integer { size = 27; align = 1; signed = false; } := uint27_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "ddb15f3f-a235-444e-9d1b-f131648b5bf1";
+       byte_order = le;
+       packet.header := struct {
+               uint32_t magic;
+               uint8_t  uuid[16];
+               uint32_t stream_id;
+               uint64_t stream_instance_id;
+       };
+};
+
+env {
+       domain = "ust";
+       tracer_name = "lttng-ust";
+       tracer_major = 2;
+       tracer_minor = 13;
+       tracer_buffering_scheme = "uid";
+       tracer_buffering_id = 1000;
+       architecture_bit_width = 64;
+       trace_name = "crlf-metadata";
+       trace_creation_datetime = "20231023T193151+0000";
+       hostname = "line-endings";
+};
+
+clock {
+       name = "monotonic";
+       uuid = "d0367630-5511-4855-a390-afe86f9b4545";
+       description = "Monotonic Clock";
+       freq = 1000000000; /* Frequency, in Hz */
+       /* clock value offset from Epoch is: offset * (1/freq) */
+       offset = 1698076473717549015;
+};
+
+typealias integer {
+       size = 27; align = 1; signed = false;
+       map = clock.monotonic.value;
+} := uint27_clock_monotonic_t;
+
+typealias integer {
+       size = 32; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint32_clock_monotonic_t;
+
+typealias integer {
+       size = 64; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint64_clock_monotonic_t;
+
+struct packet_context {
+       uint64_clock_monotonic_t timestamp_begin;
+       uint64_clock_monotonic_t timestamp_end;
+       uint64_t content_size;
+       uint64_t packet_size;
+       uint64_t packet_seq_num;
+       unsigned long events_discarded;
+       uint32_t cpu_id;
+};
+
+struct event_header_compact {
+       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
+       variant <id> {
+               struct {
+                       uint27_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+struct event_header_large {
+       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
+       variant <id> {
+               struct {
+                       uint32_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+stream {
+       id = 0;
+       event.header := struct event_header_large;
+       packet.context := struct packet_context;
+};
+
+event {
+       name = "lttng_ust_tracef:event";
+       id = 0;
+       stream_id = 0;
+       loglevel = 14;
+       fields := struct {
+               integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } __msg_length;
+               integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _msg[ __msg_length ];
+       };
+};
+
+
+
diff --git a/tests/data/plugins/src.ctf.fs/query/metadata-info-lf-metadata.expect b/tests/data/plugins/src.ctf.fs/query/metadata-info-lf-metadata.expect
new file mode 100644 (file)
index 0000000..d9237d6
--- /dev/null
@@ -0,0 +1,116 @@
+is-packetized: no
+text: /* CTF 1.8 */
+
+typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
+typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
+typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
+typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
+typealias integer { size = 64; align = 8; signed = false; } := unsigned long;
+typealias integer { size = 5; align = 1; signed = false; } := uint5_t;
+typealias integer { size = 27; align = 1; signed = false; } := uint27_t;
+
+trace {
+       major = 1;
+       minor = 8;
+       uuid = "8c609d38-ed7e-49c3-81df-33cf268062b7";
+       byte_order = le;
+       packet.header := struct {
+               uint32_t magic;
+               uint8_t  uuid[16];
+               uint32_t stream_id;
+               uint64_t stream_instance_id;
+       };
+};
+
+env {
+       domain = "ust";
+       tracer_name = "lttng-ust";
+       tracer_major = 2;
+       tracer_minor = 13;
+       tracer_buffering_scheme = "uid";
+       tracer_buffering_id = 1000;
+       architecture_bit_width = 64;
+       trace_name = "lf-metadata";
+       trace_creation_datetime = "20231023T185853+0000";
+       hostname = "line-endings";
+};
+
+clock {
+       name = "monotonic";
+       uuid = "d0367630-5511-4855-a390-afe86f9b4545";
+       description = "Monotonic Clock";
+       freq = 1000000000; /* Frequency, in Hz */
+       /* clock value offset from Epoch is: offset * (1/freq) */
+       offset = 1698076473717549018;
+};
+
+typealias integer {
+       size = 27; align = 1; signed = false;
+       map = clock.monotonic.value;
+} := uint27_clock_monotonic_t;
+
+typealias integer {
+       size = 32; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint32_clock_monotonic_t;
+
+typealias integer {
+       size = 64; align = 8; signed = false;
+       map = clock.monotonic.value;
+} := uint64_clock_monotonic_t;
+
+struct packet_context {
+       uint64_clock_monotonic_t timestamp_begin;
+       uint64_clock_monotonic_t timestamp_end;
+       uint64_t content_size;
+       uint64_t packet_size;
+       uint64_t packet_seq_num;
+       unsigned long events_discarded;
+       uint32_t cpu_id;
+};
+
+struct event_header_compact {
+       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
+       variant <id> {
+               struct {
+                       uint27_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+struct event_header_large {
+       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
+       variant <id> {
+               struct {
+                       uint32_clock_monotonic_t timestamp;
+               } compact;
+               struct {
+                       uint32_t id;
+                       uint64_clock_monotonic_t timestamp;
+               } extended;
+       } v;
+} align(8);
+
+stream {
+       id = 0;
+       event.header := struct event_header_large;
+       packet.context := struct packet_context;
+};
+
+event {
+       name = "lttng_ust_tracef:event";
+       id = 0;
+       stream_id = 0;
+       loglevel = 14;
+       fields := struct {
+               integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } __msg_length;
+               integer { size = 8; align = 8; signed = 1; encoding = UTF8; base = 10; } _msg[ __msg_length ];
+       };
+};
+
+
+
index 9c7db7cf55128335e99d264e4bf8d0736006c07c..5c534621a86ab058263a00a0136911b5cf59db2f 100644 (file)
@@ -237,8 +237,8 @@ Stream beginning:
       kernel_version: #46-Ubuntu SMP Thu Dec 6 14:45:28 UTC 2018
       sysname: Linux
       trace_buffering_scheme: global
-      trace_creation_datetime: 
-      trace_name: 
+      trace_creation_datetime:
+      trace_name:
       tracer_major: 2
       tracer_minor: 11
       tracer_name: lttng-modules
@@ -261,8 +261,8 @@ Stream beginning:
       kernel_version: #46-Ubuntu SMP Thu Dec 6 14:45:28 UTC 2018
       sysname: Linux
       trace_buffering_scheme: global
-      trace_creation_datetime: 
-      trace_name: 
+      trace_creation_datetime:
+      trace_name:
       tracer_major: 2
       tracer_minor: 11
       tracer_name: lttng-modules
@@ -285,8 +285,8 @@ Stream beginning:
       kernel_version: #46-Ubuntu SMP Thu Dec 6 14:45:28 UTC 2018
       sysname: Linux
       trace_buffering_scheme: global
-      trace_creation_datetime: 
-      trace_name: 
+      trace_creation_datetime:
+      trace_name:
       tracer_major: 2
       tracer_minor: 11
       tracer_name: lttng-modules
@@ -309,8 +309,8 @@ Stream beginning:
       kernel_version: #46-Ubuntu SMP Thu Dec 6 14:45:28 UTC 2018
       sysname: Linux
       trace_buffering_scheme: global
-      trace_creation_datetime: 
-      trace_name: 
+      trace_creation_datetime:
+      trace_name:
       tracer_major: 2
       tracer_minor: 11
       tracer_name: lttng-modules
diff --git a/tests/data/plugins/src.ctf.lttng-live/inactivity-discarded-packet.expect b/tests/data/plugins/src.ctf.lttng-live/inactivity-discarded-packet.expect
new file mode 100644 (file)
index 0000000..278818d
--- /dev/null
@@ -0,0 +1,67 @@
+Trace class:
+  Stream class (ID 0):
+    Supports packets: Yes
+    Packets have beginning default clock snapshot: Yes
+    Packets have end default clock snapshot: Yes
+    Supports discarded events: No
+    Supports discarded packets: Yes
+    Discarded packets have default clock snapshots: Yes
+    Default clock class:
+      Name: default
+      Frequency (Hz): 1,000,000,000
+      Precision (cycles): 0
+      Offset (s): 0
+      Offset (cycles): 0
+      Origin is Unix epoch: Yes
+    Event class `my-event` (ID 0):
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: stream-1
+  Trace:
+    UUID: 1c810767-575e-4c4e-afa1-5d3e15081cb9
+    Stream (ID 0, Class ID 0)
+
+[0 cycles, 0 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet beginning
+
+[10 cycles, 10 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `my-event` (Class ID 0):
+
+[20 cycles, 20 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet end
+
+[80 cycles, 80 ns from origin]
+Message iterator inactivity:
+  Clock class:
+    Name: default
+    Frequency (Hz): 1,000,000,000
+    Precision (cycles): 0
+    Offset (s): 0
+    Offset (cycles): 0
+    Origin is Unix epoch: Yes
+
+[80 cycles, 80 ns from origin]
+[121 cycles, 121 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Discarded packets (7 packets)
+
+[121 cycles, 121 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet beginning
+
+[133 cycles, 133 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `my-event` (Class ID 0):
+
+[140 cycles, 140 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet end
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/src.ctf.lttng-live/inactivity-discarded-packet.json b/tests/data/plugins/src.ctf.lttng-live/inactivity-discarded-packet.json
new file mode 100644 (file)
index 0000000..bec5dc9
--- /dev/null
@@ -0,0 +1,17 @@
+[
+    {
+       "name": "7-lost-between-2-with-index",
+        "id": 0,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "packet-seq-num/7-lost-between-2-with-index/",
+                "beacons": {
+                    "stream_0": [ 80 ]
+                }
+            }
+        ]
+    }
+]
diff --git a/tests/data/plugins/src.ctf.lttng-live/inactivity_discarded_packet.expect b/tests/data/plugins/src.ctf.lttng-live/inactivity_discarded_packet.expect
deleted file mode 100644 (file)
index 278818d..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-Trace class:
-  Stream class (ID 0):
-    Supports packets: Yes
-    Packets have beginning default clock snapshot: Yes
-    Packets have end default clock snapshot: Yes
-    Supports discarded events: No
-    Supports discarded packets: Yes
-    Discarded packets have default clock snapshots: Yes
-    Default clock class:
-      Name: default
-      Frequency (Hz): 1,000,000,000
-      Precision (cycles): 0
-      Offset (s): 0
-      Offset (cycles): 0
-      Origin is Unix epoch: Yes
-    Event class `my-event` (ID 0):
-
-[Unknown]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: stream-1
-  Trace:
-    UUID: 1c810767-575e-4c4e-afa1-5d3e15081cb9
-    Stream (ID 0, Class ID 0)
-
-[0 cycles, 0 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Packet beginning
-
-[10 cycles, 10 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Event `my-event` (Class ID 0):
-
-[20 cycles, 20 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Packet end
-
-[80 cycles, 80 ns from origin]
-Message iterator inactivity:
-  Clock class:
-    Name: default
-    Frequency (Hz): 1,000,000,000
-    Precision (cycles): 0
-    Offset (s): 0
-    Offset (cycles): 0
-    Origin is Unix epoch: Yes
-
-[80 cycles, 80 ns from origin]
-[121 cycles, 121 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Discarded packets (7 packets)
-
-[121 cycles, 121 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Packet beginning
-
-[133 cycles, 133 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Event `my-event` (Class ID 0):
-
-[140 cycles, 140 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Packet end
-
-[Unknown]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/src.ctf.lttng-live/inactivity_discarded_packet.json b/tests/data/plugins/src.ctf.lttng-live/inactivity_discarded_packet.json
deleted file mode 100644 (file)
index 47f791e..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-[
-    {
-       "name": "7_lost_between_2_with_index",
-        "id": 0,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "packet_seq_num/7_lost_between_2_with_index/",
-                "beacons": {
-                    "stream_0": [ 80 ]
-                }
-            }
-        ]
-    }
-]
diff --git a/tests/data/plugins/src.ctf.lttng-live/list-sessions.json b/tests/data/plugins/src.ctf.lttng-live/list-sessions.json
new file mode 100644 (file)
index 0000000..0c0091c
--- /dev/null
@@ -0,0 +1,38 @@
+[
+    {
+        "name": "multi-domains",
+        "id": 0,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "succeed/multi-domains/ust/"
+            }
+        ]
+    },
+    {
+        "name": "multi-domains",
+        "id": 1,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "succeed/multi-domains/kernel/"
+            }
+        ]
+    },
+    {
+       "name": "trace-with-index",
+        "id": 2,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "succeed/trace-with-index/"
+            }
+        ]
+    }
+]
diff --git a/tests/data/plugins/src.ctf.lttng-live/list_sessions.json b/tests/data/plugins/src.ctf.lttng-live/list_sessions.json
deleted file mode 100644 (file)
index 0c0091c..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-[
-    {
-        "name": "multi-domains",
-        "id": 0,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "succeed/multi-domains/ust/"
-            }
-        ]
-    },
-    {
-        "name": "multi-domains",
-        "id": 1,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "succeed/multi-domains/kernel/"
-            }
-        ]
-    },
-    {
-       "name": "trace-with-index",
-        "id": 2,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "succeed/trace-with-index/"
-            }
-        ]
-    }
-]
index d0eb26d25c5a343b37a50574d223fab0a9a7c369..e11011379c91fea4125c9c448c1465673c487463 100644 (file)
 # Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
 #
 
-import argparse
-import collections.abc
-import logging
+# pyright: strict, reportTypeCommentUsage=false,  reportMissingTypeStubs=false
+
 import os
-import os.path
 import re
+import time
 import socket
 import struct
-import sys
+import logging
+import os.path
+import argparse
 import tempfile
-import json
-from collections import namedtuple
+from abc import ABC, abstractmethod
+from typing import Dict, Union, Iterable, Optional, Sequence, overload
 
+import tjson
 
-class UnexpectedInput(RuntimeError):
-    pass
+# isort: off
+from typing import Any, Callable  # noqa: F401
+
+# isort: on
+
+
+# An entry within the index of an LTTng data stream.
+class _LttngDataStreamIndexEntry:
+    def __init__(
+        self,
+        offset_bytes: int,
+        total_size_bits: int,
+        content_size_bits: int,
+        timestamp_begin: int,
+        timestamp_end: int,
+        events_discarded: int,
+        stream_class_id: int,
+    ):
+        self._offset_bytes = offset_bytes
+        self._total_size_bits = total_size_bits
+        self._content_size_bits = content_size_bits
+        self._timestamp_begin = timestamp_begin
+        self._timestamp_end = timestamp_end
+        self._events_discarded = events_discarded
+        self._stream_class_id = stream_class_id
+
+    @property
+    def offset_bytes(self):
+        return self._offset_bytes
+
+    @property
+    def total_size_bits(self):
+        return self._total_size_bits
+
+    @property
+    def total_size_bytes(self):
+        return self._total_size_bits // 8
+
+    @property
+    def content_size_bits(self):
+        return self._content_size_bits
+
+    @property
+    def content_size_bytes(self):
+        return self._content_size_bits // 8
+
+    @property
+    def timestamp_begin(self):
+        return self._timestamp_begin
+
+    @property
+    def timestamp_end(self):
+        return self._timestamp_end
+
+    @property
+    def events_discarded(self):
+        return self._events_discarded
+
+    @property
+    def stream_class_id(self):
+        return self._stream_class_id
+
+
+# An entry within the index of an LTTng data stream. While a stream beacon entry
+# is conceptually unrelated to an index, it is sent as a reply to a
+# LttngLiveViewerGetNextDataStreamIndexEntryCommand
+class _LttngDataStreamBeaconIndexEntry:
+    def __init__(self, stream_class_id: int, timestamp: int):
+        self._stream_class_id = stream_class_id
+        self._timestamp = timestamp
+
+    @property
+    def timestamp(self):
+        return self._timestamp
+
+    @property
+    def stream_class_id(self):
+        return self._stream_class_id
+
+
+_LttngIndexEntryT = Union[_LttngDataStreamIndexEntry, _LttngDataStreamBeaconIndexEntry]
 
 
 class _LttngLiveViewerCommand:
-    def __init__(self, version):
+    def __init__(self, version: int):
         self._version = version
 
     @property
@@ -31,7 +112,7 @@ class _LttngLiveViewerCommand:
 
 
 class _LttngLiveViewerConnectCommand(_LttngLiveViewerCommand):
-    def __init__(self, version, viewer_session_id, major, minor):
+    def __init__(self, version: int, viewer_session_id: int, major: int, minor: int):
         super().__init__(version)
         self._viewer_session_id = viewer_session_id
         self._major = major
@@ -50,8 +131,12 @@ class _LttngLiveViewerConnectCommand(_LttngLiveViewerCommand):
         return self._minor
 
 
-class _LttngLiveViewerConnectReply:
-    def __init__(self, viewer_session_id, major, minor):
+class _LttngLiveViewerReply:
+    pass
+
+
+class _LttngLiveViewerConnectReply(_LttngLiveViewerReply):
+    def __init__(self, viewer_session_id: int, major: int, minor: int):
         self._viewer_session_id = viewer_session_id
         self._major = major
         self._minor = minor
@@ -76,12 +161,12 @@ class _LttngLiveViewerGetTracingSessionInfosCommand(_LttngLiveViewerCommand):
 class _LttngLiveViewerTracingSessionInfo:
     def __init__(
         self,
-        tracing_session_id,
-        live_timer_freq,
-        client_count,
-        stream_count,
-        hostname,
-        name,
+        tracing_session_id: int,
+        live_timer_freq: int,
+        client_count: int,
+        stream_count: int,
+        hostname: str,
+        name: str,
     ):
         self._tracing_session_id = tracing_session_id
         self._live_timer_freq = live_timer_freq
@@ -115,8 +200,10 @@ class _LttngLiveViewerTracingSessionInfo:
         return self._name
 
 
-class _LttngLiveViewerGetTracingSessionInfosReply:
-    def __init__(self, tracing_session_infos):
+class _LttngLiveViewerGetTracingSessionInfosReply(_LttngLiveViewerReply):
+    def __init__(
+        self, tracing_session_infos: Sequence[_LttngLiveViewerTracingSessionInfo]
+    ):
         self._tracing_session_infos = tracing_session_infos
 
     @property
@@ -129,7 +216,9 @@ class _LttngLiveViewerAttachToTracingSessionCommand(_LttngLiveViewerCommand):
         BEGINNING = 1
         LAST = 2
 
-    def __init__(self, version, tracing_session_id, offset, seek_type):
+    def __init__(
+        self, version: int, tracing_session_id: int, offset: int, seek_type: int
+    ):
         super().__init__(version)
         self._tracing_session_id = tracing_session_id
         self._offset = offset
@@ -149,7 +238,9 @@ class _LttngLiveViewerAttachToTracingSessionCommand(_LttngLiveViewerCommand):
 
 
 class _LttngLiveViewerStreamInfo:
-    def __init__(self, id, trace_id, is_metadata, path, channel_name):
+    def __init__(
+        self, id: int, trace_id: int, is_metadata: bool, path: str, channel_name: str
+    ):
         self._id = id
         self._trace_id = trace_id
         self._is_metadata = is_metadata
@@ -177,7 +268,7 @@ class _LttngLiveViewerStreamInfo:
         return self._channel_name
 
 
-class _LttngLiveViewerAttachToTracingSessionReply:
+class _LttngLiveViewerAttachToTracingSessionReply(_LttngLiveViewerReply):
     class Status:
         OK = 1
         ALREADY = 2
@@ -186,7 +277,7 @@ class _LttngLiveViewerAttachToTracingSessionReply:
         SEEK_ERROR = 5
         NO_SESSION = 6
 
-    def __init__(self, status, stream_infos):
+    def __init__(self, status: int, stream_infos: Sequence[_LttngLiveViewerStreamInfo]):
         self._status = status
         self._stream_infos = stream_infos
 
@@ -200,7 +291,7 @@ class _LttngLiveViewerAttachToTracingSessionReply:
 
 
 class _LttngLiveViewerGetNextDataStreamIndexEntryCommand(_LttngLiveViewerCommand):
-    def __init__(self, version, stream_id):
+    def __init__(self, version: int, stream_id: int):
         super().__init__(version)
         self._stream_id = stream_id
 
@@ -209,7 +300,7 @@ class _LttngLiveViewerGetNextDataStreamIndexEntryCommand(_LttngLiveViewerCommand
         return self._stream_id
 
 
-class _LttngLiveViewerGetNextDataStreamIndexEntryReply:
+class _LttngLiveViewerGetNextDataStreamIndexEntryReply(_LttngLiveViewerReply):
     class Status:
         OK = 1
         RETRY = 2
@@ -218,7 +309,13 @@ class _LttngLiveViewerGetNextDataStreamIndexEntryReply:
         INACTIVE = 5
         EOF = 6
 
-    def __init__(self, status, index_entry, has_new_metadata, has_new_data_stream):
+    def __init__(
+        self,
+        status: int,
+        index_entry: _LttngIndexEntryT,
+        has_new_metadata: bool,
+        has_new_data_stream: bool,
+    ):
         self._status = status
         self._index_entry = index_entry
         self._has_new_metadata = has_new_metadata
@@ -242,7 +339,7 @@ class _LttngLiveViewerGetNextDataStreamIndexEntryReply:
 
 
 class _LttngLiveViewerGetDataStreamPacketDataCommand(_LttngLiveViewerCommand):
-    def __init__(self, version, stream_id, offset, req_length):
+    def __init__(self, version: int, stream_id: int, offset: int, req_length: int):
         super().__init__(version)
         self._stream_id = stream_id
         self._offset = offset
@@ -261,14 +358,20 @@ class _LttngLiveViewerGetDataStreamPacketDataCommand(_LttngLiveViewerCommand):
         return self._req_length
 
 
-class _LttngLiveViewerGetDataStreamPacketDataReply:
+class _LttngLiveViewerGetDataStreamPacketDataReply(_LttngLiveViewerReply):
     class Status:
         OK = 1
         RETRY = 2
         ERROR = 3
         EOF = 4
 
-    def __init__(self, status, data, has_new_metadata, has_new_data_stream):
+    def __init__(
+        self,
+        status: int,
+        data: bytes,
+        has_new_metadata: bool,
+        has_new_data_stream: bool,
+    ):
         self._status = status
         self._data = data
         self._has_new_metadata = has_new_metadata
@@ -292,7 +395,7 @@ class _LttngLiveViewerGetDataStreamPacketDataReply:
 
 
 class _LttngLiveViewerGetMetadataStreamDataCommand(_LttngLiveViewerCommand):
-    def __init__(self, version, stream_id):
+    def __init__(self, version: int, stream_id: int):
         super().__init__(version)
         self._stream_id = stream_id
 
@@ -301,13 +404,13 @@ class _LttngLiveViewerGetMetadataStreamDataCommand(_LttngLiveViewerCommand):
         return self._stream_id
 
 
-class _LttngLiveViewerGetMetadataStreamDataContentReply:
+class _LttngLiveViewerGetMetadataStreamDataContentReply(_LttngLiveViewerReply):
     class Status:
         OK = 1
         NO_NEW = 2
         ERROR = 3
 
-    def __init__(self, status, data):
+    def __init__(self, status: int, data: bytes):
         self._status = status
         self._data = data
 
@@ -321,7 +424,7 @@ class _LttngLiveViewerGetMetadataStreamDataContentReply:
 
 
 class _LttngLiveViewerGetNewStreamInfosCommand(_LttngLiveViewerCommand):
-    def __init__(self, version, tracing_session_id):
+    def __init__(self, version: int, tracing_session_id: int):
         super().__init__(version)
         self._tracing_session_id = tracing_session_id
 
@@ -330,14 +433,14 @@ class _LttngLiveViewerGetNewStreamInfosCommand(_LttngLiveViewerCommand):
         return self._tracing_session_id
 
 
-class _LttngLiveViewerGetNewStreamInfosReply:
+class _LttngLiveViewerGetNewStreamInfosReply(_LttngLiveViewerReply):
     class Status:
         OK = 1
         NO_NEW = 2
         ERROR = 3
         HUP = 4
 
-    def __init__(self, status, stream_infos):
+    def __init__(self, status: int, stream_infos: Sequence[_LttngLiveViewerStreamInfo]):
         self._status = status
         self._stream_infos = stream_infos
 
@@ -354,12 +457,12 @@ class _LttngLiveViewerCreateViewerSessionCommand(_LttngLiveViewerCommand):
     pass
 
 
-class _LttngLiveViewerCreateViewerSessionReply:
+class _LttngLiveViewerCreateViewerSessionReply(_LttngLiveViewerReply):
     class Status:
         OK = 1
         ERROR = 2
 
-    def __init__(self, status):
+    def __init__(self, status: int):
         self._status = status
 
     @property
@@ -368,7 +471,7 @@ class _LttngLiveViewerCreateViewerSessionReply:
 
 
 class _LttngLiveViewerDetachFromTracingSessionCommand(_LttngLiveViewerCommand):
-    def __init__(self, version, tracing_session_id):
+    def __init__(self, version: int, tracing_session_id: int):
         super().__init__(version)
         self._tracing_session_id = tracing_session_id
 
@@ -377,13 +480,13 @@ class _LttngLiveViewerDetachFromTracingSessionCommand(_LttngLiveViewerCommand):
         return self._tracing_session_id
 
 
-class _LttngLiveViewerDetachFromTracingSessionReply:
+class _LttngLiveViewerDetachFromTracingSessionReply(_LttngLiveViewerReply):
     class Status:
         OK = 1
         UNKNOWN = 2
         ERROR = 3
 
-    def __init__(self, status):
+    def __init__(self, status: int):
         self._status = status
 
     @property
@@ -400,16 +503,16 @@ class _LttngLiveViewerProtocolCodec:
     def __init__(self):
         pass
 
-    def _unpack(self, fmt, data, offset=0):
+    def _unpack(self, fmt: str, data: bytes, offset: int = 0):
         fmt = "!" + fmt
         return struct.unpack_from(fmt, data, offset)
 
-    def _unpack_payload(self, fmt, data):
+    def _unpack_payload(self, fmt: str, data: bytes):
         return self._unpack(
             fmt, data, _LttngLiveViewerProtocolCodec._COMMAND_HEADER_SIZE_BYTES
         )
 
-    def decode(self, data):
+    def decode(self, data: bytes):
         if len(data) < self._COMMAND_HEADER_SIZE_BYTES:
             # Not enough data to read the command header
             return
@@ -428,9 +531,7 @@ class _LttngLiveViewerProtocolCodec:
             return
 
         if cmd_type == 1:
-            viewer_session_id, major, minor, conn_type = self._unpack_payload(
-                "QIII", data
-            )
+            viewer_session_id, major, minor, _ = self._unpack_payload("QIII", data)
             return _LttngLiveViewerConnectCommand(
                 version, viewer_session_id, major, minor
             )
@@ -465,23 +566,25 @@ class _LttngLiveViewerProtocolCodec:
                 version, tracing_session_id
             )
         else:
-            raise UnexpectedInput("Unknown command type {}".format(cmd_type))
+            raise RuntimeError("Unknown command type {}".format(cmd_type))
 
-    def _pack(self, fmt, *args):
+    def _pack(self, fmt: str, *args: Any):
         # Force network byte order
         return struct.pack("!" + fmt, *args)
 
-    def _encode_zero_padded_str(self, string, length):
+    def _encode_zero_padded_str(self, string: str, length: int):
         data = string.encode()
         return data.ljust(length, b"\x00")
 
-    def _encode_stream_info(self, info):
+    def _encode_stream_info(self, info: _LttngLiveViewerStreamInfo):
         data = self._pack("QQI", info.id, info.trace_id, int(info.is_metadata))
         data += self._encode_zero_padded_str(info.path, 4096)
         data += self._encode_zero_padded_str(info.channel_name, 255)
         return data
 
-    def _get_has_new_stuff_flags(self, has_new_metadata, has_new_data_streams):
+    def _get_has_new_stuff_flags(
+        self, has_new_metadata: bool, has_new_data_streams: bool
+    ):
         flags = 0
 
         if has_new_metadata:
@@ -492,7 +595,10 @@ class _LttngLiveViewerProtocolCodec:
 
         return flags
 
-    def encode(self, reply):
+    def encode(
+        self,
+        reply: _LttngLiveViewerReply,
+    ) -> bytes:
         if type(reply) is _LttngLiveViewerConnectReply:
             data = self._pack(
                 "QIII", reply.viewer_session_id, reply.major, reply.minor, 2
@@ -522,7 +628,7 @@ class _LttngLiveViewerProtocolCodec:
                 reply.has_new_metadata, reply.has_new_data_stream
             )
 
-            if type(entry) is _LttngDataStreamIndexEntry:
+            if isinstance(entry, _LttngDataStreamIndexEntry):
                 data = self._pack(
                     index_format,
                     entry.offset_bytes,
@@ -536,7 +642,6 @@ class _LttngLiveViewerProtocolCodec:
                     flags,
                 )
             else:
-                assert type(entry) is _LttngDataStreamBeaconEntry
                 data = self._pack(
                     index_format,
                     0,
@@ -575,100 +680,31 @@ class _LttngLiveViewerProtocolCodec:
         return data
 
 
-# An entry within the index of an LTTng data stream.
-class _LttngDataStreamIndexEntry:
-    def __init__(
-        self,
-        offset_bytes,
-        total_size_bits,
-        content_size_bits,
-        timestamp_begin,
-        timestamp_end,
-        events_discarded,
-        stream_class_id,
-    ):
-        self._offset_bytes = offset_bytes
-        self._total_size_bits = total_size_bits
-        self._content_size_bits = content_size_bits
-        self._timestamp_begin = timestamp_begin
-        self._timestamp_end = timestamp_end
-        self._events_discarded = events_discarded
-        self._stream_class_id = stream_class_id
-
-    @property
-    def offset_bytes(self):
-        return self._offset_bytes
-
-    @property
-    def total_size_bits(self):
-        return self._total_size_bits
-
-    @property
-    def total_size_bytes(self):
-        return self._total_size_bits // 8
-
-    @property
-    def content_size_bits(self):
-        return self._content_size_bits
-
-    @property
-    def content_size_bytes(self):
-        return self._content_size_bits // 8
-
-    @property
-    def timestamp_begin(self):
-        return self._timestamp_begin
-
-    @property
-    def timestamp_end(self):
-        return self._timestamp_end
-
-    @property
-    def events_discarded(self):
-        return self._events_discarded
-
-    @property
-    def stream_class_id(self):
-        return self._stream_class_id
-
-
-# An entry within the index of an LTTng data stream. While a stream beacon entry
-# is conceptually unrelated to an index, it is sent as a reply to a
-# LttngLiveViewerGetNextDataStreamIndexEntryCommand
-class _LttngDataStreamBeaconEntry:
-    def __init__(self, stream_class_id, timestamp):
-        self._stream_class_id = stream_class_id
-        self._timestamp = timestamp
-
-    @property
-    def timestamp(self):
-        return self._timestamp
-
-    @property
-    def stream_class_id(self):
-        return self._stream_class_id
-
-
-def _get_entry_timestamp_begin(entry):
-    if type(entry) is _LttngDataStreamBeaconEntry:
+def _get_entry_timestamp_begin(
+    entry: _LttngIndexEntryT,
+):
+    if isinstance(entry, _LttngDataStreamBeaconIndexEntry):
         return entry.timestamp
     else:
-        assert type(entry) is _LttngDataStreamIndexEntry
         return entry.timestamp_begin
 
 
 # The index of an LTTng data stream, a sequence of index entries.
-class _LttngDataStreamIndex(collections.abc.Sequence):
-    def __init__(self, path, beacons):
+class _LttngDataStreamIndex(Sequence[_LttngIndexEntryT]):
+    def __init__(self, path: str, beacons: Optional[tjson.ArrayVal]):
         self._path = path
         self._build()
 
         if beacons:
             stream_class_id = self._entries[0].stream_class_id
-            beacons = [
-                _LttngDataStreamBeaconEntry(stream_class_id, ts) for ts in beacons
-            ]
-            self._add_beacons(beacons)
+
+            beacons_list = []  # type: list[_LttngDataStreamBeaconIndexEntry]
+            for ts in beacons.iter(tjson.IntVal):
+                beacons_list.append(
+                    _LttngDataStreamBeaconIndexEntry(stream_class_id, ts.val)
+                )
+
+            self._add_beacons(beacons_list)
 
         logging.info(
             'Built data stream index entries: path="{}", count={}'.format(
@@ -677,8 +713,7 @@ class _LttngDataStreamIndex(collections.abc.Sequence):
         )
 
     def _build(self):
-        self._entries = []
-        assert os.path.isfile(self._path)
+        self._entries = []  # type: list[_LttngIndexEntryT]
 
         with open(self._path, "rb") as f:
             # Read header first
@@ -686,9 +721,7 @@ class _LttngDataStreamIndex(collections.abc.Sequence):
             size = struct.calcsize(fmt)
             data = f.read(size)
             assert len(data) == size
-            magic, index_major, index_minor, index_entry_length = struct.unpack(
-                fmt, data
-            )
+            magic, _, _, index_entry_length = struct.unpack(fmt, data)
             assert magic == 0xC1F1DCC1
 
             # Read index entries
@@ -733,10 +766,12 @@ class _LttngDataStreamIndex(collections.abc.Sequence):
                 # Skip anything else before the next entry
                 f.seek(index_entry_length - size, os.SEEK_CUR)
 
-    def _add_beacons(self, beacons):
+    def _add_beacons(self, beacons: Iterable[_LttngDataStreamBeaconIndexEntry]):
         # Assumes entries[n + 1].timestamp_end >= entries[n].timestamp_begin
-        def sort_key(entry):
-            if type(entry) is _LttngDataStreamBeaconEntry:
+        def sort_key(
+            entry: Union[_LttngDataStreamIndexEntry, _LttngDataStreamBeaconIndexEntry],
+        ) -> int:
+            if isinstance(entry, _LttngDataStreamBeaconIndexEntry):
                 return entry.timestamp
             else:
                 return entry.timestamp_end
@@ -744,7 +779,17 @@ class _LttngDataStreamIndex(collections.abc.Sequence):
         self._entries += beacons
         self._entries.sort(key=sort_key)
 
-    def __getitem__(self, index):
+    @overload
+    def __getitem__(self, index: int) -> _LttngIndexEntryT:
+        ...
+
+    @overload
+    def __getitem__(self, index: slice) -> Sequence[_LttngIndexEntryT]:  # noqa: F811
+        ...
+
+    def __getitem__(  # noqa: F811
+        self, index: Union[int, slice]
+    ) -> Union[_LttngIndexEntryT, Sequence[_LttngIndexEntryT],]:
         return self._entries[index]
 
     def __len__(self):
@@ -755,16 +800,35 @@ class _LttngDataStreamIndex(collections.abc.Sequence):
         return self._path
 
 
+# Any LTTng stream (metadata or data).
+class _LttngStream(ABC):
+    @abstractmethod
+    def __init__(self, creation_timestamp: int):
+        self._creation_timestamp: int = creation_timestamp
+
+    @property
+    def creation_timestamp(self):
+        return self._creation_timestamp
+
+
 # An LTTng data stream.
-class _LttngDataStream:
-    def __init__(self, path, beacons):
+class _LttngDataStream(_LttngStream):
+    def __init__(
+        self, path: str, beacons_json: Optional[tjson.ArrayVal], creation_timestamp: int
+    ):
+        super().__init__(creation_timestamp)
         self._path = path
         filename = os.path.basename(path)
         match = re.match(r"(.*)_\d+", filename)
+        if not match:
+            raise RuntimeError(
+                "Unexpected data stream file name pattern: {}".format(filename)
+            )
+
         self._channel_name = match.group(1)
         trace_dir = os.path.dirname(path)
         index_path = os.path.join(trace_dir, "index", filename + ".idx")
-        self._index = _LttngDataStreamIndex(index_path, beacons)
+        self._index = _LttngDataStreamIndex(index_path, beacons_json)
         assert os.path.isfile(path)
         self._file = open(path, "rb")
         logging.info(
@@ -785,13 +849,13 @@ class _LttngDataStream:
     def index(self):
         return self._index
 
-    def get_data(self, offset_bytes, len_bytes):
+    def get_data(self, offset_bytes: int, len_bytes: int):
         self._file.seek(offset_bytes)
         return self._file.read(len_bytes)
 
 
 class _LttngMetadataStreamSection:
-    def __init__(self, timestamp, data):
+    def __init__(self, timestamp: int, data: Optional[bytes]):
         self._timestamp = timestamp
         if data is None:
             self._data = bytes()
@@ -813,8 +877,14 @@ class _LttngMetadataStreamSection:
 
 
 # An LTTng metadata stream.
-class _LttngMetadataStream:
-    def __init__(self, metadata_file_path, config_sections):
+class _LttngMetadataStream(_LttngStream):
+    def __init__(
+        self,
+        metadata_file_path: str,
+        config_sections: Sequence[_LttngMetadataStreamSection],
+        creation_timestamp: int,
+    ):
+        super().__init__(creation_timestamp)
         self._path = metadata_file_path
         self._sections = config_sections
         logging.info(
@@ -832,55 +902,73 @@ class _LttngMetadataStream:
         return self._sections
 
 
-LttngMetadataConfigSection = namedtuple(
-    "LttngMetadataConfigSection", ["line", "timestamp", "is_empty"]
-)
+class LttngMetadataConfigSection:
+    def __init__(self, line: int, timestamp: int, is_empty: bool):
+        self._line = line
+        self._timestamp = timestamp
+        self._is_empty = is_empty
+
+    @property
+    def line(self):
+        return self._line
+
+    @property
+    def timestamp(self):
+        return self._timestamp
+
+    @property
+    def is_empty(self):
+        return self._is_empty
 
 
-def _parse_metadata_sections_config(config_sections):
-    assert config_sections is not None
-    config_metadata_sections = []
+def _parse_metadata_sections_config(metadata_sections_json: tjson.ArrayVal):
+    metadata_sections = []  # type: list[LttngMetadataConfigSection]
     append_empty_section = False
     last_timestamp = 0
     last_line = 0
 
-    for config_section in config_sections:
-        if config_section == "empty":
-            # Found a empty section marker. Actually append the section at the
-            # timestamp of the next concrete section.
-            append_empty_section = True
-        else:
-            assert type(config_section) is dict
-            line = config_section.get("line")
-            ts = config_section.get("timestamp")
+    for section in metadata_sections_json:
+        if isinstance(section, tjson.StrVal):
+            if section.val == "empty":
+                # Found an empty section marker. Actually append the
+                # section at the timestamp of the next concrete section.
+                append_empty_section = True
+            else:
+                raise ValueError("Invalid string value at {}.".format(section.path))
+        elif isinstance(section, tjson.ObjVal):
+            line = section.at("line", tjson.IntVal).val
+            ts = section.at("timestamp", tjson.IntVal).val
 
             # Sections' timestamps and lines must both be increasing.
             assert ts > last_timestamp
             last_timestamp = ts
+
             assert line > last_line
             last_line = line
 
             if append_empty_section:
-                config_metadata_sections.append(
-                    LttngMetadataConfigSection(line, ts, True)
-                )
+                metadata_sections.append(LttngMetadataConfigSection(line, ts, True))
                 append_empty_section = False
 
-            config_metadata_sections.append(LttngMetadataConfigSection(line, ts, False))
-
-    return config_metadata_sections
+            metadata_sections.append(LttngMetadataConfigSection(line, ts, False))
+        else:
+            raise TypeError(
+                "`{}`: expecting a string or object value".format(section.path)
+            )
 
+    return metadata_sections
 
-def _split_metadata_sections(metadata_file_path, raw_config_sections):
-    assert isinstance(raw_config_sections, collections.abc.Sequence)
 
-    parsed_sections = _parse_metadata_sections_config(raw_config_sections)
+def _split_metadata_sections(
+    metadata_file_path: str, metadata_sections_json: tjson.ArrayVal
+):
+    metadata_sections = _parse_metadata_sections_config(metadata_sections_json)
 
-    sections = []
+    sections = []  # type: list[_LttngMetadataStreamSection]
     with open(metadata_file_path, "r") as metadata_file:
         metadata_lines = [line for line in metadata_file]
 
-    config_metadata_sections_idx = 0
+    metadata_section_idx = 0
     curr_metadata_section = bytearray()
 
     for idx, line_content in enumerate(metadata_lines):
@@ -890,41 +978,38 @@ def _split_metadata_sections(metadata_file_path, raw_config_sections):
         curr_line_number = idx + 1
 
         # If there are no more sections, simply append the line.
-        if config_metadata_sections_idx + 1 >= len(parsed_sections):
+        if metadata_section_idx + 1 >= len(metadata_sections):
             curr_metadata_section += bytearray(line_content, "utf8")
             continue
 
-        next_section_line_number = parsed_sections[
-            config_metadata_sections_idx + 1
-        ].line
+        next_section_line_number = metadata_sections[metadata_section_idx + 1].line
 
         # If the next section begins at the current line, create a
         # section with the metadata we gathered so far.
         if curr_line_number >= next_section_line_number:
-
             # Flushing the metadata of the current section.
             sections.append(
                 _LttngMetadataStreamSection(
-                    parsed_sections[config_metadata_sections_idx].timestamp,
+                    metadata_sections[metadata_section_idx].timestamp,
                     bytes(curr_metadata_section),
                 )
             )
 
             # Move to the next section.
-            config_metadata_sections_idx += 1
+            metadata_section_idx += 1
 
             # Clear old content and append current line for the next section.
             curr_metadata_section.clear()
             curr_metadata_section += bytearray(line_content, "utf8")
 
             # Append any empty sections.
-            while parsed_sections[config_metadata_sections_idx].is_empty:
+            while metadata_sections[metadata_section_idx].is_empty:
                 sections.append(
                     _LttngMetadataStreamSection(
-                        parsed_sections[config_metadata_sections_idx].timestamp, None
+                        metadata_sections[metadata_section_idx].timestamp, None
                     )
                 )
-                config_metadata_sections_idx += 1
+                metadata_section_idx += 1
         else:
             # Append line_content to the current metadata section.
             curr_metadata_section += bytearray(line_content, "utf8")
@@ -932,7 +1017,7 @@ def _split_metadata_sections(metadata_file_path, raw_config_sections):
     # We iterated over all the lines of the metadata file. Close the current section.
     sections.append(
         _LttngMetadataStreamSection(
-            parsed_sections[config_metadata_sections_idx].timestamp,
+            metadata_sections[metadata_section_idx].timestamp,
             bytes(curr_metadata_section),
         )
     )
@@ -940,17 +1025,28 @@ def _split_metadata_sections(metadata_file_path, raw_config_sections):
     return sections
 
 
+_StreamBeaconsT = Dict[str, Iterable[int]]
+
+
 # An LTTng trace, a sequence of LTTng data streams.
-class LttngTrace(collections.abc.Sequence):
-    def __init__(self, trace_dir, metadata_sections, beacons):
-        assert os.path.isdir(trace_dir)
+class LttngTrace(Sequence[_LttngDataStream]):
+    def __init__(
+        self,
+        trace_dir: str,
+        metadata_sections_json: Optional[tjson.ArrayVal],
+        beacons_json: Optional[tjson.ObjVal],
+        creation_timestamp: int,
+    ):
         self._path = trace_dir
-        self._create_metadata_stream(trace_dir, metadata_sections)
-        self._create_data_streams(trace_dir, beacons)
+        self._creation_timestamp = creation_timestamp
+        self._create_metadata_stream(trace_dir, metadata_sections_json)
+        self._create_data_streams(trace_dir, beacons_json)
         logging.info('Built trace: path="{}"'.format(trace_dir))
 
-    def _create_data_streams(self, trace_dir, beacons):
-        data_stream_paths = []
+    def _create_data_streams(
+        self, trace_dir: str, beacons_json: Optional[tjson.ObjVal]
+    ):
+        data_stream_paths = []  # type: list[str]
 
         for filename in os.listdir(trace_dir):
             path = os.path.join(trace_dir, filename)
@@ -967,34 +1063,39 @@ class LttngTrace(collections.abc.Sequence):
             data_stream_paths.append(path)
 
         data_stream_paths.sort()
-        self._data_streams = []
+        self._data_streams = []  # type: list[_LttngDataStream]
 
         for data_stream_path in data_stream_paths:
             stream_name = os.path.basename(data_stream_path)
-            this_stream_beacons = None
-
-            if beacons is not None and stream_name in beacons:
-                this_stream_beacons = beacons[stream_name]
+            this_beacons_json = None
+            if beacons_json is not None and stream_name in beacons_json:
+                this_beacons_json = beacons_json.at(stream_name, tjson.ArrayVal)
 
             self._data_streams.append(
-                _LttngDataStream(data_stream_path, this_stream_beacons)
+                _LttngDataStream(
+                    data_stream_path, this_beacons_json, self._creation_timestamp
+                )
             )
 
-    def _create_metadata_stream(self, trace_dir, config_metadata_sections):
+    def _create_metadata_stream(
+        self, trace_dir: str, metadata_sections_json: Optional[tjson.ArrayVal]
+    ):
         metadata_path = os.path.join(trace_dir, "metadata")
-        metadata_sections = []
+        metadata_sections = []  # type: list[_LttngMetadataStreamSection]
 
-        if config_metadata_sections is None:
+        if metadata_sections_json is None:
             with open(metadata_path, "rb") as metadata_file:
                 metadata_sections.append(
                     _LttngMetadataStreamSection(0, metadata_file.read())
                 )
         else:
             metadata_sections = _split_metadata_sections(
-                metadata_path, config_metadata_sections
+                metadata_path, metadata_sections_json
             )
 
-        self._metadata_stream = _LttngMetadataStream(metadata_path, metadata_sections)
+        self._metadata_stream = _LttngMetadataStream(
+            metadata_path, metadata_sections, self.creation_timestamp
+        )
 
     @property
     def path(self):
@@ -1004,16 +1105,60 @@ class LttngTrace(collections.abc.Sequence):
     def metadata_stream(self):
         return self._metadata_stream
 
-    def __getitem__(self, index):
+    @property
+    def creation_timestamp(self):
+        return self._creation_timestamp
+
+    @overload
+    def __getitem__(self, index: int) -> _LttngDataStream:
+        ...
+
+    @overload
+    def __getitem__(self, index: slice) -> Sequence[_LttngDataStream]:  # noqa: F811
+        ...
+
+    def __getitem__(  # noqa: F811
+        self, index: Union[int, slice]
+    ) -> Union[_LttngDataStream, Sequence[_LttngDataStream]]:
         return self._data_streams[index]
 
     def __len__(self):
         return len(self._data_streams)
 
 
+# Stream (metadata or data) state specific to the LTTng live protocol.
+class _LttngLiveViewerSessionStreamState:
+    @abstractmethod
+    def __init__(self):
+        # A stream is considered "announced" when it has been returned
+        # to the LTTng live client in response to a "get new stream
+        # infos" (`_LttngLiveViewerGetNewStreamInfosCommand`) command.
+        self._announced = False  # type: bool
+        pass
+
+    @property
+    def is_announced(self):
+        return self._announced
+
+    def mark_as_announced(self):
+        self._announced = True
+
+    @property
+    @abstractmethod
+    def stream(self) -> _LttngStream:
+        pass
+
+
 # The state of a single data stream.
-class _LttngLiveViewerSessionDataStreamState:
-    def __init__(self, ts_state, info, data_stream, metadata_stream_id):
+class _LttngLiveViewerSessionDataStreamState(_LttngLiveViewerSessionStreamState):
+    def __init__(
+        self,
+        ts_state: "_LttngLiveViewerSessionTracingSessionState",
+        info: _LttngLiveViewerStreamInfo,
+        data_stream: _LttngDataStream,
+        metadata_stream_id: int,
+    ):
+        super().__init__()
         self._ts_state = ts_state
         self._info = info
         self._data_stream = data_stream
@@ -1038,7 +1183,7 @@ class _LttngLiveViewerSessionDataStreamState:
         return self._info
 
     @property
-    def data_stream(self):
+    def stream(self):
         return self._data_stream
 
     @property
@@ -1048,13 +1193,23 @@ class _LttngLiveViewerSessionDataStreamState:
 
         return self._data_stream.index[self._cur_index_entry_index]
 
+    @property
+    def metadata_stream_id(self):
+        return self._metadata_stream_id
+
     def goto_next_index_entry(self):
         self._cur_index_entry_index += 1
 
 
 # The state of a single metadata stream.
-class _LttngLiveViewerSessionMetadataStreamState:
-    def __init__(self, ts_state, info, metadata_stream):
+class _LttngLiveViewerSessionMetadataStreamState(_LttngLiveViewerSessionStreamState):
+    def __init__(
+        self,
+        ts_state: "_LttngLiveViewerSessionTracingSessionState",
+        info: _LttngLiveViewerStreamInfo,
+        metadata_stream: _LttngMetadataStream,
+    ):
+        super().__init__()
         self._ts_state = ts_state
         self._info = info
         self._metadata_stream = metadata_stream
@@ -1066,7 +1221,7 @@ class _LttngLiveViewerSessionMetadataStreamState:
         else:
             self._next_metadata_stream_section_timestamp = None
 
-        self._is_sent = False
+        self._all_data_is_sent = False
         fmt = 'Built metadata stream state: id={}, ts-id={}, ts-name="{}", path="{}"'
         logging.info(
             fmt.format(
@@ -1077,25 +1232,21 @@ class _LttngLiveViewerSessionMetadataStreamState:
             )
         )
 
-    @property
-    def trace_session_state(self):
-        return self._trace_session_state
-
     @property
     def info(self):
         return self._info
 
     @property
-    def metadata_stream(self):
+    def stream(self):
         return self._metadata_stream
 
     @property
-    def is_sent(self):
-        return self._is_sent
+    def all_data_is_sent(self):
+        return self._all_data_is_sent
 
-    @is_sent.setter
-    def is_sent(self, value):
-        self._is_sent = value
+    @all_data_is_sent.setter
+    def all_data_is_sent(self, value: bool):
+        self._all_data_is_sent = value
 
     @property
     def cur_section(self):
@@ -1120,43 +1271,104 @@ class _LttngLiveViewerSessionMetadataStreamState:
         return self._next_metadata_stream_section_timestamp
 
 
+# A tracing session descriptor.
+#
+# In the constructor, `traces` is a list of LTTng traces (`LttngTrace`
+# objects).
+class LttngTracingSessionDescriptor:
+    def __init__(
+        self,
+        name: str,
+        tracing_session_id: int,
+        hostname: str,
+        live_timer_freq: int,
+        client_count: int,
+        traces: Iterable[LttngTrace],
+    ):
+        for trace in traces:
+            if name not in trace.path:
+                fmt = "Tracing session name must be part of every trace path (`{}` not found in `{}`)"
+                raise ValueError(fmt.format(name, trace.path))
+
+        self._traces = traces
+        stream_count = sum([len(t) + 1 for t in traces])
+        self._info = _LttngLiveViewerTracingSessionInfo(
+            tracing_session_id,
+            live_timer_freq,
+            client_count,
+            stream_count,
+            hostname,
+            name,
+        )
+
+    @property
+    def traces(self):
+        return self._traces
+
+    @property
+    def info(self):
+        return self._info
+
+
 # The state of a tracing session.
 class _LttngLiveViewerSessionTracingSessionState:
-    def __init__(self, tc_descr, base_stream_id):
+    def __init__(self, tc_descr: LttngTracingSessionDescriptor, base_stream_id: int):
         self._tc_descr = tc_descr
-        self._stream_infos = []
-        self._ds_states = {}
-        self._ms_states = {}
-        stream_id = base_stream_id
+        self._client_visible_stream_infos = []  # type: list[_LttngLiveViewerStreamInfo]
+        self._ds_states = {}  # type: dict[int, _LttngLiveViewerSessionDataStreamState]
+        self._ms_states = (
+            {}
+        )  # type: dict[int, _LttngLiveViewerSessionMetadataStreamState]
+        self._last_delivered_index_timestamp = 0
+        self._last_allocated_stream_id = base_stream_id
 
         for trace in tc_descr.traces:
-            trace_id = stream_id * 1000
+            trace_stream_infos = []  # type: list[_LttngLiveViewerStreamInfo]
+            trace_id = self._last_allocated_stream_id * 1000
 
             # Metadata stream -> stream info and metadata stream state
             info = _LttngLiveViewerStreamInfo(
-                stream_id, trace_id, True, trace.metadata_stream.path, "metadata"
+                self._last_allocated_stream_id,
+                trace_id,
+                True,
+                trace.metadata_stream.path,
+                "metadata",
             )
-            self._stream_infos.append(info)
-            self._ms_states[stream_id] = _LttngLiveViewerSessionMetadataStreamState(
+
+            trace_stream_infos.append(info)
+            self._ms_states[
+                self._last_allocated_stream_id
+            ] = _LttngLiveViewerSessionMetadataStreamState(
                 self, info, trace.metadata_stream
             )
-            metadata_stream_id = stream_id
-            stream_id += 1
+            metadata_stream_id = self._last_allocated_stream_id
+            self._last_allocated_stream_id += 1
 
             # Data streams -> stream infos and data stream states
             for data_stream in trace:
                 info = _LttngLiveViewerStreamInfo(
-                    stream_id,
+                    self._last_allocated_stream_id,
                     trace_id,
                     False,
                     data_stream.path,
                     data_stream.channel_name,
                 )
-                self._stream_infos.append(info)
-                self._ds_states[stream_id] = _LttngLiveViewerSessionDataStreamState(
+                trace_stream_infos.append(info)
+                self._ds_states[
+                    self._last_allocated_stream_id
+                ] = _LttngLiveViewerSessionDataStreamState(
                     self, info, data_stream, metadata_stream_id
                 )
-                stream_id += 1
+                self._last_allocated_stream_id += 1
+
+            if trace.creation_timestamp == 0:
+                # Only announce streams for traces that are created at
+                # the origin.
+                #
+                # The rest of the streams will be discovered by the
+                # client as indexes are received with a "has new data
+                # streams" flag set in the reply.
+                self._client_visible_stream_infos.extend(trace_stream_infos)
 
         self._is_attached = False
         fmt = 'Built tracing session state: id={}, name="{}"'
@@ -1175,23 +1387,31 @@ class _LttngLiveViewerSessionTracingSessionState:
         return self._ms_states
 
     @property
-    def stream_infos(self):
-        return self._stream_infos
+    def client_visible_stream_infos(self):
+        return self._client_visible_stream_infos
 
     @property
     def has_new_metadata(self):
-        return any([not ms.is_sent for ms in self._ms_states.values()])
+        return any(
+            [
+                ms.is_announced and not ms.all_data_is_sent
+                for ms in self._ms_states.values()
+            ]
+        )
 
     @property
     def is_attached(self):
         return self._is_attached
 
     @is_attached.setter
-    def is_attached(self, value):
+    def is_attached(self, value: bool):
         self._is_attached = value
 
 
-def needs_new_metadata_section(metadata_stream_state, latest_timestamp):
+def needs_new_metadata_section(
+    metadata_stream_state: _LttngLiveViewerSessionMetadataStreamState,
+    latest_timestamp: int,
+):
     if metadata_stream_state.next_section_timestamp is None:
         return False
 
@@ -1206,13 +1426,17 @@ def needs_new_metadata_section(metadata_stream_state, latest_timestamp):
 class _LttngLiveViewerSession:
     def __init__(
         self,
-        viewer_session_id,
-        tracing_session_descriptors,
-        max_query_data_response_size,
+        viewer_session_id: int,
+        tracing_session_descriptors: Iterable[LttngTracingSessionDescriptor],
+        max_query_data_response_size: Optional[int],
     ):
         self._viewer_session_id = viewer_session_id
-        self._ts_states = {}
-        self._stream_states = {}
+        self._ts_states = (
+            {}
+        )  # type:  dict[int, _LttngLiveViewerSessionTracingSessionState]
+        self._stream_states = (
+            {}
+        )  # type: dict[int, _LttngLiveViewerSessionDataStreamState | _LttngLiveViewerSessionMetadataStreamState]
         self._max_query_data_response_size = max_query_data_response_size
         total_stream_infos = 0
 
@@ -1222,7 +1446,7 @@ class _LttngLiveViewerSession:
             )
             ts_id = ts_state.tracing_session_descriptor.info.tracing_session_id
             self._ts_states[ts_id] = ts_state
-            total_stream_infos += len(ts_state.stream_infos)
+            total_stream_infos += len(ts_state.client_visible_stream_infos)
 
             # Update session's stream states to have the new states
             self._stream_states.update(ts_state.data_stream_states)
@@ -1237,27 +1461,41 @@ class _LttngLiveViewerSession:
             _LttngLiveViewerGetNewStreamInfosCommand: self._handle_get_new_stream_infos_command,
             _LttngLiveViewerGetNextDataStreamIndexEntryCommand: self._handle_get_next_data_stream_index_entry_command,
             _LttngLiveViewerGetTracingSessionInfosCommand: self._handle_get_tracing_session_infos_command,
-        }
+        }  # type: dict[type[_LttngLiveViewerCommand], Callable[[Any], _LttngLiveViewerReply]]
 
     @property
     def viewer_session_id(self):
         return self._viewer_session_id
 
-    def _get_tracing_session_state(self, tracing_session_id):
+    def _get_tracing_session_state(self, tracing_session_id: int):
         if tracing_session_id not in self._ts_states:
-            raise UnexpectedInput(
+            raise RuntimeError(
                 "Unknown tracing session ID {}".format(tracing_session_id)
             )
 
         return self._ts_states[tracing_session_id]
 
-    def _get_stream_state(self, stream_id):
+    def _get_data_stream_state(self, stream_id: int):
         if stream_id not in self._stream_states:
-            UnexpectedInput("Unknown stream ID {}".format(stream_id))
+            RuntimeError("Unknown stream ID {}".format(stream_id))
+
+        stream = self._stream_states[stream_id]
+        if type(stream) is not _LttngLiveViewerSessionDataStreamState:
+            raise RuntimeError("Stream is not a data stream")
+
+        return stream
+
+    def _get_metadata_stream_state(self, stream_id: int):
+        if stream_id not in self._stream_states:
+            RuntimeError("Unknown stream ID {}".format(stream_id))
+
+        stream = self._stream_states[stream_id]
+        if type(stream) is not _LttngLiveViewerSessionMetadataStreamState:
+            raise RuntimeError("Stream is not a metadata stream")
 
-        return self._stream_states[stream_id]
+        return stream
 
-    def handle_command(self, cmd):
+    def handle_command(self, cmd: _LttngLiveViewerCommand):
         logging.info(
             "Handling command in viewer session: cmd-cls-name={}".format(
                 cmd.__class__.__name__
@@ -1266,20 +1504,22 @@ class _LttngLiveViewerSession:
         cmd_type = type(cmd)
 
         if cmd_type not in self._command_handlers:
-            raise UnexpectedInput(
+            raise RuntimeError(
                 "Unexpected command: cmd-cls-name={}".format(cmd.__class__.__name__)
             )
 
         return self._command_handlers[cmd_type](cmd)
 
-    def _handle_attach_to_tracing_session_command(self, cmd):
+    def _handle_attach_to_tracing_session_command(
+        self, cmd: _LttngLiveViewerAttachToTracingSessionCommand
+    ):
         fmt = 'Handling "attach to tracing session" command: ts-id={}, offset={}, seek-type={}'
         logging.info(fmt.format(cmd.tracing_session_id, cmd.offset, cmd.seek_type))
         ts_state = self._get_tracing_session_state(cmd.tracing_session_id)
         info = ts_state.tracing_session_descriptor.info
 
         if ts_state.is_attached:
-            raise UnexpectedInput(
+            raise RuntimeError(
                 "Cannot attach to tracing session `{}`: viewer is already attached".format(
                     info.name
                 )
@@ -1287,18 +1527,30 @@ class _LttngLiveViewerSession:
 
         ts_state.is_attached = True
         status = _LttngLiveViewerAttachToTracingSessionReply.Status.OK
+        stream_infos_to_announce = ts_state.client_visible_stream_infos
+
+        # Mark stream infos transmitted as part of the reply as
+        # announced.
+        for si in stream_infos_to_announce:
+            if si.is_metadata:
+                self._get_metadata_stream_state(si.id).mark_as_announced()
+            else:
+                self._get_data_stream_state(si.id).mark_as_announced()
+
         return _LttngLiveViewerAttachToTracingSessionReply(
-            status, ts_state.stream_infos
+            status, stream_infos_to_announce
         )
 
-    def _handle_detach_from_tracing_session_command(self, cmd):
+    def _handle_detach_from_tracing_session_command(
+        self, cmd: _LttngLiveViewerDetachFromTracingSessionCommand
+    ):
         fmt = 'Handling "detach from tracing session" command: ts-id={}'
         logging.info(fmt.format(cmd.tracing_session_id))
         ts_state = self._get_tracing_session_state(cmd.tracing_session_id)
         info = ts_state.tracing_session_descriptor.info
 
         if not ts_state.is_attached:
-            raise UnexpectedInput(
+            raise RuntimeError(
                 "Cannot detach to tracing session `{}`: viewer is not attached".format(
                     info.name
                 )
@@ -1308,16 +1560,30 @@ class _LttngLiveViewerSession:
         status = _LttngLiveViewerDetachFromTracingSessionReply.Status.OK
         return _LttngLiveViewerDetachFromTracingSessionReply(status)
 
-    def _handle_get_next_data_stream_index_entry_command(self, cmd):
+    @staticmethod
+    def _stream_is_ready(
+        stream_state: _LttngLiveViewerSessionStreamState, creation_timestamp: int
+    ):
+        return (
+            not stream_state.is_announced
+            and stream_state.stream.creation_timestamp <= creation_timestamp
+        )
+
+    def _needs_new_streams(self, current_timestamp: int):
+        return any(
+            self._stream_is_ready(ss, current_timestamp)
+            for ss in self._stream_states.values()
+        )
+
+    def _handle_get_next_data_stream_index_entry_command(
+        self, cmd: _LttngLiveViewerGetNextDataStreamIndexEntryCommand
+    ):
         fmt = 'Handling "get next data stream index entry" command: stream-id={}'
         logging.info(fmt.format(cmd.stream_id))
-        stream_state = self._get_stream_state(cmd.stream_id)
-        metadata_stream_state = self._get_stream_state(stream_state._metadata_stream_id)
-
-        if type(stream_state) is not _LttngLiveViewerSessionDataStreamState:
-            raise UnexpectedInput(
-                "Stream with ID {} is not a data stream".format(cmd.stream_id)
-            )
+        stream_state = self._get_data_stream_state(cmd.stream_id)
+        metadata_stream_state = self._get_metadata_stream_state(
+            stream_state.metadata_stream_id
+        )
 
         if stream_state.cur_index_entry is None:
             # The viewer is done reading this stream
@@ -1334,35 +1600,35 @@ class _LttngLiveViewerSession:
         timestamp_begin = _get_entry_timestamp_begin(stream_state.cur_index_entry)
 
         if needs_new_metadata_section(metadata_stream_state, timestamp_begin):
-            metadata_stream_state.is_sent = False
+            metadata_stream_state.all_data_is_sent = False
             metadata_stream_state.goto_next_section()
 
         # The viewer only checks the `has_new_metadata` flag if the
         # reply's status is `OK`, so we need to provide an index here
         has_new_metadata = stream_state.tracing_session_state.has_new_metadata
-        if type(stream_state.cur_index_entry) is _LttngDataStreamIndexEntry:
+        if isinstance(stream_state.cur_index_entry, _LttngDataStreamIndexEntry):
             status = _LttngLiveViewerGetNextDataStreamIndexEntryReply.Status.OK
         else:
-            assert type(stream_state.cur_index_entry) is _LttngDataStreamBeaconEntry
             status = _LttngLiveViewerGetNextDataStreamIndexEntryReply.Status.INACTIVE
 
         reply = _LttngLiveViewerGetNextDataStreamIndexEntryReply(
-            status, stream_state.cur_index_entry, has_new_metadata, False
+            status,
+            stream_state.cur_index_entry,
+            has_new_metadata,
+            self._needs_new_streams(timestamp_begin),
         )
+        self._last_delivered_index_timestamp_begin = timestamp_begin
         stream_state.goto_next_index_entry()
         return reply
 
-    def _handle_get_data_stream_packet_data_command(self, cmd):
+    def _handle_get_data_stream_packet_data_command(
+        self, cmd: _LttngLiveViewerGetDataStreamPacketDataCommand
+    ):
         fmt = 'Handling "get data stream packet data" command: stream-id={}, offset={}, req-length={}'
         logging.info(fmt.format(cmd.stream_id, cmd.offset, cmd.req_length))
-        stream_state = self._get_stream_state(cmd.stream_id)
+        stream_state = self._get_data_stream_state(cmd.stream_id)
         data_response_length = cmd.req_length
 
-        if type(stream_state) is not _LttngLiveViewerSessionDataStreamState:
-            raise UnexpectedInput(
-                "Stream with ID {} is not a data stream".format(cmd.stream_id)
-            )
-
         if stream_state.tracing_session_state.has_new_metadata:
             status = _LttngLiveViewerGetDataStreamPacketDataReply.Status.ERROR
             return _LttngLiveViewerGetDataStreamPacketDataReply(
@@ -1379,34 +1645,29 @@ class _LttngLiveViewerSession:
             fmt = 'Limiting "get data stream packet data" command: req-length={} actual response size={}'
             logging.info(fmt.format(cmd.req_length, data_response_length))
 
-        data = stream_state.data_stream.get_data(cmd.offset, data_response_length)
+        data = stream_state.stream.get_data(cmd.offset, data_response_length)
         status = _LttngLiveViewerGetDataStreamPacketDataReply.Status.OK
         return _LttngLiveViewerGetDataStreamPacketDataReply(status, data, False, False)
 
-    def _handle_get_metadata_stream_data_command(self, cmd):
+    def _handle_get_metadata_stream_data_command(
+        self, cmd: _LttngLiveViewerGetMetadataStreamDataCommand
+    ):
         fmt = 'Handling "get metadata stream data" command: stream-id={}'
         logging.info(fmt.format(cmd.stream_id))
-        metadata_stream_state = self._get_stream_state(cmd.stream_id)
+        metadata_stream_state = self._get_metadata_stream_state(cmd.stream_id)
 
-        if (
-            type(metadata_stream_state)
-            is not _LttngLiveViewerSessionMetadataStreamState
-        ):
-            raise UnexpectedInput(
-                "Stream with ID {} is not a metadata stream".format(cmd.stream_id)
-            )
-
-        if metadata_stream_state.is_sent:
+        if metadata_stream_state.all_data_is_sent:
             status = _LttngLiveViewerGetMetadataStreamDataContentReply.Status.NO_NEW
             return _LttngLiveViewerGetMetadataStreamDataContentReply(status, bytes())
 
-        metadata_stream_state.is_sent = True
+        metadata_stream_state.all_data_is_sent = True
         status = _LttngLiveViewerGetMetadataStreamDataContentReply.Status.OK
         metadata_section = metadata_stream_state.cur_section
+        assert metadata_section is not None
 
         # If we are sending an empty section, ready the next one right away.
         if len(metadata_section.data) == 0:
-            metadata_stream_state.is_sent = False
+            metadata_stream_state.all_data_is_sent = False
             metadata_stream_state.goto_next_section()
 
         fmt = 'Replying to "get metadata stream data" command: metadata-size={}'
@@ -1415,19 +1676,58 @@ class _LttngLiveViewerSession:
             status, metadata_section.data
         )
 
-    def _handle_get_new_stream_infos_command(self, cmd):
+    def _get_stream_infos_ready_for_announcement(self):
+        ready_stream_infos = []  # type: list[_LttngLiveViewerStreamInfo]
+
+        for ss in self._stream_states.values():
+            if self._stream_is_ready(ss, ss.stream.creation_timestamp):
+                ready_stream_infos.append(ss.info)
+
+        return ready_stream_infos
+
+    # A stream is considered finished if it has been announced and all
+    # of its index entries have been provided to the client.
+    def _all_streams_finished(self):
+        return all(
+            isinstance(stream_state, _LttngLiveViewerSessionMetadataStreamState)
+            or (stream_state.cur_index_entry is None and stream_state.is_announced)
+            for stream_state in self._stream_states.values()
+        )
+
+    def _handle_get_new_stream_infos_command(
+        self, cmd: _LttngLiveViewerGetNewStreamInfosCommand
+    ):
         fmt = 'Handling "get new stream infos" command: ts-id={}'
         logging.info(fmt.format(cmd.tracing_session_id))
+        newly_announced_stream_infos = self._get_stream_infos_ready_for_announcement()
+
+        # Mark stream infos transmitted as part of the reply as
+        # announced.
+        for si in newly_announced_stream_infos:
+            if si.is_metadata:
+                self._get_metadata_stream_state(si.id).mark_as_announced()
+            else:
+                self._get_data_stream_state(si.id).mark_as_announced()
+
+        status = _LttngLiveViewerGetNewStreamInfosReply.Status.OK
+
+        if len(newly_announced_stream_infos) == 0:
+            # If all streams have been transmitted and no new traces are
+            # scheduled for creation, hang up to signal that the tracing
+            # session is "done".
+            status = (
+                _LttngLiveViewerGetNewStreamInfosReply.Status.HUP
+                if self._all_streams_finished()
+                else _LttngLiveViewerGetNewStreamInfosReply.Status.NO_NEW
+            )
 
-        # As of this version, all the tracing session's stream infos are
-        # always given to the viewer when sending the "attach to tracing
-        # session" reply, so there's nothing new here. Return the `HUP`
-        # status as, if we're handling this command, the viewer consumed
-        # all the existing data streams.
-        status = _LttngLiveViewerGetNewStreamInfosReply.Status.HUP
-        return _LttngLiveViewerGetNewStreamInfosReply(status, [])
+        return _LttngLiveViewerGetNewStreamInfosReply(
+            status, newly_announced_stream_infos
+        )
 
-    def _handle_get_tracing_session_infos_command(self, cmd):
+    def _handle_get_tracing_session_infos_command(
+        self, cmd: _LttngLiveViewerGetTracingSessionInfosCommand
+    ):
         logging.info('Handling "get tracing session infos" command.')
         infos = [
             tss.tracing_session_descriptor.info for tss in self._ts_states.values()
@@ -1435,7 +1735,9 @@ class _LttngLiveViewerSession:
         infos.sort(key=lambda info: info.name)
         return _LttngLiveViewerGetTracingSessionInfosReply(infos)
 
-    def _handle_create_viewer_session_command(self, cmd):
+    def _handle_create_viewer_session_command(
+        self, cmd: _LttngLiveViewerCreateViewerSessionCommand
+    ):
         logging.info('Handling "create viewer session" command.')
         status = _LttngLiveViewerCreateViewerSessionReply.Status.OK
 
@@ -1446,9 +1748,10 @@ class _LttngLiveViewerSession:
 
 # An LTTng live TCP server.
 #
-# On creation, it binds to `localhost` with an OS-assigned TCP port. It writes
-# the decimal TCP port number to a temporary port file.  It renames the
-# temporary port file to `port_filename`.
+# On creation, it binds to `localhost` on the TCP port `port` if not `None`, or
+# on an OS-assigned TCP port otherwise. It writes the decimal TCP port number
+# to a temporary port file.  It renames the temporary port file to
+# `port_filename`.
 #
 # `tracing_session_descriptors` is a list of tracing session descriptors
 # (`LttngTracingSessionDescriptor`) to serve.
@@ -1459,7 +1762,11 @@ class _LttngLiveViewerSession:
 # returns.
 class LttngLiveServer:
     def __init__(
-        self, port_filename, tracing_session_descriptors, max_query_data_response_size
+        self,
+        port: Optional[int],
+        port_filename: Optional[str],
+        tracing_session_descriptors: Iterable[LttngTracingSessionDescriptor],
+        max_query_data_response_size: Optional[int],
     ):
         logging.info("Server configuration:")
 
@@ -1495,9 +1802,21 @@ class LttngLiveServer:
         self._codec = _LttngLiveViewerProtocolCodec()
 
         # Port 0: OS assigns an unused port
-        serv_addr = ("localhost", 0)
+        serv_addr = ("localhost", port if port is not None else 0)
         self._sock.bind(serv_addr)
-        self._write_port_to_file(port_filename)
+
+        if port_filename is not None:
+            self._write_port_to_file(port_filename)
+
+        print("Listening on port {}".format(self._server_port))
+
+        for ts_descr in tracing_session_descriptors:
+            info = ts_descr.info
+            print(
+                "net://localhost:{}/host/{}/{}".format(
+                    self._server_port, info.hostname, info.name
+                )
+            )
 
         try:
             self._listen()
@@ -1520,7 +1839,7 @@ class LttngLiveServer:
                 logging.info("Client closed connection.")
 
                 if data:
-                    raise UnexpectedInput(
+                    raise RuntimeError(
                         "Client closed connection after having sent {} command bytes.".format(
                             len(data)
                         )
@@ -1535,7 +1854,7 @@ class LttngLiveServer:
             try:
                 cmd = self._codec.decode(data)
             except struct.error as exc:
-                raise UnexpectedInput("Malformed command: {}".format(exc)) from exc
+                raise RuntimeError("Malformed command: {}".format(exc)) from exc
 
             if cmd is not None:
                 logging.info(
@@ -1545,7 +1864,7 @@ class LttngLiveServer:
                 )
                 return cmd
 
-    def _send_reply(self, reply):
+    def _send_reply(self, reply: _LttngLiveViewerReply):
         data = self._codec.encode(reply)
         logging.info(
             "Sending reply to viewer: reply-cls-name={}, length={}".format(
@@ -1559,7 +1878,7 @@ class LttngLiveServer:
         cmd = self._recv_command()
 
         if type(cmd) is not _LttngLiveViewerConnectCommand:
-            raise UnexpectedInput(
+            raise RuntimeError(
                 'First command is not "connect": cmd-cls-name={}'.format(
                     cmd.__class__.__name__
                 )
@@ -1604,54 +1923,53 @@ class LttngLiveServer:
         finally:
             self._conn.close()
 
-    def _write_port_to_file(self, port_filename):
+    def _write_port_to_file(self, port_filename: str):
         # Write the port number to a temporary file.
-        with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_port_file:
+        with tempfile.NamedTemporaryFile(
+            mode="w", delete=False, dir=os.path.dirname(port_filename)
+        ) as tmp_port_file:
             print(self._server_port, end="", file=tmp_port_file)
 
-        # Rename temporary file to real file
-        os.replace(tmp_port_file.name, port_filename)
-        logging.info(
-            'Renamed port file: src-path="{}", dst-path="{}"'.format(
-                tmp_port_file.name, port_filename
-            )
-        )
-
-
-# A tracing session descriptor.
-#
-# In the constructor, `traces` is a list of LTTng traces (`LttngTrace`
-# objects).
-class LttngTracingSessionDescriptor:
-    def __init__(
-        self, name, tracing_session_id, hostname, live_timer_freq, client_count, traces
-    ):
-        for trace in traces:
-            if name not in trace.path:
-                fmt = "Tracing session name must be part of every trace path (`{}` not found in `{}`)"
-                raise ValueError(fmt.format(name, trace.path))
-
-        self._traces = traces
-        stream_count = sum([len(t) + 1 for t in traces])
-        self._info = _LttngLiveViewerTracingSessionInfo(
-            tracing_session_id,
-            live_timer_freq,
-            client_count,
-            stream_count,
-            hostname,
-            name,
-        )
+        # Rename temporary file to real file.
+        #
+        # For unknown reasons, on Windows, moving the port file from its
+        # temporary location to its final location (where the user of
+        # the server expects it to appear) may raise a `PermissionError`
+        # exception.
+        #
+        # We suppose it's possible that something in the Windows kernel
+        # hasn't completely finished using the file when we try to move
+        # it.
+        #
+        # Use a wait-and-retry scheme as a (bad) workaround.
+        num_attempts = 5
+        retry_delay_s = 1
+
+        for attempt in reversed(range(num_attempts)):
+            try:
+                os.replace(tmp_port_file.name, port_filename)
+                logging.info(
+                    'Renamed port file: src-path="{}", dst-path="{}"'.format(
+                        tmp_port_file.name, port_filename
+                    )
+                )
+                return
+            except PermissionError:
+                logging.info(
+                    'Permission error while attempting to rename port file; retrying in {} second: src-path="{}", dst-path="{}"'.format(
+                        retry_delay_s, tmp_port_file.name, port_filename
+                    )
+                )
 
-    @property
-    def traces(self):
-        return self._traces
+                if attempt == 0:
+                    raise
 
-    @property
-    def info(self):
-        return self._info
+                time.sleep(retry_delay_s)
 
 
-def _session_descriptors_from_path(sessions_filename, trace_path_prefix):
+def _session_descriptors_from_path(
+    sessions_filename: str, trace_path_prefix: Optional[str]
+):
     # File format is:
     #
     #     [
@@ -1663,7 +1981,8 @@ def _session_descriptors_from_path(sessions_filename, trace_path_prefix):
     #             "client-count": 23,
     #             "traces": [
     #                 {
-    #                     "path": "lol"
+    #                     "path": "lol",
+    #                     creation-timestamp: 12948
     #                 },
     #                 {
     #                     "path": "meow/mix",
@@ -1681,27 +2000,44 @@ def _session_descriptors_from_path(sessions_filename, trace_path_prefix):
     #         }
     #     ]
     with open(sessions_filename, "r") as sessions_file:
-        params = json.load(sessions_file)
+        sessions_json = tjson.load(sessions_file, tjson.ArrayVal)
 
-    sessions = []
+    sessions = []  # type: list[LttngTracingSessionDescriptor]
 
-    for session in params:
-        name = session["name"]
-        tracing_session_id = session["id"]
-        hostname = session["hostname"]
-        live_timer_freq = session["live-timer-freq"]
-        client_count = session["client-count"]
-        traces = []
+    for session_json in sessions_json.iter(tjson.ObjVal):
+        name = session_json.at("name", tjson.StrVal).val
+        tracing_session_id = session_json.at("id", tjson.IntVal).val
+        hostname = session_json.at("hostname", tjson.StrVal).val
+        live_timer_freq = session_json.at("live-timer-freq", tjson.IntVal).val
+        client_count = session_json.at("client-count", tjson.IntVal).val
+        traces_json = session_json.at("traces", tjson.ArrayVal)
 
-        for trace in session["traces"]:
-            metadata_sections = trace.get("metadata-sections")
-            beacons = trace.get("beacons")
-            path = trace["path"]
+        traces = []  # type: list[LttngTrace]
 
-            if not os.path.isabs(path):
+        for trace_json in traces_json.iter(tjson.ObjVal):
+            metadata_sections = (
+                trace_json.at("metadata-sections", tjson.ArrayVal)
+                if "metadata-sections" in trace_json
+                else None
+            )
+            beacons = (
+                trace_json.at("beacons", tjson.ObjVal)
+                if "beacons" in trace_json
+                else None
+            )
+            path = trace_json.at("path", tjson.StrVal).val
+            creation_timestamp = (
+                trace_json.at("creation-timestamp", tjson.IntVal).val
+                if "creation-timestamp" in trace_json
+                else 0
+            )  # type: int
+
+            if not os.path.isabs(path) and trace_path_prefix:
                 path = os.path.join(trace_path_prefix, path)
 
-            traces.append(LttngTrace(path, metadata_sections, beacons))
+            traces.append(
+                LttngTrace(path, metadata_sections, beacons, creation_timestamp)
+            )
 
         sessions.append(
             LttngTracingSessionDescriptor(
@@ -1717,7 +2053,7 @@ def _session_descriptors_from_path(sessions_filename, trace_path_prefix):
     return sessions
 
 
-def _loglevel_parser(string):
+def _loglevel_parser(string: str):
     loglevels = {"info": logging.INFO, "warning": logging.WARNING}
     if string not in loglevels:
         msg = "{} is not a valid loglevel".format(string)
@@ -1740,10 +2076,14 @@ if __name__ == "__main__":
     loglevel_namespace, remaining_args = parser.parse_known_args()
     logging.getLogger().setLevel(_loglevel_parser(loglevel_namespace.log_level))
 
+    parser.add_argument(
+        "--port",
+        help="The port to bind to. If missing, use an OS-assigned port..",
+        type=int,
+    )
     parser.add_argument(
         "--port-filename",
         help="The final port file. This file is present when the server is ready to receive connection.",
-        required=True,
     )
     parser.add_argument(
         "--max-query-data-response-size",
@@ -1756,9 +2096,10 @@ if __name__ == "__main__":
         help="Prefix to prepend to the trace paths of session configurations",
     )
     parser.add_argument(
-        "--sessions-filename",
+        "sessions_filename",
         type=str,
         help="Path to a session configuration file",
+        metavar="sessions-filename",
     )
     parser.add_argument(
         "-h",
@@ -1769,13 +2110,14 @@ if __name__ == "__main__":
     )
 
     args = parser.parse_args(args=remaining_args)
-    try:
-        sessions = _session_descriptors_from_path(
-            args.sessions_filename,
-            args.trace_path_prefix,
-        )
-        LttngLiveServer(args.port_filename, sessions, args.max_query_data_response_size)
-    except UnexpectedInput as exc:
-        logging.error(str(exc))
-        print(exc, file=sys.stderr)
-        sys.exit(1)
+    sessions_filename = args.sessions_filename  # type: str
+    trace_path_prefix = args.trace_path_prefix  # type: str | None
+    sessions = _session_descriptors_from_path(
+        sessions_filename,
+        trace_path_prefix,
+    )
+
+    port = args.port  # type: int | None
+    port_filename = args.port_filename  # type: str | None
+    max_query_data_response_size = args.max_query_data_response_size  # type: int | None
+    LttngLiveServer(port, port_filename, sessions, max_query_data_response_size)
diff --git a/tests/data/plugins/src.ctf.lttng-live/multi-domains-inverse.json b/tests/data/plugins/src.ctf.lttng-live/multi-domains-inverse.json
new file mode 100644 (file)
index 0000000..7175502
--- /dev/null
@@ -0,0 +1,26 @@
+[
+    {
+        "name": "multi-domains",
+        "id": 0,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "succeed/multi-domains/ust/"
+            }
+        ]
+    },
+    {
+        "name": "multi-domains",
+        "id": 1,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "succeed/multi-domains/kernel/"
+            }
+        ]
+    }
+]
diff --git a/tests/data/plugins/src.ctf.lttng-live/multi-domains.json b/tests/data/plugins/src.ctf.lttng-live/multi-domains.json
new file mode 100644 (file)
index 0000000..155cacf
--- /dev/null
@@ -0,0 +1,26 @@
+[
+    {
+        "name": "multi-domains",
+        "id": 0,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "succeed/multi-domains/kernel/"
+            }
+        ]
+    },
+    {
+        "name": "multi-domains",
+        "id": 1,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "succeed/multi-domains/ust/"
+            }
+        ]
+    }
+]
diff --git a/tests/data/plugins/src.ctf.lttng-live/multi_domains.json b/tests/data/plugins/src.ctf.lttng-live/multi_domains.json
deleted file mode 100644 (file)
index 155cacf..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-[
-    {
-        "name": "multi-domains",
-        "id": 0,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "succeed/multi-domains/kernel/"
-            }
-        ]
-    },
-    {
-        "name": "multi-domains",
-        "id": 1,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "succeed/multi-domains/ust/"
-            }
-        ]
-    }
-]
diff --git a/tests/data/plugins/src.ctf.lttng-live/multi_domains_inverse.json b/tests/data/plugins/src.ctf.lttng-live/multi_domains_inverse.json
deleted file mode 100644 (file)
index 7175502..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-[
-    {
-        "name": "multi-domains",
-        "id": 0,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "succeed/multi-domains/ust/"
-            }
-        ]
-    },
-    {
-        "name": "multi-domains",
-        "id": 1,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "succeed/multi-domains/kernel/"
-            }
-        ]
-    }
-]
diff --git a/tests/data/plugins/src.ctf.lttng-live/new-streams.expect b/tests/data/plugins/src.ctf.lttng-live/new-streams.expect
new file mode 100644 (file)
index 0000000..b77c326
--- /dev/null
@@ -0,0 +1,181 @@
+Trace class:
+  Stream class (ID 0):
+    Supports packets: Yes
+    Packets have beginning default clock snapshot: Yes
+    Packets have end default clock snapshot: Yes
+    Supports discarded events: No
+    Supports discarded packets: No
+    Default clock class:
+      Name: cycle_counter_test
+      Frequency (Hz): 1,000,000,000
+      Precision (cycles): 0
+      Offset (s): 0
+      Offset (cycles): 0
+      Origin is Unix epoch: No
+      UUID: 5b59e7db-5e49-418a-9adf-e1adfdf571c4
+    Event class `first_trace_event` (ID 0):
+      Payload field class: Structure (1 member):
+        value: Unsigned integer (8-bit, Base 10)
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Stream beginning:
+  Name: stream-1
+  Trace:
+    Stream (ID 1, Class ID 0)
+
+[10 cycles, 10 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Packet beginning
+
+[10 cycles, 10 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Event `first_trace_event` (Class ID 0):
+  Payload:
+    value: 42
+
+[11 cycles, 11 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Packet end
+
+[20 cycles, 20 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Packet beginning
+
+[20 cycles, 20 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Event `first_trace_event` (Class ID 0):
+  Payload:
+    value: 42
+
+[21 cycles, 21 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Packet end
+
+[40 cycles, 40 ns from origin]
+Message iterator inactivity:
+  Clock class:
+    Name: cycle_counter_test
+    Frequency (Hz): 1,000,000,000
+    Precision (cycles): 0
+    Offset (s): 0
+    Offset (cycles): 0
+    Origin is Unix epoch: No
+    UUID: 5b59e7db-5e49-418a-9adf-e1adfdf571c4
+
+[50 cycles, 50 ns from origin]
+Message iterator inactivity:
+  Clock class:
+    Name: cycle_counter_test
+    Frequency (Hz): 1,000,000,000
+    Precision (cycles): 0
+    Offset (s): 0
+    Offset (cycles): 0
+    Origin is Unix epoch: No
+    UUID: 5b59e7db-5e49-418a-9adf-e1adfdf571c4
+
+Trace class:
+  Stream class (ID 0):
+    Supports packets: Yes
+    Packets have beginning default clock snapshot: Yes
+    Packets have end default clock snapshot: Yes
+    Supports discarded events: No
+    Supports discarded packets: No
+    Default clock class:
+      Name: cycle_counter_test
+      Frequency (Hz): 1,000,000,000
+      Precision (cycles): 0
+      Offset (s): 0
+      Offset (cycles): 0
+      Origin is Unix epoch: No
+      UUID: 5b59e7db-5e49-418a-9adf-e1adfdf571c4
+    Event class `second_trace_event` (ID 0):
+      Payload field class: Structure (1 member):
+        value: Unsigned integer (8-bit, Base 10)
+
+[Unknown]
+{Trace 1, Stream class ID 0, Stream ID 3}
+Stream beginning:
+  Name: stream-3
+  Trace:
+    Stream (ID 3, Class ID 0)
+
+[85 cycles, 85 ns from origin]
+Message iterator inactivity:
+  Clock class:
+    Name: cycle_counter_test
+    Frequency (Hz): 1,000,000,000
+    Precision (cycles): 0
+    Offset (s): 0
+    Offset (cycles): 0
+    Origin is Unix epoch: No
+    UUID: 5b59e7db-5e49-418a-9adf-e1adfdf571c4
+
+[100 cycles, 100 ns from origin]
+Message iterator inactivity:
+  Clock class:
+    Name: cycle_counter_test
+    Frequency (Hz): 1,000,000,000
+    Precision (cycles): 0
+    Offset (s): 0
+    Offset (cycles): 0
+    Origin is Unix epoch: No
+    UUID: 5b59e7db-5e49-418a-9adf-e1adfdf571c4
+
+[110 cycles, 110 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 3}
+Packet beginning
+
+[110 cycles, 110 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 3}
+Event `second_trace_event` (Class ID 0):
+  Payload:
+    value: 123
+
+[111 cycles, 111 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 3}
+Packet end
+
+[120 cycles, 120 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 3}
+Packet beginning
+
+[120 cycles, 120 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 3}
+Event `second_trace_event` (Class ID 0):
+  Payload:
+    value: 123
+
+[120 cycles, 120 ns from origin]
+Message iterator inactivity:
+  Clock class:
+    Name: cycle_counter_test
+    Frequency (Hz): 1,000,000,000
+    Precision (cycles): 0
+    Offset (s): 0
+    Offset (cycles): 0
+    Origin is Unix epoch: No
+    UUID: 5b59e7db-5e49-418a-9adf-e1adfdf571c4
+
+[121 cycles, 121 ns from origin]
+{Trace 1, Stream class ID 0, Stream ID 3}
+Packet end
+
+[Unknown]
+{Trace 1, Stream class ID 0, Stream ID 3}
+Stream end
+
+[200 cycles, 200 ns from origin]
+Message iterator inactivity:
+  Clock class:
+    Name: cycle_counter_test
+    Frequency (Hz): 1,000,000,000
+    Precision (cycles): 0
+    Offset (s): 0
+    Offset (cycles): 0
+    Origin is Unix epoch: No
+    UUID: 5b59e7db-5e49-418a-9adf-e1adfdf571c4
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Stream end
diff --git a/tests/data/plugins/src.ctf.lttng-live/new-streams.json b/tests/data/plugins/src.ctf.lttng-live/new-streams.json
new file mode 100644 (file)
index 0000000..6cf4d10
--- /dev/null
@@ -0,0 +1,22 @@
+[
+    {
+        "name": "new-streams",
+        "id": 0,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "first-trace",
+                "creation-timestamp": 0,
+                "beacons": {
+                    "first_trace_stream_0": [40, 50, 85, 100, 120, 200]
+                }
+            },
+            {
+                "path": "second-trace",
+                "creation-timestamp": 80
+            }
+        ]
+    }
+]
diff --git a/tests/data/plugins/src.ctf.lttng-live/rate-limited.json b/tests/data/plugins/src.ctf.lttng-live/rate-limited.json
new file mode 100644 (file)
index 0000000..d7022b1
--- /dev/null
@@ -0,0 +1,14 @@
+[
+    {
+       "name": "trace-with-index",
+        "id": 0,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "succeed/trace-with-index/"
+            }
+        ]
+    }
+]
diff --git a/tests/data/plugins/src.ctf.lttng-live/rate_limited.json b/tests/data/plugins/src.ctf.lttng-live/rate_limited.json
deleted file mode 100644 (file)
index d7022b1..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-[
-    {
-       "name": "trace-with-index",
-        "id": 0,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "succeed/trace-with-index/"
-            }
-        ]
-    }
-]
diff --git a/tests/data/plugins/src.ctf.lttng-live/split-metadata.expect b/tests/data/plugins/src.ctf.lttng-live/split-metadata.expect
new file mode 100644 (file)
index 0000000..17b6ccb
--- /dev/null
@@ -0,0 +1,84 @@
+Trace class:
+  Stream class (ID 0):
+    Supports packets: Yes
+    Packets have beginning default clock snapshot: Yes
+    Packets have end default clock snapshot: Yes
+    Supports discarded events: Yes
+    Discarded events have default clock snapshots: Yes
+    Supports discarded packets: Yes
+    Discarded packets have default clock snapshots: Yes
+    Default clock class:
+      Name: monotonic
+      Description: Monotonic Clock
+      Frequency (Hz): 1,000,000,000
+      Precision (cycles): 0
+      Offset (s): 1,594,406,328
+      Offset (cycles): 768,346,378
+      Origin is Unix epoch: Yes
+      UUID: 81a04b89-9028-4d3e-a28d-5fbd53a8eb9d
+    Packet context field class: Structure (1 member):
+      cpu_id: Unsigned integer (32-bit, Base 10)
+    Event class `my_app:signe_de_pia$$e` (ID 0):
+      Log level: Debug (line)
+      Payload field class: Structure (0 members)
+    Event class `my_app:signe_de_pia$$e_2` (ID 1):
+      Log level: Debug (line)
+      Payload field class: Structure (0 members)
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream beginning:
+  Name: stream-1
+  Trace:
+    UUID: 0339cd08-892d-404c-9291-64c1a8a74c81
+    Environment (10 entries):
+      architecture_bit_width: 64
+      domain: ust
+      hostname: raton
+      trace_creation_datetime: 20200715T174253-0400
+      trace_name: barney_descontie
+      tracer_buffering_id: 1000
+      tracer_buffering_scheme: uid
+      tracer_major: 2
+      tracer_minor: 12
+      tracer_name: lttng-ust
+    Stream (ID 0, Class ID 0)
+
+[443,073,474,574,097 cycles, 1,594,849,402,242,920,475 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet beginning:
+  Context:
+    cpu_id: 0
+
+[443,073,484,867,537 cycles, 1,594,849,402,253,213,915 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `my_app:signe_de_pia$$e` (Class ID 0):
+  Payload: Empty
+
+[443,076,225,270,435 cycles, 1,594,849,404,993,616,813 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet end
+
+[443,087,407,631,276 cycles, 1,594,849,416,175,977,654 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet beginning:
+  Context:
+    cpu_id: 0
+
+[443,087,407,631,276 cycles, 1,594,849,416,175,977,654 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `my_app:signe_de_pia$$e` (Class ID 0):
+  Payload: Empty
+
+[443,087,407,643,172 cycles, 1,594,849,416,175,989,550 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Event `my_app:signe_de_pia$$e_2` (Class ID 1):
+  Payload: Empty
+
+[443,089,152,508,997 cycles, 1,594,849,417,920,855,375 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Packet end
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 0}
+Stream end
diff --git a/tests/data/plugins/src.ctf.lttng-live/split-metadata.json b/tests/data/plugins/src.ctf.lttng-live/split-metadata.json
new file mode 100644 (file)
index 0000000..867e94d
--- /dev/null
@@ -0,0 +1,27 @@
+[
+    {
+       "name": "split-metadata",
+        "id": 0,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "live/split-metadata/",
+                "beacons": {
+                },
+                "metadata-sections": [
+                  {
+                    "line": 1,
+                    "timestamp": 443073474574097
+                  },
+                  "empty",
+                  {
+                    "line": 112,
+                    "timestamp": 443087407631276
+                  }
+                ]
+            }
+        ]
+    }
+]
diff --git a/tests/data/plugins/src.ctf.lttng-live/split_metadata.expect b/tests/data/plugins/src.ctf.lttng-live/split_metadata.expect
deleted file mode 100644 (file)
index 17b6ccb..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-Trace class:
-  Stream class (ID 0):
-    Supports packets: Yes
-    Packets have beginning default clock snapshot: Yes
-    Packets have end default clock snapshot: Yes
-    Supports discarded events: Yes
-    Discarded events have default clock snapshots: Yes
-    Supports discarded packets: Yes
-    Discarded packets have default clock snapshots: Yes
-    Default clock class:
-      Name: monotonic
-      Description: Monotonic Clock
-      Frequency (Hz): 1,000,000,000
-      Precision (cycles): 0
-      Offset (s): 1,594,406,328
-      Offset (cycles): 768,346,378
-      Origin is Unix epoch: Yes
-      UUID: 81a04b89-9028-4d3e-a28d-5fbd53a8eb9d
-    Packet context field class: Structure (1 member):
-      cpu_id: Unsigned integer (32-bit, Base 10)
-    Event class `my_app:signe_de_pia$$e` (ID 0):
-      Log level: Debug (line)
-      Payload field class: Structure (0 members)
-    Event class `my_app:signe_de_pia$$e_2` (ID 1):
-      Log level: Debug (line)
-      Payload field class: Structure (0 members)
-
-[Unknown]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream beginning:
-  Name: stream-1
-  Trace:
-    UUID: 0339cd08-892d-404c-9291-64c1a8a74c81
-    Environment (10 entries):
-      architecture_bit_width: 64
-      domain: ust
-      hostname: raton
-      trace_creation_datetime: 20200715T174253-0400
-      trace_name: barney_descontie
-      tracer_buffering_id: 1000
-      tracer_buffering_scheme: uid
-      tracer_major: 2
-      tracer_minor: 12
-      tracer_name: lttng-ust
-    Stream (ID 0, Class ID 0)
-
-[443,073,474,574,097 cycles, 1,594,849,402,242,920,475 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Packet beginning:
-  Context:
-    cpu_id: 0
-
-[443,073,484,867,537 cycles, 1,594,849,402,253,213,915 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Event `my_app:signe_de_pia$$e` (Class ID 0):
-  Payload: Empty
-
-[443,076,225,270,435 cycles, 1,594,849,404,993,616,813 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Packet end
-
-[443,087,407,631,276 cycles, 1,594,849,416,175,977,654 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Packet beginning:
-  Context:
-    cpu_id: 0
-
-[443,087,407,631,276 cycles, 1,594,849,416,175,977,654 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Event `my_app:signe_de_pia$$e` (Class ID 0):
-  Payload: Empty
-
-[443,087,407,643,172 cycles, 1,594,849,416,175,989,550 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Event `my_app:signe_de_pia$$e_2` (Class ID 1):
-  Payload: Empty
-
-[443,089,152,508,997 cycles, 1,594,849,417,920,855,375 ns from origin]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Packet end
-
-[Unknown]
-{Trace 0, Stream class ID 0, Stream ID 0}
-Stream end
diff --git a/tests/data/plugins/src.ctf.lttng-live/split_metadata.json b/tests/data/plugins/src.ctf.lttng-live/split_metadata.json
deleted file mode 100644 (file)
index 9b5edf2..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-[
-    {
-       "name": "split_metadata",
-        "id": 0,
-        "hostname": "hostname",
-        "live-timer-freq": 1,
-        "client-count": 0,
-        "traces": [
-            {
-                "path": "live/split_metadata/",
-                "beacons": {
-                },
-                "metadata-sections": [
-                  {
-                    "line": 1,
-                    "timestamp": 443073474574097
-                  },
-                  "empty",
-                  {
-                    "line": 112,
-                    "timestamp": 443087407631276
-                  }
-                ]
-            }
-        ]
-    }
-]
diff --git a/tests/data/plugins/src.ctf.lttng-live/stored-values.expect b/tests/data/plugins/src.ctf.lttng-live/stored-values.expect
new file mode 100644 (file)
index 0000000..6b5004d
--- /dev/null
@@ -0,0 +1,65 @@
+Trace class:
+  Stream class (ID 0):
+    Supports packets: Yes
+    Packets have beginning default clock snapshot: Yes
+    Packets have end default clock snapshot: Yes
+    Supports discarded events: No
+    Supports discarded packets: No
+    Default clock class:
+      Name: default
+      Frequency (Hz): 1,000,000,000
+      Precision (cycles): 0
+      Offset (s): 0
+      Offset (cycles): 0
+      Origin is Unix epoch: No
+    Event class `event1` (ID 1):
+      Payload field class: Structure (2 members):
+        len: Unsigned integer (8-bit, Base 10)
+        seq: Dynamic array (with length field) (Length field path [Event payload: 0]):
+          Element: Unsigned integer (8-bit, Base 10)
+    Event class `event2` (ID 2):
+      Payload field class: Structure (2 members):
+        len: Unsigned integer (8-bit, Base 10)
+        seq: Dynamic array (with length field) (Length field path [Event payload: 0]):
+          Element: Unsigned integer (8-bit, Base 10)
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Stream beginning:
+  Name: stream-1
+  Trace:
+    Stream (ID 1, Class ID 0)
+
+[10 cycles, 10 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Packet beginning
+
+[10 cycles, 10 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Event `event1` (Class ID 1):
+  Payload:
+    len: 0
+    seq: Empty
+
+[11 cycles, 11 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Packet end
+
+[20 cycles, 20 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Packet beginning
+
+[20 cycles, 20 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Event `event2` (Class ID 2):
+  Payload:
+    len: 0
+    seq: Empty
+
+[21 cycles, 21 ns from origin]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Packet end
+
+[Unknown]
+{Trace 0, Stream class ID 0, Stream ID 1}
+Stream end
diff --git a/tests/data/plugins/src.ctf.lttng-live/stored-values.json b/tests/data/plugins/src.ctf.lttng-live/stored-values.json
new file mode 100644 (file)
index 0000000..4d41e8f
--- /dev/null
@@ -0,0 +1,25 @@
+[
+    {
+        "name": "stored-values",
+        "id": 2,
+        "hostname": "hostname",
+        "live-timer-freq": 1,
+        "client-count": 0,
+        "traces": [
+            {
+                "path": "stored-values",
+                "metadata-sections": [
+                    {
+                        "line": 1,
+                        "timestamp": 1
+                    },
+                    "empty",
+                    {
+                        "line": 37,
+                        "timestamp": 20
+                    }
+                ]
+            }
+        ]
+    }
+]
index c1b68d7ffeb2c978ee3496b0fce678b20da9a87a..ccc7519b847941d10548e550f5380bdf750fe2ea 100644 (file)
 # SPDX-License-Identifier: MIT
 
-SUBDIRS =
+include $(top_srcdir)/src/Makefile.common.inc
 
 AM_CPPFLAGS += -I$(top_srcdir)/tests/utils
 
+noinst_LTLIBRARIES =
+
 COMMON_TEST_LDADD = \
        $(top_builddir)/tests/utils/tap/libtap.la \
        $(top_builddir)/tests/utils/libtestcommon.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la
+       utils/liblib-utils.la \
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/logging/liblogging.la
 
+test_bt_values_SOURCES = test-bt-values.c
 test_bt_values_LDADD = $(COMMON_TEST_LDADD) \
        $(top_builddir)/src/lib/libbabeltrace2.la
 
+test_fields_bin_SOURCES = test-fields-bin.cpp
+test_fields_bin_LDADD = $(COMMON_TEST_LDADD) \
+       $(top_builddir)/src/lib/libbabeltrace2.la \
+       $(top_builddir)/src/cpp-common/vendor/fmt/libfmt.la
+
+if ENABLE_BUILT_IN_PLUGINS
+
+test_fields_bin_LDFLAGS = $(call pluginarchive,utils)
+test_fields_bin_LDADD += \
+       $(top_builddir)/src/plugins/common/param-validation/libparam-validation.la
+
+endif # ENABLE_BUILT_IN_PLUGINS
+
+test_bt_uuid_SOURCES = test-bt-uuid.c
 test_bt_uuid_LDADD = $(COMMON_TEST_LDADD)
 
+test_trace_ir_ref_SOURCES = test-trace-ir-ref.c
 test_trace_ir_ref_LDADD = $(COMMON_TEST_LDADD) \
        $(top_builddir)/src/lib/libbabeltrace2.la \
        $(top_builddir)/src/ctf-writer/libbabeltrace2-ctf-writer.la
+nodist_EXTRA_test_trace_ir_ref_SOURCES = dummy.cpp
 
+test_graph_topo_SOURCES = test-graph-topo.c
 test_graph_topo_LDADD = $(COMMON_TEST_LDADD) \
        $(top_builddir)/src/lib/libbabeltrace2.la
+nodist_EXTRA_test_graph_topo_SOURCES = dummy.cpp
 
+test_simple_sink_SOURCES = test-simple-sink.c
 test_simple_sink_LDADD = $(COMMON_TEST_LDADD) \
        $(top_builddir)/src/lib/libbabeltrace2.la
+nodist_EXTRA_test_simple_sink_SOURCES = dummy.cpp
 
+test_remove_destruction_listener_in_destruction_listener_SOURCES = \
+       test-remove-destruction-listener-in-destruction-listener.c
 test_remove_destruction_listener_in_destruction_listener_LDADD = \
        $(COMMON_TEST_LDADD) \
        $(top_builddir)/src/lib/libbabeltrace2.la
+nodist_EXTRA_test_remove_destruction_listener_in_destruction_listener_SOURCES = dummy.cpp
 
 noinst_PROGRAMS = \
-       test_bt_uuid \
-       test_bt_values \
-       test_graph_topo \
-       test_remove_destruction_listener_in_destruction_listener \
-       test_simple_sink \
-       test_trace_ir_ref
-
-test_bt_values_SOURCES = test_bt_values.c
-test_simple_sink_SOURCES = test_simple_sink.c
-test_bt_uuid_SOURCES = test_bt_uuid.c
-test_trace_ir_ref_SOURCES = test_trace_ir_ref.c
-test_graph_topo_SOURCES = test_graph_topo.c
-test_remove_destruction_listener_in_destruction_listener_SOURCES = \
-       test_remove_destruction_listener_in_destruction_listener.c
+       test-bt-uuid \
+       test-bt-values \
+       test-graph-topo \
+       test-fields-bin \
+       test-remove-destruction-listener-in-destruction-listener \
+       test-simple-sink \
+       test-trace-ir-ref
 
 if !ENABLE_BUILT_IN_PLUGINS
-noinst_PROGRAMS += plugin
-plugin_LDADD = $(COMMON_TEST_LDADD) \
+
+# test-plugins
+
+noinst_PROGRAMS += test-plugins
+test_plugins_SOURCES = test-plugins.c
+test_plugins_LDADD = \
+       $(COMMON_TEST_LDADD) \
+       $(top_builddir)/src/lib/libbabeltrace2.la
+
+noinst_LTLIBRARIES += test-plugins-plugins/plugin-minimal.la
+test_plugins_plugins_plugin_minimal_la_SOURCES = \
+       test-plugins-plugins/minimal.c
+test_plugins_plugins_plugin_minimal_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -rpath / -avoid-version -module $(LD_NOTEXT)
+test_plugins_plugins_plugin_minimal_la_LIBADD = \
+       $(top_builddir)/src/lib/libbabeltrace2.la \
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/logging/liblogging.la
+
+noinst_LTLIBRARIES += test-plugins-plugins/plugin-sfs.la
+test_plugins_plugins_plugin_sfs_la_SOURCES = test-plugins-plugins/sfs.c
+test_plugins_plugins_plugin_sfs_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -rpath / -avoid-version -module $(LD_NOTEXT)
+test_plugins_plugins_plugin_sfs_la_LIBADD = \
+       $(top_builddir)/src/lib/libbabeltrace2.la \
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/logging/liblogging.la
+
+# test-plugin-init-fail
+
+noinst_PROGRAMS +=  test-plugin-init-fail
+test_plugin_init_fail_SOURCES = test-plugin-init-fail.cpp
+test_plugin_init_fail_LDADD = \
+       $(COMMON_TEST_LDADD) \
+       $(top_builddir)/src/cpp-common/vendor/fmt/libfmt.la \
        $(top_builddir)/src/lib/libbabeltrace2.la
-plugin_SOURCES = plugin.c
-SUBDIRS += test-plugin-plugins
+
+noinst_LTLIBRARIES += test-plugin-init-fail-plugin/plugin-init-fail.la
+
+test_plugin_init_fail_plugin_plugin_init_fail_la_SOURCES = \
+       test-plugin-init-fail-plugin/plugin-init-fail.cpp
+test_plugin_init_fail_plugin_plugin_init_fail_la_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(LT_NO_UNDEFINED) \
+       -rpath / -avoid-version -module $(LD_NOTEXT)
+test_plugin_init_fail_plugin_plugin_init_fail_la_LIBADD = \
+       $(top_builddir)/src/lib/libbabeltrace2.la
+
 endif
 
-dist_check_SCRIPTS = test_plugin
+dist_check_SCRIPTS = test-plugins.sh test-fields.sh
+
+# utils
+
+noinst_LTLIBRARIES += utils/liblib-utils.la
+utils_liblib_utils_la_SOURCES = \
+       utils/run-in.cpp \
+       utils/run-in.hpp
 
 if HAVE_PYTHON
 if DEV_MODE
-SUBDIRS += conds
+
+# conds
+
+noinst_PROGRAMS += conds/conds-triggers
+
+conds_conds_triggers_SOURCES = \
+       conds/conds-triggers.cpp \
+       conds/utils.cpp \
+       conds/utils.hpp \
+       conds/clk-cls-compat-postconds-triggers.cpp \
+       conds/clk-cls-compat-postconds-triggers.hpp
+
+conds_conds_triggers_LDADD = \
+       $(COMMON_TEST_LDADD) \
+       $(top_builddir)/src/lib/libbabeltrace2.la \
+       $(top_builddir)/src/cpp-common/vendor/fmt/libfmt.la
+
+if ENABLE_BUILT_IN_PLUGINS
+
+conds_conds_triggers_LDFLAGS = $(call pluginarchive,utils)
+conds_conds_triggers_LDADD += \
+       $(top_builddir)/src/plugins/common/param-validation/libparam-validation.la
+
+endif # ENABLE_BUILT_IN_PLUGINS
+
+dist_check_SCRIPTS += conds/test-conds.sh conds/test.py
+
 endif
 endif
diff --git a/tests/lib/conds/Makefile.am b/tests/lib/conds/Makefile.am
deleted file mode 100644 (file)
index 7b0b6d3..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-AM_CPPFLAGS += -I$(top_srcdir)/tests/utils
-
-conds_triggers_SOURCES = conds-triggers.c utils.c utils.h
-conds_triggers_LDADD = \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/lib/libbabeltrace2.la
-
-noinst_PROGRAMS = conds-triggers
-
-dist_check_SCRIPTS = test_conds test.py
diff --git a/tests/lib/conds/clk-cls-compat-postconds-triggers.cpp b/tests/lib/conds/clk-cls-compat-postconds-triggers.cpp
new file mode 100644 (file)
index 0000000..aa7e687
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2024 EfficiOS Inc.
+ */
+
+#include "cpp-common/bt2s/make-unique.hpp"
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+
+#include "../utils/run-in.hpp"
+#include "clk-cls-compat-postconds-triggers.hpp"
+
+namespace {
+
+/*
+ * `RunIn` implementation to trigger clock (in)compatibility postcondition
+ * assertions.
+ */
+class ClockClsCompatRunIn final : public RunIn
+{
+public:
+    enum class MsgType
+    {
+        StreamBeg,
+        MsgIterInactivity,
+    };
+
+    using CreateClockCls = bt2::ClockClass::Shared (*)(bt2::SelfComponent);
+
+    explicit ClockClsCompatRunIn(const MsgType msgType1, const CreateClockCls createClockCls1,
+                                 const MsgType msgType2,
+                                 const CreateClockCls createClockCls2) noexcept :
+        _mMsgType1 {msgType1},
+        _mMsgType2 {msgType2}, _mCreateClockCls1 {createClockCls1}, _mCreateClockCls2 {
+                                                                        createClockCls2}
+    {
+    }
+
+    void onMsgIterNext(bt2::SelfMessageIterator self, bt2::ConstMessageArray& msgs) override
+    {
+        /* In case the expected assertion doesn't trigger, avoid looping indefinitely. */
+        BT_ASSERT(!_mBeenThere);
+
+        const auto traceCls = self.component().createTraceClass();
+        const auto trace = traceCls->instantiate();
+
+        msgs.append(this->_createOneMsg(self, _mMsgType1, _mCreateClockCls1, *trace));
+        msgs.append(this->_createOneMsg(self, _mMsgType2, _mCreateClockCls2, *trace));
+        _mBeenThere = true;
+    }
+
+private:
+    static bt2::Message::Shared _createOneMsg(const bt2::SelfMessageIterator self,
+                                              const MsgType msgType,
+                                              const CreateClockCls createClockCls,
+                                              const bt2::Trace trace)
+    {
+        const auto clockCls = createClockCls(self.component());
+
+        switch (msgType) {
+        case MsgType::StreamBeg:
+        {
+            const auto streamCls = trace.cls().createStreamClass();
+
+            if (clockCls) {
+                streamCls->defaultClockClass(*clockCls);
+            }
+
+            return self.createStreamBeginningMessage(*streamCls->instantiate(trace));
+        }
+
+        case MsgType::MsgIterInactivity:
+            BT_ASSERT(clockCls);
+            return self.createMessageIteratorInactivityMessage(*clockCls, 12);
+        };
+
+        bt_common_abort();
+    }
+
+    MsgType _mMsgType1, _mMsgType2;
+    CreateClockCls _mCreateClockCls1, _mCreateClockCls2;
+    bool _mBeenThere = false;
+};
+
+__attribute__((used)) const char *format_as(const ClockClsCompatRunIn::MsgType msgType)
+{
+    switch (msgType) {
+    case ClockClsCompatRunIn::MsgType::StreamBeg:
+        return "sb";
+
+    case ClockClsCompatRunIn::MsgType::MsgIterInactivity:
+        return "mii";
+    }
+
+    bt_common_abort();
+}
+
+bt2::ClockClass::Shared noClockClass(bt2::SelfComponent) noexcept
+{
+    return bt2::ClockClass::Shared {};
+}
+
+const bt2c::Uuid uuidA {"f00aaf65-ebec-4eeb-85b2-fc255cf1aa8a"};
+const bt2c::Uuid uuidB {"03482981-a77b-4d7b-94c4-592bf9e91785"};
+
+} /* namespace */
+
+/*
+ * Add clock class compatibility postcondition failures triggers.
+ *
+ * Each trigger below makes a message iterator return two messages with
+ * incompatible clock classes, leading to a postcondition failure.
+ */
+void addClkClsCompatTriggers(CondTriggers& triggers)
+{
+    const auto addValidCases = [&triggers](
+                                   const ClockClsCompatRunIn::CreateClockCls createClockCls1,
+                                   const ClockClsCompatRunIn::CreateClockCls createClockCls2,
+                                   const char * const condId) {
+        /*
+         * Add triggers for all possible combinations of message types.
+         *
+         * It's not possible to create message iterator inactivity messages
+         * without a clock class.
+         */
+        static constexpr std::array<ClockClsCompatRunIn::MsgType, 2> msgTypes {
+            ClockClsCompatRunIn::MsgType::StreamBeg,
+            ClockClsCompatRunIn::MsgType::MsgIterInactivity,
+        };
+
+        const auto isInvalidCase = [](const ClockClsCompatRunIn::MsgType msgType,
+                                      const ClockClsCompatRunIn::CreateClockCls createClockCls) {
+            return msgType == ClockClsCompatRunIn::MsgType::MsgIterInactivity &&
+                   createClockCls == noClockClass;
+        };
+
+        for (const auto msgType1 : msgTypes) {
+            if (isInvalidCase(msgType1, createClockCls1)) {
+                continue;
+            }
+
+            for (const auto msgType2 : msgTypes) {
+                if (isInvalidCase(msgType2, createClockCls2)) {
+                    continue;
+                }
+
+                triggers.emplace_back(bt2s::make_unique<RunInCondTrigger<ClockClsCompatRunIn>>(
+                    ClockClsCompatRunIn {msgType1, createClockCls1, msgType2, createClockCls2},
+                    CondTrigger::Type::Post, condId, fmt::format("{}-{}", msgType1, msgType2)));
+            }
+        }
+    };
+
+    addValidCases(
+        noClockClass,
+        [](const bt2::SelfComponent self) {
+            return self.createClockClass();
+        },
+        "message-iterator-class-next-method:stream-class-has-no-clock-class");
+
+    addValidCases(
+        [](const bt2::SelfComponent self) {
+            return self.createClockClass();
+        },
+        noClockClass,
+        "message-iterator-class-next-method:stream-class-has-clock-class-with-unix-epoch-origin");
+
+    addValidCases(
+        [](const bt2::SelfComponent self) {
+            return self.createClockClass();
+        },
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false);
+            return clockCls;
+        },
+        "message-iterator-class-next-method:clock-class-has-unix-epoch-origin");
+
+    addValidCases(
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false).uuid(uuidA);
+            return clockCls;
+        },
+        noClockClass, "message-iterator-class-next-method:stream-class-has-clock-class-with-uuid");
+
+    addValidCases(
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false).uuid(uuidA);
+            return clockCls;
+        },
+        [](const bt2::SelfComponent self) {
+            return self.createClockClass();
+        },
+        "message-iterator-class-next-method:clock-class-has-non-unix-epoch-origin");
+
+    addValidCases(
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false).uuid(uuidA);
+            return clockCls;
+        },
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false);
+            return clockCls;
+        },
+        "message-iterator-class-next-method:clock-class-has-uuid");
+
+    addValidCases(
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false).uuid(uuidA);
+            return clockCls;
+        },
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false).uuid(uuidB);
+            return clockCls;
+        },
+        "message-iterator-class-next-method:clock-class-has-expected-uuid");
+
+    addValidCases(
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false);
+            return clockCls;
+        },
+        noClockClass, "message-iterator-class-next-method:stream-class-has-clock-class");
+
+    addValidCases(
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false);
+            return clockCls;
+        },
+        [](const bt2::SelfComponent self) {
+            const auto clockCls = self.createClockClass();
+
+            clockCls->originIsUnixEpoch(false);
+            return clockCls;
+        },
+        "message-iterator-class-next-method:clock-class-is-expected");
+}
diff --git a/tests/lib/conds/clk-cls-compat-postconds-triggers.hpp b/tests/lib/conds/clk-cls-compat-postconds-triggers.hpp
new file mode 100644 (file)
index 0000000..cabf25c
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2024 EfficiOS Inc.
+ */
+
+#ifndef TESTS_LIB_CONDS_CLK_CLS_COMPAT_POSTCONDS_TRIGGERS_HPP
+#define TESTS_LIB_CONDS_CLK_CLS_COMPAT_POSTCONDS_TRIGGERS_HPP
+
+#include "utils.hpp"
+
+void addClkClsCompatTriggers(CondTriggers& triggers);
+
+#endif
diff --git a/tests/lib/conds/conds-triggers.c b/tests/lib/conds/conds-triggers.c
deleted file mode 100644 (file)
index 810af97..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-
-#include "common/assert.h"
-#include "utils.h"
-
-static
-bt_field_class *get_uint_fc(bt_self_component *self_comp)
-{
-       bt_trace_class *tc = bt_trace_class_create(self_comp);
-       bt_field_class *fc;
-
-       BT_ASSERT(tc);
-       fc = bt_field_class_integer_unsigned_create(tc);
-       BT_ASSERT(fc);
-       return fc;
-}
-
-static
-void trigger_fc_int_set_field_value_range_n_0(bt_self_component *self_comp)
-{
-       bt_field_class_integer_set_field_value_range(get_uint_fc(self_comp), 0);
-}
-
-static
-void trigger_fc_int_set_field_value_range_n_gt_64(bt_self_component *self_comp)
-{
-       bt_field_class_integer_set_field_value_range(get_uint_fc(self_comp),
-               65);
-}
-
-static
-void trigger_fc_int_set_field_value_range_null(bt_self_component *self_comp)
-{
-       bt_field_class_integer_set_field_value_range(NULL, 23);
-}
-
-static
-const struct cond_trigger triggers[] = {
-       COND_TRIGGER_PRE_RUN_IN_COMP_CLS_INIT(
-               "pre:field-class-integer-set-field-value-range:valid-n",
-               "0",
-               trigger_fc_int_set_field_value_range_n_0
-       ),
-       COND_TRIGGER_PRE_RUN_IN_COMP_CLS_INIT(
-               "pre:field-class-integer-set-field-value-range:valid-n",
-               "gt-64",
-               trigger_fc_int_set_field_value_range_n_gt_64
-       ),
-       COND_TRIGGER_PRE_RUN_IN_COMP_CLS_INIT(
-               "pre:field-class-integer-set-field-value-range:not-null:field-class",
-               NULL,
-               trigger_fc_int_set_field_value_range_null
-       ),
-};
-
-int main(int argc, const char *argv[])
-{
-       cond_main(argc, argv, triggers, sizeof(triggers) / sizeof(*triggers));
-       return 0;
-}
diff --git a/tests/lib/conds/conds-triggers.cpp b/tests/lib/conds/conds-triggers.cpp
new file mode 100644 (file)
index 0000000..1c174c0
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <utility>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2/graph.hpp"
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/bt2c/span.hpp"
+#include "cpp-common/bt2s/make-unique.hpp"
+
+#include "clk-cls-compat-postconds-triggers.hpp"
+#include "utils.hpp"
+
+namespace {
+
+/*
+ * Creates a simple condition trigger, calling `func`.
+ */
+template <typename FuncT>
+CondTrigger::UP makeSimpleTrigger(FuncT&& func, const CondTrigger::Type type,
+                                  const std::string& condId,
+                                  const bt2c::CStringView nameSuffix = {})
+{
+    return bt2s::make_unique<SimpleCondTrigger>(std::forward<FuncT>(func), type, condId,
+                                                nameSuffix);
+}
+
+using OnCompInitFunc = std::function<void(bt2::SelfComponent)>;
+
+/*
+ * A "run in" class that delegates the execution to stored callables.
+ *
+ * Use the makeRunIn*Trigger() helpers below.
+ */
+class RunInDelegator final : public RunIn
+{
+public:
+    static RunInDelegator makeOnCompInit(OnCompInitFunc func)
+    {
+        return RunInDelegator {std::move(func)};
+    }
+
+    void onCompInit(const bt2::SelfComponent self) override
+    {
+        if (_mOnCompInitFunc) {
+            _mOnCompInitFunc(self);
+        }
+    }
+
+private:
+    explicit RunInDelegator(OnCompInitFunc onCompInitFunc) :
+        _mOnCompInitFunc {std::move(onCompInitFunc)}
+    {
+    }
+
+    OnCompInitFunc _mOnCompInitFunc;
+};
+
+/*
+ * Creates a condition trigger, calling `func` in a component
+ * initialization context.
+ */
+CondTrigger::UP makeRunInCompInitTrigger(OnCompInitFunc func, const CondTrigger::Type type,
+                                         const std::string& condId,
+                                         const bt2c::CStringView nameSuffix = {})
+{
+    return bt2s::make_unique<RunInCondTrigger<RunInDelegator>>(
+        RunInDelegator::makeOnCompInit(std::move(func)), type, condId, nameSuffix);
+}
+
+bt2::IntegerFieldClass::Shared createUIntFc(const bt2::SelfComponent self)
+{
+    return self.createTraceClass()->createUnsignedIntegerFieldClass();
+}
+
+} /* namespace */
+
+int main(const int argc, const char ** const argv)
+{
+    CondTriggers triggers;
+
+    triggers.emplace_back(makeSimpleTrigger(
+        [] {
+            bt2::Graph::create(292);
+        },
+        CondTrigger::Type::Pre, "graph-create:valid-mip-version"));
+
+    triggers.emplace_back(makeRunInCompInitTrigger(
+        [](const bt2::SelfComponent self) {
+            createUIntFc(self)->fieldValueRange(0);
+        },
+        CondTrigger::Type::Pre, "field-class-integer-set-field-value-range:valid-n", "0"));
+
+    triggers.emplace_back(makeRunInCompInitTrigger(
+        [](const bt2::SelfComponent self) {
+            createUIntFc(self)->fieldValueRange(65);
+        },
+        CondTrigger::Type::Pre, "field-class-integer-set-field-value-range:valid-n", "gt-64"));
+
+    triggers.emplace_back(makeSimpleTrigger(
+        [] {
+            bt_field_class_integer_set_field_value_range(nullptr, 23);
+        },
+        CondTrigger::Type::Pre, "field-class-integer-set-field-value-range:not-null:field-class"));
+
+    addClkClsCompatTriggers(triggers);
+    condMain(bt2c::makeSpan(argv, argc), triggers);
+}
diff --git a/tests/lib/conds/test-conds.sh b/tests/lib/conds/test-conds.sh
new file mode 100755 (executable)
index 0000000..37e71c5
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+reldir=lib/conds
+export BT_TESTS_LIB_CONDS_TRIGGER_BIN="$BT_TESTS_BUILDDIR/$reldir/conds-triggers"
+
+if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
+       BT_TESTS_LIB_CONDS_TRIGGER_BIN="$BT_TESTS_LIB_CONDS_TRIGGER_BIN.exe"
+fi
+
+bt_run_py_test "$BT_TESTS_SRCDIR/$reldir" test.py
index d8e5fe2e8d803b7ca0156157ca712f2b58be5bb7..2bbaa7183325af8a6d8868af82626fbeba397d2f 100644 (file)
@@ -2,15 +2,17 @@
 #
 # Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
 
-import unittest
-import subprocess
-import functools
-import signal
+# pyright: strict, reportTypeCommentUsage=false, reportMissingTypeStubs=false
+
 import os
-import os.path
 import re
-import json
+import signal
+import os.path
+import unittest
+import functools
+import subprocess
 
+import tjson
 
 # the `conds-triggers` program's full path
 _CONDS_TRIGGERS_PATH = os.environ["BT_TESTS_LIB_CONDS_TRIGGER_BIN"]
@@ -23,7 +25,7 @@ class LibPrePostCondsTestCase(unittest.TestCase):
 
 # a condition trigger descriptor (base)
 class _CondTriggerDescriptor:
-    def __init__(self, index, trigger_name, cond_id):
+    def __init__(self, index: int, trigger_name: str, cond_id: str):
         self._index = index
         self._trigger_name = trigger_name
         self._cond_id = cond_id
@@ -56,7 +58,7 @@ class _PostCondTriggerDescriptor(_CondTriggerDescriptor):
 
 
 # test method template for `LibPrePostCondsTestCase`
-def _test(self, descriptor):
+def _test(self: unittest.TestCase, descriptor: _CondTriggerDescriptor):
     # Execute:
     #
     #     $ conds-triggers run <index>
@@ -89,13 +91,13 @@ def _test(self, descriptor):
 # Condition trigger descriptors from the JSON array returned by
 #
 #     $ conds-triggers list
-def _cond_trigger_descriptors_from_json(json_descr_array):
-    descriptors = []
-    descriptor_names = set()
+def _cond_trigger_descriptors_from_json(json_descr_array: tjson.ArrayVal):
+    descriptors = []  # type: list[_CondTriggerDescriptor]
+    descriptor_names = set()  # type: set[str]
 
-    for index, json_descr in enumerate(json_descr_array):
+    for index, json_descr in enumerate(json_descr_array.iter(tjson.ObjVal)):
         # sanity check: check for duplicate
-        trigger_name = json_descr["name"]
+        trigger_name = json_descr.at("name", tjson.StrVal).val
 
         if trigger_name in descriptor_names:
             raise ValueError(
@@ -103,7 +105,7 @@ def _cond_trigger_descriptors_from_json(json_descr_array):
             )
 
         # condition ID
-        cond_id = json_descr["cond-id"]
+        cond_id = json_descr.at("cond-id", tjson.StrVal).val
 
         if cond_id.startswith("pre"):
             cond_type = _PreCondTriggerDescriptor
@@ -122,8 +124,11 @@ def _cond_trigger_descriptors_from_json(json_descr_array):
 def _create_tests():
     # Execute `conds-triggers list` to get a JSON array of condition
     # trigger descriptors.
-    json_descr_array = json.loads(
-        subprocess.check_output([_CONDS_TRIGGERS_PATH, "list"], universal_newlines=True)
+    json_descr_array = tjson.loads(
+        subprocess.check_output(
+            [_CONDS_TRIGGERS_PATH, "list"], universal_newlines=True
+        ),
+        tjson.ArrayVal,
     )
 
     # get condition trigger descriptor objects from JSON
diff --git a/tests/lib/conds/test_conds b/tests/lib/conds/test_conds
deleted file mode 100755 (executable)
index af66f6d..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-reldir=lib/conds
-export BT_TESTS_LIB_CONDS_TRIGGER_BIN="$BT_TESTS_BUILDDIR/$reldir/conds-triggers"
-
-if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
-       BT_TESTS_LIB_CONDS_TRIGGER_BIN="$BT_TESTS_LIB_CONDS_TRIGGER_BIN.exe"
-fi
-
-run_python_bt2_test "$BT_TESTS_SRCDIR/$reldir" test.py
diff --git a/tests/lib/conds/utils.c b/tests/lib/conds/utils.c
deleted file mode 100644 (file)
index 8fb5bc8..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <babeltrace2/babeltrace.h>
-#include <glib.h>
-
-#include "common/assert.h"
-#include "utils.h"
-
-typedef void (* run_in_comp_cls_init_func)(
-               bt_self_component *self_comp, void *user_data);
-
-struct comp_cls_init_method_data {
-       run_in_comp_cls_init_func func;
-       void *user_data;
-};
-
-static
-bt_component_class_initialize_method_status comp_cls_init(
-               bt_self_component_source *self_comp,
-               bt_self_component_source_configuration *conf,
-               const bt_value *params, void *init_method_data)
-{
-       struct comp_cls_init_method_data *data = init_method_data;
-
-       /* Call user function which is expected to abort */
-       data->func(bt_self_component_source_as_self_component(self_comp),
-               data->user_data);
-
-       /* Never reached! */
-       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-}
-
-static
-bt_message_iterator_class_next_method_status msg_iter_cls_next(
-               bt_self_message_iterator *self_msg_iter,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       /* Not used */
-       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-}
-
-static
-void run_in_comp_cls_init(run_in_comp_cls_init_func func,
-               void *user_data)
-{
-       bt_message_iterator_class *msg_iter_cls;
-       bt_component_class_source *comp_cls;
-       bt_component_class_set_method_status set_method_status;
-       bt_graph *graph;
-       struct comp_cls_init_method_data init_method_data = {
-               .func = func,
-               .user_data = user_data,
-       };
-
-       /* Create component class */
-       msg_iter_cls = bt_message_iterator_class_create(msg_iter_cls_next);
-       BT_ASSERT(msg_iter_cls);
-       comp_cls = bt_component_class_source_create("yo", msg_iter_cls);
-       BT_ASSERT(comp_cls);
-       set_method_status = bt_component_class_source_set_initialize_method(
-               comp_cls, comp_cls_init);
-       BT_ASSERT(set_method_status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
-
-       /* Create graph */
-       graph = bt_graph_create(0);
-       BT_ASSERT(graph);
-
-       /*
-        * Add source component: this calls the initialization method,
-        * calling `func`.
-        */
-       (void) bt_graph_add_source_component_with_initialize_method_data(graph,
-                       comp_cls, "whatever", NULL, &init_method_data,
-                       BT_LOGGING_LEVEL_NONE, NULL);
-
-       /*
-        * This point is not expected to be reached as func() is
-        * expected to abort.
-        */
-}
-
-static
-void run_in_comp_cls_init_defer(bt_self_component *self_comp,
-               void *user_data)
-{
-       cond_trigger_run_in_comp_cls_init_func user_func = user_data;
-
-       user_func(self_comp);
-}
-
-static
-void run_trigger(const struct cond_trigger *trigger)
-{
-       switch (trigger->func_type) {
-       case COND_TRIGGER_FUNC_TYPE_BASIC:
-               trigger->func.basic();
-               break;
-       case COND_TRIGGER_FUNC_TYPE_RUN_IN_COMP_CLS_INIT:
-               run_in_comp_cls_init(run_in_comp_cls_init_defer,
-                       trigger->func.run_in_comp_cls_init);
-               break;
-       default:
-               abort();
-       }
-}
-
-static
-void escape_json_string(const char *str, GString *escaped_str)
-{
-       g_string_assign(escaped_str, "");
-
-       for (const char *ch = str; *ch; ch++) {
-               if (*ch == '\\' || *ch == '"') {
-                       g_string_append_c(escaped_str, '\\');
-               }
-
-               g_string_append_c(escaped_str, *ch);
-       }
-}
-
-static
-void list_triggers(const struct cond_trigger triggers[], size_t trigger_count)
-{
-       GString *escaped_str = g_string_new(NULL);
-       size_t i;
-
-       BT_ASSERT(escaped_str);
-       printf("[");
-
-       for (i = 0; i < trigger_count; i++) {
-               const struct cond_trigger *trigger = &triggers[i];
-
-               /* Condition ID */
-               escape_json_string(trigger->cond_id, escaped_str);
-               printf("{\"cond-id\":\"%s\",", escaped_str->str);
-
-               /* Name starts with condition ID */
-               printf("\"name\":\"%s", escaped_str->str);
-
-               if (trigger->suffix) {
-                       escape_json_string(trigger->suffix, escaped_str);
-                       printf("-%s", escaped_str->str);
-               }
-
-               printf("\"}");
-
-               if (i < trigger_count - 1) {
-                       /* Comma between objects */
-                       printf(",");
-               }
-       }
-
-       printf("]");
-       g_string_free(escaped_str, TRUE);
-       fflush(stdout);
-}
-
-void cond_main(int argc, const char *argv[],
-               const struct cond_trigger triggers[], size_t trigger_count)
-{
-       BT_ASSERT(argc >= 2);
-
-       if (strcmp(argv[1], "list") == 0) {
-               list_triggers(triggers, trigger_count);
-       } else if (strcmp(argv[1], "run") == 0) {
-               int index;
-
-               BT_ASSERT(argc >= 3);
-               index = atoi(argv[2]);
-               BT_ASSERT(index >= 0 && index < trigger_count);
-               run_trigger(&triggers[index]);
-       }
-}
diff --git a/tests/lib/conds/utils.cpp b/tests/lib/conds/utils.cpp
new file mode 100644 (file)
index 0000000..4d4e3b1
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "cpp-common/vendor/fmt/core.h"
+#include "cpp-common/vendor/nlohmann/json.hpp"
+
+#include "utils.hpp"
+
+CondTrigger::CondTrigger(const Type type, const std::string& condId,
+                         const bt2c::CStringView nameSuffix) noexcept :
+    _mType {type},
+    _mCondId {fmt::format("{}:{}", type == Type::Pre ? "pre" : "post", condId)},
+    _mName {fmt::format("{}{}{}", condId, nameSuffix ? "-" : "", nameSuffix ? nameSuffix : "")}
+{
+}
+
+SimpleCondTrigger::SimpleCondTrigger(std::function<void()> func, const Type type,
+                                     const std::string& condId,
+                                     const bt2c::CStringView nameSuffix) :
+    CondTrigger {type, condId, nameSuffix},
+    _mFunc {std::move(func)}
+{
+}
+
+namespace {
+
+void listCondTriggers(const CondTriggers& condTriggers) noexcept
+{
+    auto condTriggerArray = nlohmann::json::array();
+
+    for (const auto& condTrigger : condTriggers) {
+        condTriggerArray.push_back(nlohmann::json {
+            {"cond-id", condTrigger->condId()},
+            {"name", condTrigger->name()},
+        });
+    }
+
+    fmt::println("{}", condTriggerArray.dump());
+}
+
+} /* namespace */
+
+void condMain(const bt2s::span<const char * const> argv, const CondTriggers& condTriggers) noexcept
+{
+    BT_ASSERT(argv.size() >= 2);
+
+    if (strcmp(argv[1], "list") == 0) {
+        listCondTriggers(condTriggers);
+    } else if (strcmp(argv[1], "run") == 0) {
+        /*
+         * It's expected that calling `*condTriggers[index]` below
+         * aborts (calls bt_common_abort()). In this testing context, we
+         * don't want any custom abortion command to run.
+         */
+        g_unsetenv("BABELTRACE_EXEC_ON_ABORT");
+
+        /* Call the trigger */
+        BT_ASSERT(argv.size() >= 3);
+
+        const auto index = atoi(argv[2]);
+
+        BT_ASSERT(index >= 0 && index < condTriggers.size());
+        (*condTriggers[index])();
+    }
+}
diff --git a/tests/lib/conds/utils.h b/tests/lib/conds/utils.h
deleted file mode 100644 (file)
index 1a77bde..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef TESTS_LIB_CONDS_UTILS_H
-#define TESTS_LIB_CONDS_UTILS_H
-
-enum cond_trigger_func_type {
-       COND_TRIGGER_FUNC_TYPE_BASIC,
-       COND_TRIGGER_FUNC_TYPE_RUN_IN_COMP_CLS_INIT,
-};
-
-enum cond_trigger_type {
-       COND_TRIGGER_TYPE_PRE,
-       COND_TRIGGER_TYPE_POST,
-};
-
-typedef void (* cond_trigger_basic_func)(void);
-typedef void (* cond_trigger_run_in_comp_cls_init_func)(bt_self_component *);
-
-struct cond_trigger {
-       enum cond_trigger_type type;
-       enum cond_trigger_func_type func_type;
-       const char *cond_id;
-       const char *suffix;
-       union {
-               cond_trigger_basic_func basic;
-               cond_trigger_run_in_comp_cls_init_func run_in_comp_cls_init;
-       } func;
-};
-
-#define COND_TRIGGER_PRE_BASIC(_cond_id, _suffix, _func)               \
-       {                                                               \
-               .type = COND_TRIGGER_TYPE_PRE,                          \
-               .func_type = COND_TRIGGER_FUNC_TYPE_BASIC,              \
-               .cond_id = _cond_id,                                    \
-               .suffix = _suffix,                                      \
-               .func = {                                               \
-                       .basic = _func,                                 \
-               }                                                       \
-       }
-
-#define COND_TRIGGER_POST_BASIC(_cond_id, _suffix, _func)              \
-       {                                                               \
-               .type = COND_TRIGGER_TYPE_POST,                         \
-               .func_type = COND_TRIGGER_FUNC_TYPE_BASIC,              \
-               .cond_id = _cond_id,                                    \
-               .suffix = _suffix,                                      \
-               .func = {                                               \
-                       .basic = _func,                                 \
-               }                                                       \
-       }
-
-#define COND_TRIGGER_PRE_RUN_IN_COMP_CLS_INIT(_cond_id, _suffix, _func)        \
-       {                                                               \
-               .type = COND_TRIGGER_TYPE_PRE,                          \
-               .func_type = COND_TRIGGER_FUNC_TYPE_RUN_IN_COMP_CLS_INIT, \
-               .cond_id = _cond_id,                                    \
-               .suffix = _suffix,                                      \
-               .func = {                                               \
-                       .run_in_comp_cls_init = _func,                  \
-               }                                                       \
-       }
-
-#define COND_TRIGGER_POST_RUN_IN_COMP_CLS_INIT(_cond_id, _suffix, _func) \
-       {                                                               \
-               .type = COND_TRIGGER_TYPE_POST,                         \
-               .func_type = COND_TRIGGER_FUNC_TYPE_RUN_IN_COMP_CLS_INIT, \
-               .cond_id = _cond_id,                                    \
-               .suffix = _suffix,                                      \
-               .func = {                                               \
-                       .run_in_comp_cls_init = _func,                  \
-               }                                                       \
-       }
-
-void cond_main(int argc, const char *argv[],
-               const struct cond_trigger triggers[],
-               size_t trigger_count);
-
-#endif /* TESTS_LIB_CONDS_UTILS_H */
diff --git a/tests/lib/conds/utils.hpp b/tests/lib/conds/utils.hpp
new file mode 100644 (file)
index 0000000..2ea53bc
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef TESTS_LIB_CONDS_UTILS_HPP
+#define TESTS_LIB_CONDS_UTILS_HPP
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/bt2s/span.hpp"
+
+#include "../utils/run-in.hpp"
+
+/*
+ * Abstract condition trigger class.
+ *
+ * A derived class must provide operator()() which triggers a condition
+ * of which the specific type (precondition or postcondition) and ID are
+ * provided at construction time.
+ */
+class CondTrigger
+{
+public:
+    using UP = std::unique_ptr<CondTrigger>;
+
+    /*
+     * Condition type.
+     */
+    enum class Type
+    {
+        Pre,
+        Post,
+    };
+
+protected:
+    /*
+     * Builds a condition trigger having the type `type`, the condition
+     * ID `condId` (_without_ any `pre:` or `post:` prefix), and the
+     * optional name suffix `nameSuffix`.
+     *
+     * The concatenation of `condId` and, if it's set, `-` and
+     * `*nameSuffix`, forms the name of the condition trigger. Get the
+     * name of the created condition trigger with name().
+     */
+    explicit CondTrigger(Type type, const std::string& condId,
+                         const bt2c::CStringView nameSuffix) noexcept;
+
+public:
+    virtual ~CondTrigger() = default;
+    virtual void operator()() noexcept = 0;
+
+    Type type() const noexcept
+    {
+        return _mType;
+    }
+
+    /*
+     * Condition ID, including any `pre:` or `post:` prefix.
+     */
+    const std::string& condId() const noexcept
+    {
+        return _mCondId;
+    }
+
+    const std::string& name() const noexcept
+    {
+        return _mName;
+    }
+
+private:
+    Type _mType;
+    std::string _mCondId;
+    std::string _mName;
+};
+
+/*
+ * Simple condition trigger.
+ *
+ * Implements a condition trigger where a function provided at
+ * construction time triggers a condition.
+ */
+class SimpleCondTrigger : public CondTrigger
+{
+public:
+    explicit SimpleCondTrigger(std::function<void()> func, Type type, const std::string& condId,
+                               const bt2c::CStringView nameSuffix = {});
+
+    void operator()() noexcept override
+    {
+        _mFunc();
+    }
+
+private:
+    std::function<void()> _mFunc;
+};
+
+/*
+ * Run-in condition trigger.
+ *
+ * Implements a condition trigger of which the triggering function
+ * happens in a graph or component class query context using the
+ * runIn() API.
+ */
+template <typename RunInT>
+class RunInCondTrigger : public CondTrigger
+{
+public:
+    explicit RunInCondTrigger(RunInT runIn, const Type type, const std::string& condId,
+                              const bt2c::CStringView nameSuffix = {}) :
+        CondTrigger {type, condId, nameSuffix},
+        _mRunIn {std::move(runIn)}
+    {
+    }
+
+    explicit RunInCondTrigger(const Type type, const std::string& condId,
+                              const bt2c::CStringView nameSuffix = {}) :
+        RunInCondTrigger {RunInT {}, type, condId, nameSuffix}
+    {
+    }
+
+    void operator()() noexcept override
+    {
+        runIn(_mRunIn);
+    }
+
+private:
+    RunInT _mRunIn;
+};
+
+/*
+ * List of condition triggers.
+ */
+using CondTriggers = std::vector<CondTrigger::UP>;
+
+/*
+ * The entry point of a condition trigger program.
+ *
+ * Call this from your own main() with your list of condition triggers
+ * `triggers`.
+ *
+ * Each condition trigger of `triggers` must have a unique name, as
+ * returned by CondTrigger::name().
+ *
+ * This function uses `argc` and `argv` to respond to one of the
+ * following commands:
+ *
+ * `list`:
+ *     Prints a list of condition triggers as a JSON array of objects.
+ *
+ *     Each JSON object has:
+ *
+ *     `cond-id`:
+ *         The condition ID of the trigger, as returned by
+ *         CondTrigger:condId().
+ *
+ *     `name`:
+ *         The condition ID name, as returned by CondTrigger::name().
+ *
+ * `run`:
+ *     Runs the triggering function of the condition trigger at the
+ *     index specified by the next command-line argument.
+ *
+ *     For example,
+ *
+ *         $ my-cond-trigger-program run 45
+ *
+ *     would run the function of the condition trigger `triggers[45]`.
+ *
+ *     The program is expected to abort through a libbabeltrace2
+ *     condition failure.
+ */
+void condMain(const bt2s::span<const char * const> argv, const CondTriggers& triggers) noexcept;
+
+#endif /* TESTS_LIB_CONDS_UTILS_HPP */
diff --git a/tests/lib/plugin.c b/tests/lib/plugin.c
deleted file mode 100644 (file)
index 7e3c3e7..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include "common/assert.h"
-#include <glib.h>
-#include "tap/tap.h"
-#include "common.h"
-
-#define NR_TESTS               38
-#define NON_EXISTING_PATH      "/this/hopefully/does/not/exist/5bc75f8d-0dba-4043-a509-d7984b97e42b.so"
-
-/* Those symbols are written to by some test plugins */
-static int check_env_var(const char *name)
-{
-       const char *val = getenv(name);
-
-       if (!val) {
-               return -1;
-       }
-
-       return atoi(val);
-}
-
-static void reset_test_plugin_env_vars(void)
-{
-       g_setenv("BT_TEST_PLUGIN_INITIALIZE_CALLED", "0", 1);
-       g_setenv("BT_TEST_PLUGIN_FINALIZE_CALLED", "0", 1);
-}
-
-static char *get_test_plugin_path(const char *plugin_dir,
-               const char *plugin_name)
-{
-       char *ret;
-       char *plugin_file_name;
-
-       if (asprintf(&plugin_file_name, "plugin-%s." G_MODULE_SUFFIX,
-                       plugin_name) == -1) {
-               abort();
-       }
-
-       ret = g_build_filename(plugin_dir, plugin_file_name, NULL);
-       free(plugin_file_name);
-
-       return ret;
-}
-
-static void test_minimal(const char *plugin_dir)
-{
-       const bt_plugin_set *plugin_set = NULL;
-       const bt_plugin *plugin;
-       char *minimal_path = get_test_plugin_path(plugin_dir, "minimal");
-       bt_plugin_find_all_from_file_status status;
-
-       BT_ASSERT(minimal_path);
-       diag("minimal plugin test below");
-
-       reset_test_plugin_env_vars();
-       status = bt_plugin_find_all_from_file(minimal_path, BT_FALSE,
-               &plugin_set);
-       ok(status == BT_PLUGIN_FIND_ALL_FROM_FILE_STATUS_OK,
-               "bt_plugin_find_all_from_file() succeeds with a valid file");
-       ok(plugin_set,
-               "bt_plugin_find_all_from_file() returns a plugin set");
-       ok(check_env_var("BT_TEST_PLUGIN_INITIALIZE_CALLED") == 1,
-               "plugin's initialization function is called during bt_plugin_find_all_from_file()");
-       ok(bt_plugin_set_get_plugin_count(plugin_set) == 1,
-               "bt_plugin_find_all_from_file() returns the expected number of plugins");
-       plugin = bt_plugin_set_borrow_plugin_by_index_const(plugin_set, 0);
-       ok(strcmp(bt_plugin_get_name(plugin), "test_minimal") == 0,
-               "bt_plugin_get_name() returns the expected name");
-       ok(strcmp(bt_plugin_get_description(plugin),
-               "Minimal Babeltrace plugin with no component classes") == 0,
-               "bt_plugin_get_description() returns the expected description");
-       ok(bt_plugin_get_version(plugin, NULL, NULL, NULL, NULL) ==
-               BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE,
-               "bt_plugin_get_version() fails when there's no version");
-       ok(strcmp(bt_plugin_get_author(plugin), "Janine Sutto") == 0,
-               "bt_plugin_get_author() returns the expected author");
-       ok(strcmp(bt_plugin_get_license(plugin), "Beerware") == 0,
-               "bt_plugin_get_license() returns the expected license");
-       ok(strcmp(bt_plugin_get_path(plugin), minimal_path) == 0,
-               "bt_plugin_get_path() returns the expected path");
-       ok(bt_plugin_get_source_component_class_count(plugin) == 0,
-               "bt_plugin_get_source_component_class_count() returns the expected value");
-       ok(bt_plugin_get_filter_component_class_count(plugin) == 0,
-               "bt_plugin_get_filter_component_class_count() returns the expected value");
-       ok(bt_plugin_get_sink_component_class_count(plugin) == 0,
-               "bt_plugin_get_sink_component_class_count() returns the expected value");
-       bt_plugin_set_put_ref(plugin_set);
-       ok(check_env_var("BT_TEST_PLUGIN_FINALIZE_CALLED") == 1,
-               "plugin's finalize function is called when the plugin is destroyed");
-
-       free(minimal_path);
-}
-
-static void test_sfs(const char *plugin_dir)
-{
-       const bt_plugin_set *plugin_set = NULL;
-       const bt_plugin *plugin;
-       const bt_component_class_sink *sink_comp_class;
-       const bt_component_class_source *source_comp_class;
-       const bt_component_class_filter *filter_comp_class;
-       const bt_component_sink *sink_component;
-       char *sfs_path = get_test_plugin_path(plugin_dir, "sfs");
-       unsigned int major, minor, patch;
-       const char *extra;
-       bt_value *params;
-       const bt_value *results;
-       const bt_value *object;
-       const bt_value *res_params;
-       bt_graph *graph;
-       const char *object_str;
-       bt_graph_add_component_status graph_ret;
-       bt_query_executor *query_exec;
-       int ret;
-       bt_plugin_find_all_from_file_status status;
-
-       BT_ASSERT(sfs_path);
-       diag("sfs plugin test below");
-
-       status = bt_plugin_find_all_from_file(sfs_path, BT_FALSE, &plugin_set);
-       BT_ASSERT(status == BT_PLUGIN_FIND_ALL_FROM_FILE_STATUS_OK &&
-               plugin_set && bt_plugin_set_get_plugin_count(plugin_set) == 1);
-       plugin = bt_plugin_set_borrow_plugin_by_index_const(plugin_set, 0);
-       ok(bt_plugin_get_version(plugin, &major, &minor, &patch, &extra) ==
-               BT_PROPERTY_AVAILABILITY_AVAILABLE,
-               "bt_plugin_get_version() succeeds when there's a version");
-       ok(major == 1,
-               "bt_plugin_get_version() returns the expected major version");
-       ok(minor == 2,
-               "bt_plugin_get_version() returns the expected minor version");
-       ok(patch == 3,
-               "bt_plugin_get_version() returns the expected patch version");
-       ok(strcmp(extra, "yes") == 0,
-               "bt_plugin_get_version() returns the expected extra version");
-       ok(bt_plugin_get_source_component_class_count(plugin) == 1,
-               "bt_plugin_get_source_component_class_count() returns the expected value");
-       ok(bt_plugin_get_filter_component_class_count(plugin) == 1,
-               "bt_plugin_get_filter_component_class_count() returns the expected value");
-       ok(bt_plugin_get_sink_component_class_count(plugin) == 1,
-               "bt_plugin_get_sink_component_class_count() returns the expected value");
-
-       source_comp_class = bt_plugin_borrow_source_component_class_by_name_const(
-               plugin, "source");
-       ok(source_comp_class,
-               "bt_plugin_borrow_source_component_class_by_name_const() finds a source component class");
-
-       sink_comp_class = bt_plugin_borrow_sink_component_class_by_name_const(
-               plugin, "sink");
-       ok(sink_comp_class,
-               "bt_plugin_borrow_sink_component_class_by_name_const() finds a sink component class");
-       ok(strcmp(bt_component_class_get_help(bt_component_class_sink_as_component_class_const(sink_comp_class)),
-                 "Bacon ipsum dolor amet strip steak cupim pastrami venison shoulder.\n"
-                 "Prosciutto beef ribs flank meatloaf pancetta brisket kielbasa drumstick\n"
-                 "venison tenderloin cow tail. Beef short loin shoulder meatball, sirloin\n"
-                 "ground round brisket salami cupim pork bresaola turkey bacon boudin.\n") == 0,
-               "bt_component_class_get_help() returns the expected help text");
-
-       filter_comp_class = bt_plugin_borrow_filter_component_class_by_name_const(
-               plugin, "filter");
-       ok(filter_comp_class,
-               "bt_plugin_borrow_filter_component_class_by_name_const() finds a filter component class");
-       params = bt_value_integer_signed_create_init(23);
-       BT_ASSERT(params);
-       query_exec = bt_query_executor_create(
-               bt_component_class_filter_as_component_class_const(
-                       filter_comp_class), "get-something", params);
-       BT_ASSERT(query_exec);
-       ret = bt_query_executor_query(query_exec, &results);
-       ok(ret == 0 && results, "bt_query_executor_query() succeeds");
-       BT_ASSERT(bt_value_is_array(results) && bt_value_array_get_length(results) == 2);
-       object = bt_value_array_borrow_element_by_index_const(results, 0);
-       BT_ASSERT(bt_value_is_string(object));
-       object_str = bt_value_string_get(object);
-       ok(strcmp(object_str, "get-something") == 0,
-               "bt_component_class_query() receives the expected object name");
-       res_params = bt_value_array_borrow_element_by_index_const(results, 1);
-       ok(bt_value_is_equal(res_params, params),
-               "bt_component_class_query() receives the expected parameters");
-
-       bt_component_class_sink_get_ref(sink_comp_class);
-       BT_PLUGIN_SET_PUT_REF_AND_RESET(plugin_set);
-       graph = bt_graph_create(0);
-       BT_ASSERT(graph);
-       graph_ret = bt_graph_add_sink_component(graph, sink_comp_class,
-               "the-sink", NULL, BT_LOGGING_LEVEL_NONE, &sink_component);
-       ok(graph_ret == BT_GRAPH_ADD_COMPONENT_STATUS_OK && sink_component,
-               "bt_graph_add_sink_component() still works after the plugin object is destroyed");
-       bt_graph_put_ref(graph);
-
-       free(sfs_path);
-       bt_component_class_sink_put_ref(sink_comp_class);
-       bt_value_put_ref(results);
-       bt_value_put_ref(params);
-       bt_query_executor_put_ref(query_exec);
-}
-
-static void test_create_all_from_dir(const char *plugin_dir)
-{
-       const bt_plugin_set *plugin_set;
-       bt_plugin_find_all_from_dir_status status;
-
-       diag("create from all test below");
-
-       status = bt_plugin_find_all_from_dir(NON_EXISTING_PATH, BT_FALSE,
-               BT_FALSE, &plugin_set);
-       ok(status == BT_PLUGIN_FIND_ALL_FROM_DIR_STATUS_ERROR,
-               "bt_plugin_find_all_from_dir() fails with an invalid path");
-       bt_current_thread_clear_error();
-
-       plugin_set = NULL;
-       status = bt_plugin_find_all_from_dir(plugin_dir, BT_FALSE, BT_FALSE,
-               &plugin_set);
-       ok(status == BT_PLUGIN_FIND_ALL_FROM_DIR_STATUS_OK,
-               "bt_plugin_find_all_from_dir() succeeds with a valid path");
-       ok(plugin_set,
-               "bt_plugin_find_all_from_dir() returns a plugin set with a valid path");
-
-       /* 2 or 4, if `.la` files are considered or not */
-       ok(bt_plugin_set_get_plugin_count(plugin_set) == 2 ||
-               bt_plugin_set_get_plugin_count(plugin_set) == 4,
-               "bt_plugin_find_all_from_dir() returns the expected number of plugin objects");
-
-       bt_plugin_set_put_ref(plugin_set);
-}
-
-static void test_find(const char *plugin_dir)
-{
-       int ret;
-       const bt_plugin *plugin;
-       char *plugin_path;
-       bt_plugin_find_status status;
-
-       ok(bt_plugin_find(NON_EXISTING_PATH, BT_TRUE, BT_FALSE, BT_FALSE,
-               BT_FALSE, BT_FALSE, &plugin) == BT_PLUGIN_FIND_STATUS_NOT_FOUND,
-               "bt_plugin_find() returns BT_PLUGIN_STATUS_NOT_FOUND with an unknown plugin name");
-       ret = asprintf(&plugin_path, "%s" G_SEARCHPATH_SEPARATOR_S
-                       G_DIR_SEPARATOR_S "ec1d09e5-696c-442e-b1c3-f9c6cf7f5958"
-                       G_SEARCHPATH_SEPARATOR_S G_SEARCHPATH_SEPARATOR_S
-                       G_SEARCHPATH_SEPARATOR_S "%s" G_SEARCHPATH_SEPARATOR_S
-                       "8db46494-a398-466a-9649-c765ae077629"
-                       G_SEARCHPATH_SEPARATOR_S,
-               NON_EXISTING_PATH, plugin_dir);
-       BT_ASSERT(ret > 0 && plugin_path);
-       g_setenv("BABELTRACE_PLUGIN_PATH", plugin_path, 1);
-       plugin = NULL;
-       status = bt_plugin_find("test_minimal", BT_TRUE, BT_FALSE, BT_FALSE,
-               BT_FALSE, BT_FALSE, &plugin);
-       ok(status == BT_PLUGIN_FIND_STATUS_OK,
-               "bt_plugin_find() succeeds with a plugin name it can find");
-       ok(plugin, "bt_plugin_find() returns a plugin object");
-       ok(strcmp(bt_plugin_get_author(plugin), "Janine Sutto") == 0,
-               "bt_plugin_find() finds the correct plugin for a given name");
-       BT_PLUGIN_PUT_REF_AND_RESET(plugin);
-       free(plugin_path);
-}
-
-int main(int argc, char **argv)
-{
-       int ret;
-       const char *plugin_dir;
-
-       if (argc != 2) {
-               puts("Usage: test_plugin plugin_directory");
-               ret = 1;
-               goto end;
-       }
-
-       plugin_dir = argv[1];
-       plan_tests(NR_TESTS);
-       test_minimal(plugin_dir);
-       test_sfs(plugin_dir);
-       test_create_all_from_dir(plugin_dir);
-       test_find(plugin_dir);
-       ret = exit_status();
-end:
-       return ret;
-}
diff --git a/tests/lib/test-bt-uuid.c b/tests/lib/test-bt-uuid.c
new file mode 100644 (file)
index 0000000..0c881ba
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "common/uuid.h"
+
+#include "tap/tap.h"
+
+#define NR_TESTS 23
+
+static const char valid_str_1[] = "3d260c88-75ea-47b8-a7e2-d6077c0378d9";
+static const char valid_str_2[] = "611cf3a6-a68b-4515-834f-208bc2762592";
+static const char valid_str_3[] = "1b4855cc-96de-4ae8-abe3-86449c2a43c4";
+static const char valid_str_4[] = "8ADED5B9-ACD2-439F-A60C-897403AA2AB4";
+static const char valid_str_5[] = "f109e0a2-C619-4d18-b760-20EA20E0F69A";
+
+static bt_uuid_t valid_uuid_1 = {
+       0x3d, 0x26, 0x0c, 0x88, 0x75, 0xea, 0x47, 0xb8,
+       0xa7, 0xe2, 0xd6, 0x07, 0x7c, 0x03, 0x78, 0xd9
+};
+static bt_uuid_t valid_uuid_2 = {
+       0x61, 0x1c, 0xf3, 0xa6, 0xa6, 0x8b, 0x45, 0x15,
+       0x83, 0x4f, 0x20, 0x8b, 0xc2, 0x76, 0x25, 0x92
+};
+static bt_uuid_t valid_uuid_3 = {
+       0x1b, 0x48, 0x55, 0xcc, 0x96, 0xde, 0x4a, 0xe8,
+       0xab, 0xe3, 0x86, 0x44, 0x9c, 0x2a, 0x43, 0xc4
+};
+
+static const char invalid_str_1[] = "1b485!cc-96de-4XX8-abe3-86449c2a43?4";
+static const char invalid_str_2[] = "c2e6eddb&3955&4006&be3a&70bb63bd5f25";
+static const char invalid_str_3[] = "81b1cb88-ff42-45b9-ba4d-964088ee45";
+static const char invalid_str_4[] = "2d-6c6d756574-470e-9142-a4e6ad03f143";
+static const char invalid_str_5[] = "4542ad19-9e4f-4931-8261-2101c3e089ae7";
+static const char invalid_str_6[] = "XX0123";
+
+static
+void run_test_bt_uuid_from_str(void)
+{
+       int ret;
+       bt_uuid_t uuid1;
+
+       /*
+        * Parse valid UUID strings, expect success.
+        */
+       ret = bt_uuid_from_str(valid_str_1, uuid1);
+       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_1);
+
+       ret = bt_uuid_from_str(valid_str_2, uuid1);
+       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_2);
+
+       ret = bt_uuid_from_str(valid_str_3, uuid1);
+       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_3);
+
+       ret = bt_uuid_from_str(valid_str_4, uuid1);
+       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_4);
+
+       ret = bt_uuid_from_str(valid_str_5, uuid1);
+       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_5);
+
+       /*
+        * Parse invalid UUID strings, expect failure.
+        */
+       ret = bt_uuid_from_str(invalid_str_1, uuid1);
+       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_1);
+
+       ret = bt_uuid_from_str(invalid_str_2, uuid1);
+       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_2);
+
+       ret = bt_uuid_from_str(invalid_str_3, uuid1);
+       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_3);
+
+       ret = bt_uuid_from_str(invalid_str_4, uuid1);
+       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_4);
+
+       ret = bt_uuid_from_str(invalid_str_5, uuid1);
+       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_5);
+
+       ret = bt_uuid_from_str(invalid_str_6, uuid1);
+       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_6);
+}
+
+static
+void run_test_bt_uuid_to_str(void)
+{
+       char uuid_str[BT_UUID_STR_LEN + 1];
+
+       bt_uuid_to_str(valid_uuid_1, uuid_str);
+       ok(strcmp(uuid_str, valid_str_1) == 0, "bt_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_1);
+
+       bt_uuid_to_str(valid_uuid_2, uuid_str);
+       ok(strcmp(uuid_str, valid_str_2) == 0, "bt_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_2);
+
+       bt_uuid_to_str(valid_uuid_3, uuid_str);
+       ok(strcmp(uuid_str, valid_str_3) == 0, "bt_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_3);
+}
+
+static
+void run_test_bt_uuid_compare(void)
+{
+       int ret;
+       bt_uuid_t uuid1, uuid2;
+
+       bt_uuid_from_str(valid_str_1, uuid1);
+       bt_uuid_from_str(valid_str_1, uuid2);
+       ret = bt_uuid_compare(uuid1, uuid2);
+       ok(ret == 0, "bt_uuid_compare - Compare same UUID, expect success");
+
+       bt_uuid_from_str(valid_str_2, uuid2);
+       ret = bt_uuid_compare(uuid1, uuid2);
+       ok(ret != 0, "bt_uuid_compare - Compare different UUID, expect failure");
+       ok(ret < 0, "bt_uuid_compare - Compare different UUID, expect uuid1 smaller");
+       ret = bt_uuid_compare(uuid2, uuid1);
+       ok(ret > 0, "bt_uuid_compare - Compare different UUID, expect uuid2 bigger");
+}
+
+static
+void run_test_bt_uuid_copy(void)
+{
+       int ret;
+       bt_uuid_t uuid1;
+
+       bt_uuid_copy(uuid1, valid_uuid_1);
+       ret = bt_uuid_compare(uuid1, valid_uuid_1);
+
+       ok(ret == 0, "bt_uuid_copy - Compare copied UUID with source, expect success");
+}
+
+static
+void run_test_bt_uuid_generate(void)
+{
+       int ret;
+       bt_uuid_t uuid1, uuid2;
+
+       bt_uuid_generate(uuid1);
+       bt_uuid_generate(uuid2);
+
+       ok(bt_uuid_compare(uuid1, uuid2) != 0, "bt_uuid_generate - Generated UUIDs are different");
+
+       /*
+        * Set the two most significant bits (bits 6 and 7) of the
+        * clock_seq_hi_and_reserved to zero and one, respectively.
+        */
+       ret = uuid1[8] & (1 << 6);
+       ok(ret == 0, "bt_uuid_generate - bit 6 of clock_seq_hi_and_reserved is set to zero");
+
+       ret = uuid1[8] & (1 << 7);
+       ok(ret != 0, "bt_uuid_generate - bit 7 of clock_seq_hi_and_reserved is set to one");
+
+       /*
+        * Set the four most significant bits (bits 12 through 15) of the
+        * time_hi_and_version field to the 4-bit version number from
+        * Section 4.1.3.
+        */
+       ret = uuid1[6] >> 4;
+       ok(ret == BT_UUID_VER, "bt_uuid_generate - Generated UUID version check");
+}
+
+static
+void run_test(void)
+{
+       plan_tests(NR_TESTS);
+
+       run_test_bt_uuid_from_str();
+       run_test_bt_uuid_to_str();
+       run_test_bt_uuid_compare();
+       run_test_bt_uuid_copy();
+       run_test_bt_uuid_generate();
+}
+
+int main(void)
+{
+       /* Run tap-formatted tests */
+       run_test();
+
+       return exit_status();
+}
diff --git a/tests/lib/test-bt-values.c b/tests/lib/test-bt-values.c
new file mode 100644 (file)
index 0000000..e9ec4b2
--- /dev/null
@@ -0,0 +1,1169 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc. and Linux Foundation
+ * Copyright (C) 2015 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Babeltrace value objects tests
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/assert.h"
+#include <string.h>
+#include "tap/tap.h"
+
+#define NR_TESTS 190
+
+static
+void test_null(void)
+{
+       ok(bt_value_null, "bt_value_null is not NULL");
+       ok(bt_value_is_null(bt_value_null),
+               "bt_value_null is a null value object");
+       bt_value_get_ref(bt_value_null);
+       pass("getting bt_value_null does not cause a crash");
+       bt_value_put_ref(bt_value_null);
+       pass("putting bt_value_null does not cause a crash");
+}
+
+static
+void test_bool(void)
+{
+       bt_bool value;
+       bt_value *obj;
+
+       obj = bt_value_bool_create();
+       ok(obj && bt_value_is_bool(obj),
+               "bt_value_bool_create() returns a boolean value object");
+
+       value = BT_TRUE;
+       value = bt_value_bool_get(obj);
+       ok(!value, "default boolean value object value is BT_FALSE");
+
+       bt_value_bool_set(obj, BT_FALSE);
+       bt_value_bool_set(obj, BT_TRUE);
+       value = bt_value_bool_get(obj);
+       ok(value, "bt_value_bool_set() works");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       pass("putting an existing boolean value object does not cause a crash")
+
+       value = BT_FALSE;
+       obj = bt_value_bool_create_init(BT_TRUE);
+       ok(obj && bt_value_is_bool(obj),
+               "bt_value_bool_create_init() returns a boolean value object");
+       value = bt_value_bool_get(obj);
+       ok(value,
+               "bt_value_bool_create_init() sets the appropriate initial value");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+}
+
+static
+void test_unsigned_integer(void)
+{
+       uint64_t value;
+       bt_value *obj;
+
+       obj = bt_value_integer_unsigned_create();
+       ok(obj && bt_value_is_unsigned_integer(obj),
+               "bt_value_integer_unsigned_create() returns an unsigned integer value object");
+
+       value = 1961;
+       value = bt_value_integer_unsigned_get(obj);
+       ok(value == 0, "default unsigned integer value object value is 0");
+
+       bt_value_integer_unsigned_set(obj, 98765);
+       value = bt_value_integer_unsigned_get(obj);
+       ok(value == 98765, "bt_value_integer_unsigned_bool_set() works");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       pass("putting an existing unsigned integer value object does not cause a crash")
+
+       obj = bt_value_integer_unsigned_create_init(321456987);
+       ok(obj && bt_value_is_unsigned_integer(obj),
+               "bt_value_integer_unsigned_create_init() returns an unsigned integer value object");
+       value = bt_value_integer_unsigned_get(obj);
+       ok(value == 321456987,
+               "bt_value_integer_unsigned_create_init() sets the appropriate initial value");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+}
+
+static
+void test_signed_integer(void)
+{
+       int64_t value;
+       bt_value *obj;
+
+       obj = bt_value_integer_signed_create();
+       ok(obj && bt_value_is_signed_integer(obj),
+               "bt_value_integer_signed_create() returns a signed integer value object");
+
+       value = 1961;
+       value = bt_value_integer_signed_get(obj);
+       ok(value == 0, "default signed integer value object value is 0");
+
+       bt_value_integer_signed_set(obj, 98765);
+       value = bt_value_integer_signed_get(obj);
+       ok(value == 98765, "bt_value_integer_signed_bool_set() works");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       pass("putting an existing signed integer value object does not cause a crash")
+
+       obj = bt_value_integer_signed_create_init(-321456987);
+       ok(obj && bt_value_is_signed_integer(obj),
+               "bt_value_integer_signed_create_init() returns a signed integer value object");
+       value = bt_value_integer_signed_get(obj);
+       ok(value == -321456987,
+               "bt_value_integer_signed_create_init() sets the appropriate initial value");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+}
+
+static
+void test_real(void)
+{
+       double value;
+       bt_value *obj;
+
+       obj = bt_value_real_create();
+       ok(obj && bt_value_is_real(obj),
+               "bt_value_real_create() returns a real number value object");
+
+       value = 17.34;
+       value = bt_value_real_get(obj);
+       ok(value == 0.,
+               "default real number value object value is 0");
+
+       bt_value_real_set(obj, -3.1416);
+       value = bt_value_real_get(obj);
+       ok(value == -3.1416, "bt_value_real_set() works");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       pass("putting an existing real number value object does not cause a crash")
+
+       obj = bt_value_real_create_init(33.1649758);
+       ok(obj && bt_value_is_real(obj),
+               "bt_value_real_create_init() returns a real number value object");
+       value = bt_value_real_get(obj);
+       ok(value == 33.1649758,
+               "bt_value_real_create_init() sets the appropriate initial value");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+}
+
+static
+void test_string(void)
+{
+       const char *value;
+       bt_value *obj;
+
+       obj = bt_value_string_create();
+       ok(obj && bt_value_is_string(obj),
+               "bt_value_string_create() returns a string value object");
+
+       value = bt_value_string_get(obj);
+       ok(value && strcmp(value, "") == 0,
+               "default string value object value is \"\"");
+
+       bt_value_string_set(obj, "hello worldz");
+       value = bt_value_string_get(obj);
+       ok(value && strcmp(value, "hello worldz") == 0,
+               "bt_value_string_get() works");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       pass("putting an existing string value object does not cause a crash")
+
+       obj = bt_value_string_create_init("initial value");
+       ok(obj && bt_value_is_string(obj),
+               "bt_value_string_create_init() returns a string value object");
+       value = bt_value_string_get(obj);
+       ok(value && strcmp(value, "initial value") == 0,
+               "bt_value_string_create_init() sets the appropriate initial value");
+
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+}
+
+static
+void test_array(void)
+{
+       int ret;
+       bt_bool bool_value;
+       int64_t int_value;
+       double real_value;
+       bt_value *obj;
+       const char *string_value;
+       bt_value *array_obj;
+       bt_value *appended_obj;
+
+       array_obj = bt_value_array_create();
+       ok(array_obj && bt_value_is_array(array_obj),
+               "bt_value_array_create() returns an array value object");
+       ok(bt_value_array_is_empty(array_obj),
+               "initial array value object size is 0");
+
+       obj = bt_value_integer_unsigned_create_init(345);
+       ret = bt_value_array_append_element(array_obj, obj);
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       obj = bt_value_integer_signed_create_init(-507);
+       ret |= bt_value_array_append_element(array_obj, obj);
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       obj = bt_value_real_create_init(-17.45);
+       ret |= bt_value_array_append_element(array_obj, obj);
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       obj = bt_value_bool_create_init(BT_TRUE);
+       ret |= bt_value_array_append_element(array_obj, obj);
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       ret |= bt_value_array_append_element(array_obj,
+               bt_value_null);
+       ok(!ret, "bt_value_array_append_element() succeeds");
+       ok(bt_value_array_get_length(array_obj) == 5,
+               "appending an element to an array value object increment its size");
+
+       obj = bt_value_array_borrow_element_by_index(array_obj, 0);
+       ok(bt_value_is_unsigned_integer(obj),
+               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (unsigned integer)");
+       int_value = bt_value_integer_unsigned_get(obj);
+       ok(int_value == 345,
+               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate value (unsigned integer)");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 1);
+       ok(bt_value_is_signed_integer(obj),
+               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (signed integer)");
+       int_value = bt_value_integer_signed_get(obj);
+       ok(int_value == -507,
+               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate value (signed integer)");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 2);
+       ok(bt_value_is_real(obj),
+               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (real number)");
+       real_value = bt_value_real_get(obj);
+       ok(real_value == -17.45,
+               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate value (real number)");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 3);
+       ok(bt_value_is_bool(obj),
+               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (boolean)");
+       bool_value = bt_value_bool_get(obj);
+       ok(bool_value,
+               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate value (boolean)");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 4);
+       ok(bt_value_null,
+               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (null)");
+
+       obj = bt_value_integer_signed_create_init(1001);
+       BT_ASSERT(obj);
+       ok(!bt_value_array_set_element_by_index(array_obj, 2, obj),
+               "bt_value_array_set_element_by_index() succeeds");
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       obj = bt_value_array_borrow_element_by_index(array_obj, 2);
+       ok(bt_value_is_signed_integer(obj),
+               "bt_value_array_set_element_by_index() inserts an value object with the appropriate type");
+       int_value = bt_value_integer_signed_get(obj);
+       BT_ASSERT(!ret);
+       ok(int_value == 1001,
+               "bt_value_array_set_element_by_index() inserts an value object with the appropriate value");
+
+       ret = bt_value_array_append_bool_element(array_obj,
+               BT_FALSE);
+       ok(!ret, "bt_value_array_append_bool_element() succeeds");
+       ret = bt_value_array_append_unsigned_integer_element(array_obj,
+               98765);
+       ok(!ret, "bt_value_array_append_unsigned_integer_element() succeeds");
+       ret = bt_value_array_append_signed_integer_element(array_obj,
+               -10101);
+       ok(!ret, "bt_value_array_append_signed_integer_element() succeeds");
+       ret = bt_value_array_append_real_element(array_obj,
+               2.49578);
+       ok(!ret, "bt_value_array_append_real_element() succeeds");
+       ret = bt_value_array_append_string_element(array_obj,
+               "bt_value");
+       ok(!ret, "bt_value_array_append_string_element() succeeds");
+       ret = bt_value_array_append_empty_array_element(array_obj, NULL);
+       ok(!ret, "bt_value_array_append_empty_array_element() succeeds");
+       ret = bt_value_array_append_empty_array_element(array_obj, &appended_obj);
+       ok(!ret, "bt_value_array_append_empty_array_element() with returned value object succeeds");
+       ok(appended_obj,
+               "object returned by bt_value_array_append_empty_array_element() is not NULL");
+       ok(bt_value_is_array(appended_obj),
+               "object returned by bt_value_array_append_empty_array_element() is an array value");
+       ret = bt_value_array_append_empty_map_element(array_obj, NULL);
+       ok(!ret, "bt_value_array_append_empty_map_element() succeeds");
+       ret = bt_value_array_append_empty_map_element(array_obj, &appended_obj);
+       ok(!ret, "bt_value_array_append_empty_map_element() with returned value object succeeds");
+       ok(appended_obj,
+               "object returned by bt_value_array_append_empty_map_element() is not NULL");
+       ok(bt_value_is_map(appended_obj),
+               "object returned by bt_value_array_append_empty_map_element() is an array value");
+
+       ok(bt_value_array_get_length(array_obj) == 14,
+               "the bt_value_array_append_element_*() functions increment the array value object's size");
+       ok(!bt_value_array_is_empty(array_obj),
+               "map value object is not empty");
+
+       obj = bt_value_array_borrow_element_by_index(array_obj, 5);
+       ok(bt_value_is_bool(obj),
+               "bt_value_array_append_bool_element() appends a boolean value object");
+       bool_value = bt_value_bool_get(obj);
+       ok(!bool_value,
+               "bt_value_array_append_bool_element() appends the appropriate value");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 6);
+       ok(bt_value_is_unsigned_integer(obj),
+               "bt_value_array_append_unsigned_integer_element() appends an unsigned integer value object");
+       int_value = bt_value_integer_unsigned_get(obj);
+       ok(int_value == 98765,
+               "bt_value_array_append_unsigned_integer_element() appends the appropriate value");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 7);
+       ok(bt_value_is_signed_integer(obj),
+               "bt_value_array_append_signed_integer_element() appends a signed integer value object");
+       int_value = bt_value_integer_signed_get(obj);
+       ok(int_value == -10101,
+               "bt_value_array_append_signed_integer_element() appends the appropriate value");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 8);
+       ok(bt_value_is_real(obj),
+               "bt_value_array_append_real_element() appends a real number value object");
+       real_value = bt_value_real_get(obj);
+       ok(real_value == 2.49578,
+               "bt_value_array_append_real_element() appends the appropriate value");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 9);
+       ok(bt_value_is_string(obj),
+               "bt_value_array_append_string_element() appends a string value object");
+       string_value = bt_value_string_get(obj);
+       ok(!ret && string_value && strcmp(string_value, "bt_value") == 0,
+               "bt_value_array_append_string_element() appends the appropriate value");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 10);
+       ok(bt_value_is_array(obj),
+               "bt_value_array_append_empty_array_element() appends an array value object");
+       ok(bt_value_array_is_empty(obj),
+               "bt_value_array_append_empty_array_element() an empty array value object");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 11);
+       ok(bt_value_is_array(obj),
+               "bt_value_array_append_empty_array_element() appends an array value object");
+       ok(bt_value_array_is_empty(obj),
+               "bt_value_array_append_empty_array_element() an empty array value object");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 12);
+       ok(bt_value_is_map(obj),
+               "bt_value_array_append_empty_map_element() appends a map value object");
+       ok(bt_value_map_is_empty(obj),
+               "bt_value_array_append_empty_map_element() an empty map value object");
+       obj = bt_value_array_borrow_element_by_index(array_obj, 13);
+       ok(bt_value_is_map(obj),
+               "bt_value_array_append_empty_map_element() appends a map value object");
+       ok(bt_value_map_is_empty(obj),
+               "bt_value_array_append_empty_map_element() an empty map value object");
+
+       BT_VALUE_PUT_REF_AND_RESET(array_obj);
+       pass("putting an existing array value object does not cause a crash")
+}
+
+static
+bt_value_map_foreach_entry_func_status test_map_foreach_cb_count(
+       const char *key __attribute__((unused)),
+       bt_value *object __attribute__((unused)),
+       void *data)
+{
+       int *count = data;
+
+       if (*count == 3) {
+               return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_INTERRUPT;
+       } else if (*count == 4) {
+               return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_ERROR;
+       } else if (*count == 5) {
+               return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_MEMORY_ERROR;
+       }
+
+       (*count)++;
+
+       return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_OK;
+}
+
+struct map_foreach_checklist {
+       bt_bool bool1;
+       bt_bool uint;
+       bt_bool int1;
+       bt_bool real1;
+       bt_bool null1;
+       bt_bool bool2;
+       bt_bool int2;
+       bt_bool real2;
+       bt_bool string2;
+       bt_bool array2;
+       bt_bool array3;
+       bt_bool map2;
+       bt_bool map3;
+};
+
+static
+bt_value_map_foreach_entry_func_status test_map_foreach_cb_check(
+               const char *key, bt_value *object, void *data)
+{
+       struct map_foreach_checklist *checklist = data;
+
+       if (strcmp(key, "bt_bool") == 0) {
+               if (checklist->bool1) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"bt_bool\"");
+               } else {
+                       bt_bool val = BT_FALSE;
+
+                       val = bt_value_bool_get(object);
+
+                       if (val) {
+                               pass("test_map_foreach_cb_check(): \"bt_bool\" value object has the right value");
+                               checklist->bool1 = BT_TRUE;
+                       } else {
+                               fail("test_map_foreach_cb_check(): \"bt_bool\" value object has the wrong value");
+                       }
+               }
+       } else if (strcmp(key, "uint") == 0) {
+               if (checklist->uint) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"uint\"");
+               } else {
+                       uint64_t val = 0;
+
+                       val = bt_value_integer_unsigned_get(object);
+
+                       if (val == 19457) {
+                               pass("test_map_foreach_cb_check(): \"uint\" value object has the right value");
+                               checklist->uint = BT_TRUE;
+                       } else {
+                               fail("test_map_foreach_cb_check(): \"uint\" value object has the wrong value");
+                       }
+               }
+       } else if (strcmp(key, "int") == 0) {
+               if (checklist->int1) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"int\"");
+               } else {
+                       int64_t val = 0;
+
+                       val = bt_value_integer_signed_get(object);
+
+                       if (val == -12345) {
+                               pass("test_map_foreach_cb_check(): \"int\" value object has the right value");
+                               checklist->int1 = BT_TRUE;
+                       } else {
+                               fail("test_map_foreach_cb_check(): \"int\" value object has the wrong value");
+                       }
+               }
+       } else if (strcmp(key, "real") == 0) {
+               if (checklist->real1) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"real\"");
+               } else {
+                       double val = 0;
+
+                       val = bt_value_real_get(object);
+
+                       if (val == 5.444) {
+                               pass("test_map_foreach_cb_check(): \"real\" value object has the right value");
+                               checklist->real1 = BT_TRUE;
+                       } else {
+                               fail("test_map_foreach_cb_check(): \"real\" value object has the wrong value");
+                       }
+               }
+       } else if (strcmp(key, "null") == 0) {
+               if (checklist->null1) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"bt_bool\"");
+               } else {
+                       ok(bt_value_is_null(object), "test_map_foreach_cb_check(): success getting \"null\" value object");
+                       checklist->null1 = BT_TRUE;
+               }
+       } else if (strcmp(key, "bool2") == 0) {
+               if (checklist->bool2) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"bool2\"");
+               } else {
+                       bt_bool val = BT_FALSE;
+
+                       val = bt_value_bool_get(object);
+
+                       if (val) {
+                               pass("test_map_foreach_cb_check(): \"bool2\" value object has the right value");
+                               checklist->bool2 = BT_TRUE;
+                       } else {
+                               fail("test_map_foreach_cb_check(): \"bool2\" value object has the wrong value");
+                       }
+               }
+       } else if (strcmp(key, "int2") == 0) {
+               if (checklist->int2) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"int2\"");
+               } else {
+                       int64_t val = 0;
+
+                       val = bt_value_integer_signed_get(object);
+
+                       if (val == 98765) {
+                               pass("test_map_foreach_cb_check(): \"int2\" value object has the right value");
+                               checklist->int2 = BT_TRUE;
+                       } else {
+                               fail("test_map_foreach_cb_check(): \"int2\" value object has the wrong value");
+                       }
+               }
+       } else if (strcmp(key, "real2") == 0) {
+               if (checklist->real2) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"real2\"");
+               } else {
+                       double val = 0;
+
+                       val = bt_value_real_get(object);
+
+                       if (val == -49.0001) {
+                               pass("test_map_foreach_cb_check(): \"real2\" value object has the right value");
+                               checklist->real2 = BT_TRUE;
+                       } else {
+                               fail("test_map_foreach_cb_check(): \"real2\" value object has the wrong value");
+                       }
+               }
+       } else if (strcmp(key, "string2") == 0) {
+               if (checklist->string2) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"string2\"");
+               } else {
+                       const char *val;
+
+                       val = bt_value_string_get(object);
+
+                       if (val && strcmp(val, "bt_value") == 0) {
+                               pass("test_map_foreach_cb_check(): \"string2\" value object has the right value");
+                               checklist->string2 = BT_TRUE;
+                       } else {
+                               fail("test_map_foreach_cb_check(): \"string2\" value object has the wrong value");
+                       }
+               }
+       } else if (strcmp(key, "array2") == 0) {
+               if (checklist->array2) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"array2\"");
+               } else {
+                       ok(bt_value_is_array(object), "test_map_foreach_cb_check(): success getting \"array2\" value object");
+                       ok(bt_value_array_is_empty(object),
+                               "test_map_foreach_cb_check(): \"array2\" value object is empty");
+                       checklist->array2 = BT_TRUE;
+               }
+       } else if (strcmp(key, "array3") == 0) {
+               if (checklist->array3) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"array3\"");
+               } else {
+                       ok(bt_value_is_array(object), "test_map_foreach_cb_check(): success getting \"array3\" value object");
+                       ok(bt_value_array_is_empty(object),
+                               "test_map_foreach_cb_check(): \"array3\" value object is empty");
+                       checklist->array3 = BT_TRUE;
+               }
+       } else if (strcmp(key, "map3") == 0) {
+               if (checklist->map3) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"map3\"");
+               } else {
+                       ok(bt_value_is_map(object), "test_map_foreach_cb_check(): success getting \"map3\" value object");
+                       ok(bt_value_map_is_empty(object),
+                               "test_map_foreach_cb_check(): \"map3\" value object is empty");
+                       checklist->map3 = BT_TRUE;
+               }
+       } else if (strcmp(key, "map2") == 0) {
+               if (checklist->map2) {
+                       fail("test_map_foreach_cb_check(): duplicate key \"map2\"");
+               } else {
+                       ok(bt_value_is_map(object), "test_map_foreach_cb_check(): success getting \"map2\" value object");
+                       ok(bt_value_map_is_empty(object),
+                               "test_map_foreach_cb_check(): \"map2\" value object is empty");
+                       checklist->map2 = BT_TRUE;
+               }
+       } else {
+               fail("test_map_foreach_cb_check(): unknown map key \"%s\"",
+                       key);
+       }
+
+       return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_OK;
+}
+
+static
+void test_map(void)
+{
+       int ret;
+       int count = 0;
+       bt_bool bool_value;
+       int64_t int_value;
+       double real_value;
+       bt_value *obj;
+       bt_value *map_obj;
+       bt_value *inserted_obj;
+       struct map_foreach_checklist checklist;
+
+       map_obj = bt_value_map_create();
+       ok(map_obj && bt_value_is_map(map_obj),
+               "bt_value_map_create() returns a map value object");
+       ok(bt_value_map_get_size(map_obj) == 0,
+               "initial map value object size is 0");
+
+       obj = bt_value_integer_unsigned_create_init(19457);
+       ret = bt_value_map_insert_entry(map_obj, "uint", obj);
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       obj = bt_value_integer_signed_create_init(-12345);
+       ret |= bt_value_map_insert_entry(map_obj, "int", obj);
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       obj = bt_value_real_create_init(5.444);
+       ret |= bt_value_map_insert_entry(map_obj, "real", obj);
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       obj = bt_value_bool_create();
+       ret |= bt_value_map_insert_entry(map_obj, "bt_bool", obj);
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       ret |= bt_value_map_insert_entry(map_obj, "null",
+               bt_value_null);
+       ok(!ret, "bt_value_map_insert_entry() succeeds");
+       ok(bt_value_map_get_size(map_obj) == 5,
+               "inserting an element into a map value object increment its size");
+
+       obj = bt_value_bool_create_init(BT_TRUE);
+       ret = bt_value_map_insert_entry(map_obj, "bt_bool", obj);
+       BT_VALUE_PUT_REF_AND_RESET(obj);
+       ok(!ret, "bt_value_map_insert_entry() accepts an existing key");
+
+       obj = bt_value_map_borrow_entry_value(map_obj, "life");
+       ok(!obj, "bt_value_map_borrow_entry_value() returns NULL with an non existing key");
+       obj = bt_value_map_borrow_entry_value(map_obj, "real");
+       ok(obj && bt_value_is_real(obj),
+               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (real)");
+       real_value = bt_value_real_get(obj);
+       ok(real_value == 5.444,
+               "bt_value_map_borrow_entry_value() returns an value object with the appropriate value (real)");
+       obj = bt_value_map_borrow_entry_value(map_obj, "uint");
+       ok(obj && bt_value_is_unsigned_integer(obj),
+               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (unsigned integer)");
+       int_value = bt_value_integer_unsigned_get(obj);
+       ok(int_value == 19457,
+               "bt_value_map_borrow_entry_value() returns an value object with the appropriate value (unsigned integer)");
+       obj = bt_value_map_borrow_entry_value(map_obj, "int");
+       ok(obj && bt_value_is_signed_integer(obj),
+               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (signed integer)");
+       int_value = bt_value_integer_signed_get(obj);
+       ok(int_value == -12345,
+               "bt_value_map_borrow_entry_value() returns an value object with the appropriate value (signed integer)");
+       obj = bt_value_map_borrow_entry_value(map_obj, "null");
+       ok(obj && bt_value_is_null(obj),
+               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (null)");
+       obj = bt_value_map_borrow_entry_value(map_obj, "bt_bool");
+       ok(obj && bt_value_is_bool(obj),
+               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (boolean)");
+       bool_value = bt_value_bool_get(obj);
+       ok(bool_value,
+               "bt_value_map_borrow_entry_value() returns an value object with the appropriate value (boolean)");
+
+       ret = bt_value_map_insert_bool_entry(map_obj, "bool2",
+               BT_TRUE);
+       ok(!ret, "bt_value_map_insert_bool_entry() succeeds");
+       ret = bt_value_map_insert_signed_integer_entry(map_obj, "int2",
+               98765);
+       ok(!ret, "bt_value_map_insert_signed_integer_entry() succeeds");
+       ret = bt_value_map_insert_real_entry(map_obj, "real2",
+               -49.0001);
+       ok(!ret, "bt_value_map_insert_real_entry() succeeds");
+       ret = bt_value_map_insert_string_entry(map_obj, "string2",
+               "bt_value");
+       ok(!ret, "bt_value_map_insert_string_entry() succeeds");
+       ret = bt_value_map_insert_empty_array_entry(map_obj, "array2", NULL);
+       ok(!ret, "bt_value_map_insert_empty_array_entry() succeeds");
+       ret = bt_value_map_insert_empty_array_entry(map_obj, "array3", &inserted_obj);
+       ok(!ret, "bt_value_map_insert_empty_array_entry() with returned value object succeeds");
+       ok(inserted_obj,
+               "object returned by bt_value_map_insert_empty_array_entry() is not NULL");
+       ok(bt_value_is_array(inserted_obj),
+               "object returned by bt_value_map_insert_empty_array_entry() is an array value");
+       ret = bt_value_map_insert_empty_map_entry(map_obj, "map2", NULL);
+       ok(!ret, "bt_value_map_insert_empty_map_entry() succeeds");
+       ret = bt_value_map_insert_empty_map_entry(map_obj, "map3", &inserted_obj);
+       ok(!ret, "bt_value_map_insert_empty_map_entry() with returned value object succeeds");
+       ok(inserted_obj,
+               "object returned by bt_value_map_insert_empty_map_entry() is not NULL");
+       ok(bt_value_is_map(inserted_obj),
+               "object returned by bt_value_map_insert_empty_map_entry() is an array value");
+
+       ok(bt_value_map_get_size(map_obj) == 13,
+               "the bt_value_map_insert*() functions increment the map value object's size");
+
+       ok(!bt_value_map_has_entry(map_obj, "hello"),
+               "map value object does not have key \"hello\"");
+       ok(bt_value_map_has_entry(map_obj, "bt_bool"),
+               "map value object has key \"bt_bool\"");
+       ok(bt_value_map_has_entry(map_obj, "uint"),
+               "map value object has key \"uint\"");
+       ok(bt_value_map_has_entry(map_obj, "int"),
+               "map value object has key \"int\"");
+       ok(bt_value_map_has_entry(map_obj, "real"),
+               "map value object has key \"real\"");
+       ok(bt_value_map_has_entry(map_obj, "null"),
+               "map value object has key \"null\"");
+       ok(bt_value_map_has_entry(map_obj, "bool2"),
+               "map value object has key \"bool2\"");
+       ok(bt_value_map_has_entry(map_obj, "int2"),
+               "map value object has key \"int2\"");
+       ok(bt_value_map_has_entry(map_obj, "real2"),
+               "map value object has key \"real2\"");
+       ok(bt_value_map_has_entry(map_obj, "string2"),
+               "map value object has key \"string2\"");
+       ok(bt_value_map_has_entry(map_obj, "array2"),
+               "map value object has key \"array2\"");
+       ok(bt_value_map_has_entry(map_obj, "array3"),
+               "map value object has key \"array3\"");
+       ok(bt_value_map_has_entry(map_obj, "map2"),
+               "map value object has key \"map2\"");
+       ok(bt_value_map_has_entry(map_obj, "map3"),
+               "map value object has key \"map3\"");
+
+       ret = bt_value_map_foreach_entry(map_obj, test_map_foreach_cb_count,
+               &count);
+       ok(ret == BT_VALUE_MAP_FOREACH_ENTRY_STATUS_INTERRUPTED && count == 3,
+               "bt_value_map_foreach_entry() breaks the loop when the user function returns BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_INTERRUPT");
+
+       count = 4;
+       ret = bt_value_map_foreach_entry(map_obj, test_map_foreach_cb_count,
+               &count);
+       ok(ret == BT_VALUE_MAP_FOREACH_ENTRY_STATUS_USER_ERROR,
+               "bt_value_map_foreach_entry() fails when the user function returns BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_ERROR");
+       bt_current_thread_clear_error();
+
+       count = 5;
+       ret = bt_value_map_foreach_entry(map_obj, test_map_foreach_cb_count,
+               &count);
+       ok(ret == BT_VALUE_MAP_FOREACH_ENTRY_STATUS_MEMORY_ERROR,
+               "bt_value_map_foreach_entry() fails when the user function returns BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_MEMORY_ERROR");
+       bt_current_thread_clear_error();
+
+       memset(&checklist, 0, sizeof(checklist));
+       ret = bt_value_map_foreach_entry(map_obj, test_map_foreach_cb_check,
+               &checklist);
+       ok(ret == BT_VALUE_MAP_FOREACH_ENTRY_STATUS_OK,
+               "bt_value_map_foreach_entry() succeeds with test_map_foreach_cb_check()");
+       ok(checklist.bool1 && checklist.uint && checklist.int1 &&
+               checklist.real1 && checklist.null1 && checklist.bool2 &&
+               checklist.int2 && checklist.real2 && checklist.string2 &&
+               checklist.array2 && checklist.map2,
+               "bt_value_map_foreach_entry() iterates over all the map value object's elements");
+
+       BT_VALUE_PUT_REF_AND_RESET(map_obj);
+       pass("putting an existing map value object does not cause a crash")
+}
+
+static
+void test_types(void)
+{
+       test_null();
+       test_bool();
+       test_unsigned_integer();
+       test_signed_integer();
+       test_real();
+       test_string();
+       test_array();
+       test_map();
+}
+
+static
+void test_is_equal_null(void)
+{
+       ok(bt_value_is_equal(bt_value_null, bt_value_null),
+               "null value objects are equivalent");
+}
+
+static
+void test_is_equal_bool(void)
+{
+       bt_value *bool1 =
+               bt_value_bool_create_init(BT_FALSE);
+       bt_value *bool2 =
+               bt_value_bool_create_init(BT_TRUE);
+       bt_value *bool3 =
+               bt_value_bool_create_init(BT_FALSE);
+
+       BT_ASSERT(bool1 && bool2 && bool3);
+       ok(!bt_value_is_equal(bt_value_null, bool1),
+               "cannot compare null value object and bt_bool value object");
+       ok(!bt_value_is_equal(bool1, bool2),
+               "boolean value objects are not equivalent (BT_FALSE and BT_TRUE)");
+       ok(bt_value_is_equal(bool1, bool3),
+               "boolean value objects are equivalent (BT_FALSE and BT_FALSE)");
+
+       BT_VALUE_PUT_REF_AND_RESET(bool1);
+       BT_VALUE_PUT_REF_AND_RESET(bool2);
+       BT_VALUE_PUT_REF_AND_RESET(bool3);
+}
+
+static
+void test_is_equal_unsigned_integer(void)
+{
+       bt_value *int1 =
+               bt_value_integer_unsigned_create_init(10);
+       bt_value *int2 =
+               bt_value_integer_unsigned_create_init(23);
+       bt_value *int3 =
+               bt_value_integer_unsigned_create_init(10);
+
+       BT_ASSERT(int1 && int2 && int3);
+       ok(!bt_value_is_equal(bt_value_null, int1),
+               "cannot compare null value object and unsigned integer value object");
+       ok(!bt_value_is_equal(int1, int2),
+               "unsigned integer value objects are not equivalent (10 and 23)");
+       ok(bt_value_is_equal(int1, int3),
+               "unsigned integer value objects are equivalent (10 and 10)");
+
+       BT_VALUE_PUT_REF_AND_RESET(int1);
+       BT_VALUE_PUT_REF_AND_RESET(int2);
+       BT_VALUE_PUT_REF_AND_RESET(int3);
+}
+
+static
+void test_is_equal_signed_integer(void)
+{
+       bt_value *int1 =
+               bt_value_integer_signed_create_init(10);
+       bt_value *int2 =
+               bt_value_integer_signed_create_init(-23);
+       bt_value *int3 =
+               bt_value_integer_signed_create_init(10);
+
+       BT_ASSERT(int1 && int2 && int3);
+       ok(!bt_value_is_equal(bt_value_null, int1),
+               "cannot compare null value object and signed integer value object");
+       ok(!bt_value_is_equal(int1, int2),
+               "signed integer value objects are not equivalent (10 and -23)");
+       ok(bt_value_is_equal(int1, int3),
+               "signed integer value objects are equivalent (10 and 10)");
+
+       BT_VALUE_PUT_REF_AND_RESET(int1);
+       BT_VALUE_PUT_REF_AND_RESET(int2);
+       BT_VALUE_PUT_REF_AND_RESET(int3);
+}
+
+static
+void test_is_equal_real(void)
+{
+       bt_value *real1 =
+               bt_value_real_create_init(17.38);
+       bt_value *real2 =
+               bt_value_real_create_init(-14.23);
+       bt_value *real3 =
+               bt_value_real_create_init(17.38);
+
+       BT_ASSERT(real1 && real2 && real3);
+
+       ok(!bt_value_is_equal(bt_value_null, real1),
+               "cannot compare null value object and real number value object");
+       ok(!bt_value_is_equal(real1, real2),
+               "real number value objects are not equivalent (17.38 and -14.23)");
+       ok(bt_value_is_equal(real1, real3),
+               "real number value objects are equivalent (17.38 and 17.38)");
+
+       BT_VALUE_PUT_REF_AND_RESET(real1);
+       BT_VALUE_PUT_REF_AND_RESET(real2);
+       BT_VALUE_PUT_REF_AND_RESET(real3);
+}
+
+static
+void test_is_equal_string(void)
+{
+       bt_value *string1 =
+               bt_value_string_create_init("hello");
+       bt_value *string2 =
+               bt_value_string_create_init("bt_value");
+       bt_value *string3 =
+               bt_value_string_create_init("hello");
+
+       BT_ASSERT(string1 && string2 && string3);
+
+       ok(!bt_value_is_equal(bt_value_null, string1),
+               "cannot compare null value object and string value object");
+       ok(!bt_value_is_equal(string1, string2),
+               "string value objects are not equivalent (\"hello\" and \"bt_value\")");
+       ok(bt_value_is_equal(string1, string3),
+               "string value objects are equivalent (\"hello\" and \"hello\")");
+
+       BT_VALUE_PUT_REF_AND_RESET(string1);
+       BT_VALUE_PUT_REF_AND_RESET(string2);
+       BT_VALUE_PUT_REF_AND_RESET(string3);
+}
+
+static
+void test_is_equal_array(void)
+{
+       bt_value *array1 = bt_value_array_create();
+       bt_value *array2 = bt_value_array_create();
+       bt_value *array3 = bt_value_array_create();
+       bt_value_array_append_element_status append_status;
+
+       BT_ASSERT(array1 && array2 && array3);
+
+       ok(bt_value_is_equal(array1, array2),
+               "empty array value objects are equivalent");
+
+       append_status = bt_value_array_append_signed_integer_element(array1, 23);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_real_element(array1, 14.2);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_bool_element(array1, BT_FALSE);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_real_element(array2, 14.2);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_signed_integer_element(array2, 23);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_bool_element(array2, BT_FALSE);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_signed_integer_element(array3, 23);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_real_element(array3, 14.2);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_bool_element(array3, BT_FALSE);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       BT_ASSERT(bt_value_array_get_length(array1) == 3);
+       BT_ASSERT(bt_value_array_get_length(array2) == 3);
+       BT_ASSERT(bt_value_array_get_length(array3) == 3);
+
+       ok(!bt_value_is_equal(bt_value_null, array1),
+               "cannot compare null value object and array value object");
+       ok(!bt_value_is_equal(array1, array2),
+               "array value objects are not equivalent ([23, 14.2, BT_FALSE] and [14.2, 23, BT_FALSE])");
+       ok(bt_value_is_equal(array1, array3),
+               "array value objects are equivalent ([23, 14.2, BT_FALSE] and [23, 14.2, BT_FALSE])");
+
+       BT_VALUE_PUT_REF_AND_RESET(array1);
+       BT_VALUE_PUT_REF_AND_RESET(array2);
+       BT_VALUE_PUT_REF_AND_RESET(array3);
+}
+
+static
+void test_is_equal_map(void)
+{
+       bt_value *map1 = bt_value_map_create();
+       bt_value *map2 = bt_value_map_create();
+       bt_value *map3 = bt_value_map_create();
+       bt_value_map_insert_entry_status insert_status;
+
+       BT_ASSERT(map1 && map2 && map3);
+
+       ok(bt_value_is_equal(map1, map2),
+               "empty map value objects are equivalent");
+
+
+       insert_status = bt_value_map_insert_signed_integer_entry(map1, "one", 23);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_real_entry(map1, "two", 14.2);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_bool_entry(map1, "three",
+               BT_FALSE);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_real_entry(map2, "one", 14.2);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_signed_integer_entry(map2, "two", 23);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_bool_entry(map2, "three",
+               BT_FALSE);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_bool_entry(map3, "three",
+               BT_FALSE);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_signed_integer_entry(map3, "one", 23);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_real_entry(map3, "two", 14.2);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       BT_ASSERT(bt_value_map_get_size(map1) == 3);
+       BT_ASSERT(bt_value_map_get_size(map2) == 3);
+       BT_ASSERT(bt_value_map_get_size(map3) == 3);
+
+       ok(!bt_value_is_equal(bt_value_null, map1),
+               "cannot compare null value object and map value object");
+       ok(!bt_value_is_equal(map1, map2),
+               "map value objects are not equivalent");
+       ok(bt_value_is_equal(map1, map3),
+               "map value objects are equivalent");
+
+       BT_VALUE_PUT_REF_AND_RESET(map1);
+       BT_VALUE_PUT_REF_AND_RESET(map2);
+       BT_VALUE_PUT_REF_AND_RESET(map3);
+}
+
+static
+void test_is_equal(void)
+{
+       test_is_equal_null();
+       test_is_equal_bool();
+       test_is_equal_unsigned_integer();
+       test_is_equal_signed_integer();
+       test_is_equal_real();
+       test_is_equal_string();
+       test_is_equal_array();
+       test_is_equal_map();
+}
+
+static
+void test_copy(void)
+{
+       /*
+        * Here's the deal here. If we make sure that each value object
+        * of our deep copy has a different address than its source, and
+        * that bt_value_is_equal() returns BT_TRUE for the top-level
+        * value object, taking into account that we test the
+        * correctness of bt_value_is_equal() elsewhere, then the deep
+        * copy is a success.
+        */
+       bt_value *null_copy_obj;
+       bt_value *bool_obj, *bool_copy_obj;
+       bt_value *unsigned_integer_obj, *unsigned_integer_copy_obj;
+       bt_value *signed_integer_obj, *signed_integer_copy_obj;
+       bt_value *real_obj, *real_copy_obj;
+       bt_value *string_obj, *string_copy_obj;
+       bt_value *array_obj, *array_copy_obj;
+       bt_value *map_obj, *map_copy_obj;
+       bt_value_array_append_element_status append_status;
+       bt_value_map_insert_entry_status insert_status;
+       bt_value_copy_status copy_status;
+
+       bool_obj = bt_value_bool_create_init(BT_TRUE);
+       unsigned_integer_obj = bt_value_integer_unsigned_create_init(23);
+       signed_integer_obj = bt_value_integer_signed_create_init(-47);
+       real_obj = bt_value_real_create_init(-3.1416);
+       string_obj = bt_value_string_create_init("test");
+       array_obj = bt_value_array_create();
+       map_obj = bt_value_map_create();
+
+       BT_ASSERT(bool_obj && unsigned_integer_obj && signed_integer_obj &&
+               real_obj && string_obj && array_obj && map_obj);
+
+       append_status = bt_value_array_append_element(array_obj, bool_obj);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_element(array_obj, unsigned_integer_obj);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_element(array_obj, signed_integer_obj);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_element(array_obj, real_obj);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       append_status = bt_value_array_append_element(array_obj, bt_value_null);
+       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
+       insert_status = bt_value_map_insert_entry(map_obj, "array", array_obj);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_entry(map_obj, "string", string_obj);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+
+       copy_status = bt_value_copy(map_obj, &map_copy_obj);
+       ok(copy_status == BT_VALUE_COPY_STATUS_OK && map_copy_obj,
+               "bt_value_copy() succeeds");
+
+       ok(map_obj != map_copy_obj,
+               "bt_value_copy() returns a different pointer (map)");
+       string_copy_obj = bt_value_map_borrow_entry_value(map_copy_obj,
+               "string");
+       ok(string_copy_obj != string_obj,
+               "bt_value_copy() returns a different pointer (string)");
+       array_copy_obj = bt_value_map_borrow_entry_value(map_copy_obj,
+               "array");
+       ok(array_copy_obj != array_obj,
+               "bt_value_copy() returns a different pointer (array)");
+       bool_copy_obj = bt_value_array_borrow_element_by_index(
+               array_copy_obj, 0);
+       ok(bool_copy_obj != bool_obj,
+               "bt_value_copy() returns a different pointer (bool)");
+       unsigned_integer_copy_obj = bt_value_array_borrow_element_by_index(
+               array_copy_obj, 1);
+       ok(unsigned_integer_copy_obj != unsigned_integer_obj,
+               "bt_value_copy() returns a different pointer (unsigned integer)");
+       signed_integer_copy_obj = bt_value_array_borrow_element_by_index(
+               array_copy_obj, 2);
+       ok(signed_integer_copy_obj != signed_integer_obj,
+               "bt_value_copy() returns a different pointer (signed integer)");
+       real_copy_obj = bt_value_array_borrow_element_by_index(
+               array_copy_obj, 3);
+       ok(real_copy_obj != real_obj,
+               "bt_value_copy() returns a different pointer (real)");
+       null_copy_obj = bt_value_array_borrow_element_by_index(
+               array_copy_obj, 4);
+       ok(null_copy_obj == bt_value_null,
+               "bt_value_copy() returns the same pointer (null)");
+
+       ok(bt_value_is_equal(map_obj, map_copy_obj),
+               "source and destination value objects have the same content");
+
+       BT_VALUE_PUT_REF_AND_RESET(map_copy_obj);
+       BT_VALUE_PUT_REF_AND_RESET(bool_obj);
+       BT_VALUE_PUT_REF_AND_RESET(unsigned_integer_obj);
+       BT_VALUE_PUT_REF_AND_RESET(signed_integer_obj);
+       BT_VALUE_PUT_REF_AND_RESET(real_obj);
+       BT_VALUE_PUT_REF_AND_RESET(string_obj);
+       BT_VALUE_PUT_REF_AND_RESET(array_obj);
+       BT_VALUE_PUT_REF_AND_RESET(map_obj);
+}
+
+static
+bt_bool compare_map_elements(const bt_value *map_a, const bt_value *map_b,
+               const char *key)
+{
+       const bt_value *elem_a = NULL;
+       const bt_value *elem_b = NULL;
+       bt_bool equal;
+
+       elem_a = bt_value_map_borrow_entry_value_const(map_a, key);
+       elem_b = bt_value_map_borrow_entry_value_const(map_b, key);
+       equal = bt_value_is_equal(elem_a, elem_b);
+       return equal;
+}
+
+static
+void test_extend(void)
+{
+       bt_value *base_map = bt_value_map_create();
+       bt_value *extension_map = bt_value_map_create();
+       bt_value *extended_map = NULL;
+       bt_value *array = bt_value_array_create();
+       bt_value_map_insert_entry_status insert_status;
+       bt_value_copy_status copy_status;
+       bt_value_map_extend_status extend_status;
+
+       BT_ASSERT(base_map);
+       BT_ASSERT(extension_map);
+       BT_ASSERT(array);
+       insert_status = bt_value_map_insert_bool_entry(base_map, "file",
+               BT_TRUE);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_bool_entry(base_map, "edit",
+               BT_FALSE);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_signed_integer_entry(base_map,
+               "selection", 17);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_signed_integer_entry(base_map, "find",
+               -34);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_bool_entry(extension_map, "edit",
+               BT_TRUE);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_signed_integer_entry(extension_map,
+               "find", 101);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       insert_status = bt_value_map_insert_real_entry(extension_map,
+               "project", -404);
+       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
+       copy_status = bt_value_copy(base_map, &extended_map);
+       BT_ASSERT(copy_status == BT_VALUE_COPY_STATUS_OK);
+       extend_status = bt_value_map_extend(extended_map, extension_map);
+       ok(extend_status == BT_VALUE_MAP_EXTEND_STATUS_OK &&
+               extended_map, "bt_value_map_extend() succeeds");
+       ok(bt_value_map_get_size(extended_map) == 5,
+               "bt_value_map_extend() returns a map object with the correct size");
+       ok(compare_map_elements(base_map,
+                               extended_map, "file"),
+               "bt_value_map_extend() picks the appropriate element (file)");
+       ok(compare_map_elements(extension_map,
+                               extended_map, "edit"),
+               "bt_value_map_extend() picks the appropriate element (edit)");
+       ok(compare_map_elements(base_map,
+                               extended_map, "selection"),
+               "bt_value_map_extend() picks the appropriate element (selection)");
+       ok(compare_map_elements(extension_map,
+                               extended_map, "find"),
+               "bt_value_map_extend() picks the appropriate element (find)");
+       ok(compare_map_elements(extension_map,
+                               extended_map, "project"),
+               "bt_value_map_extend() picks the appropriate element (project)");
+
+       BT_VALUE_PUT_REF_AND_RESET(array);
+       BT_VALUE_PUT_REF_AND_RESET(base_map);
+       BT_VALUE_PUT_REF_AND_RESET(extension_map);
+       BT_VALUE_PUT_REF_AND_RESET(extended_map);
+}
+
+int main(void)
+{
+       plan_tests(NR_TESTS);
+       test_types();
+       test_is_equal();
+       test_copy();
+       test_extend();
+       return exit_status();
+}
diff --git a/tests/lib/test-fields-bin.cpp b/tests/lib/test-fields-bin.cpp
new file mode 100644 (file)
index 0000000..3d2d9ad
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2023 EfficiOS Inc.
+ */
+
+#include "common/assert.h"
+
+#include "utils/run-in.hpp"
+
+#include "tap/tap.h"
+
+namespace {
+
+constexpr int NR_TESTS = 2;
+
+class TestStringClear final : public RunIn
+{
+public:
+    void onMsgIterInit(const bt2::SelfMessageIterator self) override
+    {
+        /* Boilerplate to get a string field */
+        const auto traceCls = self.component().createTraceClass();
+        const auto streamCls = traceCls->createStreamClass();
+        const auto eventCls = streamCls->createEventClass();
+        const auto payloadCls = traceCls->createStructureFieldClass();
+
+        payloadCls->appendMember("str", *traceCls->createStringFieldClass());
+        eventCls->payloadFieldClass(*payloadCls);
+
+        const auto trace = traceCls->instantiate();
+        const auto stream = streamCls->instantiate(*trace);
+        const auto msg = self.createEventMessage(*eventCls, *stream);
+        const auto field = (*msg->event().payloadField())["str"]->asString();
+
+        /* Set the field to a known non-empty value */
+        *field = "pomme";
+        BT_ASSERT(field.value() == "pomme");
+
+        /* Clear the field, verify its value and length */
+        field.clear();
+        ok(field.value() == "", "string field is empty");
+        ok(field.length() == 0, "string field length is 0");
+    }
+};
+
+} /* namespace */
+
+int main()
+{
+    plan_tests(NR_TESTS);
+
+    TestStringClear testStringClear;
+    runIn(testStringClear);
+
+    return exit_status();
+}
diff --git a/tests/lib/test-fields.sh b/tests/lib/test-fields.sh
new file mode 100755 (executable)
index 0000000..b0d6772
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2023 EfficiOS, Inc.
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+bt_run_in_py_env "${BT_TESTS_BUILDDIR}/lib/test-fields-bin"
diff --git a/tests/lib/test-graph-topo.c b/tests/lib/test-graph-topo.c
new file mode 100644 (file)
index 0000000..3bd3f36
--- /dev/null
@@ -0,0 +1,723 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/assert.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <glib.h>
+
+#include "tap/tap.h"
+
+#define NR_TESTS       26
+
+enum event_type {
+       SRC_COMP_OUTPUT_PORT_CONNECTED,
+       SINK_COMP_INPUT_PORT_CONNECTED,
+       GRAPH_SRC_OUTPUT_PORT_ADDED,
+       GRAPH_SINK_INPUT_PORT_ADDED,
+};
+
+enum test {
+       TEST_EMPTY_GRAPH,
+       TEST_SIMPLE,
+       TEST_SRC_PORT_CONNECTED_ERROR,
+       TEST_SINK_PORT_CONNECTED_ERROR,
+       TEST_SRC_ADDS_PORT_IN_PORT_CONNECTED,
+};
+
+struct event {
+       enum event_type type;
+
+       union {
+               struct {
+                       const bt_component *comp;
+                       const bt_port *self_port;
+                       const bt_port *other_port;
+               } src_comp_output_port_connected;
+
+               struct {
+                       const bt_component *comp;
+                       const bt_port *self_port;
+                       const bt_port *other_port;
+               } sink_comp_input_port_connected;
+
+               struct {
+                       const bt_component *comp;
+                       const bt_port *port;
+               } graph_src_output_port_added;
+
+               struct {
+                       const bt_component *comp;
+                       const bt_port *port;
+               } graph_sink_input_port_added;
+       } data;
+};
+
+static GArray *events;
+static bt_message_iterator_class *msg_iter_class;
+static bt_component_class_source *src_comp_class;
+static bt_component_class_sink *sink_comp_class;
+static enum test current_test;
+
+static
+void clear_events(void)
+{
+       g_array_set_size(events, 0);
+}
+
+static
+void append_event(struct event *event)
+{
+       g_array_append_val(events, *event);
+}
+
+static
+bool compare_events(struct event *ev_a, struct event *ev_b)
+{
+       if (ev_a->type != ev_b->type) {
+               return false;
+       }
+
+       switch (ev_a->type) {
+               case SRC_COMP_OUTPUT_PORT_CONNECTED:
+                       if (ev_a->data.src_comp_output_port_connected.comp !=
+                                       ev_b->data.src_comp_output_port_connected.comp) {
+                               return false;
+                       }
+
+                       if (ev_a->data.src_comp_output_port_connected.self_port !=
+                                       ev_b->data.src_comp_output_port_connected.self_port) {
+                               return false;
+                       }
+
+                       if (ev_a->data.src_comp_output_port_connected.other_port !=
+                                       ev_b->data.src_comp_output_port_connected.other_port) {
+                               return false;
+                       }
+                       break;
+               case SINK_COMP_INPUT_PORT_CONNECTED:
+                       if (ev_a->data.sink_comp_input_port_connected.comp !=
+                                       ev_b->data.sink_comp_input_port_connected.comp) {
+                               return false;
+                       }
+
+                       if (ev_a->data.sink_comp_input_port_connected.self_port !=
+                                       ev_b->data.sink_comp_input_port_connected.self_port) {
+                               return false;
+                       }
+
+                       if (ev_a->data.sink_comp_input_port_connected.other_port !=
+                                       ev_b->data.sink_comp_input_port_connected.other_port) {
+                               return false;
+                       }
+                       break;
+               case GRAPH_SRC_OUTPUT_PORT_ADDED:
+                       if (ev_a->data.graph_src_output_port_added.comp !=
+                                       ev_b->data.graph_src_output_port_added.comp) {
+                               return false;
+                       }
+
+                       if (ev_a->data.graph_src_output_port_added.port !=
+                                       ev_b->data.graph_src_output_port_added.port) {
+                               return false;
+                       }
+                       break;
+               case GRAPH_SINK_INPUT_PORT_ADDED:
+                       if (ev_a->data.graph_sink_input_port_added.comp !=
+                                       ev_b->data.graph_sink_input_port_added.comp) {
+                               return false;
+                       }
+
+                       if (ev_a->data.graph_sink_input_port_added.port !=
+                                       ev_b->data.graph_sink_input_port_added.port) {
+                               return false;
+                       }
+                       break;
+               default:
+                       abort();
+       }
+
+       return true;
+}
+
+static
+bool has_event(struct event *event)
+{
+       size_t i;
+
+       for (i = 0; i < events->len; i++) {
+               struct event *ev = &bt_g_array_index(events, struct event, i);
+
+               if (compare_events(event, ev)) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static
+size_t event_pos(struct event *event)
+{
+       size_t i;
+
+       for (i = 0; i < events->len; i++) {
+               struct event *ev = &bt_g_array_index(events, struct event, i);
+
+               if (compare_events(event, ev)) {
+                       return i;
+               }
+       }
+
+       return SIZE_MAX;
+}
+
+static
+bt_message_iterator_class_next_method_status src_iter_next(
+               bt_self_message_iterator *self_iterator __attribute__((unused)),
+               bt_message_array_const msgs __attribute__((unused)),
+               uint64_t capacity __attribute__((unused)),
+               uint64_t *count __attribute__((unused)))
+{
+       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+}
+
+static
+bt_component_class_port_connected_method_status src_output_port_connected(
+               bt_self_component_source *self_comp,
+               bt_self_component_port_output *self_comp_port,
+               const bt_port_input *other_port)
+{
+       int ret;
+       struct event event = {
+               .type = SRC_COMP_OUTPUT_PORT_CONNECTED,
+               .data.src_comp_output_port_connected = {
+                       .comp = bt_self_component_as_component(
+                               bt_self_component_source_as_self_component(
+                                       self_comp)),
+                       .self_port = bt_self_component_port_as_port(
+                               bt_self_component_port_output_as_self_component_port(
+                                       self_comp_port)),
+                       .other_port = bt_port_input_as_port_const(other_port),
+               },
+       };
+
+       append_event(&event);
+
+       switch (current_test) {
+       case TEST_SRC_ADDS_PORT_IN_PORT_CONNECTED:
+               ret = bt_self_component_source_add_output_port(
+                       self_comp, "hello", NULL, NULL);
+               BT_ASSERT(ret == 0);
+               break;
+       case TEST_SRC_PORT_CONNECTED_ERROR:
+               return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_ERROR;
+       default:
+               break;
+       }
+
+       return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_OK;
+}
+
+static
+bt_component_class_port_connected_method_status sink_input_port_connected(
+               bt_self_component_sink *self_comp,
+               bt_self_component_port_input *self_comp_port,
+               const bt_port_output *other_port)
+{
+       struct event event = {
+               .type = SINK_COMP_INPUT_PORT_CONNECTED,
+               .data.sink_comp_input_port_connected = {
+                       .comp = bt_self_component_as_component(
+                               bt_self_component_sink_as_self_component(
+                                       self_comp)),
+                       .self_port = bt_self_component_port_as_port(
+                               bt_self_component_port_input_as_self_component_port(
+                                       self_comp_port)),
+                       .other_port = bt_port_output_as_port_const(other_port),
+               },
+       };
+
+       append_event(&event);
+
+       if (current_test == TEST_SINK_PORT_CONNECTED_ERROR) {
+               return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_ERROR;
+       } else {
+               return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_OK;
+       }
+}
+
+static
+bt_component_class_initialize_method_status src_init(
+       bt_self_component_source *self_comp,
+       bt_self_component_source_configuration *config __attribute__((unused)),
+       const bt_value *params __attribute__((unused)),
+       void *init_method_data __attribute__((unused)))
+{
+       int ret;
+
+       ret = bt_self_component_source_add_output_port(
+               self_comp, "out", NULL, NULL);
+       BT_ASSERT(ret == 0);
+       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+}
+
+static
+bt_component_class_initialize_method_status sink_init(
+       bt_self_component_sink *self_comp,
+       bt_self_component_sink_configuration *config __attribute__((unused)),
+       const bt_value *params __attribute__((unused)),
+       void *init_method_data __attribute__((unused)))
+{
+       int ret;
+
+       ret = bt_self_component_sink_add_input_port(self_comp,
+               "in", NULL, NULL);
+       BT_ASSERT(ret == 0);
+       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+}
+
+static
+bt_component_class_sink_consume_method_status sink_consume(
+               bt_self_component_sink *self_comp __attribute__((unused)))
+{
+       return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+}
+
+static
+bt_graph_listener_func_status graph_src_output_port_added(
+               const bt_component_source *comp, const bt_port_output *port,
+               void *data __attribute__((unused)))
+{
+       struct event event = {
+               .type = GRAPH_SRC_OUTPUT_PORT_ADDED,
+               .data.graph_src_output_port_added = {
+                       .comp = bt_component_source_as_component_const(comp),
+                       .port = bt_port_output_as_port_const(port),
+               },
+       };
+
+       append_event(&event);
+
+       return BT_GRAPH_LISTENER_FUNC_STATUS_OK;
+}
+
+static
+bt_graph_listener_func_status graph_sink_input_port_added(
+               const bt_component_sink *comp, const bt_port_input *port,
+               void *data __attribute__((unused)))
+{
+       struct event event = {
+               .type = GRAPH_SINK_INPUT_PORT_ADDED,
+               .data.graph_sink_input_port_added = {
+                       .comp = bt_component_sink_as_component_const(comp),
+                       .port = bt_port_input_as_port_const(port),
+               },
+       };
+
+       append_event(&event);
+
+       return BT_GRAPH_LISTENER_FUNC_STATUS_OK;
+}
+
+static
+void init_test(void)
+{
+       int ret;
+
+       msg_iter_class = bt_message_iterator_class_create(src_iter_next);
+       BT_ASSERT(msg_iter_class);
+
+       src_comp_class = bt_component_class_source_create(
+               "src", msg_iter_class);
+       BT_ASSERT(src_comp_class);
+       ret = bt_component_class_source_set_initialize_method(
+               src_comp_class, src_init);
+       BT_ASSERT(ret == 0);
+       ret = bt_component_class_source_set_output_port_connected_method(
+               src_comp_class, src_output_port_connected);
+       BT_ASSERT(ret == 0);
+       sink_comp_class = bt_component_class_sink_create("sink",
+               sink_consume);
+       BT_ASSERT(sink_comp_class);
+       ret = bt_component_class_sink_set_initialize_method(sink_comp_class,
+               sink_init);
+       BT_ASSERT(ret == 0);
+       ret = bt_component_class_sink_set_input_port_connected_method(
+               sink_comp_class, sink_input_port_connected);
+       BT_ASSERT(ret == 0);
+       events = g_array_new(FALSE, TRUE, sizeof(struct event));
+       BT_ASSERT(events);
+}
+
+static
+void fini_test(void)
+{
+       bt_component_class_source_put_ref(src_comp_class);
+       bt_component_class_sink_put_ref(sink_comp_class);
+       g_array_free(events, TRUE);
+       BT_MESSAGE_ITERATOR_CLASS_PUT_REF_AND_RESET(msg_iter_class);
+}
+
+static
+const bt_component_source *create_src(bt_graph *graph)
+{
+       const bt_component_source *comp;
+       int ret;
+
+       ret = bt_graph_add_source_component(graph, src_comp_class,
+               "src-comp", NULL, BT_LOGGING_LEVEL_NONE, &comp);
+       BT_ASSERT(ret == 0);
+       return comp;
+}
+
+static
+const bt_component_sink *create_sink(bt_graph *graph)
+{
+       const bt_component_sink *comp;
+       int ret;
+
+       ret = bt_graph_add_sink_component(graph, sink_comp_class,
+               "sink-comp", NULL, BT_LOGGING_LEVEL_NONE, &comp);
+       BT_ASSERT(ret == 0);
+       return comp;
+}
+
+static
+bt_graph *create_graph(void)
+{
+       bt_graph *graph = bt_graph_create(0);
+       int ret;
+
+       BT_ASSERT(graph);
+       ret = bt_graph_add_source_component_output_port_added_listener(
+               graph, graph_src_output_port_added, NULL, NULL);
+       BT_ASSERT(ret >= 0);
+       ret = bt_graph_add_sink_component_input_port_added_listener(
+               graph, graph_sink_input_port_added, NULL, NULL);
+       BT_ASSERT(ret >= 0);
+       return graph;
+}
+
+static
+void prepare_test(enum test test, const char *name)
+{
+       clear_events();
+       current_test = test;
+       diag("test: %s", name);
+}
+
+static
+void test_src_adds_port_in_port_connected(void)
+{
+       const bt_component_source *src;
+       const bt_component_sink *sink;
+       const bt_component *gsrc;
+       const bt_component *gsink;
+       bt_graph *graph;
+       const bt_port_output *src_def_port;
+       const bt_port_output *src_hello_port;
+       const bt_port_input *sink_def_port;
+       const bt_port *gsrc_def_port;
+       const bt_port *gsrc_hello_port;
+       const bt_port *gsink_def_port;
+       struct event event;
+       bt_graph_connect_ports_status status;
+       size_t src_port_connected_pos;
+       size_t graph_port_added_src_pos;
+
+       prepare_test(TEST_SRC_ADDS_PORT_IN_PORT_CONNECTED,
+               "source adds port in port connected");
+       graph = create_graph();
+       BT_ASSERT(graph);
+       src = create_src(graph);
+       sink = create_sink(graph);
+       src_def_port = bt_component_source_borrow_output_port_by_name_const(src,
+                                                                           "out");
+       BT_ASSERT(src_def_port);
+       sink_def_port = bt_component_sink_borrow_input_port_by_name_const(sink,
+                                                                         "in");
+       BT_ASSERT(sink_def_port);
+       status = bt_graph_connect_ports(graph, src_def_port,
+               sink_def_port, NULL);
+       BT_ASSERT(status == 0);
+       src_hello_port = bt_component_source_borrow_output_port_by_name_const(src,
+                                                                             "hello");
+       BT_ASSERT(src_hello_port);
+       gsrc = bt_component_source_as_component_const(src);
+       gsink = bt_component_sink_as_component_const(sink);
+       gsrc_def_port = bt_port_output_as_port_const(src_def_port);
+       gsrc_hello_port = bt_port_output_as_port_const(src_hello_port);
+       gsink_def_port = bt_port_input_as_port_const(sink_def_port);
+
+       /* We're supposed to have 5 events */
+       ok(events->len == 5, "we have the expected number of events");
+
+       /* Source's port added */
+       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
+       event.data.graph_src_output_port_added.comp = gsrc;
+       event.data.graph_src_output_port_added.port = gsrc_def_port;
+       ok(has_event(&event), "got the expected graph's port added event (for source, initial)");
+
+       /* Sink's port added */
+       event.type = GRAPH_SINK_INPUT_PORT_ADDED;
+       event.data.graph_sink_input_port_added.comp = gsink;
+       event.data.graph_sink_input_port_added.port = gsink_def_port;
+       ok(has_event(&event), "got the expected graph's port added event (for sink, initial)");
+
+       /* Source's port connected */
+       event.type = SRC_COMP_OUTPUT_PORT_CONNECTED;
+       event.data.src_comp_output_port_connected.comp = gsrc;
+       event.data.src_comp_output_port_connected.self_port = gsrc_def_port;
+       event.data.src_comp_output_port_connected.other_port = gsink_def_port;
+       ok(has_event(&event), "got the expected source's port connected event");
+       src_port_connected_pos = event_pos(&event);
+
+       /* Graph's port added (source) */
+       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
+       event.data.graph_src_output_port_added.comp = gsrc;
+       event.data.graph_src_output_port_added.port = gsrc_hello_port;
+       ok(has_event(&event), "got the expected graph's port added event (for source)");
+       graph_port_added_src_pos = event_pos(&event);
+
+       /* Sink's port connected */
+       event.type = SINK_COMP_INPUT_PORT_CONNECTED;
+       event.data.sink_comp_input_port_connected.comp = gsink;
+       event.data.sink_comp_input_port_connected.self_port = gsink_def_port;
+       event.data.sink_comp_input_port_connected.other_port = gsrc_def_port;
+       ok(has_event(&event), "got the expected sink's port connected event");
+
+       /* Order of events */
+       ok(src_port_connected_pos < graph_port_added_src_pos,
+               "event order is good");
+
+       bt_graph_put_ref(graph);
+}
+
+static
+void test_simple(void)
+{
+       const bt_component_source *src;
+       const bt_component_sink *sink;
+       const bt_component *gsrc;
+       const bt_component *gsink;
+       bt_graph *graph;
+       const bt_port_output *src_def_port;
+       const bt_port_input *sink_def_port;
+       const bt_port *gsrc_def_port;
+       const bt_port *gsink_def_port;
+       struct event event;
+       bt_graph_connect_ports_status status;
+
+       prepare_test(TEST_SIMPLE, "simple");
+       graph = create_graph();
+       BT_ASSERT(graph);
+       src = create_src(graph);
+       sink = create_sink(graph);
+       src_def_port = bt_component_source_borrow_output_port_by_name_const(src,
+                                                                           "out");
+       BT_ASSERT(src_def_port);
+       sink_def_port = bt_component_sink_borrow_input_port_by_name_const(sink,
+                                                                         "in");
+       BT_ASSERT(sink_def_port);
+       status = bt_graph_connect_ports(graph, src_def_port,
+               sink_def_port, NULL);
+       BT_ASSERT(status == 0);
+       gsrc = bt_component_source_as_component_const(src);
+       gsink = bt_component_sink_as_component_const(sink);
+       gsrc_def_port = bt_port_output_as_port_const(src_def_port);
+       gsink_def_port = bt_port_input_as_port_const(sink_def_port);
+
+       /* We're supposed to have 4 events */
+       ok(events->len == 4, "we have the expected number of events");
+
+       /* Source's port added */
+       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
+       event.data.graph_src_output_port_added.comp = gsrc;
+       event.data.graph_src_output_port_added.port = gsrc_def_port;
+       ok(has_event(&event), "got the expected graph's port added event (for source, initial)");
+
+       /* Sink's port added */
+       event.type = GRAPH_SINK_INPUT_PORT_ADDED;
+       event.data.graph_sink_input_port_added.comp = gsink;
+       event.data.graph_sink_input_port_added.port = gsink_def_port;
+       ok(has_event(&event), "got the expected graph's port added event (for sink, initial)");
+
+       /* Source's port connected */
+       event.type = SRC_COMP_OUTPUT_PORT_CONNECTED;
+       event.data.src_comp_output_port_connected.comp = gsrc;
+       event.data.src_comp_output_port_connected.self_port = gsrc_def_port;
+       event.data.src_comp_output_port_connected.other_port = gsink_def_port;
+       ok(has_event(&event), "got the expected source's port connected event");
+
+       /* Sink's port connected */
+       event.type = SINK_COMP_INPUT_PORT_CONNECTED;
+       event.data.sink_comp_input_port_connected.comp = gsink;
+       event.data.sink_comp_input_port_connected.self_port = gsink_def_port;
+       event.data.sink_comp_input_port_connected.other_port = gsrc_def_port;
+       ok(has_event(&event), "got the expected sink's port connected event");
+
+       bt_graph_put_ref(graph);
+}
+
+static
+void test_src_port_connected_error(void)
+{
+       const bt_component_source *src;
+       const bt_component_sink *sink;
+       const bt_component *gsrc;
+       const bt_component *gsink;
+       bt_graph *graph;
+       const bt_port_output *src_def_port;
+       const bt_port_input *sink_def_port;
+       const bt_port *gsrc_def_port;
+       const bt_port *gsink_def_port;
+       const bt_connection *conn = NULL;
+       struct event event;
+       bt_graph_connect_ports_status status;
+
+       prepare_test(TEST_SRC_PORT_CONNECTED_ERROR, "port connected error: source");
+       graph = create_graph();
+       BT_ASSERT(graph);
+       src = create_src(graph);
+       sink = create_sink(graph);
+       src_def_port = bt_component_source_borrow_output_port_by_name_const(src,
+                                                                           "out");
+       BT_ASSERT(src_def_port);
+       sink_def_port = bt_component_sink_borrow_input_port_by_name_const(sink,
+                                                                         "in");
+       BT_ASSERT(sink_def_port);
+       status = bt_graph_connect_ports(graph, src_def_port,
+               sink_def_port, &conn);
+       ok(status != BT_GRAPH_CONNECT_PORTS_STATUS_OK,
+               "bt_graph_connect_ports() returns an error");
+       bt_current_thread_clear_error();
+       ok(!conn, "returned connection is still NULL");
+       gsrc = bt_component_source_as_component_const(src);
+       gsink = bt_component_sink_as_component_const(sink);
+       gsrc_def_port = bt_port_output_as_port_const(src_def_port);
+       gsink_def_port = bt_port_input_as_port_const(sink_def_port);
+
+       /* We're supposed to have 3 events */
+       ok(events->len == 3, "we have the expected number of events");
+
+       /* Source's port added */
+       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
+       event.data.graph_src_output_port_added.comp = gsrc;
+       event.data.graph_src_output_port_added.port = gsrc_def_port;
+       ok(has_event(&event), "got the expected graph's port added event (for source, initial)");
+
+       /* Sink's port added */
+       event.type = GRAPH_SINK_INPUT_PORT_ADDED;
+       event.data.graph_sink_input_port_added.comp = gsink;
+       event.data.graph_sink_input_port_added.port = gsink_def_port;
+       ok(has_event(&event), "got the expected graph's port added event (for sink, initial)");
+
+       /* Source's port connected */
+       event.type = SRC_COMP_OUTPUT_PORT_CONNECTED;
+       event.data.src_comp_output_port_connected.comp = gsrc;
+       event.data.src_comp_output_port_connected.self_port = gsrc_def_port;
+       event.data.src_comp_output_port_connected.other_port = gsink_def_port;
+       ok(has_event(&event), "got the expected source's port connected event");
+
+       bt_graph_put_ref(graph);
+}
+
+static
+void test_sink_port_connected_error(void)
+{
+       const bt_component_source *src;
+       const bt_component_sink *sink;
+       const bt_component *gsrc;
+       const bt_component *gsink;
+       bt_graph *graph;
+       const bt_port_output *src_def_port;
+       const bt_port_input *sink_def_port;
+       const bt_port *gsrc_def_port;
+       const bt_port *gsink_def_port;
+       const bt_connection *conn = NULL;
+       struct event event;
+       bt_graph_connect_ports_status status;
+
+       prepare_test(TEST_SINK_PORT_CONNECTED_ERROR, "port connected error: sink");
+       graph = create_graph();
+       BT_ASSERT(graph);
+       src = create_src(graph);
+       sink = create_sink(graph);
+       src_def_port = bt_component_source_borrow_output_port_by_name_const(src,
+                                                                           "out");
+       BT_ASSERT(src_def_port);
+       sink_def_port = bt_component_sink_borrow_input_port_by_name_const(sink,
+                                                                         "in");
+       BT_ASSERT(sink_def_port);
+       status = bt_graph_connect_ports(graph, src_def_port,
+               sink_def_port, &conn);
+       ok(status != BT_GRAPH_CONNECT_PORTS_STATUS_OK,
+               "bt_graph_connect_ports() returns an error");
+       bt_current_thread_clear_error();
+       ok(!conn, "returned connection is still NULL");
+       gsrc = bt_component_source_as_component_const(src);
+       gsink = bt_component_sink_as_component_const(sink);
+       gsrc_def_port = bt_port_output_as_port_const(src_def_port);
+       gsink_def_port = bt_port_input_as_port_const(sink_def_port);
+
+       /* We're supposed to have 4 events */
+       ok(events->len == 4, "we have the expected number of events");
+
+       /* Source's port added */
+       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
+       event.data.graph_src_output_port_added.comp = gsrc;
+       event.data.graph_src_output_port_added.port = gsrc_def_port;
+       ok(has_event(&event), "got the expected graph's port added event (for source, initial)");
+
+       /* Sink's port added */
+       event.type = GRAPH_SINK_INPUT_PORT_ADDED;
+       event.data.graph_sink_input_port_added.comp = gsink;
+       event.data.graph_sink_input_port_added.port = gsink_def_port;
+       ok(has_event(&event), "got the expected graph's port added event (for sink, initial)");
+
+       /* Source's port connected */
+       event.type = SRC_COMP_OUTPUT_PORT_CONNECTED;
+       event.data.src_comp_output_port_connected.comp = gsrc;
+       event.data.src_comp_output_port_connected.self_port = gsrc_def_port;
+       event.data.src_comp_output_port_connected.other_port = gsink_def_port;
+       ok(has_event(&event), "got the expected source's port connected event");
+
+       /* Sink's port connected */
+       event.type = SINK_COMP_INPUT_PORT_CONNECTED;
+       event.data.sink_comp_input_port_connected.comp = gsink;
+       event.data.sink_comp_input_port_connected.self_port = gsink_def_port;
+       event.data.sink_comp_input_port_connected.other_port = gsrc_def_port;
+       ok(has_event(&event), "got the expected sink's port connected event");
+
+       bt_graph_put_ref(graph);
+}
+
+static
+void test_empty_graph(void)
+{
+       bt_graph *graph;
+
+       prepare_test(TEST_EMPTY_GRAPH, "empty graph");
+       graph = create_graph();
+       ok(events->len == 0, "empty graph generates no events");
+       bt_graph_put_ref(graph);
+}
+
+int main(void)
+{
+       plan_tests(NR_TESTS);
+       init_test();
+       test_empty_graph();
+       test_simple();
+       test_src_port_connected_error();
+       test_sink_port_connected_error();
+       test_src_adds_port_in_port_connected();
+       fini_test();
+       return exit_status();
+}
diff --git a/tests/lib/test-plugin-init-fail-plugin/plugin-init-fail.cpp b/tests/lib/test-plugin-init-fail-plugin/plugin-init-fail.cpp
new file mode 100644 (file)
index 0000000..44bc117
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2024 EfficiOS, Inc.
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+static bt_plugin_initialize_func_status plugin_init(bt_self_plugin *)
+{
+    BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN("plugin-init-fail",
+                                                      "This is the error message");
+    return BT_PLUGIN_INITIALIZE_FUNC_STATUS_ERROR;
+}
+
+BT_PLUGIN_MODULE();
+BT_PLUGIN(test_init_fail);
+BT_PLUGIN_DESCRIPTION("Babeltrace plugin with init function that fails");
+BT_PLUGIN_AUTHOR("Sophie Couturier");
+BT_PLUGIN_LICENSE("Beerware");
+BT_PLUGIN_INITIALIZE_FUNC(plugin_init);
diff --git a/tests/lib/test-plugin-init-fail.cpp b/tests/lib/test-plugin-init-fail.cpp
new file mode 100644 (file)
index 0000000..6aac20d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2024 EfficiOS, Inc.
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/common.h"
+#include "cpp-common/bt2/exc.hpp"
+#include "cpp-common/bt2/plugin-load.hpp"
+#include "cpp-common/bt2c/c-string-view.hpp"
+#include "cpp-common/vendor/fmt/core.h"
+
+#include "tap/tap.h"
+
+namespace {
+
+void testFailOnLoadErrorTrue(const char * const pluginDir)
+{
+    plan_tests(1);
+
+    try {
+        bt2::findAllPluginsFromDir(pluginDir, false, true);
+        bt_common_abort();
+    } catch (const bt2::Error& exc) {
+        fmt::print("{}\n", exc.what());
+
+        const auto error = bt_current_thread_take_error();
+
+        /*
+         * The last error cause must be the one which the initialization
+         * function of our plugin appended.
+         */
+        const auto cause = bt_error_borrow_cause_by_index(error, 0);
+        const bt2c::CStringView msg {bt_error_cause_get_message(cause)};
+
+        ok(msg == "This is the error message", "Message of error cause 0 is expected");
+        bt_error_release(error);
+    }
+}
+
+void testFailOnLoadErrorFalse(const char * const pluginDir)
+{
+    plan_tests(1);
+
+    const auto plugins = bt2::findAllPluginsFromDir(pluginDir, false, false);
+
+    ok(!plugins, "No plugin set returned");
+}
+
+} /* namespace */
+
+int main(const int argc, const char ** const argv)
+{
+    if (argc != 3) {
+        fmt::print(stderr,
+                   "Usage: {} INIT-FAIL-PLUGIN-DIR FAIL-ON-LOAD-ERROR\n"
+                   "\n"
+                   "FAIL-ON-LOAD-ERROR must be `yes` or `no`\n",
+                   argv[0]);
+        return 1;
+    }
+
+    const auto pluginDir = argv[1];
+    const bt2c::CStringView failOnLoadErrorStr {argv[2]};
+
+    if (failOnLoadErrorStr == "yes") {
+        testFailOnLoadErrorTrue(pluginDir);
+    } else if (failOnLoadErrorStr == "no") {
+        testFailOnLoadErrorFalse(pluginDir);
+    } else {
+        fmt::print(stderr,
+                   "ERROR: Invalid value `{}` for FAIL-ON-LOAD-ERROR (expecting `yes` or `no`).\n",
+                   failOnLoadErrorStr);
+        return 1;
+    }
+
+    return exit_status();
+}
diff --git a/tests/lib/test-plugin-init-fail.sh b/tests/lib/test-plugin-init-fail.sh
new file mode 100755 (executable)
index 0000000..661023a
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2024 EfficiOS, Inc.
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+ret=0
+
+"${BT_TESTS_BUILDDIR}/lib/test-plugin-init-fail" "${BT_TESTS_BUILDDIR}/lib/test-plugin-init-fail-plugin/.libs" yes || ret=1
+"${BT_TESTS_BUILDDIR}/lib/test-plugin-init-fail" "${BT_TESTS_BUILDDIR}/lib/test-plugin-init-fail-plugin/.libs" no || ret=1
+
+exit $ret
diff --git a/tests/lib/test-plugin-plugins/Makefile.am b/tests/lib/test-plugin-plugins/Makefile.am
deleted file mode 100644 (file)
index 4725a56..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# SPDX-License-Identifier: MIT
-
-noinst_LTLIBRARIES = plugin-minimal.la plugin-sfs.la
-
-# the minimal plugin
-plugin_minimal_la_SOURCES = minimal.c
-plugin_minimal_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LT_NO_UNDEFINED) \
-       -rpath / -avoid-version -module $(LD_NOTEXT)
-plugin_minimal_la_LIBADD = \
-       $(top_builddir)/src/lib/libbabeltrace2.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la
-
-# source/filter/sink plugin
-plugin_sfs_la_SOURCES = sfs.c
-plugin_sfs_la_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(LT_NO_UNDEFINED) \
-       -rpath / -avoid-version -module $(LD_NOTEXT)
-plugin_sfs_la_LIBADD = \
-       $(top_builddir)/src/lib/libbabeltrace2.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la
diff --git a/tests/lib/test-plugin-plugins/minimal.c b/tests/lib/test-plugin-plugins/minimal.c
deleted file mode 100644 (file)
index 693f9db..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include <stdlib.h>
-#include <glib.h>
-
-static bt_plugin_initialize_func_status plugin_init(bt_self_plugin *plugin)
-{
-       g_setenv("BT_TEST_PLUGIN_INITIALIZE_CALLED", "1", 1);
-       return BT_PLUGIN_INITIALIZE_FUNC_STATUS_OK;
-}
-
-static void plugin_finalize(void)
-{
-       g_setenv("BT_TEST_PLUGIN_FINALIZE_CALLED", "1", 1);
-}
-
-BT_PLUGIN_MODULE();
-BT_PLUGIN(test_minimal);
-BT_PLUGIN_DESCRIPTION("Minimal Babeltrace plugin with no component classes");
-BT_PLUGIN_AUTHOR("Janine Sutto");
-BT_PLUGIN_LICENSE("Beerware");
-BT_PLUGIN_INITIALIZE_FUNC(plugin_init);
-BT_PLUGIN_FINALIZE_FUNC(plugin_finalize);
diff --git a/tests/lib/test-plugin-plugins/sfs.c b/tests/lib/test-plugin-plugins/sfs.c
deleted file mode 100644 (file)
index 6fbd76d..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/assert.h"
-
-static bt_component_class_sink_consume_method_status sink_consume(
-               bt_self_component_sink *self_comp)
-{
-       return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
-}
-
-static bt_component_class_get_supported_mip_versions_method_status
-sink_get_supported_mip_versions(
-               bt_self_component_class_sink *source_component_class,
-               const bt_value *params, void *initialize_method_data,
-               bt_logging_level logging_level,
-               bt_integer_range_set_unsigned *supported_versions)
-{
-       return (int) bt_integer_range_set_unsigned_add_range(
-               supported_versions, 0, 0);
-}
-
-static bt_message_iterator_class_initialize_method_status
-src_dummy_iterator_init_method(
-               bt_self_message_iterator *self_msg_iter,
-               bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *self_port)
-{
-       return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
-}
-
-static bt_message_iterator_class_initialize_method_status
-flt_dummy_iterator_init_method(
-               bt_self_message_iterator *self_msg_iter,
-               bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *self_port)
-{
-       return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
-}
-
-static void dummy_iterator_finalize_method(
-               bt_self_message_iterator *self_msg_iter)
-{
-}
-
-static bt_message_iterator_class_next_method_status
-dummy_iterator_next_method(
-               bt_self_message_iterator *self_msg_iter,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-}
-
-static bt_component_class_query_method_status flt_query_method(
-               bt_self_component_class_filter *component_class,
-               bt_private_query_executor *priv_query_exec,
-               const char *object, const bt_value *params,
-               __attribute__((unused)) void *method_data,
-               const bt_value **result)
-{
-       bt_value *res = bt_value_array_create();
-       bt_value *val;
-       *result = res;
-       int iret;
-
-       BT_ASSERT(*result);
-       iret = bt_value_array_append_string_element(res, object);
-       BT_ASSERT(iret == 0);
-       iret = bt_value_copy(params, &val);
-       BT_ASSERT(iret == 0);
-       iret = bt_value_array_append_element(res, val);
-       BT_ASSERT(iret == 0);
-       bt_value_put_ref(val);
-       return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-}
-
-BT_PLUGIN_MODULE();
-BT_PLUGIN(test_sfs);
-BT_PLUGIN_DESCRIPTION("Babeltrace plugin with source, sink, and filter component classes");
-BT_PLUGIN_AUTHOR("Janine Sutto");
-BT_PLUGIN_LICENSE("Beerware");
-BT_PLUGIN_VERSION(1, 2, 3, "yes");
-
-BT_PLUGIN_SOURCE_COMPONENT_CLASS(source, dummy_iterator_next_method);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION(source, "A source.");
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(source,
-       src_dummy_iterator_init_method);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(source,
-       dummy_iterator_finalize_method);
-
-BT_PLUGIN_SINK_COMPONENT_CLASS(sink, sink_consume);
-BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(sink, "A sink.");
-BT_PLUGIN_SINK_COMPONENT_CLASS_HELP(sink,
-       "Bacon ipsum dolor amet strip steak cupim pastrami venison shoulder.\n"
-       "Prosciutto beef ribs flank meatloaf pancetta brisket kielbasa drumstick\n"
-       "venison tenderloin cow tail. Beef short loin shoulder meatball, sirloin\n"
-       "ground round brisket salami cupim pork bresaola turkey bacon boudin.\n"
-);
-BT_PLUGIN_SINK_COMPONENT_CLASS_GET_SUPPORTED_MIP_VERSIONS_METHOD(sink,
-       sink_get_supported_mip_versions);
-
-BT_PLUGIN_FILTER_COMPONENT_CLASS(filter, dummy_iterator_next_method);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(filter, "A filter.");
-BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(filter,
-       flt_dummy_iterator_init_method);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(filter,
-       dummy_iterator_finalize_method);
-BT_PLUGIN_FILTER_COMPONENT_CLASS_QUERY_METHOD(filter, flt_query_method);
diff --git a/tests/lib/test-plugins-plugins/minimal.c b/tests/lib/test-plugins-plugins/minimal.c
new file mode 100644 (file)
index 0000000..7f9e016
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include <stdlib.h>
+#include <glib.h>
+
+static bt_plugin_initialize_func_status plugin_init(
+               bt_self_plugin *plugin __attribute__((unused)))
+{
+       g_setenv("BT_TEST_PLUGIN_INITIALIZE_CALLED", "1", 1);
+       return BT_PLUGIN_INITIALIZE_FUNC_STATUS_OK;
+}
+
+static void plugin_finalize(void)
+{
+       g_setenv("BT_TEST_PLUGIN_FINALIZE_CALLED", "1", 1);
+}
+
+BT_PLUGIN_MODULE();
+BT_PLUGIN(test_minimal);
+BT_PLUGIN_DESCRIPTION("Minimal Babeltrace plugin with no component classes");
+BT_PLUGIN_AUTHOR("Janine Sutto");
+BT_PLUGIN_LICENSE("Beerware");
+BT_PLUGIN_INITIALIZE_FUNC(plugin_init);
+BT_PLUGIN_FINALIZE_FUNC(plugin_finalize);
diff --git a/tests/lib/test-plugins-plugins/sfs.c b/tests/lib/test-plugins-plugins/sfs.c
new file mode 100644 (file)
index 0000000..d060879
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/assert.h"
+
+static bt_component_class_sink_consume_method_status sink_consume(
+               bt_self_component_sink *self_comp __attribute__((unused)))
+{
+       return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+}
+
+static bt_component_class_get_supported_mip_versions_method_status
+sink_get_supported_mip_versions(
+               bt_self_component_class_sink *source_component_class __attribute__((unused)),
+               const bt_value *params __attribute__((unused)),
+               void *initialize_method_data __attribute__((unused)),
+               bt_logging_level logging_level __attribute__((unused)),
+               bt_integer_range_set_unsigned *supported_versions)
+{
+       return (int) bt_integer_range_set_unsigned_add_range(
+               supported_versions, 0, 0);
+}
+
+static bt_message_iterator_class_initialize_method_status
+src_dummy_iterator_init_method(
+               bt_self_message_iterator *self_msg_iter __attribute__((unused)),
+               bt_self_message_iterator_configuration *config __attribute__((unused)),
+               bt_self_component_port_output *self_port __attribute__((unused)))
+{
+       return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
+}
+
+static bt_message_iterator_class_initialize_method_status
+flt_dummy_iterator_init_method(
+               bt_self_message_iterator *self_msg_iter __attribute__((unused)),
+               bt_self_message_iterator_configuration *config __attribute__((unused)),
+               bt_self_component_port_output *self_port __attribute__((unused)))
+{
+       return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
+}
+
+static void dummy_iterator_finalize_method(
+               bt_self_message_iterator *self_msg_iter __attribute__((unused)))
+{
+}
+
+static bt_message_iterator_class_next_method_status
+dummy_iterator_next_method(
+               bt_self_message_iterator *self_msg_iter __attribute__((unused)),
+               bt_message_array_const msgs __attribute__((unused)),
+               uint64_t capacity __attribute__((unused)),
+               uint64_t *count __attribute__((unused)))
+{
+       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+}
+
+static bt_component_class_query_method_status flt_query_method(
+               bt_self_component_class_filter *component_class __attribute__((unused)),
+               bt_private_query_executor *priv_query_exec __attribute__((unused)),
+               const char *object, const bt_value *params,
+               __attribute__((unused)) void *method_data,
+               const bt_value **result)
+{
+       bt_value *res = bt_value_array_create();
+       bt_value *val;
+       *result = res;
+       int iret;
+
+       BT_ASSERT(*result);
+       iret = bt_value_array_append_string_element(res, object);
+       BT_ASSERT(iret == 0);
+       iret = bt_value_copy(params, &val);
+       BT_ASSERT(iret == 0);
+       iret = bt_value_array_append_element(res, val);
+       BT_ASSERT(iret == 0);
+       bt_value_put_ref(val);
+       return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+}
+
+BT_PLUGIN_MODULE();
+BT_PLUGIN(test_sfs);
+BT_PLUGIN_DESCRIPTION("Babeltrace plugin with source, sink, and filter component classes");
+BT_PLUGIN_AUTHOR("Janine Sutto");
+BT_PLUGIN_LICENSE("Beerware");
+BT_PLUGIN_VERSION(1, 2, 3, "yes");
+
+BT_PLUGIN_SOURCE_COMPONENT_CLASS(source, dummy_iterator_next_method);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION(source, "A source.");
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(source,
+       src_dummy_iterator_init_method);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(source,
+       dummy_iterator_finalize_method);
+
+BT_PLUGIN_SINK_COMPONENT_CLASS(sink, sink_consume);
+BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(sink, "A sink.");
+BT_PLUGIN_SINK_COMPONENT_CLASS_HELP(sink,
+       "Bacon ipsum dolor amet strip steak cupim pastrami venison shoulder.\n"
+       "Prosciutto beef ribs flank meatloaf pancetta brisket kielbasa drumstick\n"
+       "venison tenderloin cow tail. Beef short loin shoulder meatball, sirloin\n"
+       "ground round brisket salami cupim pork bresaola turkey bacon boudin.\n"
+);
+BT_PLUGIN_SINK_COMPONENT_CLASS_GET_SUPPORTED_MIP_VERSIONS_METHOD(sink,
+       sink_get_supported_mip_versions);
+
+BT_PLUGIN_FILTER_COMPONENT_CLASS(filter, dummy_iterator_next_method);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(filter, "A filter.");
+BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(filter,
+       flt_dummy_iterator_init_method);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(filter,
+       dummy_iterator_finalize_method);
+BT_PLUGIN_FILTER_COMPONENT_CLASS_QUERY_METHOD(filter, flt_query_method);
diff --git a/tests/lib/test-plugins.c b/tests/lib/test-plugins.c
new file mode 100644 (file)
index 0000000..7e3c3e7
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "common/assert.h"
+#include <glib.h>
+#include "tap/tap.h"
+#include "common.h"
+
+#define NR_TESTS               38
+#define NON_EXISTING_PATH      "/this/hopefully/does/not/exist/5bc75f8d-0dba-4043-a509-d7984b97e42b.so"
+
+/* Those symbols are written to by some test plugins */
+static int check_env_var(const char *name)
+{
+       const char *val = getenv(name);
+
+       if (!val) {
+               return -1;
+       }
+
+       return atoi(val);
+}
+
+static void reset_test_plugin_env_vars(void)
+{
+       g_setenv("BT_TEST_PLUGIN_INITIALIZE_CALLED", "0", 1);
+       g_setenv("BT_TEST_PLUGIN_FINALIZE_CALLED", "0", 1);
+}
+
+static char *get_test_plugin_path(const char *plugin_dir,
+               const char *plugin_name)
+{
+       char *ret;
+       char *plugin_file_name;
+
+       if (asprintf(&plugin_file_name, "plugin-%s." G_MODULE_SUFFIX,
+                       plugin_name) == -1) {
+               abort();
+       }
+
+       ret = g_build_filename(plugin_dir, plugin_file_name, NULL);
+       free(plugin_file_name);
+
+       return ret;
+}
+
+static void test_minimal(const char *plugin_dir)
+{
+       const bt_plugin_set *plugin_set = NULL;
+       const bt_plugin *plugin;
+       char *minimal_path = get_test_plugin_path(plugin_dir, "minimal");
+       bt_plugin_find_all_from_file_status status;
+
+       BT_ASSERT(minimal_path);
+       diag("minimal plugin test below");
+
+       reset_test_plugin_env_vars();
+       status = bt_plugin_find_all_from_file(minimal_path, BT_FALSE,
+               &plugin_set);
+       ok(status == BT_PLUGIN_FIND_ALL_FROM_FILE_STATUS_OK,
+               "bt_plugin_find_all_from_file() succeeds with a valid file");
+       ok(plugin_set,
+               "bt_plugin_find_all_from_file() returns a plugin set");
+       ok(check_env_var("BT_TEST_PLUGIN_INITIALIZE_CALLED") == 1,
+               "plugin's initialization function is called during bt_plugin_find_all_from_file()");
+       ok(bt_plugin_set_get_plugin_count(plugin_set) == 1,
+               "bt_plugin_find_all_from_file() returns the expected number of plugins");
+       plugin = bt_plugin_set_borrow_plugin_by_index_const(plugin_set, 0);
+       ok(strcmp(bt_plugin_get_name(plugin), "test_minimal") == 0,
+               "bt_plugin_get_name() returns the expected name");
+       ok(strcmp(bt_plugin_get_description(plugin),
+               "Minimal Babeltrace plugin with no component classes") == 0,
+               "bt_plugin_get_description() returns the expected description");
+       ok(bt_plugin_get_version(plugin, NULL, NULL, NULL, NULL) ==
+               BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE,
+               "bt_plugin_get_version() fails when there's no version");
+       ok(strcmp(bt_plugin_get_author(plugin), "Janine Sutto") == 0,
+               "bt_plugin_get_author() returns the expected author");
+       ok(strcmp(bt_plugin_get_license(plugin), "Beerware") == 0,
+               "bt_plugin_get_license() returns the expected license");
+       ok(strcmp(bt_plugin_get_path(plugin), minimal_path) == 0,
+               "bt_plugin_get_path() returns the expected path");
+       ok(bt_plugin_get_source_component_class_count(plugin) == 0,
+               "bt_plugin_get_source_component_class_count() returns the expected value");
+       ok(bt_plugin_get_filter_component_class_count(plugin) == 0,
+               "bt_plugin_get_filter_component_class_count() returns the expected value");
+       ok(bt_plugin_get_sink_component_class_count(plugin) == 0,
+               "bt_plugin_get_sink_component_class_count() returns the expected value");
+       bt_plugin_set_put_ref(plugin_set);
+       ok(check_env_var("BT_TEST_PLUGIN_FINALIZE_CALLED") == 1,
+               "plugin's finalize function is called when the plugin is destroyed");
+
+       free(minimal_path);
+}
+
+static void test_sfs(const char *plugin_dir)
+{
+       const bt_plugin_set *plugin_set = NULL;
+       const bt_plugin *plugin;
+       const bt_component_class_sink *sink_comp_class;
+       const bt_component_class_source *source_comp_class;
+       const bt_component_class_filter *filter_comp_class;
+       const bt_component_sink *sink_component;
+       char *sfs_path = get_test_plugin_path(plugin_dir, "sfs");
+       unsigned int major, minor, patch;
+       const char *extra;
+       bt_value *params;
+       const bt_value *results;
+       const bt_value *object;
+       const bt_value *res_params;
+       bt_graph *graph;
+       const char *object_str;
+       bt_graph_add_component_status graph_ret;
+       bt_query_executor *query_exec;
+       int ret;
+       bt_plugin_find_all_from_file_status status;
+
+       BT_ASSERT(sfs_path);
+       diag("sfs plugin test below");
+
+       status = bt_plugin_find_all_from_file(sfs_path, BT_FALSE, &plugin_set);
+       BT_ASSERT(status == BT_PLUGIN_FIND_ALL_FROM_FILE_STATUS_OK &&
+               plugin_set && bt_plugin_set_get_plugin_count(plugin_set) == 1);
+       plugin = bt_plugin_set_borrow_plugin_by_index_const(plugin_set, 0);
+       ok(bt_plugin_get_version(plugin, &major, &minor, &patch, &extra) ==
+               BT_PROPERTY_AVAILABILITY_AVAILABLE,
+               "bt_plugin_get_version() succeeds when there's a version");
+       ok(major == 1,
+               "bt_plugin_get_version() returns the expected major version");
+       ok(minor == 2,
+               "bt_plugin_get_version() returns the expected minor version");
+       ok(patch == 3,
+               "bt_plugin_get_version() returns the expected patch version");
+       ok(strcmp(extra, "yes") == 0,
+               "bt_plugin_get_version() returns the expected extra version");
+       ok(bt_plugin_get_source_component_class_count(plugin) == 1,
+               "bt_plugin_get_source_component_class_count() returns the expected value");
+       ok(bt_plugin_get_filter_component_class_count(plugin) == 1,
+               "bt_plugin_get_filter_component_class_count() returns the expected value");
+       ok(bt_plugin_get_sink_component_class_count(plugin) == 1,
+               "bt_plugin_get_sink_component_class_count() returns the expected value");
+
+       source_comp_class = bt_plugin_borrow_source_component_class_by_name_const(
+               plugin, "source");
+       ok(source_comp_class,
+               "bt_plugin_borrow_source_component_class_by_name_const() finds a source component class");
+
+       sink_comp_class = bt_plugin_borrow_sink_component_class_by_name_const(
+               plugin, "sink");
+       ok(sink_comp_class,
+               "bt_plugin_borrow_sink_component_class_by_name_const() finds a sink component class");
+       ok(strcmp(bt_component_class_get_help(bt_component_class_sink_as_component_class_const(sink_comp_class)),
+                 "Bacon ipsum dolor amet strip steak cupim pastrami venison shoulder.\n"
+                 "Prosciutto beef ribs flank meatloaf pancetta brisket kielbasa drumstick\n"
+                 "venison tenderloin cow tail. Beef short loin shoulder meatball, sirloin\n"
+                 "ground round brisket salami cupim pork bresaola turkey bacon boudin.\n") == 0,
+               "bt_component_class_get_help() returns the expected help text");
+
+       filter_comp_class = bt_plugin_borrow_filter_component_class_by_name_const(
+               plugin, "filter");
+       ok(filter_comp_class,
+               "bt_plugin_borrow_filter_component_class_by_name_const() finds a filter component class");
+       params = bt_value_integer_signed_create_init(23);
+       BT_ASSERT(params);
+       query_exec = bt_query_executor_create(
+               bt_component_class_filter_as_component_class_const(
+                       filter_comp_class), "get-something", params);
+       BT_ASSERT(query_exec);
+       ret = bt_query_executor_query(query_exec, &results);
+       ok(ret == 0 && results, "bt_query_executor_query() succeeds");
+       BT_ASSERT(bt_value_is_array(results) && bt_value_array_get_length(results) == 2);
+       object = bt_value_array_borrow_element_by_index_const(results, 0);
+       BT_ASSERT(bt_value_is_string(object));
+       object_str = bt_value_string_get(object);
+       ok(strcmp(object_str, "get-something") == 0,
+               "bt_component_class_query() receives the expected object name");
+       res_params = bt_value_array_borrow_element_by_index_const(results, 1);
+       ok(bt_value_is_equal(res_params, params),
+               "bt_component_class_query() receives the expected parameters");
+
+       bt_component_class_sink_get_ref(sink_comp_class);
+       BT_PLUGIN_SET_PUT_REF_AND_RESET(plugin_set);
+       graph = bt_graph_create(0);
+       BT_ASSERT(graph);
+       graph_ret = bt_graph_add_sink_component(graph, sink_comp_class,
+               "the-sink", NULL, BT_LOGGING_LEVEL_NONE, &sink_component);
+       ok(graph_ret == BT_GRAPH_ADD_COMPONENT_STATUS_OK && sink_component,
+               "bt_graph_add_sink_component() still works after the plugin object is destroyed");
+       bt_graph_put_ref(graph);
+
+       free(sfs_path);
+       bt_component_class_sink_put_ref(sink_comp_class);
+       bt_value_put_ref(results);
+       bt_value_put_ref(params);
+       bt_query_executor_put_ref(query_exec);
+}
+
+static void test_create_all_from_dir(const char *plugin_dir)
+{
+       const bt_plugin_set *plugin_set;
+       bt_plugin_find_all_from_dir_status status;
+
+       diag("create from all test below");
+
+       status = bt_plugin_find_all_from_dir(NON_EXISTING_PATH, BT_FALSE,
+               BT_FALSE, &plugin_set);
+       ok(status == BT_PLUGIN_FIND_ALL_FROM_DIR_STATUS_ERROR,
+               "bt_plugin_find_all_from_dir() fails with an invalid path");
+       bt_current_thread_clear_error();
+
+       plugin_set = NULL;
+       status = bt_plugin_find_all_from_dir(plugin_dir, BT_FALSE, BT_FALSE,
+               &plugin_set);
+       ok(status == BT_PLUGIN_FIND_ALL_FROM_DIR_STATUS_OK,
+               "bt_plugin_find_all_from_dir() succeeds with a valid path");
+       ok(plugin_set,
+               "bt_plugin_find_all_from_dir() returns a plugin set with a valid path");
+
+       /* 2 or 4, if `.la` files are considered or not */
+       ok(bt_plugin_set_get_plugin_count(plugin_set) == 2 ||
+               bt_plugin_set_get_plugin_count(plugin_set) == 4,
+               "bt_plugin_find_all_from_dir() returns the expected number of plugin objects");
+
+       bt_plugin_set_put_ref(plugin_set);
+}
+
+static void test_find(const char *plugin_dir)
+{
+       int ret;
+       const bt_plugin *plugin;
+       char *plugin_path;
+       bt_plugin_find_status status;
+
+       ok(bt_plugin_find(NON_EXISTING_PATH, BT_TRUE, BT_FALSE, BT_FALSE,
+               BT_FALSE, BT_FALSE, &plugin) == BT_PLUGIN_FIND_STATUS_NOT_FOUND,
+               "bt_plugin_find() returns BT_PLUGIN_STATUS_NOT_FOUND with an unknown plugin name");
+       ret = asprintf(&plugin_path, "%s" G_SEARCHPATH_SEPARATOR_S
+                       G_DIR_SEPARATOR_S "ec1d09e5-696c-442e-b1c3-f9c6cf7f5958"
+                       G_SEARCHPATH_SEPARATOR_S G_SEARCHPATH_SEPARATOR_S
+                       G_SEARCHPATH_SEPARATOR_S "%s" G_SEARCHPATH_SEPARATOR_S
+                       "8db46494-a398-466a-9649-c765ae077629"
+                       G_SEARCHPATH_SEPARATOR_S,
+               NON_EXISTING_PATH, plugin_dir);
+       BT_ASSERT(ret > 0 && plugin_path);
+       g_setenv("BABELTRACE_PLUGIN_PATH", plugin_path, 1);
+       plugin = NULL;
+       status = bt_plugin_find("test_minimal", BT_TRUE, BT_FALSE, BT_FALSE,
+               BT_FALSE, BT_FALSE, &plugin);
+       ok(status == BT_PLUGIN_FIND_STATUS_OK,
+               "bt_plugin_find() succeeds with a plugin name it can find");
+       ok(plugin, "bt_plugin_find() returns a plugin object");
+       ok(strcmp(bt_plugin_get_author(plugin), "Janine Sutto") == 0,
+               "bt_plugin_find() finds the correct plugin for a given name");
+       BT_PLUGIN_PUT_REF_AND_RESET(plugin);
+       free(plugin_path);
+}
+
+int main(int argc, char **argv)
+{
+       int ret;
+       const char *plugin_dir;
+
+       if (argc != 2) {
+               puts("Usage: test_plugin plugin_directory");
+               ret = 1;
+               goto end;
+       }
+
+       plugin_dir = argv[1];
+       plan_tests(NR_TESTS);
+       test_minimal(plugin_dir);
+       test_sfs(plugin_dir);
+       test_create_all_from_dir(plugin_dir);
+       test_find(plugin_dir);
+       ret = exit_status();
+end:
+       return ret;
+}
diff --git a/tests/lib/test-plugins.sh b/tests/lib/test-plugins.sh
new file mode 100755 (executable)
index 0000000..f83e42b
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+"${BT_TESTS_BUILDDIR}/lib/test-plugins" "${BT_TESTS_BUILDDIR}/lib/test-plugins-plugins/.libs"
diff --git a/tests/lib/test-remove-destruction-listener-in-destruction-listener.c b/tests/lib/test-remove-destruction-listener-in-destruction-listener.c
new file mode 100644 (file)
index 0000000..806e561
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2019 Efficios, Inc.
+ */
+
+/*
+ * Test that remove a trace class or trace destruction listener from within
+ * a destruction listener of the same object works.
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include <common/assert.h>
+#include <stdbool.h>
+
+#include "tap/tap.h"
+
+#define NR_TESTS 16
+
+static bt_listener_id trace_class_destroyed_1_id;
+static bt_listener_id trace_class_destroyed_2_id;
+static bt_listener_id trace_class_destroyed_3_id;
+static bt_listener_id trace_class_destroyed_4_id;
+static bt_listener_id trace_class_destroyed_5_id;
+
+static bt_listener_id trace_destroyed_1_id;
+static bt_listener_id trace_destroyed_2_id;
+static bt_listener_id trace_destroyed_3_id;
+static bt_listener_id trace_destroyed_4_id;
+static bt_listener_id trace_destroyed_5_id;
+
+static bool trace_class_destroyed_1_called = false;
+static bool trace_class_destroyed_2_called = false;
+static bool trace_class_destroyed_3_called = false;
+static bool trace_class_destroyed_4_called = false;
+static bool trace_class_destroyed_5_called = false;
+
+static bool trace_destroyed_1_called = false;
+static bool trace_destroyed_2_called = false;
+static bool trace_destroyed_3_called = false;
+static bool trace_destroyed_4_called = false;
+static bool trace_destroyed_5_called = false;
+
+static
+void trace_class_destroyed_1(const bt_trace_class *tc __attribute__((unused)),
+               void *data __attribute__((unused)))
+{
+       trace_class_destroyed_1_called = true;
+}
+
+static
+void trace_class_destroyed_2(const bt_trace_class *tc,
+               void *data __attribute__((unused)))
+{
+       bt_trace_class_remove_listener_status remove_listener_status;
+
+       trace_class_destroyed_2_called = true;
+
+       /* Remove self.  You shall not crash. */
+       remove_listener_status = bt_trace_class_remove_destruction_listener(
+               tc, trace_class_destroyed_2_id);
+       ok(remove_listener_status == BT_TRACE_CLASS_REMOVE_LISTENER_STATUS_OK,
+               "remove trace class listener 2 from 2");
+}
+
+static
+void trace_class_destroyed_3(const bt_trace_class *tc,
+               void *data __attribute__((unused)))
+{
+       bt_trace_class_remove_listener_status remove_listener_status;
+
+       trace_class_destroyed_3_called = true;
+
+       /* Remove an already called listener. */
+       remove_listener_status = bt_trace_class_remove_destruction_listener(
+               tc, trace_class_destroyed_1_id);
+       ok(remove_listener_status == BT_TRACE_CLASS_REMOVE_LISTENER_STATUS_OK,
+               "remove trace class listener 1 from 3");
+}
+
+static
+void trace_class_destroyed_4(const bt_trace_class *tc,
+               void *data __attribute__((unused)))
+{
+       bt_trace_class_remove_listener_status remove_listener_status;
+
+       trace_class_destroyed_4_called = true;
+
+       /* Remove a not yet called listener. */
+       remove_listener_status = bt_trace_class_remove_destruction_listener(
+               tc, trace_class_destroyed_5_id);
+       ok(remove_listener_status == BT_TRACE_CLASS_REMOVE_LISTENER_STATUS_OK,
+               "remove trace class listener 5 from 4");
+}
+
+static
+void trace_class_destroyed_5(const bt_trace_class *tc __attribute__((unused)),
+               void *data __attribute__((unused)))
+{
+       trace_class_destroyed_5_called = true;
+}
+
+static
+void trace_destroyed_1(const bt_trace *t __attribute__((unused)),
+               void *data __attribute__((unused)))
+{
+       trace_destroyed_1_called = true;
+}
+
+static
+void trace_destroyed_2(const bt_trace *t,
+               void *data __attribute__((unused)))
+{
+       bt_trace_remove_listener_status remove_listener_status;
+
+       trace_destroyed_2_called = true;
+
+       /* Remove self.  You shall not crash. */
+       remove_listener_status = bt_trace_remove_destruction_listener(
+               t, trace_destroyed_2_id);
+       ok(remove_listener_status == BT_TRACE_REMOVE_LISTENER_STATUS_OK,
+               "remove trace listener 2 from 2");
+}
+
+static
+void trace_destroyed_3(const bt_trace *t,
+               void *data __attribute__((unused)))
+{
+       bt_trace_remove_listener_status remove_listener_status;
+
+       trace_destroyed_3_called = true;
+
+       /* Remove an already called listener. */
+       remove_listener_status = bt_trace_remove_destruction_listener(
+               t, trace_destroyed_1_id);
+       ok(remove_listener_status == BT_TRACE_REMOVE_LISTENER_STATUS_OK,
+               "remove trace listener 1 from 3");
+}
+
+static
+void trace_destroyed_4(const bt_trace *t,
+               void *data __attribute__((unused)))
+{
+       bt_trace_remove_listener_status remove_listener_status;
+
+       trace_destroyed_4_called = true;
+
+       /* Remove a not yet called listener. */
+       remove_listener_status = bt_trace_remove_destruction_listener(
+               t, trace_destroyed_5_id);
+       ok(remove_listener_status == BT_TRACE_REMOVE_LISTENER_STATUS_OK,
+               "remove trace listener 5 from 4");
+}
+
+static
+void trace_destroyed_5(const bt_trace *t __attribute__((unused)),
+               void *data __attribute__((unused)))
+{
+       trace_destroyed_5_called = true;
+}
+
+static
+bt_component_class_initialize_method_status hello_init(
+               bt_self_component_source *self_component,
+               bt_self_component_source_configuration *config __attribute__((unused)),
+               const bt_value *params __attribute__((unused)),
+               void *init_method_data __attribute__((unused)))
+{
+       bt_self_component *self_comp;
+       bt_trace_class *tc;
+       bt_trace *t;
+       bt_trace_class_add_listener_status trace_class_add_listener_status;
+       bt_trace_add_listener_status trace_add_listener_status;
+
+       self_comp = bt_self_component_source_as_self_component(self_component);
+       tc = bt_trace_class_create(self_comp);
+       BT_ASSERT(tc);
+
+       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
+               tc, trace_class_destroyed_1, NULL, &trace_class_destroyed_1_id);
+       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
+
+       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
+               tc, trace_class_destroyed_2, NULL, &trace_class_destroyed_2_id);
+       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
+
+       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
+               tc, trace_class_destroyed_3, NULL, &trace_class_destroyed_3_id);
+       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
+
+       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
+               tc, trace_class_destroyed_4, NULL, &trace_class_destroyed_4_id);
+       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
+
+       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
+               tc, trace_class_destroyed_5, NULL, &trace_class_destroyed_5_id);
+       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
+
+       t = bt_trace_create(tc);
+       BT_ASSERT(t);
+
+       trace_add_listener_status = bt_trace_add_destruction_listener(
+               t, trace_destroyed_1, NULL, &trace_destroyed_1_id);
+       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
+
+       trace_add_listener_status = bt_trace_add_destruction_listener(
+               t, trace_destroyed_2, NULL, &trace_destroyed_2_id);
+       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
+
+       trace_add_listener_status = bt_trace_add_destruction_listener(
+               t, trace_destroyed_3, NULL, &trace_destroyed_3_id);
+       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
+
+       trace_add_listener_status = bt_trace_add_destruction_listener(
+               t, trace_destroyed_4, NULL, &trace_destroyed_4_id);
+       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
+
+       trace_add_listener_status = bt_trace_add_destruction_listener(
+               t, trace_destroyed_5, NULL, &trace_destroyed_5_id);
+       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
+
+       /* Destroy the trace. */
+       bt_trace_put_ref(t);
+
+       ok(trace_destroyed_1_called, "trace destruction listener 1 called");
+       ok(trace_destroyed_2_called, "trace destruction listener 2 called");
+       ok(trace_destroyed_3_called, "trace destruction listener 3 called");
+       ok(trace_destroyed_4_called, "trace destruction listener 4 called");
+       ok(!trace_destroyed_5_called, "trace destruction listener 5 not called");
+
+       /* Destroy the trace class. */
+       bt_trace_class_put_ref(tc);
+
+       ok(trace_class_destroyed_1_called, "trace class destruction listener 1 called");
+       ok(trace_class_destroyed_2_called, "trace class destruction listener 2 called");
+       ok(trace_class_destroyed_3_called, "trace class destruction listener 3 called");
+       ok(trace_class_destroyed_4_called, "trace class destruction listener 4 called");
+       ok(!trace_class_destroyed_5_called, "trace class destruction listener 5 not called");
+
+       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+}
+
+static
+bt_message_iterator_class_next_method_status hello_iter_next(
+               bt_self_message_iterator *message_iterator __attribute__((unused)),
+               bt_message_array_const msgs __attribute__((unused)),
+               uint64_t capacity __attribute__((unused)),
+               uint64_t *count __attribute__((unused)))
+{
+       BT_ASSERT(false);
+       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+}
+
+int main(void)
+{
+       bt_graph *graph;
+       bt_message_iterator_class *msg_iter_cls;
+       bt_component_class_source *source_cc;
+       bt_component_class_set_method_status set_method_status;
+       bt_graph_add_component_status add_component_status;
+       const bt_component_source *source;
+
+       plan_tests(NR_TESTS);
+
+       msg_iter_cls = bt_message_iterator_class_create(hello_iter_next);
+       BT_ASSERT(msg_iter_cls);
+
+       source_cc = bt_component_class_source_create("Hello", msg_iter_cls);
+       BT_ASSERT(source_cc);
+
+       set_method_status = bt_component_class_source_set_initialize_method(
+               source_cc, hello_init);
+       BT_ASSERT(set_method_status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+
+       graph = bt_graph_create(0);
+       BT_ASSERT(graph);
+
+       add_component_status = bt_graph_add_source_component(
+               graph, source_cc, "name", NULL,
+               BT_LOGGING_LEVEL_WARNING, &source);
+       BT_ASSERT(add_component_status == BT_GRAPH_ADD_COMPONENT_STATUS_OK);
+
+       bt_component_class_source_put_ref(source_cc);
+       bt_message_iterator_class_put_ref(msg_iter_cls);
+       bt_graph_put_ref(graph);
+
+       return exit_status();
+}
diff --git a/tests/lib/test-simple-sink.c b/tests/lib/test-simple-sink.c
new file mode 100644 (file)
index 0000000..4bf97d4
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/assert.h"
+#include <string.h>
+#include "tap/tap.h"
+
+#define NR_TESTS 68
+
+struct test_data {
+       bt_graph_simple_sink_component_initialize_func_status init_status;
+       bt_graph_simple_sink_component_consume_func_status consume_status;
+};
+
+static
+bt_graph_simple_sink_component_initialize_func_status simple_INITIALIZE_func(
+               bt_message_iterator *iterator,
+               void *data)
+{
+       struct test_data *test_data = data;
+
+       ok(iterator, "Message iterator is not NULL in initialization function");
+       ok(data, "Data is not NULL in initialization function");
+       return test_data->init_status;
+}
+
+static
+bt_graph_simple_sink_component_consume_func_status simple_consume_func(
+               bt_message_iterator *iterator,
+               void *data)
+{
+       struct test_data *test_data = data;
+
+       ok(iterator, "Message iterator is not NULL in consume function");
+       ok(data, "Data is not NULL in consume function");
+       return test_data->consume_status;
+}
+
+static
+void simple_fini_func(void *data)
+{
+       ok(data, "Data is not NULL in finalization function");
+}
+
+static
+bt_component_class_initialize_method_status src_init(
+               bt_self_component_source *self_comp,
+               bt_self_component_source_configuration *config __attribute__((unused)),
+               const bt_value *params __attribute__((unused)),
+               void *init_method_data __attribute__((unused)))
+{
+       bt_self_component_add_port_status status;
+
+       status = bt_self_component_source_add_output_port(self_comp,
+               "out", NULL, NULL);
+       BT_ASSERT(status == BT_SELF_COMPONENT_ADD_PORT_STATUS_OK);
+       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+}
+
+static
+bt_message_iterator_class_next_method_status src_iter_next(
+               bt_self_message_iterator *message_iterator __attribute__((unused)),
+               bt_message_array_const msgs __attribute__((unused)),
+               uint64_t capacity __attribute__((unused)),
+               uint64_t *count __attribute__((unused)))
+{
+       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
+}
+
+static
+bt_graph *create_graph_with_source(const bt_port_output **out_port)
+{
+       bt_message_iterator_class *msg_iter_cls;
+       bt_component_class_source *src_comp_cls;
+       bt_graph *graph;
+       const bt_component_source *src_comp = NULL;
+       bt_graph_add_component_status add_comp_status;
+       bt_component_class_set_method_status set_method_status;
+
+       BT_ASSERT(out_port);
+
+       msg_iter_cls = bt_message_iterator_class_create(src_iter_next);
+       BT_ASSERT(msg_iter_cls);
+
+       src_comp_cls = bt_component_class_source_create("src", msg_iter_cls);
+       BT_ASSERT(src_comp_cls);
+       set_method_status = bt_component_class_source_set_initialize_method(
+               src_comp_cls, src_init);
+       BT_ASSERT(set_method_status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
+       graph = bt_graph_create(0);
+       BT_ASSERT(graph);
+       add_comp_status = bt_graph_add_source_component(graph, src_comp_cls,
+               "src", NULL, BT_LOGGING_LEVEL_NONE, &src_comp);
+       BT_ASSERT(add_comp_status == BT_GRAPH_ADD_COMPONENT_STATUS_OK);
+       BT_ASSERT(src_comp);
+       *out_port = bt_component_source_borrow_output_port_by_index_const(
+               src_comp, 0);
+       BT_ASSERT(*out_port);
+       bt_component_class_source_put_ref(src_comp_cls);
+       bt_message_iterator_class_put_ref(msg_iter_cls);
+       return graph;
+}
+
+static
+void test_simple_expect_run_once_status(
+               bt_graph_simple_sink_component_initialize_func_status init_status,
+               bt_graph_simple_sink_component_consume_func_status consume_status,
+               bt_graph_run_once_status exp_run_once_status)
+{
+       const bt_port_output *src_out_port = NULL;
+       bt_graph *graph;
+       const bt_component_sink *sink_comp = NULL;
+       const bt_port_input *sink_in_port;
+       bt_graph_add_component_status add_comp_status;
+       bt_graph_run_once_status run_once_status;
+       bt_graph_connect_ports_status connect_status;
+       struct test_data test_data = {
+               .init_status = init_status,
+               .consume_status = consume_status,
+       };
+       const struct bt_error *err;
+
+       graph = create_graph_with_source(&src_out_port);
+       BT_ASSERT(graph);
+       BT_ASSERT(src_out_port);
+
+       add_comp_status = bt_graph_add_simple_sink_component(graph, "sink",
+               simple_INITIALIZE_func, simple_consume_func, simple_fini_func,
+               &test_data, &sink_comp);
+       BT_ASSERT(add_comp_status == BT_GRAPH_ADD_COMPONENT_STATUS_OK);
+       BT_ASSERT(sink_comp);
+
+       sink_in_port = bt_component_sink_borrow_input_port_by_name_const(
+               sink_comp, "in");
+       ok(sink_in_port,
+               "Simple sink component has an input port named \"in\"");
+
+       connect_status = bt_graph_connect_ports(graph, src_out_port,
+               sink_in_port, NULL);
+       ok(connect_status == BT_GRAPH_CONNECT_PORTS_STATUS_OK,
+               "Simple sink component's \"in\" port is connectable");
+
+       run_once_status = bt_graph_run_once(graph);
+       ok(run_once_status == exp_run_once_status,
+               "Graph \"run once\" status is the expected one (status code: %d)",
+               run_once_status);
+
+       err = bt_current_thread_take_error();
+       ok((run_once_status < 0) == (err != NULL),
+               "Current thread error is set if bt_graph_run_once returned an error");
+
+       bt_graph_put_ref(graph);
+       if (err) {
+               bt_error_release(err);
+       }
+}
+
+int main(void)
+{
+       plan_tests(NR_TESTS);
+
+       /* Test initialization function status */
+       test_simple_expect_run_once_status(
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK,
+               BT_GRAPH_RUN_ONCE_STATUS_OK);
+       test_simple_expect_run_once_status(
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_ERROR,
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK,
+               BT_GRAPH_RUN_ONCE_STATUS_ERROR);
+       test_simple_expect_run_once_status(
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_MEMORY_ERROR,
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK,
+               BT_GRAPH_RUN_ONCE_STATUS_MEMORY_ERROR);
+
+       /* Test "consume" function status */
+       test_simple_expect_run_once_status(
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK,
+               BT_GRAPH_RUN_ONCE_STATUS_OK);
+       test_simple_expect_run_once_status(
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_ERROR,
+               BT_GRAPH_RUN_ONCE_STATUS_ERROR);
+       test_simple_expect_run_once_status(
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_MEMORY_ERROR,
+               BT_GRAPH_RUN_ONCE_STATUS_MEMORY_ERROR);
+       test_simple_expect_run_once_status(
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_AGAIN,
+               BT_GRAPH_RUN_ONCE_STATUS_AGAIN);
+       test_simple_expect_run_once_status(
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
+               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_END,
+               BT_GRAPH_RUN_ONCE_STATUS_END);
+
+       return exit_status();
+}
diff --git a/tests/lib/test-trace-ir-ref.c b/tests/lib/test-trace-ir-ref.c
new file mode 100644 (file)
index 0000000..a235104
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * Trace IR Reference Count test
+ */
+
+#include <stdio.h>
+#include "tap/tap.h"
+#include <babeltrace2/babeltrace.h>
+#include "lib/object.h"
+#include "compat/stdlib.h"
+#include "common/assert.h"
+#include <babeltrace2-ctf-writer/writer.h>
+#include <babeltrace2-ctf-writer/clock.h>
+#include <babeltrace2-ctf-writer/clock-class.h>
+#include <babeltrace2-ctf-writer/stream.h>
+#include <babeltrace2-ctf-writer/event.h>
+#include <babeltrace2-ctf-writer/event-types.h>
+#include <babeltrace2-ctf-writer/event-fields.h>
+#include <babeltrace2-ctf-writer/stream-class.h>
+#include <babeltrace2-ctf-writer/trace.h>
+#include "common.h"
+
+#define NR_TESTS 37
+
+struct bt_user {
+       bt_trace_class *tc;
+       bt_stream_class *sc;
+       bt_event_class *ec;
+       bt_stream *stream;
+       bt_event *event;
+};
+
+struct writer_user {
+       struct bt_ctf_writer *writer;
+       struct bt_ctf_trace *tc;
+       struct bt_ctf_stream_class *sc;
+       struct bt_ctf_event_class *ec;
+       struct bt_ctf_stream *stream;
+       struct bt_ctf_event *event;
+};
+
+const char *writer_user_names[] = {
+       "writer",
+       "trace",
+       "stream class",
+       "event class",
+       "stream",
+       "event",
+};
+
+static const size_t WRITER_USER_NR_ELEMENTS =
+       sizeof(struct writer_user) / sizeof(void *);
+
+/**
+ * Returns a structure containing the following fields:
+ *     - uint8_t payload_8;
+ *     - uint16_t payload_16;
+ *     - uint32_t payload_32;
+ */
+static bt_field_class *create_integer_struct(bt_trace_class *trace_class)
+{
+       int ret;
+       bt_field_class *structure = NULL;
+       bt_field_class *ui8 = NULL, *ui16 = NULL, *ui32 = NULL;
+
+       structure = bt_field_class_structure_create(trace_class);
+       BT_ASSERT(structure);
+       ui8 = bt_field_class_integer_unsigned_create(trace_class);
+       BT_ASSERT(ui8);
+       bt_field_class_integer_set_field_value_range(ui8, 8);
+       ret = bt_field_class_structure_append_member(structure,
+               "payload_8", ui8);
+       BT_ASSERT(ret == 0);
+       ui16 = bt_field_class_integer_unsigned_create(trace_class);
+       BT_ASSERT(ui16);
+       bt_field_class_integer_set_field_value_range(ui16, 16);
+       ret = bt_field_class_structure_append_member(structure,
+               "payload_16", ui16);
+       BT_ASSERT(ret == 0);
+       ui32 = bt_field_class_integer_unsigned_create(trace_class);
+       BT_ASSERT(ui32);
+       bt_field_class_integer_set_field_value_range(ui32, 32);
+       ret = bt_field_class_structure_append_member(structure,
+               "payload_32", ui32);
+       BT_ASSERT(ret == 0);
+       BT_FIELD_CLASS_PUT_REF_AND_RESET(ui8);
+       BT_FIELD_CLASS_PUT_REF_AND_RESET(ui16);
+       BT_FIELD_CLASS_PUT_REF_AND_RESET(ui32);
+       return structure;
+}
+
+static struct bt_ctf_field_type *create_writer_integer_struct(void)
+{
+       int ret;
+       struct bt_ctf_field_type *structure = NULL;
+       struct bt_ctf_field_type *ui8 = NULL, *ui16 = NULL, *ui32 = NULL;
+
+       structure = bt_ctf_field_type_structure_create();
+       BT_ASSERT(structure);
+       ui8 = bt_ctf_field_type_integer_create(8);
+       BT_ASSERT(ui8);
+       ret = bt_ctf_field_type_structure_add_field(structure, ui8,
+                       "payload_8");
+       BT_ASSERT(ret == 0);
+       ui16 = bt_ctf_field_type_integer_create(16);
+       BT_ASSERT(ui16);
+       ret = bt_ctf_field_type_structure_add_field(structure, ui16,
+                       "payload_16");
+       BT_ASSERT(ret == 0);
+       ui32 = bt_ctf_field_type_integer_create(32);
+       BT_ASSERT(ui32);
+       ret = bt_ctf_field_type_structure_add_field(structure, ui32,
+                       "payload_32");
+       BT_ASSERT(ret == 0);
+       BT_OBJECT_PUT_REF_AND_RESET(ui8);
+       BT_OBJECT_PUT_REF_AND_RESET(ui16);
+       BT_OBJECT_PUT_REF_AND_RESET(ui32);
+       return structure;
+}
+
+/**
+ * A simple event has the following payload:
+ *     - uint8_t payload_8;
+ *     - uint16_t payload_16;
+ *     - uint32_t payload_32;
+ */
+static bt_event_class *create_simple_event(
+               bt_stream_class *sc, const char *name)
+{
+       int ret;
+       bt_event_class *event = NULL;
+       bt_field_class *payload = NULL;
+
+       BT_ASSERT(name);
+       event = bt_event_class_create(sc);
+       BT_ASSERT(event);
+       ret = bt_event_class_set_name(event, name);
+       BT_ASSERT(ret == 0);
+       payload = create_integer_struct(bt_stream_class_borrow_trace_class(sc));
+       BT_ASSERT(payload);
+       ret = bt_event_class_set_payload_field_class(event, payload);
+       BT_ASSERT(ret == 0);
+       BT_FIELD_CLASS_PUT_REF_AND_RESET(payload);
+       return event;
+}
+
+/**
+ * A complex event has the following payload:
+ *     - uint8_t payload_8;
+ *     - uint16_t payload_16;
+ *     - uint32_t payload_32;
+ *     - struct payload_struct:
+ *           - uint8_t payload_8;
+ *           - uint16_t payload_16;
+ *           - uint32_t payload_32;
+ */
+static bt_event_class *create_complex_event(bt_stream_class *sc,
+               const char *name)
+{
+       int ret;
+       bt_event_class *event = NULL;
+       bt_field_class *inner = NULL, *outer = NULL;
+       bt_trace_class *trace_class = bt_stream_class_borrow_trace_class(sc);
+
+       BT_ASSERT(name);
+       event = bt_event_class_create(sc);
+       BT_ASSERT(event);
+       ret = bt_event_class_set_name(event, name);
+       BT_ASSERT(ret == 0);
+       outer = create_integer_struct(trace_class);
+       BT_ASSERT(outer);
+       inner = create_integer_struct(trace_class);
+       BT_ASSERT(inner);
+       ret = bt_field_class_structure_append_member(outer,
+               "payload_struct", inner);
+       BT_ASSERT(ret == 0);
+       ret = bt_event_class_set_payload_field_class(event, outer);
+       BT_ASSERT(ret == 0);
+       BT_FIELD_CLASS_PUT_REF_AND_RESET(inner);
+       BT_FIELD_CLASS_PUT_REF_AND_RESET(outer);
+       return event;
+}
+
+static void create_sc1(bt_trace_class *trace_class)
+{
+       int ret;
+       bt_event_class *ec1 = NULL, *ec2 = NULL;
+       bt_stream_class *sc1 = NULL, *ret_stream = NULL;
+
+       sc1 = bt_stream_class_create(trace_class);
+       BT_ASSERT(sc1);
+       ret = bt_stream_class_set_name(sc1, "sc1");
+       BT_ASSERT(ret == 0);
+       ec1 = create_complex_event(sc1, "ec1");
+       BT_ASSERT(ec1);
+       ec2 = create_simple_event(sc1, "ec2");
+       BT_ASSERT(ec2);
+       ret_stream = bt_event_class_borrow_stream_class(ec1);
+       ok(ret_stream == sc1, "Borrow parent stream SC1 from EC1");
+       ret_stream = bt_event_class_borrow_stream_class(ec2);
+       ok(ret_stream == sc1, "Borrow parent stream SC1 from EC2");
+       BT_EVENT_CLASS_PUT_REF_AND_RESET(ec1);
+       BT_EVENT_CLASS_PUT_REF_AND_RESET(ec2);
+       BT_STREAM_CLASS_PUT_REF_AND_RESET(sc1);
+}
+
+static void create_sc2(bt_trace_class *trace_class)
+{
+       int ret;
+       bt_event_class *ec3 = NULL;
+       bt_stream_class *sc2 = NULL, *ret_stream = NULL;
+
+       sc2 = bt_stream_class_create(trace_class);
+       BT_ASSERT(sc2);
+       ret = bt_stream_class_set_name(sc2, "sc2");
+       BT_ASSERT(ret == 0);
+       ec3 = create_simple_event(sc2, "ec3");
+       ret_stream = bt_event_class_borrow_stream_class(ec3);
+       ok(ret_stream == sc2, "Borrow parent stream SC2 from EC3");
+       BT_EVENT_CLASS_PUT_REF_AND_RESET(ec3);
+       BT_STREAM_CLASS_PUT_REF_AND_RESET(sc2);
+}
+
+static bt_trace_class *create_tc1(bt_self_component_source *self_comp)
+{
+       bt_trace_class *tc1 = NULL;
+
+       tc1 = bt_trace_class_create(
+               bt_self_component_source_as_self_component(self_comp));
+       BT_ASSERT(tc1);
+       create_sc1(tc1);
+       create_sc2(tc1);
+       return tc1;
+}
+
+static void init_weak_refs(bt_trace_class *tc,
+               bt_trace_class **tc1,
+               bt_stream_class **sc1,
+               bt_stream_class **sc2,
+               bt_event_class **ec1,
+               bt_event_class **ec2,
+               bt_event_class **ec3)
+{
+       *tc1 = tc;
+       *sc1 = bt_trace_class_borrow_stream_class_by_index(tc, 0);
+       *sc2 = bt_trace_class_borrow_stream_class_by_index(tc, 1);
+       *ec1 = bt_stream_class_borrow_event_class_by_index(*sc1, 0);
+       *ec2 = bt_stream_class_borrow_event_class_by_index(*sc1, 1);
+       *ec3 = bt_stream_class_borrow_event_class_by_index(*sc2, 0);
+}
+
+static void test_example_scenario(bt_self_component_source *self_comp)
+{
+       /*
+        * Weak pointers to trace IR objects are to be used very
+        * carefully. This is NOT a good practice and is strongly
+        * discouraged; this is only done to facilitate the validation
+        * of expected reference counts without affecting them by taking
+        * "real" references to the objects.
+        */
+       bt_trace_class *tc1 = NULL, *weak_tc1 = NULL;
+       bt_stream_class *weak_sc1 = NULL, *weak_sc2 = NULL;
+       bt_event_class *weak_ec1 = NULL, *weak_ec2 = NULL,
+                       *weak_ec3 = NULL;
+       struct bt_user user_a = { 0 }, user_b = { 0 }, user_c = { 0 };
+
+       /* The only reference which exists at this point is on TC1. */
+       tc1 = create_tc1(self_comp);
+       ok(tc1, "Initialize trace");
+       BT_ASSERT(tc1);
+       init_weak_refs(tc1, &weak_tc1, &weak_sc1, &weak_sc2, &weak_ec1,
+                       &weak_ec2, &weak_ec3);
+       ok(bt_object_get_ref_count((void *) weak_sc1) == 0,
+                       "Initial SC1 reference count is 0");
+       ok(bt_object_get_ref_count((void *) weak_sc2) == 0,
+                       "Initial SC2 reference count is 0");
+       ok(bt_object_get_ref_count((void *) weak_ec1) == 0,
+                       "Initial EC1 reference count is 0");
+       ok(bt_object_get_ref_count((void *) weak_ec2) == 0,
+                       "Initial EC2 reference count is 0");
+       ok(bt_object_get_ref_count((void *) weak_ec3) == 0,
+                       "Initial EC3 reference count is 0");
+
+       /* User A has ownership of the trace. */
+       BT_OBJECT_MOVE_REF(user_a.tc, tc1);
+       ok(bt_object_get_ref_count((void *) user_a.tc) == 1,
+                       "TC1 reference count is 1");
+
+       /* User A acquires a reference to SC2 from TC1. */
+       user_a.sc = bt_trace_class_borrow_stream_class_by_index(
+                       user_a.tc, 1);
+       bt_stream_class_get_ref(user_a.sc);
+       ok(user_a.sc, "User A acquires SC2 from TC1");
+       ok(bt_object_get_ref_count((void *) weak_tc1) == 2,
+                       "TC1 reference count is 2");
+       ok(bt_object_get_ref_count((void *) weak_sc2) == 1,
+                       "SC2 reference count is 1");
+
+       /* User A acquires a reference to EC3 from SC2. */
+       user_a.ec = bt_stream_class_borrow_event_class_by_index(
+                       user_a.sc, 0);
+       bt_event_class_get_ref(user_a.ec);
+       ok(user_a.ec, "User A acquires EC3 from SC2");
+       ok(bt_object_get_ref_count((void *) weak_tc1) == 2,
+                       "TC1 reference count is 2");
+       ok(bt_object_get_ref_count((void *) weak_sc2) == 2,
+                       "SC2 reference count is 2");
+       ok(bt_object_get_ref_count((void *) weak_ec3) == 1,
+                       "EC3 reference count is 1");
+
+       /* User A releases its reference to SC2. */
+       diag("User A releases SC2");
+       BT_STREAM_CLASS_PUT_REF_AND_RESET(user_a.sc);
+       /*
+        * We keep the pointer to SC2 around to validate its reference
+        * count.
+        */
+       ok(bt_object_get_ref_count((void *) weak_tc1) == 2,
+                       "TC1 reference count is 2");
+       ok(bt_object_get_ref_count((void *) weak_sc2) == 1,
+                       "SC2 reference count is 1");
+       ok(bt_object_get_ref_count((void *) weak_ec3) == 1,
+                       "EC3 reference count is 1");
+
+       /* User A releases its reference to TC1. */
+       diag("User A releases TC1");
+       BT_TRACE_CLASS_PUT_REF_AND_RESET(user_a.tc);
+       /*
+        * We keep the pointer to TC1 around to validate its reference
+        * count.
+        */
+       ok(bt_object_get_ref_count((void *) weak_tc1) == 1,
+                       "TC1 reference count is 1");
+       ok(bt_object_get_ref_count((void *) weak_sc2) == 1,
+                       "SC2 reference count is 1");
+       ok(bt_object_get_ref_count((void *) weak_ec3) == 1,
+                       "EC3 reference count is 1");
+
+       /* User B acquires a reference to SC1. */
+       diag("User B acquires a reference to SC1");
+       user_b.sc = weak_sc1;
+       bt_stream_class_get_ref(user_b.sc);
+       ok(bt_object_get_ref_count((void *) weak_tc1) == 2,
+                       "TC1 reference count is 2");
+       ok(bt_object_get_ref_count((void *) weak_sc1) == 1,
+                       "SC1 reference count is 1");
+
+       /* User C acquires a reference to EC1. */
+       diag("User C acquires a reference to EC1");
+       user_c.ec = bt_stream_class_borrow_event_class_by_index(
+                       user_b.sc, 0);
+       bt_event_class_get_ref(user_c.ec);
+       ok(bt_object_get_ref_count((void *) weak_ec1) == 1,
+                       "EC1 reference count is 1");
+       ok(bt_object_get_ref_count((void *) weak_sc1) == 2,
+                       "SC1 reference count is 2");
+
+       /* User A releases its reference on EC3. */
+       diag("User A releases its reference on EC3");
+       BT_EVENT_CLASS_PUT_REF_AND_RESET(user_a.ec);
+       ok(bt_object_get_ref_count((void *) weak_ec3) == 0,
+                       "EC3 reference count is 1");
+       ok(bt_object_get_ref_count((void *) weak_sc2) == 0,
+                       "SC2 reference count is 0");
+       ok(bt_object_get_ref_count((void *) weak_tc1) == 1,
+                       "TC1 reference count is 1");
+
+       /* User B releases its reference on SC1. */
+       diag("User B releases its reference on SC1");
+       BT_STREAM_CLASS_PUT_REF_AND_RESET(user_b.sc);
+       ok(bt_object_get_ref_count((void *) weak_sc1) == 1,
+                       "SC1 reference count is 1");
+
+       /*
+        * User C is the sole owner of an object and is keeping the whole
+        * trace hierarchy "alive" by holding a reference to EC1.
+        */
+       ok(bt_object_get_ref_count((void *) weak_tc1) == 1,
+                       "TC1 reference count is 1");
+       ok(bt_object_get_ref_count((void *) weak_sc1) == 1,
+                       "SC1 reference count is 1");
+       ok(bt_object_get_ref_count((void *) weak_sc2) == 0,
+                       "SC2 reference count is 0");
+       ok(bt_object_get_ref_count((void *) weak_ec1) == 1,
+                       "EC1 reference count is 1");
+       ok(bt_object_get_ref_count((void *) weak_ec2) == 0,
+                       "EC2 reference count is 0");
+       ok(bt_object_get_ref_count((void *) weak_ec3) == 0,
+                       "EC3 reference count is 0");
+
+       /* Reclaim last reference held by User C. */
+       BT_EVENT_CLASS_PUT_REF_AND_RESET(user_c.ec);
+}
+
+static
+bt_component_class_initialize_method_status src_init(
+       bt_self_component_source *self_comp,
+       bt_self_component_source_configuration *config __attribute__((unused)),
+       const bt_value *params __attribute__((unused)),
+       void *init_method_data __attribute__((unused)))
+{
+       test_example_scenario(self_comp);
+       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+}
+
+static
+bt_message_iterator_class_next_method_status src_iter_next(
+               bt_self_message_iterator *self_iterator __attribute__((unused)),
+               bt_message_array_const msgs __attribute__((unused)),
+               uint64_t capacity __attribute__((unused)),
+               uint64_t *count __attribute__((unused)))
+{
+       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+}
+
+static void test_example_scenario_in_graph(void)
+{
+       bt_message_iterator_class *msg_iter_cls;
+       bt_component_class_source *comp_cls;
+       bt_graph *graph;
+       int ret;
+
+       msg_iter_cls = bt_message_iterator_class_create(src_iter_next);
+       BT_ASSERT(msg_iter_cls);
+
+       comp_cls = bt_component_class_source_create("src", msg_iter_cls);
+       BT_ASSERT(comp_cls);
+       ret = bt_component_class_source_set_initialize_method(comp_cls, src_init);
+       BT_ASSERT(ret == 0);
+       graph = bt_graph_create(0);
+       ret = bt_graph_add_source_component(graph, comp_cls, "src-comp",
+               NULL, BT_LOGGING_LEVEL_NONE, NULL);
+       BT_ASSERT(ret == 0);
+       bt_graph_put_ref(graph);
+       bt_component_class_source_put_ref(comp_cls);
+       bt_message_iterator_class_put_ref(msg_iter_cls);
+}
+
+static void create_writer_user_full(struct writer_user *user)
+{
+       gchar *trace_path;
+       struct bt_ctf_field_type *ft;
+       struct bt_ctf_field *field;
+       struct bt_ctf_clock *clock;
+       int ret;
+
+       trace_path = g_build_filename(g_get_tmp_dir(), "ctfwriter_XXXXXX", NULL);
+       if (!bt_mkdtemp(trace_path)) {
+               perror("# perror");
+       }
+
+       user->writer = bt_ctf_writer_create(trace_path);
+       BT_ASSERT(user->writer);
+       ret = bt_ctf_writer_set_byte_order(user->writer,
+               BT_CTF_BYTE_ORDER_LITTLE_ENDIAN);
+       BT_ASSERT(ret == 0);
+       user->tc = bt_ctf_writer_get_trace(user->writer);
+       BT_ASSERT(user->tc);
+       user->sc = bt_ctf_stream_class_create("sc");
+       BT_ASSERT(user->sc);
+       clock = bt_ctf_clock_create("the_clock");
+       BT_ASSERT(clock);
+       ret = bt_ctf_writer_add_clock(user->writer, clock);
+       BT_ASSERT(!ret);
+       ret = bt_ctf_stream_class_set_clock(user->sc, clock);
+       BT_ASSERT(!ret);
+       BT_OBJECT_PUT_REF_AND_RESET(clock);
+       user->stream = bt_ctf_writer_create_stream(user->writer, user->sc);
+       BT_ASSERT(user->stream);
+       user->ec = bt_ctf_event_class_create("ec");
+       BT_ASSERT(user->ec);
+       ft = create_writer_integer_struct();
+       BT_ASSERT(ft);
+       ret = bt_ctf_event_class_set_payload_field_type(user->ec, ft);
+       BT_OBJECT_PUT_REF_AND_RESET(ft);
+       BT_ASSERT(!ret);
+       ret = bt_ctf_stream_class_add_event_class(user->sc, user->ec);
+       BT_ASSERT(!ret);
+       user->event = bt_ctf_event_create(user->ec);
+       BT_ASSERT(user->event);
+       field = bt_ctf_event_get_payload(user->event, "payload_8");
+       BT_ASSERT(field);
+       ret = bt_ctf_field_integer_unsigned_set_value(field, 10);
+       BT_ASSERT(!ret);
+       BT_OBJECT_PUT_REF_AND_RESET(field);
+       field = bt_ctf_event_get_payload(user->event, "payload_16");
+       BT_ASSERT(field);
+       ret = bt_ctf_field_integer_unsigned_set_value(field, 20);
+       BT_ASSERT(!ret);
+       BT_OBJECT_PUT_REF_AND_RESET(field);
+       field = bt_ctf_event_get_payload(user->event, "payload_32");
+       BT_ASSERT(field);
+       ret = bt_ctf_field_integer_unsigned_set_value(field, 30);
+       BT_ASSERT(!ret);
+       BT_OBJECT_PUT_REF_AND_RESET(field);
+       ret = bt_ctf_stream_append_event(user->stream, user->event);
+       BT_ASSERT(!ret);
+       recursive_rmdir(trace_path);
+       g_free(trace_path);
+}
+
+static void test_put_order_swap(size_t *array, size_t a, size_t b)
+{
+       size_t temp = array[a];
+
+       array[a] = array[b];
+       array[b] = temp;
+}
+
+static void test_put_order_put_objects(size_t *array, size_t size)
+{
+       size_t i;
+       struct writer_user user = { 0 };
+       void **objects = (void *) &user;
+
+       create_writer_user_full(&user);
+       printf("# ");
+
+       for (i = 0; i < size; ++i) {
+               void *obj = objects[array[i]];
+
+               printf("%s", writer_user_names[array[i]]);
+               BT_OBJECT_PUT_REF_AND_RESET(obj);
+
+               if (i < size - 1) {
+                       printf(" -> ");
+               }
+       }
+
+       puts("");
+}
+
+static void test_put_order_permute(size_t *array, int k, size_t size)
+{
+       if (k == 0) {
+               test_put_order_put_objects(array, size);
+       } else {
+               int i;
+
+               for (i = k - 1; i >= 0; i--) {
+                       size_t next_k = k - 1;
+
+                       test_put_order_swap(array, i, next_k);
+                       test_put_order_permute(array, next_k, size);
+                       test_put_order_swap(array, i, next_k);
+               }
+       }
+}
+
+static void test_put_order(void)
+{
+       size_t i;
+       size_t array[WRITER_USER_NR_ELEMENTS];
+
+       /* Initialize array of indexes */
+       for (i = 0; i < WRITER_USER_NR_ELEMENTS; ++i) {
+               array[i] = i;
+       }
+
+       test_put_order_permute(array, WRITER_USER_NR_ELEMENTS,
+               WRITER_USER_NR_ELEMENTS);
+}
+
+/**
+ * The objective of this test is to implement and expand upon the scenario
+ * described in the reference counting documentation and ensure that any node of
+ * the Trace, Stream Class, Event Class, Stream and Event hierarchy keeps all
+ * other "alive" and reachable.
+ *
+ * External tools (e.g. valgrind) should be used to confirm that this
+ * known-good test does not leak memory.
+ */
+int main(void)
+{
+       /* Initialize tap harness before any tests */
+       plan_tests(NR_TESTS);
+
+       test_example_scenario_in_graph();
+       test_put_order();
+
+       return exit_status();
+}
diff --git a/tests/lib/test_bt_uuid.c b/tests/lib/test_bt_uuid.c
deleted file mode 100644 (file)
index 8a0b610..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include <tap/tap.h>
-
-#include "common/uuid.h"
-
-#define NR_TESTS 23
-
-static const char valid_str_1[] = "3d260c88-75ea-47b8-a7e2-d6077c0378d9";
-static const char valid_str_2[] = "611cf3a6-a68b-4515-834f-208bc2762592";
-static const char valid_str_3[] = "1b4855cc-96de-4ae8-abe3-86449c2a43c4";
-static const char valid_str_4[] = "8ADED5B9-ACD2-439F-A60C-897403AA2AB4";
-static const char valid_str_5[] = "f109e0a2-C619-4d18-b760-20EA20E0F69A";
-
-static bt_uuid_t valid_uuid_1 = {
-       0x3d, 0x26, 0x0c, 0x88, 0x75, 0xea, 0x47, 0xb8,
-       0xa7, 0xe2, 0xd6, 0x07, 0x7c, 0x03, 0x78, 0xd9
-};
-static bt_uuid_t valid_uuid_2 = {
-       0x61, 0x1c, 0xf3, 0xa6, 0xa6, 0x8b, 0x45, 0x15,
-       0x83, 0x4f, 0x20, 0x8b, 0xc2, 0x76, 0x25, 0x92
-};
-static bt_uuid_t valid_uuid_3 = {
-       0x1b, 0x48, 0x55, 0xcc, 0x96, 0xde, 0x4a, 0xe8,
-       0xab, 0xe3, 0x86, 0x44, 0x9c, 0x2a, 0x43, 0xc4
-};
-
-static const char invalid_str_1[] = "1b485!cc-96de-4XX8-abe3-86449c2a43?4";
-static const char invalid_str_2[] = "c2e6eddb&3955&4006&be3a&70bb63bd5f25";
-static const char invalid_str_3[] = "81b1cb88-ff42-45b9-ba4d-964088ee45";
-static const char invalid_str_4[] = "2d-6c6d756574-470e-9142-a4e6ad03f143";
-static const char invalid_str_5[] = "4542ad19-9e4f-4931-8261-2101c3e089ae7";
-static const char invalid_str_6[] = "XX0123";
-
-static
-void run_test_bt_uuid_from_str(void)
-{
-       int ret;
-       bt_uuid_t uuid1;
-
-       /*
-        * Parse valid UUID strings, expect success.
-        */
-       ret = bt_uuid_from_str(valid_str_1, uuid1);
-       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_1);
-
-       ret = bt_uuid_from_str(valid_str_2, uuid1);
-       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_2);
-
-       ret = bt_uuid_from_str(valid_str_3, uuid1);
-       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_3);
-
-       ret = bt_uuid_from_str(valid_str_4, uuid1);
-       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_4);
-
-       ret = bt_uuid_from_str(valid_str_5, uuid1);
-       ok(ret == 0, "bt_uuid_from_str - Parse valid string '%s', expect success", valid_str_5);
-
-       /*
-        * Parse invalid UUID strings, expect failure.
-        */
-       ret = bt_uuid_from_str(invalid_str_1, uuid1);
-       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_1);
-
-       ret = bt_uuid_from_str(invalid_str_2, uuid1);
-       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_2);
-
-       ret = bt_uuid_from_str(invalid_str_3, uuid1);
-       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_3);
-
-       ret = bt_uuid_from_str(invalid_str_4, uuid1);
-       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_4);
-
-       ret = bt_uuid_from_str(invalid_str_5, uuid1);
-       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_5);
-
-       ret = bt_uuid_from_str(invalid_str_6, uuid1);
-       ok(ret != 0, "bt_uuid_from_str - Parse invalid string '%s', expect failure", invalid_str_6);
-}
-
-static
-void run_test_bt_uuid_to_str(void)
-{
-       char uuid_str[BT_UUID_STR_LEN + 1];
-
-       bt_uuid_to_str(valid_uuid_1, uuid_str);
-       ok(strcmp(uuid_str, valid_str_1) == 0, "bt_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_1);
-
-       bt_uuid_to_str(valid_uuid_2, uuid_str);
-       ok(strcmp(uuid_str, valid_str_2) == 0, "bt_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_2);
-
-       bt_uuid_to_str(valid_uuid_3, uuid_str);
-       ok(strcmp(uuid_str, valid_str_3) == 0, "bt_uuid_to_str - Convert UUID '%s' to string, expect success", valid_str_3);
-}
-
-static
-void run_test_bt_uuid_compare(void)
-{
-       int ret;
-       bt_uuid_t uuid1, uuid2;
-
-       bt_uuid_from_str(valid_str_1, uuid1);
-       bt_uuid_from_str(valid_str_1, uuid2);
-       ret = bt_uuid_compare(uuid1, uuid2);
-       ok(ret == 0, "bt_uuid_compare - Compare same UUID, expect success");
-
-       bt_uuid_from_str(valid_str_2, uuid2);
-       ret = bt_uuid_compare(uuid1, uuid2);
-       ok(ret != 0, "bt_uuid_compare - Compare different UUID, expect failure");
-       ok(ret < 0, "bt_uuid_compare - Compare different UUID, expect uuid1 smaller");
-       ret = bt_uuid_compare(uuid2, uuid1);
-       ok(ret > 0, "bt_uuid_compare - Compare different UUID, expect uuid2 bigger");
-}
-
-static
-void run_test_bt_uuid_copy(void)
-{
-       int ret;
-       bt_uuid_t uuid1;
-
-       bt_uuid_copy(uuid1, valid_uuid_1);
-       ret = bt_uuid_compare(uuid1, valid_uuid_1);
-
-       ok(ret == 0, "bt_uuid_copy - Compare copied UUID with source, expect success");
-}
-
-static
-void run_test_bt_uuid_generate(void)
-{
-       int ret;
-       bt_uuid_t uuid1, uuid2;
-
-       bt_uuid_generate(uuid1);
-       bt_uuid_generate(uuid2);
-
-       ok(bt_uuid_compare(uuid1, uuid2) != 0, "bt_uuid_generate - Generated UUIDs are different");
-
-       /*
-        * Set the two most significant bits (bits 6 and 7) of the
-        * clock_seq_hi_and_reserved to zero and one, respectively.
-        */
-       ret = uuid1[8] & (1 << 6);
-       ok(ret == 0, "bt_uuid_generate - bit 6 of clock_seq_hi_and_reserved is set to zero");
-
-       ret = uuid1[8] & (1 << 7);
-       ok(ret != 0, "bt_uuid_generate - bit 7 of clock_seq_hi_and_reserved is set to one");
-
-       /*
-        * Set the four most significant bits (bits 12 through 15) of the
-        * time_hi_and_version field to the 4-bit version number from
-        * Section 4.1.3.
-        */
-       ret = uuid1[6] >> 4;
-       ok(ret == BT_UUID_VER, "bt_uuid_generate - Generated UUID version check");
-}
-
-static
-void run_test(void)
-{
-       plan_tests(NR_TESTS);
-
-       run_test_bt_uuid_from_str();
-       run_test_bt_uuid_to_str();
-       run_test_bt_uuid_compare();
-       run_test_bt_uuid_copy();
-       run_test_bt_uuid_generate();
-}
-
-int main(int argc, char **argv)
-{
-       /* Run tap-formated tests */
-       run_test();
-
-       return exit_status();
-}
diff --git a/tests/lib/test_bt_values.c b/tests/lib/test_bt_values.c
deleted file mode 100644 (file)
index cc3cbd9..0000000
+++ /dev/null
@@ -1,1168 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2015 EfficiOS Inc. and Linux Foundation
- * Copyright (C) 2015 Philippe Proulx <pproulx@efficios.com>
- *
- * Babeltrace value objects tests
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/assert.h"
-#include <string.h>
-#include "tap/tap.h"
-
-#define NR_TESTS 190
-
-static
-void test_null(void)
-{
-       ok(bt_value_null, "bt_value_null is not NULL");
-       ok(bt_value_is_null(bt_value_null),
-               "bt_value_null is a null value object");
-       bt_value_get_ref(bt_value_null);
-       pass("getting bt_value_null does not cause a crash");
-       bt_value_put_ref(bt_value_null);
-       pass("putting bt_value_null does not cause a crash");
-}
-
-static
-void test_bool(void)
-{
-       bt_bool value;
-       bt_value *obj;
-
-       obj = bt_value_bool_create();
-       ok(obj && bt_value_is_bool(obj),
-               "bt_value_bool_create() returns a boolean value object");
-
-       value = BT_TRUE;
-       value = bt_value_bool_get(obj);
-       ok(!value, "default boolean value object value is BT_FALSE");
-
-       bt_value_bool_set(obj, BT_FALSE);
-       bt_value_bool_set(obj, BT_TRUE);
-       value = bt_value_bool_get(obj);
-       ok(value, "bt_value_bool_set() works");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       pass("putting an existing boolean value object does not cause a crash")
-
-       value = BT_FALSE;
-       obj = bt_value_bool_create_init(BT_TRUE);
-       ok(obj && bt_value_is_bool(obj),
-               "bt_value_bool_create_init() returns a boolean value object");
-       value = bt_value_bool_get(obj);
-       ok(value,
-               "bt_value_bool_create_init() sets the appropriate initial value");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-}
-
-static
-void test_unsigned_integer(void)
-{
-       uint64_t value;
-       bt_value *obj;
-
-       obj = bt_value_integer_unsigned_create();
-       ok(obj && bt_value_is_unsigned_integer(obj),
-               "bt_value_integer_unsigned_create() returns an unsigned integer value object");
-
-       value = 1961;
-       value = bt_value_integer_unsigned_get(obj);
-       ok(value == 0, "default unsigned integer value object value is 0");
-
-       bt_value_integer_unsigned_set(obj, 98765);
-       value = bt_value_integer_unsigned_get(obj);
-       ok(value == 98765, "bt_value_integer_unsigned_bool_set() works");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       pass("putting an existing unsigned integer value object does not cause a crash")
-
-       obj = bt_value_integer_unsigned_create_init(321456987);
-       ok(obj && bt_value_is_unsigned_integer(obj),
-               "bt_value_integer_unsigned_create_init() returns an unsigned integer value object");
-       value = bt_value_integer_unsigned_get(obj);
-       ok(value == 321456987,
-               "bt_value_integer_unsigned_create_init() sets the appropriate initial value");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-}
-
-static
-void test_signed_integer(void)
-{
-       int64_t value;
-       bt_value *obj;
-
-       obj = bt_value_integer_signed_create();
-       ok(obj && bt_value_is_signed_integer(obj),
-               "bt_value_integer_signed_create() returns a signed integer value object");
-
-       value = 1961;
-       value = bt_value_integer_signed_get(obj);
-       ok(value == 0, "default signed integer value object value is 0");
-
-       bt_value_integer_signed_set(obj, 98765);
-       value = bt_value_integer_signed_get(obj);
-       ok(value == 98765, "bt_value_integer_signed_bool_set() works");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       pass("putting an existing signed integer value object does not cause a crash")
-
-       obj = bt_value_integer_signed_create_init(-321456987);
-       ok(obj && bt_value_is_signed_integer(obj),
-               "bt_value_integer_signed_create_init() returns a signed integer value object");
-       value = bt_value_integer_signed_get(obj);
-       ok(value == -321456987,
-               "bt_value_integer_signed_create_init() sets the appropriate initial value");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-}
-
-static
-void test_real(void)
-{
-       double value;
-       bt_value *obj;
-
-       obj = bt_value_real_create();
-       ok(obj && bt_value_is_real(obj),
-               "bt_value_real_create() returns a real number value object");
-
-       value = 17.34;
-       value = bt_value_real_get(obj);
-       ok(value == 0.,
-               "default real number value object value is 0");
-
-       bt_value_real_set(obj, -3.1416);
-       value = bt_value_real_get(obj);
-       ok(value == -3.1416, "bt_value_real_set() works");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       pass("putting an existing real number value object does not cause a crash")
-
-       obj = bt_value_real_create_init(33.1649758);
-       ok(obj && bt_value_is_real(obj),
-               "bt_value_real_create_init() returns a real number value object");
-       value = bt_value_real_get(obj);
-       ok(value == 33.1649758,
-               "bt_value_real_create_init() sets the appropriate initial value");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-}
-
-static
-void test_string(void)
-{
-       const char *value;
-       bt_value *obj;
-
-       obj = bt_value_string_create();
-       ok(obj && bt_value_is_string(obj),
-               "bt_value_string_create() returns a string value object");
-
-       value = bt_value_string_get(obj);
-       ok(value && strcmp(value, "") == 0,
-               "default string value object value is \"\"");
-
-       bt_value_string_set(obj, "hello worldz");
-       value = bt_value_string_get(obj);
-       ok(value && strcmp(value, "hello worldz") == 0,
-               "bt_value_string_get() works");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       pass("putting an existing string value object does not cause a crash")
-
-       obj = bt_value_string_create_init("initial value");
-       ok(obj && bt_value_is_string(obj),
-               "bt_value_string_create_init() returns a string value object");
-       value = bt_value_string_get(obj);
-       ok(value && strcmp(value, "initial value") == 0,
-               "bt_value_string_create_init() sets the appropriate initial value");
-
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-}
-
-static
-void test_array(void)
-{
-       int ret;
-       bt_bool bool_value;
-       int64_t int_value;
-       double real_value;
-       bt_value *obj;
-       const char *string_value;
-       bt_value *array_obj;
-       bt_value *appended_obj;
-
-       array_obj = bt_value_array_create();
-       ok(array_obj && bt_value_is_array(array_obj),
-               "bt_value_array_create() returns an array value object");
-       ok(bt_value_array_is_empty(array_obj),
-               "initial array value object size is 0");
-
-       obj = bt_value_integer_unsigned_create_init(345);
-       ret = bt_value_array_append_element(array_obj, obj);
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       obj = bt_value_integer_signed_create_init(-507);
-       ret |= bt_value_array_append_element(array_obj, obj);
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       obj = bt_value_real_create_init(-17.45);
-       ret |= bt_value_array_append_element(array_obj, obj);
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       obj = bt_value_bool_create_init(BT_TRUE);
-       ret |= bt_value_array_append_element(array_obj, obj);
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       ret |= bt_value_array_append_element(array_obj,
-               bt_value_null);
-       ok(!ret, "bt_value_array_append_element() succeeds");
-       ok(bt_value_array_get_length(array_obj) == 5,
-               "appending an element to an array value object increment its size");
-
-       obj = bt_value_array_borrow_element_by_index(array_obj, 0);
-       ok(bt_value_is_unsigned_integer(obj),
-               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (unsigned integer)");
-       int_value = bt_value_integer_unsigned_get(obj);
-       ok(int_value == 345,
-               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate value (unsigned integer)");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 1);
-       ok(bt_value_is_signed_integer(obj),
-               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (signed integer)");
-       int_value = bt_value_integer_signed_get(obj);
-       ok(int_value == -507,
-               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate value (signed integer)");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 2);
-       ok(bt_value_is_real(obj),
-               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (real number)");
-       real_value = bt_value_real_get(obj);
-       ok(real_value == -17.45,
-               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate value (real number)");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 3);
-       ok(bt_value_is_bool(obj),
-               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (boolean)");
-       bool_value = bt_value_bool_get(obj);
-       ok(bool_value,
-               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate value (boolean)");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 4);
-       ok(bt_value_null,
-               "bt_value_array_borrow_element_by_index() returns an value object with the appropriate type (null)");
-
-       obj = bt_value_integer_signed_create_init(1001);
-       BT_ASSERT(obj);
-       ok(!bt_value_array_set_element_by_index(array_obj, 2, obj),
-               "bt_value_array_set_element_by_index() succeeds");
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       obj = bt_value_array_borrow_element_by_index(array_obj, 2);
-       ok(bt_value_is_signed_integer(obj),
-               "bt_value_array_set_element_by_index() inserts an value object with the appropriate type");
-       int_value = bt_value_integer_signed_get(obj);
-       BT_ASSERT(!ret);
-       ok(int_value == 1001,
-               "bt_value_array_set_element_by_index() inserts an value object with the appropriate value");
-
-       ret = bt_value_array_append_bool_element(array_obj,
-               BT_FALSE);
-       ok(!ret, "bt_value_array_append_bool_element() succeeds");
-       ret = bt_value_array_append_unsigned_integer_element(array_obj,
-               98765);
-       ok(!ret, "bt_value_array_append_unsigned_integer_element() succeeds");
-       ret = bt_value_array_append_signed_integer_element(array_obj,
-               -10101);
-       ok(!ret, "bt_value_array_append_signed_integer_element() succeeds");
-       ret = bt_value_array_append_real_element(array_obj,
-               2.49578);
-       ok(!ret, "bt_value_array_append_real_element() succeeds");
-       ret = bt_value_array_append_string_element(array_obj,
-               "bt_value");
-       ok(!ret, "bt_value_array_append_string_element() succeeds");
-       ret = bt_value_array_append_empty_array_element(array_obj, NULL);
-       ok(!ret, "bt_value_array_append_empty_array_element() succeeds");
-       ret = bt_value_array_append_empty_array_element(array_obj, &appended_obj);
-       ok(!ret, "bt_value_array_append_empty_array_element() with returned value object succeeds");
-       ok(appended_obj,
-               "object returned by bt_value_array_append_empty_array_element() is not NULL");
-       ok(bt_value_is_array(appended_obj),
-               "object returned by bt_value_array_append_empty_array_element() is an array value");
-       ret = bt_value_array_append_empty_map_element(array_obj, NULL);
-       ok(!ret, "bt_value_array_append_empty_map_element() succeeds");
-       ret = bt_value_array_append_empty_map_element(array_obj, &appended_obj);
-       ok(!ret, "bt_value_array_append_empty_map_element() with returned value object succeeds");
-       ok(appended_obj,
-               "object returned by bt_value_array_append_empty_map_element() is not NULL");
-       ok(bt_value_is_map(appended_obj),
-               "object returned by bt_value_array_append_empty_map_element() is an array value");
-
-       ok(bt_value_array_get_length(array_obj) == 14,
-               "the bt_value_array_append_element_*() functions increment the array value object's size");
-       ok(!bt_value_array_is_empty(array_obj),
-               "map value object is not empty");
-
-       obj = bt_value_array_borrow_element_by_index(array_obj, 5);
-       ok(bt_value_is_bool(obj),
-               "bt_value_array_append_bool_element() appends a boolean value object");
-       bool_value = bt_value_bool_get(obj);
-       ok(!bool_value,
-               "bt_value_array_append_bool_element() appends the appropriate value");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 6);
-       ok(bt_value_is_unsigned_integer(obj),
-               "bt_value_array_append_unsigned_integer_element() appends an unsigned integer value object");
-       int_value = bt_value_integer_unsigned_get(obj);
-       ok(int_value == 98765,
-               "bt_value_array_append_unsigned_integer_element() appends the appropriate value");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 7);
-       ok(bt_value_is_signed_integer(obj),
-               "bt_value_array_append_signed_integer_element() appends a signed integer value object");
-       int_value = bt_value_integer_signed_get(obj);
-       ok(int_value == -10101,
-               "bt_value_array_append_signed_integer_element() appends the appropriate value");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 8);
-       ok(bt_value_is_real(obj),
-               "bt_value_array_append_real_element() appends a real number value object");
-       real_value = bt_value_real_get(obj);
-       ok(real_value == 2.49578,
-               "bt_value_array_append_real_element() appends the appropriate value");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 9);
-       ok(bt_value_is_string(obj),
-               "bt_value_array_append_string_element() appends a string value object");
-       string_value = bt_value_string_get(obj);
-       ok(!ret && string_value && strcmp(string_value, "bt_value") == 0,
-               "bt_value_array_append_string_element() appends the appropriate value");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 10);
-       ok(bt_value_is_array(obj),
-               "bt_value_array_append_empty_array_element() appends an array value object");
-       ok(bt_value_array_is_empty(obj),
-               "bt_value_array_append_empty_array_element() an empty array value object");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 11);
-       ok(bt_value_is_array(obj),
-               "bt_value_array_append_empty_array_element() appends an array value object");
-       ok(bt_value_array_is_empty(obj),
-               "bt_value_array_append_empty_array_element() an empty array value object");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 12);
-       ok(bt_value_is_map(obj),
-               "bt_value_array_append_empty_map_element() appends a map value object");
-       ok(bt_value_map_is_empty(obj),
-               "bt_value_array_append_empty_map_element() an empty map value object");
-       obj = bt_value_array_borrow_element_by_index(array_obj, 13);
-       ok(bt_value_is_map(obj),
-               "bt_value_array_append_empty_map_element() appends a map value object");
-       ok(bt_value_map_is_empty(obj),
-               "bt_value_array_append_empty_map_element() an empty map value object");
-
-       BT_VALUE_PUT_REF_AND_RESET(array_obj);
-       pass("putting an existing array value object does not cause a crash")
-}
-
-static
-bt_value_map_foreach_entry_func_status test_map_foreach_cb_count(
-               const char *key, bt_value *object,
-       void *data)
-{
-       int *count = data;
-
-       if (*count == 3) {
-               return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_INTERRUPT;
-       } else if (*count == 4) {
-               return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_ERROR;
-       } else if (*count == 5) {
-               return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_MEMORY_ERROR;
-       }
-
-       (*count)++;
-
-       return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_OK;
-}
-
-struct map_foreach_checklist {
-       bt_bool bool1;
-       bt_bool uint;
-       bt_bool int1;
-       bt_bool real1;
-       bt_bool null1;
-       bt_bool bool2;
-       bt_bool int2;
-       bt_bool real2;
-       bt_bool string2;
-       bt_bool array2;
-       bt_bool array3;
-       bt_bool map2;
-       bt_bool map3;
-};
-
-static
-bt_value_map_foreach_entry_func_status test_map_foreach_cb_check(
-               const char *key, bt_value *object, void *data)
-{
-       struct map_foreach_checklist *checklist = data;
-
-       if (strcmp(key, "bt_bool") == 0) {
-               if (checklist->bool1) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"bt_bool\"");
-               } else {
-                       bt_bool val = BT_FALSE;
-
-                       val = bt_value_bool_get(object);
-
-                       if (val) {
-                               pass("test_map_foreach_cb_check(): \"bt_bool\" value object has the right value");
-                               checklist->bool1 = BT_TRUE;
-                       } else {
-                               fail("test_map_foreach_cb_check(): \"bt_bool\" value object has the wrong value");
-                       }
-               }
-       } else if (strcmp(key, "uint") == 0) {
-               if (checklist->uint) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"uint\"");
-               } else {
-                       uint64_t val = 0;
-
-                       val = bt_value_integer_unsigned_get(object);
-
-                       if (val == 19457) {
-                               pass("test_map_foreach_cb_check(): \"uint\" value object has the right value");
-                               checklist->uint = BT_TRUE;
-                       } else {
-                               fail("test_map_foreach_cb_check(): \"uint\" value object has the wrong value");
-                       }
-               }
-       } else if (strcmp(key, "int") == 0) {
-               if (checklist->int1) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"int\"");
-               } else {
-                       int64_t val = 0;
-
-                       val = bt_value_integer_signed_get(object);
-
-                       if (val == -12345) {
-                               pass("test_map_foreach_cb_check(): \"int\" value object has the right value");
-                               checklist->int1 = BT_TRUE;
-                       } else {
-                               fail("test_map_foreach_cb_check(): \"int\" value object has the wrong value");
-                       }
-               }
-       } else if (strcmp(key, "real") == 0) {
-               if (checklist->real1) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"real\"");
-               } else {
-                       double val = 0;
-
-                       val = bt_value_real_get(object);
-
-                       if (val == 5.444) {
-                               pass("test_map_foreach_cb_check(): \"real\" value object has the right value");
-                               checklist->real1 = BT_TRUE;
-                       } else {
-                               fail("test_map_foreach_cb_check(): \"real\" value object has the wrong value");
-                       }
-               }
-       } else if (strcmp(key, "null") == 0) {
-               if (checklist->null1) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"bt_bool\"");
-               } else {
-                       ok(bt_value_is_null(object), "test_map_foreach_cb_check(): success getting \"null\" value object");
-                       checklist->null1 = BT_TRUE;
-               }
-       } else if (strcmp(key, "bool2") == 0) {
-               if (checklist->bool2) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"bool2\"");
-               } else {
-                       bt_bool val = BT_FALSE;
-
-                       val = bt_value_bool_get(object);
-
-                       if (val) {
-                               pass("test_map_foreach_cb_check(): \"bool2\" value object has the right value");
-                               checklist->bool2 = BT_TRUE;
-                       } else {
-                               fail("test_map_foreach_cb_check(): \"bool2\" value object has the wrong value");
-                       }
-               }
-       } else if (strcmp(key, "int2") == 0) {
-               if (checklist->int2) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"int2\"");
-               } else {
-                       int64_t val = 0;
-
-                       val = bt_value_integer_signed_get(object);
-
-                       if (val == 98765) {
-                               pass("test_map_foreach_cb_check(): \"int2\" value object has the right value");
-                               checklist->int2 = BT_TRUE;
-                       } else {
-                               fail("test_map_foreach_cb_check(): \"int2\" value object has the wrong value");
-                       }
-               }
-       } else if (strcmp(key, "real2") == 0) {
-               if (checklist->real2) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"real2\"");
-               } else {
-                       double val = 0;
-
-                       val = bt_value_real_get(object);
-
-                       if (val == -49.0001) {
-                               pass("test_map_foreach_cb_check(): \"real2\" value object has the right value");
-                               checklist->real2 = BT_TRUE;
-                       } else {
-                               fail("test_map_foreach_cb_check(): \"real2\" value object has the wrong value");
-                       }
-               }
-       } else if (strcmp(key, "string2") == 0) {
-               if (checklist->string2) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"string2\"");
-               } else {
-                       const char *val;
-
-                       val = bt_value_string_get(object);
-
-                       if (val && strcmp(val, "bt_value") == 0) {
-                               pass("test_map_foreach_cb_check(): \"string2\" value object has the right value");
-                               checklist->string2 = BT_TRUE;
-                       } else {
-                               fail("test_map_foreach_cb_check(): \"string2\" value object has the wrong value");
-                       }
-               }
-       } else if (strcmp(key, "array2") == 0) {
-               if (checklist->array2) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"array2\"");
-               } else {
-                       ok(bt_value_is_array(object), "test_map_foreach_cb_check(): success getting \"array2\" value object");
-                       ok(bt_value_array_is_empty(object),
-                               "test_map_foreach_cb_check(): \"array2\" value object is empty");
-                       checklist->array2 = BT_TRUE;
-               }
-       } else if (strcmp(key, "array3") == 0) {
-               if (checklist->array3) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"array3\"");
-               } else {
-                       ok(bt_value_is_array(object), "test_map_foreach_cb_check(): success getting \"array3\" value object");
-                       ok(bt_value_array_is_empty(object),
-                               "test_map_foreach_cb_check(): \"array3\" value object is empty");
-                       checklist->array3 = BT_TRUE;
-               }
-       } else if (strcmp(key, "map3") == 0) {
-               if (checklist->map3) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"map3\"");
-               } else {
-                       ok(bt_value_is_map(object), "test_map_foreach_cb_check(): success getting \"map3\" value object");
-                       ok(bt_value_map_is_empty(object),
-                               "test_map_foreach_cb_check(): \"map3\" value object is empty");
-                       checklist->map3 = BT_TRUE;
-               }
-       } else if (strcmp(key, "map2") == 0) {
-               if (checklist->map2) {
-                       fail("test_map_foreach_cb_check(): duplicate key \"map2\"");
-               } else {
-                       ok(bt_value_is_map(object), "test_map_foreach_cb_check(): success getting \"map2\" value object");
-                       ok(bt_value_map_is_empty(object),
-                               "test_map_foreach_cb_check(): \"map2\" value object is empty");
-                       checklist->map2 = BT_TRUE;
-               }
-       } else {
-               fail("test_map_foreach_cb_check(): unknown map key \"%s\"",
-                       key);
-       }
-
-       return BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_OK;
-}
-
-static
-void test_map(void)
-{
-       int ret;
-       int count = 0;
-       bt_bool bool_value;
-       int64_t int_value;
-       double real_value;
-       bt_value *obj;
-       bt_value *map_obj;
-       bt_value *inserted_obj;
-       struct map_foreach_checklist checklist;
-
-       map_obj = bt_value_map_create();
-       ok(map_obj && bt_value_is_map(map_obj),
-               "bt_value_map_create() returns a map value object");
-       ok(bt_value_map_get_size(map_obj) == 0,
-               "initial map value object size is 0");
-
-       obj = bt_value_integer_unsigned_create_init(19457);
-       ret = bt_value_map_insert_entry(map_obj, "uint", obj);
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       obj = bt_value_integer_signed_create_init(-12345);
-       ret |= bt_value_map_insert_entry(map_obj, "int", obj);
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       obj = bt_value_real_create_init(5.444);
-       ret |= bt_value_map_insert_entry(map_obj, "real", obj);
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       obj = bt_value_bool_create();
-       ret |= bt_value_map_insert_entry(map_obj, "bt_bool", obj);
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       ret |= bt_value_map_insert_entry(map_obj, "null",
-               bt_value_null);
-       ok(!ret, "bt_value_map_insert_entry() succeeds");
-       ok(bt_value_map_get_size(map_obj) == 5,
-               "inserting an element into a map value object increment its size");
-
-       obj = bt_value_bool_create_init(BT_TRUE);
-       ret = bt_value_map_insert_entry(map_obj, "bt_bool", obj);
-       BT_VALUE_PUT_REF_AND_RESET(obj);
-       ok(!ret, "bt_value_map_insert_entry() accepts an existing key");
-
-       obj = bt_value_map_borrow_entry_value(map_obj, "life");
-       ok(!obj, "bt_value_map_borrow_entry_value() returns NULL with an non existing key");
-       obj = bt_value_map_borrow_entry_value(map_obj, "real");
-       ok(obj && bt_value_is_real(obj),
-               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (real)");
-       real_value = bt_value_real_get(obj);
-       ok(real_value == 5.444,
-               "bt_value_map_borrow_entry_value() returns an value object with the appropriate value (real)");
-       obj = bt_value_map_borrow_entry_value(map_obj, "uint");
-       ok(obj && bt_value_is_unsigned_integer(obj),
-               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (unsigned integer)");
-       int_value = bt_value_integer_unsigned_get(obj);
-       ok(int_value == 19457,
-               "bt_value_map_borrow_entry_value() returns an value object with the appropriate value (unsigned integer)");
-       obj = bt_value_map_borrow_entry_value(map_obj, "int");
-       ok(obj && bt_value_is_signed_integer(obj),
-               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (signed integer)");
-       int_value = bt_value_integer_signed_get(obj);
-       ok(int_value == -12345,
-               "bt_value_map_borrow_entry_value() returns an value object with the appropriate value (signed integer)");
-       obj = bt_value_map_borrow_entry_value(map_obj, "null");
-       ok(obj && bt_value_is_null(obj),
-               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (null)");
-       obj = bt_value_map_borrow_entry_value(map_obj, "bt_bool");
-       ok(obj && bt_value_is_bool(obj),
-               "bt_value_map_borrow_entry_value() returns an value object with the appropriate type (boolean)");
-       bool_value = bt_value_bool_get(obj);
-       ok(bool_value,
-               "bt_value_map_borrow_entry_value() returns an value object with the appropriate value (boolean)");
-
-       ret = bt_value_map_insert_bool_entry(map_obj, "bool2",
-               BT_TRUE);
-       ok(!ret, "bt_value_map_insert_bool_entry() succeeds");
-       ret = bt_value_map_insert_signed_integer_entry(map_obj, "int2",
-               98765);
-       ok(!ret, "bt_value_map_insert_signed_integer_entry() succeeds");
-       ret = bt_value_map_insert_real_entry(map_obj, "real2",
-               -49.0001);
-       ok(!ret, "bt_value_map_insert_real_entry() succeeds");
-       ret = bt_value_map_insert_string_entry(map_obj, "string2",
-               "bt_value");
-       ok(!ret, "bt_value_map_insert_string_entry() succeeds");
-       ret = bt_value_map_insert_empty_array_entry(map_obj, "array2", NULL);
-       ok(!ret, "bt_value_map_insert_empty_array_entry() succeeds");
-       ret = bt_value_map_insert_empty_array_entry(map_obj, "array3", &inserted_obj);
-       ok(!ret, "bt_value_map_insert_empty_array_entry() with returned value object succeeds");
-       ok(inserted_obj,
-               "object returned by bt_value_map_insert_empty_array_entry() is not NULL");
-       ok(bt_value_is_array(inserted_obj),
-               "object returned by bt_value_map_insert_empty_array_entry() is an array value");
-       ret = bt_value_map_insert_empty_map_entry(map_obj, "map2", NULL);
-       ok(!ret, "bt_value_map_insert_empty_map_entry() succeeds");
-       ret = bt_value_map_insert_empty_map_entry(map_obj, "map3", &inserted_obj);
-       ok(!ret, "bt_value_map_insert_empty_map_entry() with returned value object succeeds");
-       ok(inserted_obj,
-               "object returned by bt_value_map_insert_empty_map_entry() is not NULL");
-       ok(bt_value_is_map(inserted_obj),
-               "object returned by bt_value_map_insert_empty_map_entry() is an array value");
-
-       ok(bt_value_map_get_size(map_obj) == 13,
-               "the bt_value_map_insert*() functions increment the map value object's size");
-
-       ok(!bt_value_map_has_entry(map_obj, "hello"),
-               "map value object does not have key \"hello\"");
-       ok(bt_value_map_has_entry(map_obj, "bt_bool"),
-               "map value object has key \"bt_bool\"");
-       ok(bt_value_map_has_entry(map_obj, "uint"),
-               "map value object has key \"uint\"");
-       ok(bt_value_map_has_entry(map_obj, "int"),
-               "map value object has key \"int\"");
-       ok(bt_value_map_has_entry(map_obj, "real"),
-               "map value object has key \"real\"");
-       ok(bt_value_map_has_entry(map_obj, "null"),
-               "map value object has key \"null\"");
-       ok(bt_value_map_has_entry(map_obj, "bool2"),
-               "map value object has key \"bool2\"");
-       ok(bt_value_map_has_entry(map_obj, "int2"),
-               "map value object has key \"int2\"");
-       ok(bt_value_map_has_entry(map_obj, "real2"),
-               "map value object has key \"real2\"");
-       ok(bt_value_map_has_entry(map_obj, "string2"),
-               "map value object has key \"string2\"");
-       ok(bt_value_map_has_entry(map_obj, "array2"),
-               "map value object has key \"array2\"");
-       ok(bt_value_map_has_entry(map_obj, "array3"),
-               "map value object has key \"array3\"");
-       ok(bt_value_map_has_entry(map_obj, "map2"),
-               "map value object has key \"map2\"");
-       ok(bt_value_map_has_entry(map_obj, "map3"),
-               "map value object has key \"map3\"");
-
-       ret = bt_value_map_foreach_entry(map_obj, test_map_foreach_cb_count,
-               &count);
-       ok(ret == BT_VALUE_MAP_FOREACH_ENTRY_STATUS_INTERRUPTED && count == 3,
-               "bt_value_map_foreach_entry() breaks the loop when the user function returns BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_INTERRUPT");
-
-       count = 4;
-       ret = bt_value_map_foreach_entry(map_obj, test_map_foreach_cb_count,
-               &count);
-       ok(ret == BT_VALUE_MAP_FOREACH_ENTRY_STATUS_USER_ERROR,
-               "bt_value_map_foreach_entry() fails when the user function returns BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_ERROR");
-       bt_current_thread_clear_error();
-
-       count = 5;
-       ret = bt_value_map_foreach_entry(map_obj, test_map_foreach_cb_count,
-               &count);
-       ok(ret == BT_VALUE_MAP_FOREACH_ENTRY_STATUS_MEMORY_ERROR,
-               "bt_value_map_foreach_entry() fails when the user function returns BT_VALUE_MAP_FOREACH_ENTRY_FUNC_STATUS_MEMORY_ERROR");
-       bt_current_thread_clear_error();
-
-       memset(&checklist, 0, sizeof(checklist));
-       ret = bt_value_map_foreach_entry(map_obj, test_map_foreach_cb_check,
-               &checklist);
-       ok(ret == BT_VALUE_MAP_FOREACH_ENTRY_STATUS_OK,
-               "bt_value_map_foreach_entry() succeeds with test_map_foreach_cb_check()");
-       ok(checklist.bool1 && checklist.uint && checklist.int1 &&
-               checklist.real1 && checklist.null1 && checklist.bool2 &&
-               checklist.int2 && checklist.real2 && checklist.string2 &&
-               checklist.array2 && checklist.map2,
-               "bt_value_map_foreach_entry() iterates over all the map value object's elements");
-
-       BT_VALUE_PUT_REF_AND_RESET(map_obj);
-       pass("putting an existing map value object does not cause a crash")
-}
-
-static
-void test_types(void)
-{
-       test_null();
-       test_bool();
-       test_unsigned_integer();
-       test_signed_integer();
-       test_real();
-       test_string();
-       test_array();
-       test_map();
-}
-
-static
-void test_is_equal_null(void)
-{
-       ok(bt_value_is_equal(bt_value_null, bt_value_null),
-               "null value objects are equivalent");
-}
-
-static
-void test_is_equal_bool(void)
-{
-       bt_value *bool1 =
-               bt_value_bool_create_init(BT_FALSE);
-       bt_value *bool2 =
-               bt_value_bool_create_init(BT_TRUE);
-       bt_value *bool3 =
-               bt_value_bool_create_init(BT_FALSE);
-
-       BT_ASSERT(bool1 && bool2 && bool3);
-       ok(!bt_value_is_equal(bt_value_null, bool1),
-               "cannot compare null value object and bt_bool value object");
-       ok(!bt_value_is_equal(bool1, bool2),
-               "boolean value objects are not equivalent (BT_FALSE and BT_TRUE)");
-       ok(bt_value_is_equal(bool1, bool3),
-               "boolean value objects are equivalent (BT_FALSE and BT_FALSE)");
-
-       BT_VALUE_PUT_REF_AND_RESET(bool1);
-       BT_VALUE_PUT_REF_AND_RESET(bool2);
-       BT_VALUE_PUT_REF_AND_RESET(bool3);
-}
-
-static
-void test_is_equal_unsigned_integer(void)
-{
-       bt_value *int1 =
-               bt_value_integer_unsigned_create_init(10);
-       bt_value *int2 =
-               bt_value_integer_unsigned_create_init(23);
-       bt_value *int3 =
-               bt_value_integer_unsigned_create_init(10);
-
-       BT_ASSERT(int1 && int2 && int3);
-       ok(!bt_value_is_equal(bt_value_null, int1),
-               "cannot compare null value object and unsigned integer value object");
-       ok(!bt_value_is_equal(int1, int2),
-               "unsigned integer value objects are not equivalent (10 and 23)");
-       ok(bt_value_is_equal(int1, int3),
-               "unsigned integer value objects are equivalent (10 and 10)");
-
-       BT_VALUE_PUT_REF_AND_RESET(int1);
-       BT_VALUE_PUT_REF_AND_RESET(int2);
-       BT_VALUE_PUT_REF_AND_RESET(int3);
-}
-
-static
-void test_is_equal_signed_integer(void)
-{
-       bt_value *int1 =
-               bt_value_integer_signed_create_init(10);
-       bt_value *int2 =
-               bt_value_integer_signed_create_init(-23);
-       bt_value *int3 =
-               bt_value_integer_signed_create_init(10);
-
-       BT_ASSERT(int1 && int2 && int3);
-       ok(!bt_value_is_equal(bt_value_null, int1),
-               "cannot compare null value object and signed integer value object");
-       ok(!bt_value_is_equal(int1, int2),
-               "signed integer value objects are not equivalent (10 and -23)");
-       ok(bt_value_is_equal(int1, int3),
-               "signed integer value objects are equivalent (10 and 10)");
-
-       BT_VALUE_PUT_REF_AND_RESET(int1);
-       BT_VALUE_PUT_REF_AND_RESET(int2);
-       BT_VALUE_PUT_REF_AND_RESET(int3);
-}
-
-static
-void test_is_equal_real(void)
-{
-       bt_value *real1 =
-               bt_value_real_create_init(17.38);
-       bt_value *real2 =
-               bt_value_real_create_init(-14.23);
-       bt_value *real3 =
-               bt_value_real_create_init(17.38);
-
-       BT_ASSERT(real1 && real2 && real3);
-
-       ok(!bt_value_is_equal(bt_value_null, real1),
-               "cannot compare null value object and real number value object");
-       ok(!bt_value_is_equal(real1, real2),
-               "real number value objects are not equivalent (17.38 and -14.23)");
-       ok(bt_value_is_equal(real1, real3),
-               "real number value objects are equivalent (17.38 and 17.38)");
-
-       BT_VALUE_PUT_REF_AND_RESET(real1);
-       BT_VALUE_PUT_REF_AND_RESET(real2);
-       BT_VALUE_PUT_REF_AND_RESET(real3);
-}
-
-static
-void test_is_equal_string(void)
-{
-       bt_value *string1 =
-               bt_value_string_create_init("hello");
-       bt_value *string2 =
-               bt_value_string_create_init("bt_value");
-       bt_value *string3 =
-               bt_value_string_create_init("hello");
-
-       BT_ASSERT(string1 && string2 && string3);
-
-       ok(!bt_value_is_equal(bt_value_null, string1),
-               "cannot compare null value object and string value object");
-       ok(!bt_value_is_equal(string1, string2),
-               "string value objects are not equivalent (\"hello\" and \"bt_value\")");
-       ok(bt_value_is_equal(string1, string3),
-               "string value objects are equivalent (\"hello\" and \"hello\")");
-
-       BT_VALUE_PUT_REF_AND_RESET(string1);
-       BT_VALUE_PUT_REF_AND_RESET(string2);
-       BT_VALUE_PUT_REF_AND_RESET(string3);
-}
-
-static
-void test_is_equal_array(void)
-{
-       bt_value *array1 = bt_value_array_create();
-       bt_value *array2 = bt_value_array_create();
-       bt_value *array3 = bt_value_array_create();
-       bt_value_array_append_element_status append_status;
-
-       BT_ASSERT(array1 && array2 && array3);
-
-       ok(bt_value_is_equal(array1, array2),
-               "empty array value objects are equivalent");
-
-       append_status = bt_value_array_append_signed_integer_element(array1, 23);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_real_element(array1, 14.2);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_bool_element(array1, BT_FALSE);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_real_element(array2, 14.2);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_signed_integer_element(array2, 23);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_bool_element(array2, BT_FALSE);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_signed_integer_element(array3, 23);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_real_element(array3, 14.2);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_bool_element(array3, BT_FALSE);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       BT_ASSERT(bt_value_array_get_length(array1) == 3);
-       BT_ASSERT(bt_value_array_get_length(array2) == 3);
-       BT_ASSERT(bt_value_array_get_length(array3) == 3);
-
-       ok(!bt_value_is_equal(bt_value_null, array1),
-               "cannot compare null value object and array value object");
-       ok(!bt_value_is_equal(array1, array2),
-               "array value objects are not equivalent ([23, 14.2, BT_FALSE] and [14.2, 23, BT_FALSE])");
-       ok(bt_value_is_equal(array1, array3),
-               "array value objects are equivalent ([23, 14.2, BT_FALSE] and [23, 14.2, BT_FALSE])");
-
-       BT_VALUE_PUT_REF_AND_RESET(array1);
-       BT_VALUE_PUT_REF_AND_RESET(array2);
-       BT_VALUE_PUT_REF_AND_RESET(array3);
-}
-
-static
-void test_is_equal_map(void)
-{
-       bt_value *map1 = bt_value_map_create();
-       bt_value *map2 = bt_value_map_create();
-       bt_value *map3 = bt_value_map_create();
-       bt_value_map_insert_entry_status insert_status;
-
-       BT_ASSERT(map1 && map2 && map3);
-
-       ok(bt_value_is_equal(map1, map2),
-               "empty map value objects are equivalent");
-
-
-       insert_status = bt_value_map_insert_signed_integer_entry(map1, "one", 23);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_real_entry(map1, "two", 14.2);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_bool_entry(map1, "three",
-               BT_FALSE);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_real_entry(map2, "one", 14.2);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_signed_integer_entry(map2, "two", 23);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_bool_entry(map2, "three",
-               BT_FALSE);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_bool_entry(map3, "three",
-               BT_FALSE);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_signed_integer_entry(map3, "one", 23);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_real_entry(map3, "two", 14.2);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       BT_ASSERT(bt_value_map_get_size(map1) == 3);
-       BT_ASSERT(bt_value_map_get_size(map2) == 3);
-       BT_ASSERT(bt_value_map_get_size(map3) == 3);
-
-       ok(!bt_value_is_equal(bt_value_null, map1),
-               "cannot compare null value object and map value object");
-       ok(!bt_value_is_equal(map1, map2),
-               "map value objects are not equivalent");
-       ok(bt_value_is_equal(map1, map3),
-               "map value objects are equivalent");
-
-       BT_VALUE_PUT_REF_AND_RESET(map1);
-       BT_VALUE_PUT_REF_AND_RESET(map2);
-       BT_VALUE_PUT_REF_AND_RESET(map3);
-}
-
-static
-void test_is_equal(void)
-{
-       test_is_equal_null();
-       test_is_equal_bool();
-       test_is_equal_unsigned_integer();
-       test_is_equal_signed_integer();
-       test_is_equal_real();
-       test_is_equal_string();
-       test_is_equal_array();
-       test_is_equal_map();
-}
-
-static
-void test_copy(void)
-{
-       /*
-        * Here's the deal here. If we make sure that each value object
-        * of our deep copy has a different address than its source, and
-        * that bt_value_is_equal() returns BT_TRUE for the top-level
-        * value object, taking into account that we test the
-        * correctness of bt_value_is_equal() elsewhere, then the deep
-        * copy is a success.
-        */
-       bt_value *null_copy_obj;
-       bt_value *bool_obj, *bool_copy_obj;
-       bt_value *unsigned_integer_obj, *unsigned_integer_copy_obj;
-       bt_value *signed_integer_obj, *signed_integer_copy_obj;
-       bt_value *real_obj, *real_copy_obj;
-       bt_value *string_obj, *string_copy_obj;
-       bt_value *array_obj, *array_copy_obj;
-       bt_value *map_obj, *map_copy_obj;
-       bt_value_array_append_element_status append_status;
-       bt_value_map_insert_entry_status insert_status;
-       bt_value_copy_status copy_status;
-
-       bool_obj = bt_value_bool_create_init(BT_TRUE);
-       unsigned_integer_obj = bt_value_integer_unsigned_create_init(23);
-       signed_integer_obj = bt_value_integer_signed_create_init(-47);
-       real_obj = bt_value_real_create_init(-3.1416);
-       string_obj = bt_value_string_create_init("test");
-       array_obj = bt_value_array_create();
-       map_obj = bt_value_map_create();
-
-       BT_ASSERT(bool_obj && unsigned_integer_obj && signed_integer_obj &&
-               real_obj && string_obj && array_obj && map_obj);
-
-       append_status = bt_value_array_append_element(array_obj, bool_obj);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_element(array_obj, unsigned_integer_obj);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_element(array_obj, signed_integer_obj);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_element(array_obj, real_obj);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       append_status = bt_value_array_append_element(array_obj, bt_value_null);
-       BT_ASSERT(append_status == BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK);
-       insert_status = bt_value_map_insert_entry(map_obj, "array", array_obj);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_entry(map_obj, "string", string_obj);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-
-       copy_status = bt_value_copy(map_obj, &map_copy_obj);
-       ok(copy_status == BT_VALUE_COPY_STATUS_OK && map_copy_obj,
-               "bt_value_copy() succeeds");
-
-       ok(map_obj != map_copy_obj,
-               "bt_value_copy() returns a different pointer (map)");
-       string_copy_obj = bt_value_map_borrow_entry_value(map_copy_obj,
-               "string");
-       ok(string_copy_obj != string_obj,
-               "bt_value_copy() returns a different pointer (string)");
-       array_copy_obj = bt_value_map_borrow_entry_value(map_copy_obj,
-               "array");
-       ok(array_copy_obj != array_obj,
-               "bt_value_copy() returns a different pointer (array)");
-       bool_copy_obj = bt_value_array_borrow_element_by_index(
-               array_copy_obj, 0);
-       ok(bool_copy_obj != bool_obj,
-               "bt_value_copy() returns a different pointer (bool)");
-       unsigned_integer_copy_obj = bt_value_array_borrow_element_by_index(
-               array_copy_obj, 1);
-       ok(unsigned_integer_copy_obj != unsigned_integer_obj,
-               "bt_value_copy() returns a different pointer (unsigned integer)");
-       signed_integer_copy_obj = bt_value_array_borrow_element_by_index(
-               array_copy_obj, 2);
-       ok(signed_integer_copy_obj != signed_integer_obj,
-               "bt_value_copy() returns a different pointer (signed integer)");
-       real_copy_obj = bt_value_array_borrow_element_by_index(
-               array_copy_obj, 3);
-       ok(real_copy_obj != real_obj,
-               "bt_value_copy() returns a different pointer (real)");
-       null_copy_obj = bt_value_array_borrow_element_by_index(
-               array_copy_obj, 4);
-       ok(null_copy_obj == bt_value_null,
-               "bt_value_copy() returns the same pointer (null)");
-
-       ok(bt_value_is_equal(map_obj, map_copy_obj),
-               "source and destination value objects have the same content");
-
-       BT_VALUE_PUT_REF_AND_RESET(map_copy_obj);
-       BT_VALUE_PUT_REF_AND_RESET(bool_obj);
-       BT_VALUE_PUT_REF_AND_RESET(unsigned_integer_obj);
-       BT_VALUE_PUT_REF_AND_RESET(signed_integer_obj);
-       BT_VALUE_PUT_REF_AND_RESET(real_obj);
-       BT_VALUE_PUT_REF_AND_RESET(string_obj);
-       BT_VALUE_PUT_REF_AND_RESET(array_obj);
-       BT_VALUE_PUT_REF_AND_RESET(map_obj);
-}
-
-static
-bt_bool compare_map_elements(const bt_value *map_a, const bt_value *map_b,
-               const char *key)
-{
-       const bt_value *elem_a = NULL;
-       const bt_value *elem_b = NULL;
-       bt_bool equal;
-
-       elem_a = bt_value_map_borrow_entry_value_const(map_a, key);
-       elem_b = bt_value_map_borrow_entry_value_const(map_b, key);
-       equal = bt_value_is_equal(elem_a, elem_b);
-       return equal;
-}
-
-static
-void test_extend(void)
-{
-       bt_value *base_map = bt_value_map_create();
-       bt_value *extension_map = bt_value_map_create();
-       bt_value *extended_map = NULL;
-       bt_value *array = bt_value_array_create();
-       bt_value_map_insert_entry_status insert_status;
-       bt_value_copy_status copy_status;
-       bt_value_map_extend_status extend_status;
-
-       BT_ASSERT(base_map);
-       BT_ASSERT(extension_map);
-       BT_ASSERT(array);
-       insert_status = bt_value_map_insert_bool_entry(base_map, "file",
-               BT_TRUE);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_bool_entry(base_map, "edit",
-               BT_FALSE);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_signed_integer_entry(base_map,
-               "selection", 17);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_signed_integer_entry(base_map, "find",
-               -34);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_bool_entry(extension_map, "edit",
-               BT_TRUE);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_signed_integer_entry(extension_map,
-               "find", 101);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       insert_status = bt_value_map_insert_real_entry(extension_map,
-               "project", -404);
-       BT_ASSERT(insert_status == BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK);
-       copy_status = bt_value_copy(base_map, &extended_map);
-       BT_ASSERT(copy_status == BT_VALUE_COPY_STATUS_OK);
-       extend_status = bt_value_map_extend(extended_map, extension_map);
-       ok(extend_status == BT_VALUE_MAP_EXTEND_STATUS_OK &&
-               extended_map, "bt_value_map_extend() succeeds");
-       ok(bt_value_map_get_size(extended_map) == 5,
-               "bt_value_map_extend() returns a map object with the correct size");
-       ok(compare_map_elements(base_map,
-                               extended_map, "file"),
-               "bt_value_map_extend() picks the appropriate element (file)");
-       ok(compare_map_elements(extension_map,
-                               extended_map, "edit"),
-               "bt_value_map_extend() picks the appropriate element (edit)");
-       ok(compare_map_elements(base_map,
-                               extended_map, "selection"),
-               "bt_value_map_extend() picks the appropriate element (selection)");
-       ok(compare_map_elements(extension_map,
-                               extended_map, "find"),
-               "bt_value_map_extend() picks the appropriate element (find)");
-       ok(compare_map_elements(extension_map,
-                               extended_map, "project"),
-               "bt_value_map_extend() picks the appropriate element (project)");
-
-       BT_VALUE_PUT_REF_AND_RESET(array);
-       BT_VALUE_PUT_REF_AND_RESET(base_map);
-       BT_VALUE_PUT_REF_AND_RESET(extension_map);
-       BT_VALUE_PUT_REF_AND_RESET(extended_map);
-}
-
-int main(void)
-{
-       plan_tests(NR_TESTS);
-       test_types();
-       test_is_equal();
-       test_copy();
-       test_extend();
-       return exit_status();
-}
diff --git a/tests/lib/test_graph_topo.c b/tests/lib/test_graph_topo.c
deleted file mode 100644 (file)
index cd3284b..0000000
+++ /dev/null
@@ -1,719 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/assert.h"
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <glib.h>
-
-#include "tap/tap.h"
-
-#define NR_TESTS       26
-
-enum event_type {
-       SRC_COMP_OUTPUT_PORT_CONNECTED,
-       SINK_COMP_INPUT_PORT_CONNECTED,
-       GRAPH_SRC_OUTPUT_PORT_ADDED,
-       GRAPH_SINK_INPUT_PORT_ADDED,
-};
-
-enum test {
-       TEST_EMPTY_GRAPH,
-       TEST_SIMPLE,
-       TEST_SRC_PORT_CONNECTED_ERROR,
-       TEST_SINK_PORT_CONNECTED_ERROR,
-       TEST_SRC_ADDS_PORT_IN_PORT_CONNECTED,
-};
-
-struct event {
-       enum event_type type;
-
-       union {
-               struct {
-                       const bt_component *comp;
-                       const bt_port *self_port;
-                       const bt_port *other_port;
-               } src_comp_output_port_connected;
-
-               struct {
-                       const bt_component *comp;
-                       const bt_port *self_port;
-                       const bt_port *other_port;
-               } sink_comp_input_port_connected;
-
-               struct {
-                       const bt_component *comp;
-                       const bt_port *port;
-               } graph_src_output_port_added;
-
-               struct {
-                       const bt_component *comp;
-                       const bt_port *port;
-               } graph_sink_input_port_added;
-       } data;
-};
-
-static GArray *events;
-static bt_message_iterator_class *msg_iter_class;
-static bt_component_class_source *src_comp_class;
-static bt_component_class_sink *sink_comp_class;
-static enum test current_test;
-
-static
-void clear_events(void)
-{
-       g_array_set_size(events, 0);
-}
-
-static
-void append_event(struct event *event)
-{
-       g_array_append_val(events, *event);
-}
-
-static
-bool compare_events(struct event *ev_a, struct event *ev_b)
-{
-       if (ev_a->type != ev_b->type) {
-               return false;
-       }
-
-       switch (ev_a->type) {
-               case SRC_COMP_OUTPUT_PORT_CONNECTED:
-                       if (ev_a->data.src_comp_output_port_connected.comp !=
-                                       ev_b->data.src_comp_output_port_connected.comp) {
-                               return false;
-                       }
-
-                       if (ev_a->data.src_comp_output_port_connected.self_port !=
-                                       ev_b->data.src_comp_output_port_connected.self_port) {
-                               return false;
-                       }
-
-                       if (ev_a->data.src_comp_output_port_connected.other_port !=
-                                       ev_b->data.src_comp_output_port_connected.other_port) {
-                               return false;
-                       }
-                       break;
-               case SINK_COMP_INPUT_PORT_CONNECTED:
-                       if (ev_a->data.sink_comp_input_port_connected.comp !=
-                                       ev_b->data.sink_comp_input_port_connected.comp) {
-                               return false;
-                       }
-
-                       if (ev_a->data.sink_comp_input_port_connected.self_port !=
-                                       ev_b->data.sink_comp_input_port_connected.self_port) {
-                               return false;
-                       }
-
-                       if (ev_a->data.sink_comp_input_port_connected.other_port !=
-                                       ev_b->data.sink_comp_input_port_connected.other_port) {
-                               return false;
-                       }
-                       break;
-               case GRAPH_SRC_OUTPUT_PORT_ADDED:
-                       if (ev_a->data.graph_src_output_port_added.comp !=
-                                       ev_b->data.graph_src_output_port_added.comp) {
-                               return false;
-                       }
-
-                       if (ev_a->data.graph_src_output_port_added.port !=
-                                       ev_b->data.graph_src_output_port_added.port) {
-                               return false;
-                       }
-                       break;
-               case GRAPH_SINK_INPUT_PORT_ADDED:
-                       if (ev_a->data.graph_sink_input_port_added.comp !=
-                                       ev_b->data.graph_sink_input_port_added.comp) {
-                               return false;
-                       }
-
-                       if (ev_a->data.graph_sink_input_port_added.port !=
-                                       ev_b->data.graph_sink_input_port_added.port) {
-                               return false;
-                       }
-                       break;
-               default:
-                       abort();
-       }
-
-       return true;
-}
-
-static
-bool has_event(struct event *event)
-{
-       size_t i;
-
-       for (i = 0; i < events->len; i++) {
-               struct event *ev = &g_array_index(events, struct event, i);
-
-               if (compare_events(event, ev)) {
-                       return true;
-               }
-       }
-
-       return false;
-}
-
-static
-size_t event_pos(struct event *event)
-{
-       size_t i;
-
-       for (i = 0; i < events->len; i++) {
-               struct event *ev = &g_array_index(events, struct event, i);
-
-               if (compare_events(event, ev)) {
-                       return i;
-               }
-       }
-
-       return SIZE_MAX;
-}
-
-static
-bt_message_iterator_class_next_method_status src_iter_next(
-               bt_self_message_iterator *self_iterator,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-}
-
-static
-bt_component_class_port_connected_method_status src_output_port_connected(
-               bt_self_component_source *self_comp,
-               bt_self_component_port_output *self_comp_port,
-               const bt_port_input *other_port)
-{
-       int ret;
-       struct event event = {
-               .type = SRC_COMP_OUTPUT_PORT_CONNECTED,
-               .data.src_comp_output_port_connected = {
-                       .comp = bt_self_component_as_component(
-                               bt_self_component_source_as_self_component(
-                                       self_comp)),
-                       .self_port = bt_self_component_port_as_port(
-                               bt_self_component_port_output_as_self_component_port(
-                                       self_comp_port)),
-                       .other_port = bt_port_input_as_port_const(other_port),
-               },
-       };
-
-       append_event(&event);
-
-       switch (current_test) {
-       case TEST_SRC_ADDS_PORT_IN_PORT_CONNECTED:
-               ret = bt_self_component_source_add_output_port(
-                       self_comp, "hello", NULL, NULL);
-               BT_ASSERT(ret == 0);
-               break;
-       case TEST_SRC_PORT_CONNECTED_ERROR:
-               return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_ERROR;
-       default:
-               break;
-       }
-
-       return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_OK;
-}
-
-static
-bt_component_class_port_connected_method_status sink_input_port_connected(
-               bt_self_component_sink *self_comp,
-               bt_self_component_port_input *self_comp_port,
-               const bt_port_output *other_port)
-{
-       struct event event = {
-               .type = SINK_COMP_INPUT_PORT_CONNECTED,
-               .data.sink_comp_input_port_connected = {
-                       .comp = bt_self_component_as_component(
-                               bt_self_component_sink_as_self_component(
-                                       self_comp)),
-                       .self_port = bt_self_component_port_as_port(
-                               bt_self_component_port_input_as_self_component_port(
-                                       self_comp_port)),
-                       .other_port = bt_port_output_as_port_const(other_port),
-               },
-       };
-
-       append_event(&event);
-
-       if (current_test == TEST_SINK_PORT_CONNECTED_ERROR) {
-               return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_ERROR;
-       } else {
-               return BT_COMPONENT_CLASS_PORT_CONNECTED_METHOD_STATUS_OK;
-       }
-}
-
-static
-bt_component_class_initialize_method_status src_init(
-       bt_self_component_source *self_comp,
-       bt_self_component_source_configuration *config,
-       const bt_value *params, void *init_method_data)
-{
-       int ret;
-
-       ret = bt_self_component_source_add_output_port(
-               self_comp, "out", NULL, NULL);
-       BT_ASSERT(ret == 0);
-       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-}
-
-static
-bt_component_class_initialize_method_status sink_init(
-       bt_self_component_sink *self_comp,
-       bt_self_component_sink_configuration *config,
-       const bt_value *params, void *init_method_data)
-{
-       int ret;
-
-       ret = bt_self_component_sink_add_input_port(self_comp,
-               "in", NULL, NULL);
-       BT_ASSERT(ret == 0);
-       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-}
-
-static
-bt_component_class_sink_consume_method_status sink_consume(
-               bt_self_component_sink *self_comp)
-{
-       return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
-}
-
-static
-bt_graph_listener_func_status graph_src_output_port_added(
-               const bt_component_source *comp, const bt_port_output *port,
-               void *data)
-{
-       struct event event = {
-               .type = GRAPH_SRC_OUTPUT_PORT_ADDED,
-               .data.graph_src_output_port_added = {
-                       .comp = bt_component_source_as_component_const(comp),
-                       .port = bt_port_output_as_port_const(port),
-               },
-       };
-
-       append_event(&event);
-
-       return BT_GRAPH_LISTENER_FUNC_STATUS_OK;
-}
-
-static
-bt_graph_listener_func_status graph_sink_input_port_added(
-               const bt_component_sink *comp, const bt_port_input *port,
-               void *data)
-{
-       struct event event = {
-               .type = GRAPH_SINK_INPUT_PORT_ADDED,
-               .data.graph_sink_input_port_added = {
-                       .comp = bt_component_sink_as_component_const(comp),
-                       .port = bt_port_input_as_port_const(port),
-               },
-       };
-
-       append_event(&event);
-
-       return BT_GRAPH_LISTENER_FUNC_STATUS_OK;
-}
-
-static
-void init_test(void)
-{
-       int ret;
-
-       msg_iter_class = bt_message_iterator_class_create(src_iter_next);
-       BT_ASSERT(msg_iter_class);
-
-       src_comp_class = bt_component_class_source_create(
-               "src", msg_iter_class);
-       BT_ASSERT(src_comp_class);
-       ret = bt_component_class_source_set_initialize_method(
-               src_comp_class, src_init);
-       BT_ASSERT(ret == 0);
-       ret = bt_component_class_source_set_output_port_connected_method(
-               src_comp_class, src_output_port_connected);
-       BT_ASSERT(ret == 0);
-       sink_comp_class = bt_component_class_sink_create("sink",
-               sink_consume);
-       BT_ASSERT(sink_comp_class);
-       ret = bt_component_class_sink_set_initialize_method(sink_comp_class,
-               sink_init);
-       BT_ASSERT(ret == 0);
-       ret = bt_component_class_sink_set_input_port_connected_method(
-               sink_comp_class, sink_input_port_connected);
-       BT_ASSERT(ret == 0);
-       events = g_array_new(FALSE, TRUE, sizeof(struct event));
-       BT_ASSERT(events);
-}
-
-static
-void fini_test(void)
-{
-       bt_component_class_source_put_ref(src_comp_class);
-       bt_component_class_sink_put_ref(sink_comp_class);
-       g_array_free(events, TRUE);
-}
-
-static
-const bt_component_source *create_src(bt_graph *graph)
-{
-       const bt_component_source *comp;
-       int ret;
-
-       ret = bt_graph_add_source_component(graph, src_comp_class,
-               "src-comp", NULL, BT_LOGGING_LEVEL_NONE, &comp);
-       BT_ASSERT(ret == 0);
-       return comp;
-}
-
-static
-const bt_component_sink *create_sink(bt_graph *graph)
-{
-       const bt_component_sink *comp;
-       int ret;
-
-       ret = bt_graph_add_sink_component(graph, sink_comp_class,
-               "sink-comp", NULL, BT_LOGGING_LEVEL_NONE, &comp);
-       BT_ASSERT(ret == 0);
-       return comp;
-}
-
-static
-bt_graph *create_graph(void)
-{
-       bt_graph *graph = bt_graph_create(0);
-       int ret;
-
-       BT_ASSERT(graph);
-       ret = bt_graph_add_source_component_output_port_added_listener(
-               graph, graph_src_output_port_added, NULL, NULL);
-       BT_ASSERT(ret >= 0);
-       ret = bt_graph_add_sink_component_input_port_added_listener(
-               graph, graph_sink_input_port_added, NULL, NULL);
-       BT_ASSERT(ret >= 0);
-       return graph;
-}
-
-static
-void prepare_test(enum test test, const char *name)
-{
-       clear_events();
-       current_test = test;
-       diag("test: %s", name);
-}
-
-static
-void test_src_adds_port_in_port_connected(void)
-{
-       const bt_component_source *src;
-       const bt_component_sink *sink;
-       const bt_component *gsrc;
-       const bt_component *gsink;
-       bt_graph *graph;
-       const bt_port_output *src_def_port;
-       const bt_port_output *src_hello_port;
-       const bt_port_input *sink_def_port;
-       const bt_port *gsrc_def_port;
-       const bt_port *gsrc_hello_port;
-       const bt_port *gsink_def_port;
-       struct event event;
-       bt_graph_connect_ports_status status;
-       size_t src_port_connected_pos;
-       size_t graph_port_added_src_pos;
-
-       prepare_test(TEST_SRC_ADDS_PORT_IN_PORT_CONNECTED,
-               "source adds port in port connected");
-       graph = create_graph();
-       BT_ASSERT(graph);
-       src = create_src(graph);
-       sink = create_sink(graph);
-       src_def_port = bt_component_source_borrow_output_port_by_name_const(src,
-                                                                           "out");
-       BT_ASSERT(src_def_port);
-       sink_def_port = bt_component_sink_borrow_input_port_by_name_const(sink,
-                                                                         "in");
-       BT_ASSERT(sink_def_port);
-       status = bt_graph_connect_ports(graph, src_def_port,
-               sink_def_port, NULL);
-       BT_ASSERT(status == 0);
-       src_hello_port = bt_component_source_borrow_output_port_by_name_const(src,
-                                                                             "hello");
-       BT_ASSERT(src_hello_port);
-       gsrc = bt_component_source_as_component_const(src);
-       gsink = bt_component_sink_as_component_const(sink);
-       gsrc_def_port = bt_port_output_as_port_const(src_def_port);
-       gsrc_hello_port = bt_port_output_as_port_const(src_hello_port);
-       gsink_def_port = bt_port_input_as_port_const(sink_def_port);
-
-       /* We're supposed to have 5 events */
-       ok(events->len == 5, "we have the expected number of events");
-
-       /* Source's port added */
-       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
-       event.data.graph_src_output_port_added.comp = gsrc;
-       event.data.graph_src_output_port_added.port = gsrc_def_port;
-       ok(has_event(&event), "got the expected graph's port added event (for source, initial)");
-
-       /* Sink's port added */
-       event.type = GRAPH_SINK_INPUT_PORT_ADDED;
-       event.data.graph_sink_input_port_added.comp = gsink;
-       event.data.graph_sink_input_port_added.port = gsink_def_port;
-       ok(has_event(&event), "got the expected graph's port added event (for sink, initial)");
-
-       /* Source's port connected */
-       event.type = SRC_COMP_OUTPUT_PORT_CONNECTED;
-       event.data.src_comp_output_port_connected.comp = gsrc;
-       event.data.src_comp_output_port_connected.self_port = gsrc_def_port;
-       event.data.src_comp_output_port_connected.other_port = gsink_def_port;
-       ok(has_event(&event), "got the expected source's port connected event");
-       src_port_connected_pos = event_pos(&event);
-
-       /* Graph's port added (source) */
-       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
-       event.data.graph_src_output_port_added.comp = gsrc;
-       event.data.graph_src_output_port_added.port = gsrc_hello_port;
-       ok(has_event(&event), "got the expected graph's port added event (for source)");
-       graph_port_added_src_pos = event_pos(&event);
-
-       /* Sink's port connected */
-       event.type = SINK_COMP_INPUT_PORT_CONNECTED;
-       event.data.sink_comp_input_port_connected.comp = gsink;
-       event.data.sink_comp_input_port_connected.self_port = gsink_def_port;
-       event.data.sink_comp_input_port_connected.other_port = gsrc_def_port;
-       ok(has_event(&event), "got the expected sink's port connected event");
-
-       /* Order of events */
-       ok(src_port_connected_pos < graph_port_added_src_pos,
-               "event order is good");
-
-       bt_graph_put_ref(graph);
-}
-
-static
-void test_simple(void)
-{
-       const bt_component_source *src;
-       const bt_component_sink *sink;
-       const bt_component *gsrc;
-       const bt_component *gsink;
-       bt_graph *graph;
-       const bt_port_output *src_def_port;
-       const bt_port_input *sink_def_port;
-       const bt_port *gsrc_def_port;
-       const bt_port *gsink_def_port;
-       struct event event;
-       bt_graph_connect_ports_status status;
-
-       prepare_test(TEST_SIMPLE, "simple");
-       graph = create_graph();
-       BT_ASSERT(graph);
-       src = create_src(graph);
-       sink = create_sink(graph);
-       src_def_port = bt_component_source_borrow_output_port_by_name_const(src,
-                                                                           "out");
-       BT_ASSERT(src_def_port);
-       sink_def_port = bt_component_sink_borrow_input_port_by_name_const(sink,
-                                                                         "in");
-       BT_ASSERT(sink_def_port);
-       status = bt_graph_connect_ports(graph, src_def_port,
-               sink_def_port, NULL);
-       BT_ASSERT(status == 0);
-       gsrc = bt_component_source_as_component_const(src);
-       gsink = bt_component_sink_as_component_const(sink);
-       gsrc_def_port = bt_port_output_as_port_const(src_def_port);
-       gsink_def_port = bt_port_input_as_port_const(sink_def_port);
-
-       /* We're supposed to have 4 events */
-       ok(events->len == 4, "we have the expected number of events");
-
-       /* Source's port added */
-       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
-       event.data.graph_src_output_port_added.comp = gsrc;
-       event.data.graph_src_output_port_added.port = gsrc_def_port;
-       ok(has_event(&event), "got the expected graph's port added event (for source, initial)");
-
-       /* Sink's port added */
-       event.type = GRAPH_SINK_INPUT_PORT_ADDED;
-       event.data.graph_sink_input_port_added.comp = gsink;
-       event.data.graph_sink_input_port_added.port = gsink_def_port;
-       ok(has_event(&event), "got the expected graph's port added event (for sink, initial)");
-
-       /* Source's port connected */
-       event.type = SRC_COMP_OUTPUT_PORT_CONNECTED;
-       event.data.src_comp_output_port_connected.comp = gsrc;
-       event.data.src_comp_output_port_connected.self_port = gsrc_def_port;
-       event.data.src_comp_output_port_connected.other_port = gsink_def_port;
-       ok(has_event(&event), "got the expected source's port connected event");
-
-       /* Sink's port connected */
-       event.type = SINK_COMP_INPUT_PORT_CONNECTED;
-       event.data.sink_comp_input_port_connected.comp = gsink;
-       event.data.sink_comp_input_port_connected.self_port = gsink_def_port;
-       event.data.sink_comp_input_port_connected.other_port = gsrc_def_port;
-       ok(has_event(&event), "got the expected sink's port connected event");
-
-       bt_graph_put_ref(graph);
-}
-
-static
-void test_src_port_connected_error(void)
-{
-       const bt_component_source *src;
-       const bt_component_sink *sink;
-       const bt_component *gsrc;
-       const bt_component *gsink;
-       bt_graph *graph;
-       const bt_port_output *src_def_port;
-       const bt_port_input *sink_def_port;
-       const bt_port *gsrc_def_port;
-       const bt_port *gsink_def_port;
-       const bt_connection *conn = NULL;
-       struct event event;
-       bt_graph_connect_ports_status status;
-
-       prepare_test(TEST_SRC_PORT_CONNECTED_ERROR, "port connected error: source");
-       graph = create_graph();
-       BT_ASSERT(graph);
-       src = create_src(graph);
-       sink = create_sink(graph);
-       src_def_port = bt_component_source_borrow_output_port_by_name_const(src,
-                                                                           "out");
-       BT_ASSERT(src_def_port);
-       sink_def_port = bt_component_sink_borrow_input_port_by_name_const(sink,
-                                                                         "in");
-       BT_ASSERT(sink_def_port);
-       status = bt_graph_connect_ports(graph, src_def_port,
-               sink_def_port, &conn);
-       ok(status != BT_GRAPH_CONNECT_PORTS_STATUS_OK,
-               "bt_graph_connect_ports() returns an error");
-       bt_current_thread_clear_error();
-       ok(!conn, "returned connection is still NULL");
-       gsrc = bt_component_source_as_component_const(src);
-       gsink = bt_component_sink_as_component_const(sink);
-       gsrc_def_port = bt_port_output_as_port_const(src_def_port);
-       gsink_def_port = bt_port_input_as_port_const(sink_def_port);
-
-       /* We're supposed to have 3 events */
-       ok(events->len == 3, "we have the expected number of events");
-
-       /* Source's port added */
-       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
-       event.data.graph_src_output_port_added.comp = gsrc;
-       event.data.graph_src_output_port_added.port = gsrc_def_port;
-       ok(has_event(&event), "got the expected graph's port added event (for source, initial)");
-
-       /* Sink's port added */
-       event.type = GRAPH_SINK_INPUT_PORT_ADDED;
-       event.data.graph_sink_input_port_added.comp = gsink;
-       event.data.graph_sink_input_port_added.port = gsink_def_port;
-       ok(has_event(&event), "got the expected graph's port added event (for sink, initial)");
-
-       /* Source's port connected */
-       event.type = SRC_COMP_OUTPUT_PORT_CONNECTED;
-       event.data.src_comp_output_port_connected.comp = gsrc;
-       event.data.src_comp_output_port_connected.self_port = gsrc_def_port;
-       event.data.src_comp_output_port_connected.other_port = gsink_def_port;
-       ok(has_event(&event), "got the expected source's port connected event");
-
-       bt_graph_put_ref(graph);
-}
-
-static
-void test_sink_port_connected_error(void)
-{
-       const bt_component_source *src;
-       const bt_component_sink *sink;
-       const bt_component *gsrc;
-       const bt_component *gsink;
-       bt_graph *graph;
-       const bt_port_output *src_def_port;
-       const bt_port_input *sink_def_port;
-       const bt_port *gsrc_def_port;
-       const bt_port *gsink_def_port;
-       const bt_connection *conn = NULL;
-       struct event event;
-       bt_graph_connect_ports_status status;
-
-       prepare_test(TEST_SINK_PORT_CONNECTED_ERROR, "port connected error: sink");
-       graph = create_graph();
-       BT_ASSERT(graph);
-       src = create_src(graph);
-       sink = create_sink(graph);
-       src_def_port = bt_component_source_borrow_output_port_by_name_const(src,
-                                                                           "out");
-       BT_ASSERT(src_def_port);
-       sink_def_port = bt_component_sink_borrow_input_port_by_name_const(sink,
-                                                                         "in");
-       BT_ASSERT(sink_def_port);
-       status = bt_graph_connect_ports(graph, src_def_port,
-               sink_def_port, &conn);
-       ok(status != BT_GRAPH_CONNECT_PORTS_STATUS_OK,
-               "bt_graph_connect_ports() returns an error");
-       bt_current_thread_clear_error();
-       ok(!conn, "returned connection is still NULL");
-       gsrc = bt_component_source_as_component_const(src);
-       gsink = bt_component_sink_as_component_const(sink);
-       gsrc_def_port = bt_port_output_as_port_const(src_def_port);
-       gsink_def_port = bt_port_input_as_port_const(sink_def_port);
-
-       /* We're supposed to have 4 events */
-       ok(events->len == 4, "we have the expected number of events");
-
-       /* Source's port added */
-       event.type = GRAPH_SRC_OUTPUT_PORT_ADDED;
-       event.data.graph_src_output_port_added.comp = gsrc;
-       event.data.graph_src_output_port_added.port = gsrc_def_port;
-       ok(has_event(&event), "got the expected graph's port added event (for source, initial)");
-
-       /* Sink's port added */
-       event.type = GRAPH_SINK_INPUT_PORT_ADDED;
-       event.data.graph_sink_input_port_added.comp = gsink;
-       event.data.graph_sink_input_port_added.port = gsink_def_port;
-       ok(has_event(&event), "got the expected graph's port added event (for sink, initial)");
-
-       /* Source's port connected */
-       event.type = SRC_COMP_OUTPUT_PORT_CONNECTED;
-       event.data.src_comp_output_port_connected.comp = gsrc;
-       event.data.src_comp_output_port_connected.self_port = gsrc_def_port;
-       event.data.src_comp_output_port_connected.other_port = gsink_def_port;
-       ok(has_event(&event), "got the expected source's port connected event");
-
-       /* Sink's port connected */
-       event.type = SINK_COMP_INPUT_PORT_CONNECTED;
-       event.data.sink_comp_input_port_connected.comp = gsink;
-       event.data.sink_comp_input_port_connected.self_port = gsink_def_port;
-       event.data.sink_comp_input_port_connected.other_port = gsrc_def_port;
-       ok(has_event(&event), "got the expected sink's port connected event");
-
-       bt_graph_put_ref(graph);
-}
-
-static
-void test_empty_graph(void)
-{
-       bt_graph *graph;
-
-       prepare_test(TEST_EMPTY_GRAPH, "empty graph");
-       graph = create_graph();
-       ok(events->len == 0, "empty graph generates no events");
-       bt_graph_put_ref(graph);
-}
-
-int main(int argc, char **argv)
-{
-       plan_tests(NR_TESTS);
-       init_test();
-       test_empty_graph();
-       test_simple();
-       test_src_port_connected_error();
-       test_sink_port_connected_error();
-       test_src_adds_port_in_port_connected();
-       fini_test();
-       return exit_status();
-}
diff --git a/tests/lib/test_plugin b/tests/lib/test_plugin
deleted file mode 100755 (executable)
index b9a3fcd..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-"${BT_TESTS_BUILDDIR}/lib/plugin" "${BT_TESTS_BUILDDIR}/lib/test-plugin-plugins/.libs"
diff --git a/tests/lib/test_remove_destruction_listener_in_destruction_listener.c b/tests/lib/test_remove_destruction_listener_in_destruction_listener.c
deleted file mode 100644 (file)
index a3827dd..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2019 Efficios, Inc.
- */
-
-/*
- * Test that remove a trace class or trace destruction listener from within
- * a destruction listener of the same object works.
- */
-
-#include <babeltrace2/babeltrace.h>
-#include <common/assert.h>
-#include <tap/tap.h>
-#include <stdbool.h>
-
-#define NR_TESTS 16
-
-static bt_listener_id trace_class_destroyed_1_id;
-static bt_listener_id trace_class_destroyed_2_id;
-static bt_listener_id trace_class_destroyed_3_id;
-static bt_listener_id trace_class_destroyed_4_id;
-static bt_listener_id trace_class_destroyed_5_id;
-
-static bt_listener_id trace_destroyed_1_id;
-static bt_listener_id trace_destroyed_2_id;
-static bt_listener_id trace_destroyed_3_id;
-static bt_listener_id trace_destroyed_4_id;
-static bt_listener_id trace_destroyed_5_id;
-
-static bool trace_class_destroyed_1_called = false;
-static bool trace_class_destroyed_2_called = false;
-static bool trace_class_destroyed_3_called = false;
-static bool trace_class_destroyed_4_called = false;
-static bool trace_class_destroyed_5_called = false;
-
-static bool trace_destroyed_1_called = false;
-static bool trace_destroyed_2_called = false;
-static bool trace_destroyed_3_called = false;
-static bool trace_destroyed_4_called = false;
-static bool trace_destroyed_5_called = false;
-
-static
-void trace_class_destroyed_1(const bt_trace_class *tc, void *data)
-{
-       trace_class_destroyed_1_called = true;
-}
-
-static
-void trace_class_destroyed_2(const bt_trace_class *tc, void *data)
-{
-       bt_trace_class_remove_listener_status remove_listener_status;
-
-       trace_class_destroyed_2_called = true;
-
-       /* Remove self.  You shall not crash. */
-       remove_listener_status = bt_trace_class_remove_destruction_listener(
-               tc, trace_class_destroyed_2_id);
-       ok(remove_listener_status == BT_TRACE_CLASS_REMOVE_LISTENER_STATUS_OK,
-               "remove trace class listener 2 from 2");
-}
-
-static
-void trace_class_destroyed_3(const bt_trace_class *tc, void *data)
-{
-       bt_trace_class_remove_listener_status remove_listener_status;
-
-       trace_class_destroyed_3_called = true;
-
-       /* Remove an already called listener. */
-       remove_listener_status = bt_trace_class_remove_destruction_listener(
-               tc, trace_class_destroyed_1_id);
-       ok(remove_listener_status == BT_TRACE_CLASS_REMOVE_LISTENER_STATUS_OK,
-               "remove trace class listener 1 from 3");
-}
-
-static
-void trace_class_destroyed_4(const bt_trace_class *tc, void *data)
-{
-       bt_trace_class_remove_listener_status remove_listener_status;
-
-       trace_class_destroyed_4_called = true;
-
-       /* Remove a not yet called listener. */
-       remove_listener_status = bt_trace_class_remove_destruction_listener(
-               tc, trace_class_destroyed_5_id);
-       ok(remove_listener_status == BT_TRACE_CLASS_REMOVE_LISTENER_STATUS_OK,
-               "remove trace class listener 5 from 4");
-}
-
-static
-void trace_class_destroyed_5(const bt_trace_class *tc, void *data)
-{
-       trace_class_destroyed_5_called = true;
-}
-
-static
-void trace_destroyed_1(const bt_trace *t, void *data)
-{
-       trace_destroyed_1_called = true;
-}
-
-static
-void trace_destroyed_2(const bt_trace *t, void *data)
-{
-       bt_trace_remove_listener_status remove_listener_status;
-
-       trace_destroyed_2_called = true;
-
-       /* Remove self.  You shall not crash. */
-       remove_listener_status = bt_trace_remove_destruction_listener(
-               t, trace_destroyed_2_id);
-       ok(remove_listener_status == BT_TRACE_REMOVE_LISTENER_STATUS_OK,
-               "remove trace listener 2 from 2");
-}
-
-static
-void trace_destroyed_3(const bt_trace *t, void *data)
-{
-       bt_trace_remove_listener_status remove_listener_status;
-
-       trace_destroyed_3_called = true;
-
-       /* Remove an already called listener. */
-       remove_listener_status = bt_trace_remove_destruction_listener(
-               t, trace_destroyed_1_id);
-       ok(remove_listener_status == BT_TRACE_REMOVE_LISTENER_STATUS_OK,
-               "remove trace listener 1 from 3");
-}
-
-static
-void trace_destroyed_4(const bt_trace *t, void *data)
-{
-       bt_trace_remove_listener_status remove_listener_status;
-
-       trace_destroyed_4_called = true;
-
-       /* Remove a not yet called listener. */
-       remove_listener_status = bt_trace_remove_destruction_listener(
-               t, trace_destroyed_5_id);
-       ok(remove_listener_status == BT_TRACE_REMOVE_LISTENER_STATUS_OK,
-               "remove trace listener 5 from 4");
-}
-
-static
-void trace_destroyed_5(const bt_trace *t, void *data)
-{
-       trace_destroyed_5_called = true;
-}
-
-static
-bt_component_class_initialize_method_status hello_init(
-               bt_self_component_source *self_component,
-               bt_self_component_source_configuration *config,
-               const bt_value *params, void *init_method_data)
-{
-       bt_self_component *self_comp;
-       bt_trace_class *tc;
-       bt_trace *t;
-       bt_trace_class_add_listener_status trace_class_add_listener_status;
-       bt_trace_add_listener_status trace_add_listener_status;
-
-       self_comp = bt_self_component_source_as_self_component(self_component);
-       tc = bt_trace_class_create(self_comp);
-       BT_ASSERT(tc);
-
-       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
-               tc, trace_class_destroyed_1, NULL, &trace_class_destroyed_1_id);
-       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
-
-       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
-               tc, trace_class_destroyed_2, NULL, &trace_class_destroyed_2_id);
-       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
-
-       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
-               tc, trace_class_destroyed_3, NULL, &trace_class_destroyed_3_id);
-       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
-
-       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
-               tc, trace_class_destroyed_4, NULL, &trace_class_destroyed_4_id);
-       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
-
-       trace_class_add_listener_status = bt_trace_class_add_destruction_listener(
-               tc, trace_class_destroyed_5, NULL, &trace_class_destroyed_5_id);
-       BT_ASSERT(trace_class_add_listener_status == BT_TRACE_CLASS_ADD_LISTENER_STATUS_OK);
-
-       t = bt_trace_create(tc);
-       BT_ASSERT(t);
-
-       trace_add_listener_status = bt_trace_add_destruction_listener(
-               t, trace_destroyed_1, NULL, &trace_destroyed_1_id);
-       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
-
-       trace_add_listener_status = bt_trace_add_destruction_listener(
-               t, trace_destroyed_2, NULL, &trace_destroyed_2_id);
-       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
-
-       trace_add_listener_status = bt_trace_add_destruction_listener(
-               t, trace_destroyed_3, NULL, &trace_destroyed_3_id);
-       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
-
-       trace_add_listener_status = bt_trace_add_destruction_listener(
-               t, trace_destroyed_4, NULL, &trace_destroyed_4_id);
-       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
-
-       trace_add_listener_status = bt_trace_add_destruction_listener(
-               t, trace_destroyed_5, NULL, &trace_destroyed_5_id);
-       BT_ASSERT(trace_add_listener_status == BT_TRACE_ADD_LISTENER_STATUS_OK);
-
-       /* Destroy the trace. */
-       bt_trace_put_ref(t);
-
-       ok(trace_destroyed_1_called, "trace destruction listener 1 called");
-       ok(trace_destroyed_2_called, "trace destruction listener 2 called");
-       ok(trace_destroyed_3_called, "trace destruction listener 3 called");
-       ok(trace_destroyed_4_called, "trace destruction listener 4 called");
-       ok(!trace_destroyed_5_called, "trace destruction listener 5 not called");
-
-       /* Destroy the trace class. */
-       bt_trace_class_put_ref(tc);
-
-       ok(trace_class_destroyed_1_called, "trace class destruction listener 1 called");
-       ok(trace_class_destroyed_2_called, "trace class destruction listener 2 called");
-       ok(trace_class_destroyed_3_called, "trace class destruction listener 3 called");
-       ok(trace_class_destroyed_4_called, "trace class destruction listener 4 called");
-       ok(!trace_class_destroyed_5_called, "trace class destruction listener 5 not called");
-
-       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-}
-
-static
-bt_message_iterator_class_next_method_status hello_iter_next(
-               bt_self_message_iterator *message_iterator,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       BT_ASSERT(false);
-       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-}
-
-int main(int argc, char **argv)
-{
-       bt_graph *graph;
-       bt_message_iterator_class *msg_iter_cls;
-       bt_component_class_source *source_cc;
-       bt_component_class_set_method_status set_method_status;
-       bt_graph_add_component_status add_component_status;
-       const bt_component_source *source;
-
-       plan_tests(NR_TESTS);
-
-       msg_iter_cls = bt_message_iterator_class_create(hello_iter_next);
-       BT_ASSERT(msg_iter_cls);
-
-       source_cc = bt_component_class_source_create("Hello", msg_iter_cls);
-       BT_ASSERT(source_cc);
-
-       set_method_status = bt_component_class_source_set_initialize_method(
-               source_cc, hello_init);
-       BT_ASSERT(set_method_status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
-
-       graph = bt_graph_create(0);
-       BT_ASSERT(graph);
-
-       add_component_status = bt_graph_add_source_component(
-               graph, source_cc, "name", NULL,
-               BT_LOGGING_LEVEL_WARNING, &source);
-       BT_ASSERT(add_component_status == BT_GRAPH_ADD_COMPONENT_STATUS_OK);
-
-       bt_component_class_source_put_ref(source_cc);
-       bt_message_iterator_class_put_ref(msg_iter_cls);
-       bt_graph_put_ref(graph);
-
-       return exit_status();
-}
diff --git a/tests/lib/test_simple_sink.c b/tests/lib/test_simple_sink.c
deleted file mode 100644 (file)
index 952b6bf..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/assert.h"
-#include <string.h>
-#include "tap/tap.h"
-
-#define NR_TESTS 68
-
-struct test_data {
-       bt_graph_simple_sink_component_initialize_func_status init_status;
-       bt_graph_simple_sink_component_consume_func_status consume_status;
-};
-
-static
-bt_graph_simple_sink_component_initialize_func_status simple_INITIALIZE_func(
-               bt_message_iterator *iterator,
-               void *data)
-{
-       struct test_data *test_data = data;
-
-       ok(iterator, "Message iterator is not NULL in initialization function");
-       ok(data, "Data is not NULL in initialization function");
-       return test_data->init_status;
-}
-
-static
-bt_graph_simple_sink_component_consume_func_status simple_consume_func(
-               bt_message_iterator *iterator,
-               void *data)
-{
-       struct test_data *test_data = data;
-
-       ok(iterator, "Message iterator is not NULL in consume function");
-       ok(data, "Data is not NULL in consume function");
-       return test_data->consume_status;
-}
-
-static
-void simple_fini_func(void *data)
-{
-       ok(data, "Data is not NULL in finalization function");
-}
-
-static
-bt_component_class_initialize_method_status src_init(
-               bt_self_component_source *self_comp,
-               bt_self_component_source_configuration *config,
-               const bt_value *params, void *init_method_data)
-{
-       bt_self_component_add_port_status status;
-
-       status = bt_self_component_source_add_output_port(self_comp,
-               "out", NULL, NULL);
-       BT_ASSERT(status == BT_SELF_COMPONENT_ADD_PORT_STATUS_OK);
-       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-}
-
-static
-bt_message_iterator_class_next_method_status src_iter_next(
-               bt_self_message_iterator *message_iterator,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
-}
-
-static
-bt_graph *create_graph_with_source(const bt_port_output **out_port)
-{
-       bt_message_iterator_class *msg_iter_cls;
-       bt_component_class_source *src_comp_cls;
-       bt_graph *graph;
-       const bt_component_source *src_comp = NULL;
-       bt_graph_add_component_status add_comp_status;
-       bt_component_class_set_method_status set_method_status;
-
-       BT_ASSERT(out_port);
-
-       msg_iter_cls = bt_message_iterator_class_create(src_iter_next);
-       BT_ASSERT(msg_iter_cls);
-
-       src_comp_cls = bt_component_class_source_create("src", msg_iter_cls);
-       BT_ASSERT(src_comp_cls);
-       set_method_status = bt_component_class_source_set_initialize_method(
-               src_comp_cls, src_init);
-       BT_ASSERT(set_method_status == BT_COMPONENT_CLASS_SET_METHOD_STATUS_OK);
-       graph = bt_graph_create(0);
-       BT_ASSERT(graph);
-       add_comp_status = bt_graph_add_source_component(graph, src_comp_cls,
-               "src", NULL, BT_LOGGING_LEVEL_NONE, &src_comp);
-       BT_ASSERT(add_comp_status == BT_GRAPH_ADD_COMPONENT_STATUS_OK);
-       BT_ASSERT(src_comp);
-       *out_port = bt_component_source_borrow_output_port_by_index_const(
-               src_comp, 0);
-       BT_ASSERT(*out_port);
-       bt_component_class_source_put_ref(src_comp_cls);
-       bt_message_iterator_class_put_ref(msg_iter_cls);
-       return graph;
-}
-
-static
-void test_simple_expect_run_once_status(
-               bt_graph_simple_sink_component_initialize_func_status init_status,
-               bt_graph_simple_sink_component_consume_func_status consume_status,
-               bt_graph_run_once_status exp_run_once_status)
-{
-       const bt_port_output *src_out_port = NULL;
-       bt_graph *graph;
-       const bt_component_sink *sink_comp = NULL;
-       const bt_port_input *sink_in_port;
-       bt_graph_add_component_status add_comp_status;
-       bt_graph_run_once_status run_once_status;
-       bt_graph_connect_ports_status connect_status;
-       struct test_data test_data = {
-               .init_status = init_status,
-               .consume_status = consume_status,
-       };
-       const struct bt_error *err;
-
-       graph = create_graph_with_source(&src_out_port);
-       BT_ASSERT(graph);
-       BT_ASSERT(src_out_port);
-
-       add_comp_status = bt_graph_add_simple_sink_component(graph, "sink",
-               simple_INITIALIZE_func, simple_consume_func, simple_fini_func,
-               &test_data, &sink_comp);
-       BT_ASSERT(add_comp_status == BT_GRAPH_ADD_COMPONENT_STATUS_OK);
-       BT_ASSERT(sink_comp);
-
-       sink_in_port = bt_component_sink_borrow_input_port_by_name_const(
-               sink_comp, "in");
-       ok(sink_in_port,
-               "Simple sink component has an input port named \"in\"");
-
-       connect_status = bt_graph_connect_ports(graph, src_out_port,
-               sink_in_port, NULL);
-       ok(connect_status == BT_GRAPH_CONNECT_PORTS_STATUS_OK,
-               "Simple sink component's \"in\" port is connectable");
-
-       run_once_status = bt_graph_run_once(graph);
-       ok(run_once_status == exp_run_once_status,
-               "Graph \"run once\" status is the expected one (status code: %d)",
-               run_once_status);
-
-       err = bt_current_thread_take_error();
-       ok((run_once_status < 0) == (err != NULL),
-               "Current thread error is set if bt_graph_run_once returned an error");
-
-       bt_graph_put_ref(graph);
-       if (err) {
-               bt_error_release(err);
-       }
-}
-
-int main(void)
-{
-       plan_tests(NR_TESTS);
-
-       /* Test initialization function status */
-       test_simple_expect_run_once_status(
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK,
-               BT_GRAPH_RUN_ONCE_STATUS_OK);
-       test_simple_expect_run_once_status(
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_ERROR,
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK,
-               BT_GRAPH_RUN_ONCE_STATUS_ERROR);
-       test_simple_expect_run_once_status(
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_MEMORY_ERROR,
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK,
-               BT_GRAPH_RUN_ONCE_STATUS_MEMORY_ERROR);
-
-       /* Test "consume" function status */
-       test_simple_expect_run_once_status(
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK,
-               BT_GRAPH_RUN_ONCE_STATUS_OK);
-       test_simple_expect_run_once_status(
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_ERROR,
-               BT_GRAPH_RUN_ONCE_STATUS_ERROR);
-       test_simple_expect_run_once_status(
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_MEMORY_ERROR,
-               BT_GRAPH_RUN_ONCE_STATUS_MEMORY_ERROR);
-       test_simple_expect_run_once_status(
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_AGAIN,
-               BT_GRAPH_RUN_ONCE_STATUS_AGAIN);
-       test_simple_expect_run_once_status(
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_INITIALIZE_FUNC_STATUS_OK,
-               BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_END,
-               BT_GRAPH_RUN_ONCE_STATUS_END);
-
-       return exit_status();
-}
diff --git a/tests/lib/test_trace_ir_ref.c b/tests/lib/test_trace_ir_ref.c
deleted file mode 100644 (file)
index 70caf29..0000000
+++ /dev/null
@@ -1,583 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Trace IR Reference Count test
- */
-
-#include <stdio.h>
-#include "tap/tap.h"
-#include <babeltrace2/babeltrace.h>
-#include "lib/object.h"
-#include "compat/stdlib.h"
-#include "common/assert.h"
-#include <babeltrace2-ctf-writer/writer.h>
-#include <babeltrace2-ctf-writer/clock.h>
-#include <babeltrace2-ctf-writer/clock-class.h>
-#include <babeltrace2-ctf-writer/stream.h>
-#include <babeltrace2-ctf-writer/event.h>
-#include <babeltrace2-ctf-writer/event-types.h>
-#include <babeltrace2-ctf-writer/event-fields.h>
-#include <babeltrace2-ctf-writer/stream-class.h>
-#include <babeltrace2-ctf-writer/trace.h>
-#include "common.h"
-
-#define NR_TESTS 37
-
-struct bt_user {
-       bt_trace_class *tc;
-       bt_stream_class *sc;
-       bt_event_class *ec;
-       bt_stream *stream;
-       bt_event *event;
-};
-
-struct writer_user {
-       struct bt_ctf_writer *writer;
-       struct bt_ctf_trace *tc;
-       struct bt_ctf_stream_class *sc;
-       struct bt_ctf_event_class *ec;
-       struct bt_ctf_stream *stream;
-       struct bt_ctf_event *event;
-};
-
-const char *writer_user_names[] = {
-       "writer",
-       "trace",
-       "stream class",
-       "event class",
-       "stream",
-       "event",
-};
-
-static const size_t WRITER_USER_NR_ELEMENTS =
-       sizeof(struct writer_user) / sizeof(void *);
-
-/**
- * Returns a structure containing the following fields:
- *     - uint8_t payload_8;
- *     - uint16_t payload_16;
- *     - uint32_t payload_32;
- */
-static bt_field_class *create_integer_struct(bt_trace_class *trace_class)
-{
-       int ret;
-       bt_field_class *structure = NULL;
-       bt_field_class *ui8 = NULL, *ui16 = NULL, *ui32 = NULL;
-
-       structure = bt_field_class_structure_create(trace_class);
-       BT_ASSERT(structure);
-       ui8 = bt_field_class_integer_unsigned_create(trace_class);
-       BT_ASSERT(ui8);
-       bt_field_class_integer_set_field_value_range(ui8, 8);
-       ret = bt_field_class_structure_append_member(structure,
-               "payload_8", ui8);
-       BT_ASSERT(ret == 0);
-       ui16 = bt_field_class_integer_unsigned_create(trace_class);
-       BT_ASSERT(ui16);
-       bt_field_class_integer_set_field_value_range(ui16, 16);
-       ret = bt_field_class_structure_append_member(structure,
-               "payload_16", ui16);
-       BT_ASSERT(ret == 0);
-       ui32 = bt_field_class_integer_unsigned_create(trace_class);
-       BT_ASSERT(ui32);
-       bt_field_class_integer_set_field_value_range(ui32, 32);
-       ret = bt_field_class_structure_append_member(structure,
-               "payload_32", ui32);
-       BT_ASSERT(ret == 0);
-       BT_FIELD_CLASS_PUT_REF_AND_RESET(ui8);
-       BT_FIELD_CLASS_PUT_REF_AND_RESET(ui16);
-       BT_FIELD_CLASS_PUT_REF_AND_RESET(ui32);
-       return structure;
-}
-
-static struct bt_ctf_field_type *create_writer_integer_struct(void)
-{
-       int ret;
-       struct bt_ctf_field_type *structure = NULL;
-       struct bt_ctf_field_type *ui8 = NULL, *ui16 = NULL, *ui32 = NULL;
-
-       structure = bt_ctf_field_type_structure_create();
-       BT_ASSERT(structure);
-       ui8 = bt_ctf_field_type_integer_create(8);
-       BT_ASSERT(ui8);
-       ret = bt_ctf_field_type_structure_add_field(structure, ui8,
-                       "payload_8");
-       BT_ASSERT(ret == 0);
-       ui16 = bt_ctf_field_type_integer_create(16);
-       BT_ASSERT(ui16);
-       ret = bt_ctf_field_type_structure_add_field(structure, ui16,
-                       "payload_16");
-       BT_ASSERT(ret == 0);
-       ui32 = bt_ctf_field_type_integer_create(32);
-       BT_ASSERT(ui32);
-       ret = bt_ctf_field_type_structure_add_field(structure, ui32,
-                       "payload_32");
-       BT_ASSERT(ret == 0);
-       BT_OBJECT_PUT_REF_AND_RESET(ui8);
-       BT_OBJECT_PUT_REF_AND_RESET(ui16);
-       BT_OBJECT_PUT_REF_AND_RESET(ui32);
-       return structure;
-}
-
-/**
- * A simple event has the following payload:
- *     - uint8_t payload_8;
- *     - uint16_t payload_16;
- *     - uint32_t payload_32;
- */
-static bt_event_class *create_simple_event(
-               bt_stream_class *sc, const char *name)
-{
-       int ret;
-       bt_event_class *event = NULL;
-       bt_field_class *payload = NULL;
-
-       BT_ASSERT(name);
-       event = bt_event_class_create(sc);
-       BT_ASSERT(event);
-       ret = bt_event_class_set_name(event, name);
-       BT_ASSERT(ret == 0);
-       payload = create_integer_struct(bt_stream_class_borrow_trace_class(sc));
-       BT_ASSERT(payload);
-       ret = bt_event_class_set_payload_field_class(event, payload);
-       BT_ASSERT(ret == 0);
-       BT_FIELD_CLASS_PUT_REF_AND_RESET(payload);
-       return event;
-}
-
-/**
- * A complex event has the following payload:
- *     - uint8_t payload_8;
- *     - uint16_t payload_16;
- *     - uint32_t payload_32;
- *     - struct payload_struct:
- *           - uint8_t payload_8;
- *           - uint16_t payload_16;
- *           - uint32_t payload_32;
- */
-static bt_event_class *create_complex_event(bt_stream_class *sc,
-               const char *name)
-{
-       int ret;
-       bt_event_class *event = NULL;
-       bt_field_class *inner = NULL, *outer = NULL;
-       bt_trace_class *trace_class = bt_stream_class_borrow_trace_class(sc);
-
-       BT_ASSERT(name);
-       event = bt_event_class_create(sc);
-       BT_ASSERT(event);
-       ret = bt_event_class_set_name(event, name);
-       BT_ASSERT(ret == 0);
-       outer = create_integer_struct(trace_class);
-       BT_ASSERT(outer);
-       inner = create_integer_struct(trace_class);
-       BT_ASSERT(inner);
-       ret = bt_field_class_structure_append_member(outer,
-               "payload_struct", inner);
-       BT_ASSERT(ret == 0);
-       ret = bt_event_class_set_payload_field_class(event, outer);
-       BT_ASSERT(ret == 0);
-       BT_FIELD_CLASS_PUT_REF_AND_RESET(inner);
-       BT_FIELD_CLASS_PUT_REF_AND_RESET(outer);
-       return event;
-}
-
-static void create_sc1(bt_trace_class *trace_class)
-{
-       int ret;
-       bt_event_class *ec1 = NULL, *ec2 = NULL;
-       bt_stream_class *sc1 = NULL, *ret_stream = NULL;
-
-       sc1 = bt_stream_class_create(trace_class);
-       BT_ASSERT(sc1);
-       ret = bt_stream_class_set_name(sc1, "sc1");
-       BT_ASSERT(ret == 0);
-       ec1 = create_complex_event(sc1, "ec1");
-       BT_ASSERT(ec1);
-       ec2 = create_simple_event(sc1, "ec2");
-       BT_ASSERT(ec2);
-       ret_stream = bt_event_class_borrow_stream_class(ec1);
-       ok(ret_stream == sc1, "Borrow parent stream SC1 from EC1");
-       ret_stream = bt_event_class_borrow_stream_class(ec2);
-       ok(ret_stream == sc1, "Borrow parent stream SC1 from EC2");
-       BT_EVENT_CLASS_PUT_REF_AND_RESET(ec1);
-       BT_EVENT_CLASS_PUT_REF_AND_RESET(ec2);
-       BT_STREAM_CLASS_PUT_REF_AND_RESET(sc1);
-}
-
-static void create_sc2(bt_trace_class *trace_class)
-{
-       int ret;
-       bt_event_class *ec3 = NULL;
-       bt_stream_class *sc2 = NULL, *ret_stream = NULL;
-
-       sc2 = bt_stream_class_create(trace_class);
-       BT_ASSERT(sc2);
-       ret = bt_stream_class_set_name(sc2, "sc2");
-       BT_ASSERT(ret == 0);
-       ec3 = create_simple_event(sc2, "ec3");
-       ret_stream = bt_event_class_borrow_stream_class(ec3);
-       ok(ret_stream == sc2, "Borrow parent stream SC2 from EC3");
-       BT_EVENT_CLASS_PUT_REF_AND_RESET(ec3);
-       BT_STREAM_CLASS_PUT_REF_AND_RESET(sc2);
-}
-
-static bt_trace_class *create_tc1(bt_self_component_source *self_comp)
-{
-       bt_trace_class *tc1 = NULL;
-
-       tc1 = bt_trace_class_create(
-               bt_self_component_source_as_self_component(self_comp));
-       BT_ASSERT(tc1);
-       create_sc1(tc1);
-       create_sc2(tc1);
-       return tc1;
-}
-
-static void init_weak_refs(bt_trace_class *tc,
-               bt_trace_class **tc1,
-               bt_stream_class **sc1,
-               bt_stream_class **sc2,
-               bt_event_class **ec1,
-               bt_event_class **ec2,
-               bt_event_class **ec3)
-{
-       *tc1 = tc;
-       *sc1 = bt_trace_class_borrow_stream_class_by_index(tc, 0);
-       *sc2 = bt_trace_class_borrow_stream_class_by_index(tc, 1);
-       *ec1 = bt_stream_class_borrow_event_class_by_index(*sc1, 0);
-       *ec2 = bt_stream_class_borrow_event_class_by_index(*sc1, 1);
-       *ec3 = bt_stream_class_borrow_event_class_by_index(*sc2, 0);
-}
-
-static void test_example_scenario(bt_self_component_source *self_comp)
-{
-       /*
-        * Weak pointers to trace IR objects are to be used very
-        * carefully. This is NOT a good practice and is strongly
-        * discouraged; this is only done to facilitate the validation
-        * of expected reference counts without affecting them by taking
-        * "real" references to the objects.
-        */
-       bt_trace_class *tc1 = NULL, *weak_tc1 = NULL;
-       bt_stream_class *weak_sc1 = NULL, *weak_sc2 = NULL;
-       bt_event_class *weak_ec1 = NULL, *weak_ec2 = NULL,
-                       *weak_ec3 = NULL;
-       struct bt_user user_a = { 0 }, user_b = { 0 }, user_c = { 0 };
-
-       /* The only reference which exists at this point is on TC1. */
-       tc1 = create_tc1(self_comp);
-       ok(tc1, "Initialize trace");
-       BT_ASSERT(tc1);
-       init_weak_refs(tc1, &weak_tc1, &weak_sc1, &weak_sc2, &weak_ec1,
-                       &weak_ec2, &weak_ec3);
-       ok(bt_object_get_ref_count((void *) weak_sc1) == 0,
-                       "Initial SC1 reference count is 0");
-       ok(bt_object_get_ref_count((void *) weak_sc2) == 0,
-                       "Initial SC2 reference count is 0");
-       ok(bt_object_get_ref_count((void *) weak_ec1) == 0,
-                       "Initial EC1 reference count is 0");
-       ok(bt_object_get_ref_count((void *) weak_ec2) == 0,
-                       "Initial EC2 reference count is 0");
-       ok(bt_object_get_ref_count((void *) weak_ec3) == 0,
-                       "Initial EC3 reference count is 0");
-
-       /* User A has ownership of the trace. */
-       BT_OBJECT_MOVE_REF(user_a.tc, tc1);
-       ok(bt_object_get_ref_count((void *) user_a.tc) == 1,
-                       "TC1 reference count is 1");
-
-       /* User A acquires a reference to SC2 from TC1. */
-       user_a.sc = bt_trace_class_borrow_stream_class_by_index(
-                       user_a.tc, 1);
-       bt_stream_class_get_ref(user_a.sc);
-       ok(user_a.sc, "User A acquires SC2 from TC1");
-       ok(bt_object_get_ref_count((void *) weak_tc1) == 2,
-                       "TC1 reference count is 2");
-       ok(bt_object_get_ref_count((void *) weak_sc2) == 1,
-                       "SC2 reference count is 1");
-
-       /* User A acquires a reference to EC3 from SC2. */
-       user_a.ec = bt_stream_class_borrow_event_class_by_index(
-                       user_a.sc, 0);
-       bt_event_class_get_ref(user_a.ec);
-       ok(user_a.ec, "User A acquires EC3 from SC2");
-       ok(bt_object_get_ref_count((void *) weak_tc1) == 2,
-                       "TC1 reference count is 2");
-       ok(bt_object_get_ref_count((void *) weak_sc2) == 2,
-                       "SC2 reference count is 2");
-       ok(bt_object_get_ref_count((void *) weak_ec3) == 1,
-                       "EC3 reference count is 1");
-
-       /* User A releases its reference to SC2. */
-       diag("User A releases SC2");
-       BT_STREAM_CLASS_PUT_REF_AND_RESET(user_a.sc);
-       /*
-        * We keep the pointer to SC2 around to validate its reference
-        * count.
-        */
-       ok(bt_object_get_ref_count((void *) weak_tc1) == 2,
-                       "TC1 reference count is 2");
-       ok(bt_object_get_ref_count((void *) weak_sc2) == 1,
-                       "SC2 reference count is 1");
-       ok(bt_object_get_ref_count((void *) weak_ec3) == 1,
-                       "EC3 reference count is 1");
-
-       /* User A releases its reference to TC1. */
-       diag("User A releases TC1");
-       BT_TRACE_CLASS_PUT_REF_AND_RESET(user_a.tc);
-       /*
-        * We keep the pointer to TC1 around to validate its reference
-        * count.
-        */
-       ok(bt_object_get_ref_count((void *) weak_tc1) == 1,
-                       "TC1 reference count is 1");
-       ok(bt_object_get_ref_count((void *) weak_sc2) == 1,
-                       "SC2 reference count is 1");
-       ok(bt_object_get_ref_count((void *) weak_ec3) == 1,
-                       "EC3 reference count is 1");
-
-       /* User B acquires a reference to SC1. */
-       diag("User B acquires a reference to SC1");
-       user_b.sc = weak_sc1;
-       bt_stream_class_get_ref(user_b.sc);
-       ok(bt_object_get_ref_count((void *) weak_tc1) == 2,
-                       "TC1 reference count is 2");
-       ok(bt_object_get_ref_count((void *) weak_sc1) == 1,
-                       "SC1 reference count is 1");
-
-       /* User C acquires a reference to EC1. */
-       diag("User C acquires a reference to EC1");
-       user_c.ec = bt_stream_class_borrow_event_class_by_index(
-                       user_b.sc, 0);
-       bt_event_class_get_ref(user_c.ec);
-       ok(bt_object_get_ref_count((void *) weak_ec1) == 1,
-                       "EC1 reference count is 1");
-       ok(bt_object_get_ref_count((void *) weak_sc1) == 2,
-                       "SC1 reference count is 2");
-
-       /* User A releases its reference on EC3. */
-       diag("User A releases its reference on EC3");
-       BT_EVENT_CLASS_PUT_REF_AND_RESET(user_a.ec);
-       ok(bt_object_get_ref_count((void *) weak_ec3) == 0,
-                       "EC3 reference count is 1");
-       ok(bt_object_get_ref_count((void *) weak_sc2) == 0,
-                       "SC2 reference count is 0");
-       ok(bt_object_get_ref_count((void *) weak_tc1) == 1,
-                       "TC1 reference count is 1");
-
-       /* User B releases its reference on SC1. */
-       diag("User B releases its reference on SC1");
-       BT_STREAM_CLASS_PUT_REF_AND_RESET(user_b.sc);
-       ok(bt_object_get_ref_count((void *) weak_sc1) == 1,
-                       "SC1 reference count is 1");
-
-       /*
-        * User C is the sole owner of an object and is keeping the whole
-        * trace hierarchy "alive" by holding a reference to EC1.
-        */
-       ok(bt_object_get_ref_count((void *) weak_tc1) == 1,
-                       "TC1 reference count is 1");
-       ok(bt_object_get_ref_count((void *) weak_sc1) == 1,
-                       "SC1 reference count is 1");
-       ok(bt_object_get_ref_count((void *) weak_sc2) == 0,
-                       "SC2 reference count is 0");
-       ok(bt_object_get_ref_count((void *) weak_ec1) == 1,
-                       "EC1 reference count is 1");
-       ok(bt_object_get_ref_count((void *) weak_ec2) == 0,
-                       "EC2 reference count is 0");
-       ok(bt_object_get_ref_count((void *) weak_ec3) == 0,
-                       "EC3 reference count is 0");
-
-       /* Reclaim last reference held by User C. */
-       BT_EVENT_CLASS_PUT_REF_AND_RESET(user_c.ec);
-}
-
-static
-bt_component_class_initialize_method_status src_init(
-       bt_self_component_source *self_comp,
-       bt_self_component_source_configuration *config,
-       const bt_value *params, void *init_method_data)
-{
-       test_example_scenario(self_comp);
-       return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-}
-
-static
-bt_message_iterator_class_next_method_status src_iter_next(
-               bt_self_message_iterator *self_iterator,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-}
-
-static void test_example_scenario_in_graph(void)
-{
-       bt_message_iterator_class *msg_iter_cls;
-       bt_component_class_source *comp_cls;
-       bt_graph *graph;
-       int ret;
-
-       msg_iter_cls = bt_message_iterator_class_create(src_iter_next);
-       BT_ASSERT(msg_iter_cls);
-
-       comp_cls = bt_component_class_source_create("src", msg_iter_cls);
-       BT_ASSERT(comp_cls);
-       ret = bt_component_class_source_set_initialize_method(comp_cls, src_init);
-       BT_ASSERT(ret == 0);
-       graph = bt_graph_create(0);
-       ret = bt_graph_add_source_component(graph, comp_cls, "src-comp",
-               NULL, BT_LOGGING_LEVEL_NONE, NULL);
-       BT_ASSERT(ret == 0);
-       bt_graph_put_ref(graph);
-       bt_component_class_source_put_ref(comp_cls);
-       bt_message_iterator_class_put_ref(msg_iter_cls);
-}
-
-static void create_writer_user_full(struct writer_user *user)
-{
-       gchar *trace_path;
-       struct bt_ctf_field_type *ft;
-       struct bt_ctf_field *field;
-       struct bt_ctf_clock *clock;
-       int ret;
-
-       trace_path = g_build_filename(g_get_tmp_dir(), "ctfwriter_XXXXXX", NULL);
-       if (!bt_mkdtemp(trace_path)) {
-               perror("# perror");
-       }
-
-       user->writer = bt_ctf_writer_create(trace_path);
-       BT_ASSERT(user->writer);
-       ret = bt_ctf_writer_set_byte_order(user->writer,
-               BT_CTF_BYTE_ORDER_LITTLE_ENDIAN);
-       BT_ASSERT(ret == 0);
-       user->tc = bt_ctf_writer_get_trace(user->writer);
-       BT_ASSERT(user->tc);
-       user->sc = bt_ctf_stream_class_create("sc");
-       BT_ASSERT(user->sc);
-       clock = bt_ctf_clock_create("the_clock");
-       BT_ASSERT(clock);
-       ret = bt_ctf_writer_add_clock(user->writer, clock);
-       BT_ASSERT(!ret);
-       ret = bt_ctf_stream_class_set_clock(user->sc, clock);
-       BT_ASSERT(!ret);
-       BT_OBJECT_PUT_REF_AND_RESET(clock);
-       user->stream = bt_ctf_writer_create_stream(user->writer, user->sc);
-       BT_ASSERT(user->stream);
-       user->ec = bt_ctf_event_class_create("ec");
-       BT_ASSERT(user->ec);
-       ft = create_writer_integer_struct();
-       BT_ASSERT(ft);
-       ret = bt_ctf_event_class_set_payload_field_type(user->ec, ft);
-       BT_OBJECT_PUT_REF_AND_RESET(ft);
-       BT_ASSERT(!ret);
-       ret = bt_ctf_stream_class_add_event_class(user->sc, user->ec);
-       BT_ASSERT(!ret);
-       user->event = bt_ctf_event_create(user->ec);
-       BT_ASSERT(user->event);
-       field = bt_ctf_event_get_payload(user->event, "payload_8");
-       BT_ASSERT(field);
-       ret = bt_ctf_field_integer_unsigned_set_value(field, 10);
-       BT_ASSERT(!ret);
-       BT_OBJECT_PUT_REF_AND_RESET(field);
-       field = bt_ctf_event_get_payload(user->event, "payload_16");
-       BT_ASSERT(field);
-       ret = bt_ctf_field_integer_unsigned_set_value(field, 20);
-       BT_ASSERT(!ret);
-       BT_OBJECT_PUT_REF_AND_RESET(field);
-       field = bt_ctf_event_get_payload(user->event, "payload_32");
-       BT_ASSERT(field);
-       ret = bt_ctf_field_integer_unsigned_set_value(field, 30);
-       BT_ASSERT(!ret);
-       BT_OBJECT_PUT_REF_AND_RESET(field);
-       ret = bt_ctf_stream_append_event(user->stream, user->event);
-       BT_ASSERT(!ret);
-       recursive_rmdir(trace_path);
-       g_free(trace_path);
-}
-
-static void test_put_order_swap(size_t *array, size_t a, size_t b)
-{
-       size_t temp = array[a];
-
-       array[a] = array[b];
-       array[b] = temp;
-}
-
-static void test_put_order_put_objects(size_t *array, size_t size)
-{
-       size_t i;
-       struct writer_user user = { 0 };
-       void **objects = (void *) &user;
-
-       create_writer_user_full(&user);
-       printf("# ");
-
-       for (i = 0; i < size; ++i) {
-               void *obj = objects[array[i]];
-
-               printf("%s", writer_user_names[array[i]]);
-               BT_OBJECT_PUT_REF_AND_RESET(obj);
-
-               if (i < size - 1) {
-                       printf(" -> ");
-               }
-       }
-
-       puts("");
-}
-
-static void test_put_order_permute(size_t *array, int k, size_t size)
-{
-       if (k == 0) {
-               test_put_order_put_objects(array, size);
-       } else {
-               int i;
-
-               for (i = k - 1; i >= 0; i--) {
-                       size_t next_k = k - 1;
-
-                       test_put_order_swap(array, i, next_k);
-                       test_put_order_permute(array, next_k, size);
-                       test_put_order_swap(array, i, next_k);
-               }
-       }
-}
-
-static void test_put_order(void)
-{
-       size_t i;
-       size_t array[WRITER_USER_NR_ELEMENTS];
-
-       /* Initialize array of indexes */
-       for (i = 0; i < WRITER_USER_NR_ELEMENTS; ++i) {
-               array[i] = i;
-       }
-
-       test_put_order_permute(array, WRITER_USER_NR_ELEMENTS,
-               WRITER_USER_NR_ELEMENTS);
-}
-
-/**
- * The objective of this test is to implement and expand upon the scenario
- * described in the reference counting documentation and ensure that any node of
- * the Trace, Stream Class, Event Class, Stream and Event hiearchy keeps all
- * other "alive" and reachable.
- *
- * External tools (e.g. valgrind) should be used to confirm that this
- * known-good test does not leak memory.
- */
-int main(int argc, char **argv)
-{
-       /* Initialize tap harness before any tests */
-       plan_tests(NR_TESTS);
-
-       test_example_scenario_in_graph();
-       test_put_order();
-
-       return exit_status();
-}
diff --git a/tests/lib/utils/run-in.cpp b/tests/lib/utils/run-in.cpp
new file mode 100644 (file)
index 0000000..e31bbc5
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020-2023 EfficiOS, inc.
+ */
+
+#include "common/assert.h"
+#include "cpp-common/bt2/component-class-dev.hpp"
+#include "cpp-common/bt2/component-class.hpp"
+#include "cpp-common/bt2/graph.hpp"
+#include "cpp-common/bt2/plugin-load.hpp"
+#include "cpp-common/bt2/plugin.hpp"
+#include "cpp-common/bt2/query-executor.hpp"
+#include "cpp-common/bt2c/call.hpp"
+
+#include "run-in.hpp"
+
+void RunIn::onQuery(bt2::SelfComponentClass)
+{
+}
+
+void RunIn::onCompInit(bt2::SelfComponent)
+{
+}
+
+void RunIn::onMsgIterInit(bt2::SelfMessageIterator)
+{
+}
+
+void RunIn::onMsgIterNext(bt2::SelfMessageIterator, bt2::ConstMessageArray&)
+{
+}
+
+namespace {
+
+class RunInSource;
+
+class RunInSourceMsgIter final : public bt2::UserMessageIterator<RunInSourceMsgIter, RunInSource>
+{
+public:
+    explicit RunInSourceMsgIter(const bt2::SelfMessageIterator self,
+                                bt2::SelfMessageIteratorConfiguration,
+                                const bt2::SelfComponentOutputPort port) :
+        bt2::UserMessageIterator<RunInSourceMsgIter, RunInSource> {self, "RUN-IN-SRC-MSG-ITER"},
+        _mRunIn {&port.data<RunIn>()}, _mSelf {self}
+    {
+        _mRunIn->onMsgIterInit(self);
+    }
+
+    void _next(bt2::ConstMessageArray& msgs)
+    {
+        _mRunIn->onMsgIterNext(_mSelf, msgs);
+    }
+
+private:
+    RunIn *_mRunIn;
+    bt2::SelfMessageIterator _mSelf;
+};
+
+class RunInSource final :
+    public bt2::UserSourceComponent<RunInSource, RunInSourceMsgIter, RunIn, RunIn>
+{
+public:
+    static constexpr auto name = "run-in-src";
+
+    explicit RunInSource(const bt2::SelfSourceComponent self, bt2::ConstMapValue,
+                         RunIn * const runIn) :
+        bt2::UserSourceComponent<RunInSource, RunInSourceMsgIter, RunIn, RunIn> {self,
+                                                                                 "RUN-IN-SRC"},
+        _mRunIn {runIn}
+    {
+        this->_addOutputPort("out", *runIn);
+        _mRunIn->onCompInit(self);
+    }
+
+    static bt2::Value::Shared _query(const bt2::SelfComponentClass self, bt2::PrivateQueryExecutor,
+                                     bt2c::CStringView, bt2::ConstValue, RunIn *data)
+    {
+        data->onQuery(self);
+        return bt2::NullValue {}.shared();
+    }
+
+private:
+    RunIn *_mRunIn;
+};
+
+} /* namespace */
+
+void runIn(RunIn& runIn)
+{
+    const auto srcCompCls = bt2::SourceComponentClass::create<RunInSource>();
+
+    /* Execute a query */
+    bt2::QueryExecutor::create(*srcCompCls, "object-name", runIn)->query();
+
+    /* Create graph */
+    const auto graph = bt2::Graph::create(0);
+
+    /* Add custom source component (executes `compCtxFunc`) */
+    const auto srcComp = graph->addComponent(*srcCompCls, "the-source", runIn);
+
+    /* Add dummy sink component */
+    const auto sinkComp = bt2c::call([&] {
+        const auto utilsPlugin = bt2::findPlugin("utils");
+
+        BT_ASSERT(utilsPlugin);
+
+        const auto dummySinkCompCls = utilsPlugin->sinkComponentClasses()["dummy"];
+
+        BT_ASSERT(dummySinkCompCls);
+
+        return graph->addComponent(*dummySinkCompCls, "the-sink");
+    });
+
+    /* Connect ports */
+    const auto outPort = srcComp.outputPorts()["out"];
+    BT_ASSERT(outPort);
+
+    const auto inPort = sinkComp.inputPorts()["in"];
+    BT_ASSERT(inPort);
+
+    graph->connectPorts(*outPort, *inPort);
+
+    /* Run graph (executes `msgIterCtxFunc`) */
+    graph->run();
+}
diff --git a/tests/lib/utils/run-in.hpp b/tests/lib/utils/run-in.hpp
new file mode 100644 (file)
index 0000000..54e8ec2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020-2023 EfficiOS, inc.
+ */
+
+#ifndef TESTS_LIB_UTILS_RUN_IN_HPP
+#define TESTS_LIB_UTILS_RUN_IN_HPP
+
+#include <babeltrace2/babeltrace.h>
+
+#include "cpp-common/bt2/self-component-class.hpp"
+#include "cpp-common/bt2/self-component-port.hpp"
+#include "cpp-common/bt2/self-message-iterator.hpp"
+
+/*
+ * Base class from which to inherit to call runIn().
+ *
+ * Override any of the on*() methods to get your statements executed in
+ * a specific context.
+ */
+class RunIn
+{
+public:
+    virtual ~RunIn() = default;
+
+    /*
+     * Called when querying the component class `self`.
+     */
+    virtual void onQuery(bt2::SelfComponentClass self);
+
+    /*
+     * Called when initializing the component `self`.
+     */
+    virtual void onCompInit(bt2::SelfComponent self);
+
+    /*
+     * Called when initializing the message iterator `self`.
+     */
+    virtual void onMsgIterInit(bt2::SelfMessageIterator self);
+
+    /*
+     * Called within the "next" method of `self` to return the messages
+     * `msgs`.
+     */
+    virtual void onMsgIterNext(bt2::SelfMessageIterator self, bt2::ConstMessageArray& msgs);
+};
+
+/*
+ * Runs a simple graph (one source and one sink component), calling the
+ * `on*()` methods of `runIn` along the way.
+ */
+void runIn(RunIn& runIn);
+
+#endif /* TESTS_LIB_UTILS_RUN_IN_HPP */
index e33f986fc22af709d9b090885e6ede28490788f7..14cf3d63006726988cda803bc71aadecea06ae33 100644 (file)
@@ -2,12 +2,12 @@
 
 AM_CPPFLAGS += -I$(top_srcdir)/tests/utils
 
-noinst_PROGRAMS = test_param_validation
-test_param_validation_SOURCES = test_param_validation.c
+noinst_PROGRAMS = test-param-validation
+test_param_validation_SOURCES = test-param-validation.c
 test_param_validation_LDADD = \
-       $(top_builddir)/src/param-parse/libbabeltrace2-param-parse.la \
-       $(top_builddir)/src/plugins/common/param-validation/libbabeltrace2-param-validation.la \
+       $(top_builddir)/src/param-parse/libparam-parse.la \
+       $(top_builddir)/src/plugins/common/param-validation/libparam-validation.la \
        $(top_builddir)/src/lib/libbabeltrace2.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/logging/liblogging.la \
        $(top_builddir)/tests/utils/tap/libtap.la
diff --git a/tests/param-validation/test-param-validation.c b/tests/param-validation/test-param-validation.c
new file mode 100644 (file)
index 0000000..5607d32
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) EfficiOS Inc.
+ */
+
+#include "tap/tap.h"
+#include "param-parse/param-parse.h"
+#include "plugins/common/param-validation/param-validation.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static
+enum bt_param_validation_status run_test(
+               const char *params_str,
+               const struct bt_param_validation_map_value_entry_descr *entries,
+               const char *test_name,
+               const char *expected_error)
+{
+       GString *err = g_string_new(NULL);
+       const bt_value *params;
+       enum bt_param_validation_status status;
+       gchar *validate_error = NULL;
+
+       if (!err) {
+               fprintf(stderr, "Failed to allocated a GString.\n");
+               abort();
+       }
+
+       params = bt_param_parse(params_str, err);
+
+       if (!params) {
+               fprintf(stderr, "Could not parse params: `%s`, %s\n",
+                       params_str, err->str);
+               abort();
+       }
+
+       status = bt_param_validation_validate(params, entries, &validate_error);
+
+       if (expected_error) {
+               /* We expect a failure. */
+               ok(status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR,
+                       "%s: validation fails", test_name);
+               ok(validate_error, "%s: error string is not NULL", test_name);
+
+#define BT_FMT "%s: error string contains expected string"
+               if (validate_error && strstr(validate_error, expected_error)) {
+                       pass(BT_FMT, test_name);
+               } else {
+                       fail(BT_FMT, test_name);
+                       diag("could not find `%s` in `%s`", expected_error, validate_error);
+               }
+#undef BT_FMT
+
+               g_free(validate_error);
+       } else {
+               /* We expect a success. */
+               ok(status == BT_PARAM_VALIDATION_STATUS_OK, "%s: validation succeeds", test_name);
+               ok(!validate_error, "%s: error string is NULL", test_name);
+       }
+
+       bt_value_put_ref(params);
+       g_string_free(err, TRUE);
+
+       return status;
+}
+
+static
+void test_map_valid(void)
+{
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
+               { "fenouil", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_STRING } },
+               { "panais", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type =  BT_VALUE_TYPE_BOOL } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("carotte=2,fenouil=\"miam\"", entries, "valid map", NULL);
+}
+
+static
+void test_map_missing_key(void)
+{
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
+               { "tomate", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("carotte=2", entries, "missing key in map",
+               "Error validating parameters: missing mandatory entry `tomate`");
+}
+
+static
+void test_map_unexpected_key(void)
+{
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("tomate=2", entries, "unexpected key in map", "unexpected key `tomate`");
+}
+
+static
+void test_map_invalid_entry_value_type(void)
+{
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carottes", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("carottes=\"orange\"", entries, "map entry with unexpected type",
+               "Error validating parameter `carottes`: unexpected type: expected-type=SIGNED_INTEGER, actual-type=STRING");
+}
+
+static
+void test_nested_error(void)
+{
+       const struct bt_param_validation_map_value_entry_descr poireau_entries[] = {
+               { "navet", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END,
+       };
+
+       const struct bt_param_validation_map_value_entry_descr carottes_elem_entries[] = {
+               { "poireau", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_MAP, .map = {
+                       .entries = poireau_entries,
+               } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END,
+       };
+
+       const struct bt_param_validation_value_descr carottes_elem = {
+               .type = BT_VALUE_TYPE_MAP,
+               .map = {
+                       .entries = carottes_elem_entries,
+               }
+       };
+
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carottes", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
+                       .min_length = 0,
+                       .max_length = BT_PARAM_VALIDATION_INFINITE,
+                       .element_type = &carottes_elem,
+               } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("carottes=[{poireau={navet=7}}, {poireau={}}]", entries, "error nested in maps and arrays",
+               "Error validating parameter `carottes[1].poireau`: missing mandatory entry `navet`");
+}
+
+static
+void test_array_valid(void)
+{
+       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
+
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
+                       .min_length = 2,
+                       .max_length = 22,
+                       .element_type = &carotte_elem,
+               } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("carotte=[true, false, true]", entries, "valid array", NULL);
+}
+
+static
+void test_array_empty_valid(void)
+{
+       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
+
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
+                       .min_length = 0,
+                       .max_length = 2,
+                       .element_type = &carotte_elem,
+               } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("carotte=[]", entries, "valid empty array", NULL);
+}
+
+static
+void test_array_invalid_too_small(void)
+{
+       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
+
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
+                       .min_length = 1,
+                       .max_length = 100,
+                       .element_type = &carotte_elem,
+               } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("carotte=[]", entries, "array too small",
+               "Error validating parameter `carotte`: array is smaller than the minimum length: array-length=0, min-length=1");
+}
+
+static
+void test_array_invalid_too_large(void)
+{
+       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
+
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
+                       .min_length = 2,
+                       .max_length = 2,
+                       .element_type = &carotte_elem,
+               } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("carotte=[true, false, false]", entries, "array too large",
+               "Error validating parameter `carotte`: array is larger than the maximum length: array-length=3, max-length=2");
+}
+
+static
+void test_array_invalid_elem_type(void)
+{
+       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
+
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
+                       .min_length = 3,
+                       .max_length = 3,
+                       .element_type = &carotte_elem,
+               } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("carotte=[true, false, 2]", entries, "array with invalid element type",
+               "Error validating parameter `carotte[2]`: unexpected type: expected-type=BOOL, actual-type=SIGNED_INTEGER");
+}
+
+static
+void test_string_valid_without_choices(void)
+{
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "haricot", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_STRING, { } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("haricot=\"vert\"", entries, "valid string without choices", NULL);
+}
+
+static
+void test_string_valid_with_choices(void)
+{
+       const char *haricot_choices[] = {"vert", "jaune", "rouge", NULL};
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "haricot", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_STRING, .string = {
+                       .choices = haricot_choices,
+               } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("haricot=\"jaune\"", entries, "valid string with choices", NULL);
+}
+
+static
+void test_string_invalid_choice(void)
+{
+       const char *haricot_choices[] = {"vert", "jaune", "rouge", NULL};
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "haricot", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_STRING, .string = {
+                       .choices = haricot_choices,
+               } } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("haricot=\"violet\"", entries, "string with invalid choice",
+               "Error validating parameter `haricot`: string is not amongst the available choices: string=violet, choices=[vert, jaune, rouge]");
+}
+
+static
+enum bt_param_validation_status custom_validation_func_valid(
+               const bt_value *value,
+               struct bt_param_validation_context *context __attribute__((unused)))
+{
+       ok(bt_value_get_type(value) == BT_VALUE_TYPE_UNSIGNED_INTEGER,
+               "type of value passed to custom function is as expected");
+       ok(bt_value_integer_unsigned_get(value) == 1234,
+               "value passed to custom function is as expected");
+       return BT_PARAM_VALIDATION_STATUS_OK;
+}
+
+static
+void test_custom_validation_func_valid(void)
+{
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "navet", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, {
+                       .validation_func = custom_validation_func_valid,
+               } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("navet=+1234", entries, "custom validation function with valid value", NULL);
+}
+
+static
+enum bt_param_validation_status custom_validation_func_invalid(
+               const bt_value *value __attribute__((unused)),
+               struct bt_param_validation_context *context)
+{
+       return bt_param_validation_error(context, "wrooooong");
+}
+
+static
+void test_custom_validation_func_invalid(void)
+{
+       const struct bt_param_validation_map_value_entry_descr entries[] = {
+               { "navet", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, {
+                       .validation_func = custom_validation_func_invalid,
+               } },
+               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+       };
+
+       run_test("navet=+1234", entries, "custom validation function with invalid value",
+               "Error validating parameter `navet`: wrooooong");
+}
+
+int main(void)
+{
+       plan_tests(41);
+
+       test_map_valid();
+
+       test_map_missing_key();
+       test_map_unexpected_key();
+       test_map_invalid_entry_value_type();
+
+       test_array_valid();
+       test_array_empty_valid();
+
+       test_array_invalid_too_small();
+       test_array_invalid_too_large();
+       test_array_invalid_elem_type();
+
+       test_string_valid_without_choices();
+       test_string_valid_with_choices();
+
+       test_string_invalid_choice();
+
+       test_custom_validation_func_valid();
+       test_custom_validation_func_invalid();
+
+       test_nested_error();
+
+       return exit_status();
+}
diff --git a/tests/param-validation/test_param_validation.c b/tests/param-validation/test_param_validation.c
deleted file mode 100644 (file)
index fa14010..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) EfficiOS Inc.
- */
-
-#include "tap/tap.h"
-#include "param-parse/param-parse.h"
-#include "plugins/common/param-validation/param-validation.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-static
-enum bt_param_validation_status run_test(
-               const char *params_str,
-               const struct bt_param_validation_map_value_entry_descr *entries,
-               const char *test_name,
-               const char *expected_error)
-{
-       GString *err = g_string_new(NULL);
-       const bt_value *params;
-       enum bt_param_validation_status status;
-       gchar *validate_error = NULL;
-
-       if (!err) {
-               fprintf(stderr, "Failed to allocated a GString.\n");
-               abort();
-       }
-
-       params = bt_param_parse(params_str, err);
-
-       if (!params) {
-               fprintf(stderr, "Could not parse params: `%s`, %s\n",
-                       params_str, err->str);
-               abort();
-       }
-
-       status = bt_param_validation_validate(params, entries, &validate_error);
-
-       if (expected_error) {
-               /* We expect a failure. */
-               ok(status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR,
-                       "%s: validation fails", test_name);
-               ok(validate_error, "%s: error string is not NULL", test_name);
-
-#define BT_FMT "%s: error string contains expected string"
-               if (validate_error && strstr(validate_error, expected_error)) {
-                       pass(BT_FMT, test_name);
-               } else {
-                       fail(BT_FMT, test_name);
-                       diag("could not find `%s` in `%s`", expected_error, validate_error);
-               }
-#undef BT_FMT
-
-               g_free(validate_error);
-       } else {
-               /* We expect a success. */
-               ok(status == BT_PARAM_VALIDATION_STATUS_OK, "%s: validation succeeds", test_name);
-               ok(!validate_error, "%s: error string is NULL", test_name);
-       }
-
-       bt_value_put_ref(params);
-       g_string_free(err, TRUE);
-
-       return status;
-}
-
-static
-void test_map_valid(void)
-{
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
-               { "fenouil", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_STRING } },
-               { "panais", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type =  BT_VALUE_TYPE_BOOL } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("carotte=2,fenouil=\"miam\"", entries, "valid map", NULL);
-}
-
-static
-void test_map_missing_key(void)
-{
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
-               { "tomate", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("carotte=2", entries, "missing key in map",
-               "Error validating parameters: missing mandatory entry `tomate`");
-}
-
-static
-void test_map_unexpected_key(void)
-{
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("tomate=2", entries, "unexpected key in map", "unexpected key `tomate`");
-}
-
-static
-void test_map_invalid_entry_value_type(void)
-{
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carottes", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("carottes=\"orange\"", entries, "map entry with unexpected type",
-               "Error validating parameter `carottes`: unexpected type: expected-type=SIGNED_INTEGER, actual-type=STRING");
-}
-
-static
-void test_nested_error(void)
-{
-       const struct bt_param_validation_map_value_entry_descr poireau_entries[] = {
-               { "navet", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END,
-       };
-
-       const struct bt_param_validation_map_value_entry_descr carottes_elem_entries[] = {
-               { "poireau", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_MAP, .map = {
-                       .entries = poireau_entries,
-               } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END,
-       };
-
-       const struct bt_param_validation_value_descr carottes_elem = {
-               .type = BT_VALUE_TYPE_MAP,
-               .map = {
-                       .entries = carottes_elem_entries,
-               }
-       };
-
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carottes", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
-                       .min_length = 0,
-                       .max_length = BT_PARAM_VALIDATION_INFINITE,
-                       .element_type = &carottes_elem,
-               } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("carottes=[{poireau={navet=7}}, {poireau={}}]", entries, "error nested in maps and arrays",
-               "Error validating parameter `carottes[1].poireau`: missing mandatory entry `navet`");
-}
-
-static
-void test_array_valid(void)
-{
-       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
-
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
-                       .min_length = 2,
-                       .max_length = 22,
-                       .element_type = &carotte_elem,
-               } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("carotte=[true, false, true]", entries, "valid array", NULL);
-}
-
-static
-void test_array_empty_valid(void)
-{
-       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
-
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
-                       .min_length = 0,
-                       .max_length = 2,
-                       .element_type = &carotte_elem,
-               } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("carotte=[]", entries, "valid empty array", NULL);
-}
-
-static
-void test_array_invalid_too_small(void)
-{
-       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
-
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
-                       .min_length = 1,
-                       .max_length = 100,
-                       .element_type = &carotte_elem,
-               } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("carotte=[]", entries, "array too small",
-               "Error validating parameter `carotte`: array is smaller than the minimum length: array-length=0, min-length=1");
-}
-
-static
-void test_array_invalid_too_large(void)
-{
-       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
-
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
-                       .min_length = 2,
-                       .max_length = 2,
-                       .element_type = &carotte_elem,
-               } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("carotte=[true, false, false]", entries, "array too large",
-               "Error validating parameter `carotte`: array is larger than the maximum length: array-length=3, max-length=2");
-}
-
-static
-void test_array_invalid_elem_type(void)
-{
-       const struct bt_param_validation_value_descr carotte_elem = { .type = BT_VALUE_TYPE_BOOL, {} };
-
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "carotte", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
-                       .min_length = 3,
-                       .max_length = 3,
-                       .element_type = &carotte_elem,
-               } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("carotte=[true, false, 2]", entries, "array with invalid element type",
-               "Error validating parameter `carotte[2]`: unexpected type: expected-type=BOOL, actual-type=SIGNED_INTEGER");
-}
-
-static
-void test_string_valid_without_choices(void)
-{
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "haricot", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_STRING, { } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("haricot=\"vert\"", entries, "valid string without choices", NULL);
-}
-
-static
-void test_string_valid_with_choices(void)
-{
-       const char *haricot_choices[] = {"vert", "jaune", "rouge", NULL};
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "haricot", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_STRING, .string = {
-                       .choices = haricot_choices,
-               } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("haricot=\"jaune\"", entries, "valid string with choices", NULL);
-}
-
-static
-void test_string_invalid_choice(void)
-{
-       const char *haricot_choices[] = {"vert", "jaune", "rouge", NULL};
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "haricot", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_STRING, .string = {
-                       .choices = haricot_choices,
-               } } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("haricot=\"violet\"", entries, "string with invalid choice",
-               "Error validating parameter `haricot`: string is not amongst the available choices: string=violet, choices=[vert, jaune, rouge]");
-}
-
-static
-enum bt_param_validation_status custom_validation_func_valid(
-               const bt_value *value,
-               struct bt_param_validation_context *context)
-{
-       ok(bt_value_get_type(value) == BT_VALUE_TYPE_UNSIGNED_INTEGER,
-               "type of value passed to custom function is as expected");
-       ok(bt_value_integer_unsigned_get(value) == 1234,
-               "value passed to custom function is as expected");
-       return BT_PARAM_VALIDATION_STATUS_OK;
-}
-
-static
-void test_custom_validation_func_valid(void)
-{
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "navet", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, {
-                       .validation_func = custom_validation_func_valid,
-               } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("navet=+1234", entries, "custom validation function with valid value", NULL);
-}
-
-static
-enum bt_param_validation_status custom_validation_func_invalid(
-               const bt_value *value,
-               struct bt_param_validation_context *context)
-{
-       return bt_param_validation_error(context, "wrooooong");
-}
-
-static
-void test_custom_validation_func_invalid(void)
-{
-       const struct bt_param_validation_map_value_entry_descr entries[] = {
-               { "navet", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, {
-                       .validation_func = custom_validation_func_invalid,
-               } },
-               BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-       };
-
-       run_test("navet=+1234", entries, "custom validation function with invalid value",
-               "Error validating parameter `navet`: wrooooong");
-}
-
-int main(void)
-{
-       plan_tests(41);
-
-       test_map_valid();
-
-       test_map_missing_key();
-       test_map_unexpected_key();
-       test_map_invalid_entry_value_type();
-
-       test_array_valid();
-       test_array_empty_valid();
-
-       test_array_invalid_too_small();
-       test_array_invalid_too_large();
-       test_array_invalid_elem_type();
-
-       test_string_valid_without_choices();
-       test_string_valid_with_choices();
-
-       test_string_invalid_choice();
-
-       test_custom_validation_func_valid();
-       test_custom_validation_func_invalid();
-
-       test_nested_error();
-
-       return exit_status();
-}
index 1a1cefc7b18171df799960ccf46b2ed6edd12eed..1bd548ddc80f8c211e4a1f8b2dc90cc55641ec83 100644 (file)
@@ -5,15 +5,15 @@ AM_CPPFLAGS += -I$(top_srcdir)/tests/utils -I$(top_srcdir)/src/plugins
 LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la
 
 dist_check_SCRIPTS = \
-       test_bin_info_i386-linux-gnu \
-       test_bin_info_powerpc64le-linux-gnu \
-       test_bin_info_powerpc-linux-gnu \
-       test_bin_info_x86_64-linux-gnu \
-       test_dwarf_i386-linux-gnu \
-       test_dwarf_powerpc64le-linux-gnu \
-       test_dwarf_powerpc-linux-gnu \
-       test_dwarf_x86_64-linux-gnu \
-       test_succeed
+       test-bin-info-i386-linux-gnu.sh \
+       test-bin-info-powerpc64le-linux-gnu.sh \
+       test-bin-info-powerpc-linux-gnu.sh \
+       test-bin-info-x86-64-linux-gnu.sh \
+       test-dwarf-i386-linux-gnu.sh \
+       test-dwarf-powerpc64le-linux-gnu.sh \
+       test-dwarf-powerpc-linux-gnu.sh \
+       test-dwarf-x86-64-linux-gnu.sh \
+       test-succeed.sh
 
 noinst_PROGRAMS =
 
@@ -22,24 +22,26 @@ if !ENABLE_BUILT_IN_PLUGINS
 endif # !ENABLE_BUILT_IN_PLUGINS
 
 if ENABLE_DEBUG_INFO
-noinst_PROGRAMS += test_dwarf test_bin_info
+noinst_PROGRAMS += test-dwarf test-bin-info
 
 test_dwarf_LDADD = \
        $(top_builddir)/src/plugins/lttng-utils/debug-info/libdebug-info.la \
-       $(top_builddir)/src/fd-cache/libbabeltrace2-fd-cache.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
+       $(top_builddir)/src/fd-cache/libfd-cache.la \
+       $(top_builddir)/src/logging/liblogging.la \
+       $(top_builddir)/src/common/libcommon.la \
        $(ELFUTILS_LIBS) \
        $(LIBTAP)
-test_dwarf_SOURCES = test_dwarf.c
+test_dwarf_SOURCES = test-dwarf.c
 
 test_bin_info_LDADD = \
        $(top_builddir)/src/plugins/lttng-utils/debug-info/libdebug-info.la \
-       $(top_builddir)/src/fd-cache/libbabeltrace2-fd-cache.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
+       $(top_builddir)/src/fd-cache/libfd-cache.la \
+       $(top_builddir)/src/logging/liblogging.la \
+       $(top_builddir)/src/common/libcommon.la \
        $(top_builddir)/src/lib/libbabeltrace2.la \
        $(ELFUTILS_LIBS) \
        $(LIBTAP)
-test_bin_info_SOURCES = test_bin_info.c
+test_bin_info_SOURCES = test-bin-info.c
+nodist_EXTRA_test_bin_info_SOURCES = dummy.cpp
+
 endif # ENABLE_DEBUG_INFO
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-bin-info-i386-linux-gnu.sh b/tests/plugins/flt.lttng-utils.debug-info/test-bin-info-i386-linux-gnu.sh
new file mode 100755 (executable)
index 0000000..3411c12
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/flt.lttng-utils.debug-info"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/i386-linux-gnu"
+
+"$this_dir_build/test-bin-info" \
+       --foo-addr=0x1c8d \
+       --printf-offset=0xda \
+       --printf-lineno=36 \
+       --tp-offset=0x12 \
+       --tp-lineno=35 \
+       --debug-link-crc=0xdeead493 \
+       --debug-info-dir "$debug_info_data" \
+       --build-id cdd98cdd87f7fe64c13b6daad553987eafd40cbb
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-bin-info-powerpc-linux-gnu.sh b/tests/plugins/flt.lttng-utils.debug-info/test-bin-info-powerpc-linux-gnu.sh
new file mode 100755 (executable)
index 0000000..d1716af
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/flt.lttng-utils.debug-info"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/powerpc-linux-gnu"
+
+"$this_dir_build/test-bin-info" \
+       --foo-addr=0x23bc \
+       --printf-offset=0x114 \
+       --printf-lineno=36 \
+       --tp-offset=0x28 \
+       --tp-lineno=35 \
+       --debug-link-crc=0xd7b98958 \
+       --debug-info-dir "$debug_info_data" \
+       --build-id cdd98cdd87f7fe64c13b6daad553987eafd40cbb
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-bin-info-powerpc64le-linux-gnu.sh b/tests/plugins/flt.lttng-utils.debug-info/test-bin-info-powerpc64le-linux-gnu.sh
new file mode 100755 (executable)
index 0000000..f118b5f
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/flt.lttng-utils.debug-info"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/powerpc64le-linux-gnu"
+
+"$this_dir_build/test-bin-info" \
+       --foo-addr=0x2e7c \
+       --printf-offset=0x190 \
+       --printf-lineno=36 \
+       --tp-offset=0x1c \
+       --tp-lineno=35 \
+       --debug-link-crc=0x9b8eb2ff \
+       --debug-info-dir "$debug_info_data" \
+       --build-id cdd98cdd87f7fe64c13b6daad553987eafd40cbb
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-bin-info-x86-64-linux-gnu.sh b/tests/plugins/flt.lttng-utils.debug-info/test-bin-info-x86-64-linux-gnu.sh
new file mode 100755 (executable)
index 0000000..d3f35b8
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/flt.lttng-utils.debug-info"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/x86-64-linux-gnu"
+
+"$this_dir_build/test-bin-info" \
+       --foo-addr=0x2277 \
+       --printf-offset=0xf0 \
+       --printf-lineno=36 \
+       --tp-offset=0x89 \
+       --tp-lineno=35 \
+       --debug-link-crc=0x289a8fdc \
+       --debug-info-dir "$debug_info_data" \
+       --build-id cdd98cdd87f7fe64c13b6daad553987eafd40cbb
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-bin-info.c b/tests/plugins/flt.lttng-utils.debug-info/test-bin-info.c
new file mode 100644 (file)
index 0000000..3e6e7a6
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc. and Linux Foundation
+ * Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+ * Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+ *
+ * Babeltrace SO info tests
+ */
+
+#define BT_LOG_OUTPUT_LEVEL ((bt_logging_level) BT_LOG_WARNING)
+#define BT_LOG_TAG "TEST/BIN-INFO"
+#include "logging/log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <glib.h>
+
+#include "common/macros.h"
+#include "common/assert.h"
+#include <lttng-utils/debug-info/bin-info.h>
+
+#include "tap/tap.h"
+
+#define NR_TESTS 57
+
+#define SO_NAME "libhello-so"
+#define DEBUG_NAME "libhello-so.debug"
+#define FUNC_FOO_FILENAME "./libhello.c"
+#define FUNC_FOO_PRINTF_NAME_FMT "foo+0x%" PRIx64
+#define FUNC_FOO_NAME_LEN 64
+
+#define DWARF_DIR_NAME "dwarf-full"
+#define ELF_DIR_NAME "elf-only"
+#define BUILDID_DIR_NAME "build-id"
+#define DEBUGLINK_DIR_NAME "debug-link"
+
+/* Lower bound of PIC address mapping */
+#define SO_LOW_ADDR 0x400000
+/* Size of PIC address mapping */
+#define SO_MEMSZ 0x800000
+/* An address outside the PIC mapping */
+#define SO_INV_ADDR 0x200000
+
+#define BUILD_ID_HEX_LEN 20
+
+static uint64_t opt_func_foo_addr;
+static uint64_t opt_func_foo_printf_offset;
+static uint64_t opt_func_foo_printf_line_no;
+static uint64_t opt_func_foo_tp_offset;
+static uint64_t opt_func_foo_tp_line_no;
+static uint64_t opt_debug_link_crc;
+static gchar *opt_build_id;
+static gchar *opt_debug_info_dir;
+
+static uint64_t func_foo_printf_addr;
+static uint64_t func_foo_tp_addr;
+static char func_foo_printf_name[FUNC_FOO_NAME_LEN];
+static uint8_t build_id[BUILD_ID_HEX_LEN];
+
+static GOptionEntry entries[] = {
+       {"foo-addr", 0, 0, G_OPTION_ARG_INT64, &opt_func_foo_addr,
+        "Offset to printf in foo", "0xX"},
+       {"printf-offset", 0, 0, G_OPTION_ARG_INT64, &opt_func_foo_printf_offset,
+        "Offset to printf in foo", "0xX"},
+       {"printf-lineno", 0, 0, G_OPTION_ARG_INT64,
+        &opt_func_foo_printf_line_no, "Line number to printf in foo", "N"},
+       {"tp-offset", 0, 0, G_OPTION_ARG_INT64, &opt_func_foo_tp_offset,
+        "Offset to tp in foo", "0xX"},
+       {"tp-lineno", 0, 0, G_OPTION_ARG_INT64, &opt_func_foo_tp_line_no,
+        "Line number to tp in foo", "N"},
+       {"debug-link-crc", 0, 0, G_OPTION_ARG_INT64, &opt_debug_link_crc,
+        "Debug link CRC", "0xX"},
+       {"build-id", 0, 0, G_OPTION_ARG_STRING, &opt_build_id, "Build ID",
+        "XXXXXXXXXXXXXXX"},
+       {"debug-info-dir", 0, 0, G_OPTION_ARG_STRING, &opt_debug_info_dir,
+        "Debug info directory", NULL},
+       {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}};
+
+static
+int build_id_to_bin(void)
+{
+       int ret, len, i;
+
+       if (!opt_build_id) {
+               goto error;
+       }
+
+       len = strnlen(opt_build_id, BUILD_ID_HEX_LEN * 2);
+       if (len != (BUILD_ID_HEX_LEN * 2)) {
+               goto error;
+       }
+
+       for (i = 0; i < (len / 2); i++) {
+               ret = sscanf(opt_build_id + 2 * i, "%02hhx", &build_id[i]);
+               if (ret != 1) {
+                       goto error;
+               }
+       }
+
+       if (i != BUILD_ID_HEX_LEN) {
+               goto error;
+       }
+
+       return 0;
+error:
+       return -1;
+}
+
+static
+void subtest_has_address(struct bin_info *bin, uint64_t addr)
+{
+       int ret;
+
+       ret = bin_info_has_address(bin, SO_LOW_ADDR - 1);
+       ok(ret == 0, "bin_info_has_address - address under SO's range");
+
+       ret = bin_info_has_address(bin, SO_LOW_ADDR);
+       ok(ret == 1, "bin_info_has_address - lower bound of SO's range");
+
+       ret = bin_info_has_address(bin, addr);
+       ok(ret == 1, "bin_info_has_address - address in SO's range");
+
+       ret = bin_info_has_address(bin, SO_LOW_ADDR + SO_MEMSZ - 1);
+       ok(ret == 1, "bin_info_has_address - upper bound of SO's range");
+
+       ret = bin_info_has_address(bin, SO_LOW_ADDR + SO_MEMSZ);
+       ok(ret == 0, "bin_info_has_address - address above SO's range");
+}
+
+static
+void subtest_lookup_function_name(struct bin_info *bin, uint64_t addr,
+                                        char *func_name)
+{
+       int ret;
+       char *_func_name = NULL;
+
+       ret = bin_info_lookup_function_name(bin, addr, &_func_name);
+       ok(ret == 0, "bin_info_lookup_function_name successful at 0x%" PRIx64, addr);
+       if (_func_name) {
+               ok(strcmp(_func_name, func_name) == 0,
+                  "bin_info_lookup_function_name - correct function name (%s == %s)",
+                  func_name, _func_name);
+               free(_func_name);
+               _func_name = NULL;
+       } else {
+               skip(1,
+                    "bin_info_lookup_function_name - function name is NULL");
+       }
+
+       /* Test function name lookup - erroneous address */
+       ret = bin_info_lookup_function_name(bin, SO_INV_ADDR, &_func_name);
+       ok(ret == -1 && !_func_name,
+          "bin_info_lookup_function_name - fail on invalid addr");
+       free(_func_name);
+}
+
+static
+void subtest_lookup_source_location(struct bin_info *bin, uint64_t addr,
+                                          uint64_t line_no, const char *filename)
+{
+       int ret;
+       struct source_location *src_loc = NULL;
+
+       ret = bin_info_lookup_source_location(bin, addr, &src_loc);
+       ok(ret == 0, "bin_info_lookup_source_location successful at 0x%" PRIx64,
+          addr);
+       if (src_loc) {
+               ok(src_loc->line_no == line_no,
+                  "bin_info_lookup_source_location - correct line_no (%" PRIu64 " == %" PRIu64 ")",
+                  line_no, src_loc->line_no);
+               ok(strcmp(src_loc->filename, filename) == 0,
+                  "bin_info_lookup_source_location - correct filename (%s == %s)",
+                  filename, src_loc->filename);
+               source_location_destroy(src_loc);
+               src_loc = NULL;
+       } else {
+               fail("bin_info_lookup_source_location - src_loc is NULL");
+               fail("bin_info_lookup_source_location - src_loc is NULL");
+       }
+
+       /* Test source location lookup - erroneous address */
+       ret = bin_info_lookup_source_location(bin, SO_INV_ADDR, &src_loc);
+       ok(ret == -1 && !src_loc,
+          "bin_info_lookup_source_location - fail on invalid addr");
+       if (src_loc) {
+               source_location_destroy(src_loc);
+       }
+}
+
+static
+void test_bin_info_build_id(const char *bin_info_dir)
+{
+       int ret;
+       char *data_dir, *bin_path;
+       struct bin_info *bin = NULL;
+       struct bt_fd_cache fdc;
+       uint8_t invalid_build_id[BUILD_ID_HEX_LEN] = {
+               0xa3, 0xfd, 0x8b, 0xff, 0x45, 0xe1, 0xa9, 0x32, 0x15, 0xdd,
+               0x6d, 0xaa, 0xd5, 0x53, 0x98, 0x7e, 0xaf, 0xd4, 0x0c, 0xbb
+       };
+
+       diag("bin-info tests - separate DWARF via build ID");
+
+       data_dir = g_build_filename(bin_info_dir, BUILDID_DIR_NAME, NULL);
+       bin_path =
+               g_build_filename(bin_info_dir, BUILDID_DIR_NAME, SO_NAME, NULL);
+
+       if (!data_dir || !bin_path) {
+               exit(EXIT_FAILURE);
+       }
+
+       ret = bt_fd_cache_init(&fdc, BT_LOG_OUTPUT_LEVEL);
+       if (ret != 0) {
+               diag("Failed to initialize FD cache");
+               exit(EXIT_FAILURE);
+       }
+
+       bin = bin_info_create(&fdc, bin_path, SO_LOW_ADDR, SO_MEMSZ, true,
+                             data_dir, NULL, BT_LOG_OUTPUT_LEVEL, NULL);
+       ok(bin, "bin_info_create successful (%s)", bin_path);
+
+       /* Test setting invalid build_id */
+       ret = bin_info_set_build_id(bin, invalid_build_id, BUILD_ID_HEX_LEN);
+       ok(ret == -1, "bin_info_set_build_id fail on invalid build_id");
+
+       /* Test setting correct build_id */
+       ret = bin_info_set_build_id(bin, build_id, BUILD_ID_HEX_LEN);
+       ok(ret == 0, "bin_info_set_build_id successful");
+
+       /* Test bin_info_has_address */
+       subtest_has_address(bin, func_foo_printf_addr);
+
+       /* Test function name lookup (with DWARF) */
+       subtest_lookup_function_name(bin, func_foo_printf_addr,
+                                    func_foo_printf_name);
+
+       /* Test source location lookup */
+       subtest_lookup_source_location(bin, func_foo_printf_addr,
+                                      opt_func_foo_printf_line_no,
+                                      FUNC_FOO_FILENAME);
+
+       bin_info_destroy(bin);
+       bt_fd_cache_fini(&fdc);
+       g_free(data_dir);
+       g_free(bin_path);
+}
+
+static
+void test_bin_info_debug_link(const char *bin_info_dir)
+{
+       int ret;
+       char *data_dir, *bin_path;
+       struct bin_info *bin = NULL;
+       struct bt_fd_cache fdc;
+
+       diag("bin-info tests - separate DWARF via debug link");
+
+       data_dir = g_build_filename(bin_info_dir, DEBUGLINK_DIR_NAME, NULL);
+       bin_path = g_build_filename(bin_info_dir, DEBUGLINK_DIR_NAME, SO_NAME,
+                                   NULL);
+
+       if (!data_dir || !bin_path) {
+               exit(EXIT_FAILURE);
+       }
+
+       ret = bt_fd_cache_init(&fdc, BT_LOG_OUTPUT_LEVEL);
+       if (ret != 0) {
+               diag("Failed to initialize FD cache");
+               exit(EXIT_FAILURE);
+       }
+
+       bin = bin_info_create(&fdc, bin_path, SO_LOW_ADDR, SO_MEMSZ, true,
+               data_dir, NULL, BT_LOG_OUTPUT_LEVEL, NULL);
+       ok(bin, "bin_info_create successful (%s)", bin_path);
+
+       /* Test setting debug link */
+       ret = bin_info_set_debug_link(bin, DEBUG_NAME, opt_debug_link_crc);
+       ok(ret == 0, "bin_info_set_debug_link successful");
+
+       /* Test bin_info_has_address */
+       subtest_has_address(bin, func_foo_printf_addr);
+
+       /* Test function name lookup (with DWARF) */
+       subtest_lookup_function_name(bin, func_foo_printf_addr,
+                                    func_foo_printf_name);
+
+       /* Test source location lookup */
+       subtest_lookup_source_location(bin, func_foo_printf_addr,
+                                      opt_func_foo_printf_line_no,
+                                      FUNC_FOO_FILENAME);
+
+       bin_info_destroy(bin);
+       bt_fd_cache_fini(&fdc);
+       g_free(data_dir);
+       g_free(bin_path);
+}
+
+static
+void test_bin_info_elf(const char *bin_info_dir)
+{
+       int ret;
+       char *data_dir, *bin_path;
+       struct bin_info *bin = NULL;
+       struct source_location *src_loc = NULL;
+       struct bt_fd_cache fdc;
+
+       diag("bin-info tests - ELF only");
+
+       data_dir = g_build_filename(bin_info_dir, ELF_DIR_NAME, NULL);
+       bin_path = g_build_filename(bin_info_dir, ELF_DIR_NAME, SO_NAME, NULL);
+
+       if (!data_dir || !bin_path) {
+               exit(EXIT_FAILURE);
+       }
+
+       ret = bt_fd_cache_init(&fdc, BT_LOG_OUTPUT_LEVEL);
+       if (ret != 0) {
+               diag("Failed to initialize FD cache");
+               exit(EXIT_FAILURE);
+       }
+
+       bin = bin_info_create(&fdc, bin_path, SO_LOW_ADDR, SO_MEMSZ, true,
+               data_dir, NULL, BT_LOG_OUTPUT_LEVEL, NULL);
+       ok(bin, "bin_info_create successful (%s)", bin_path);
+
+       /* Test bin_info_has_address */
+       subtest_has_address(bin, func_foo_printf_addr);
+
+       /* Test function name lookup (with ELF) */
+       subtest_lookup_function_name(bin, func_foo_printf_addr,
+                                    func_foo_printf_name);
+
+       /* Test source location location - should fail on ELF only file  */
+       ret = bin_info_lookup_source_location(bin, func_foo_printf_addr,
+                                             &src_loc);
+       ok(ret == -1,
+          "bin_info_lookup_source_location - fail on ELF only file");
+
+       source_location_destroy(src_loc);
+       bin_info_destroy(bin);
+       bt_fd_cache_fini(&fdc);
+       g_free(data_dir);
+       g_free(bin_path);
+}
+
+static
+void test_bin_info_bundled(const char *bin_info_dir)
+{
+       int ret;
+       char *data_dir, *bin_path;
+       struct bin_info *bin = NULL;
+       struct bt_fd_cache fdc;
+
+       diag("bin-info tests - DWARF bundled in SO file");
+
+       data_dir = g_build_filename(bin_info_dir, DWARF_DIR_NAME, NULL);
+       bin_path =
+               g_build_filename(bin_info_dir, DWARF_DIR_NAME, SO_NAME, NULL);
+
+       if (!data_dir || !bin_path) {
+               exit(EXIT_FAILURE);
+       }
+
+       ret = bt_fd_cache_init(&fdc, BT_LOG_OUTPUT_LEVEL);
+       if (ret != 0) {
+               diag("Failed to initialize FD cache");
+               exit(EXIT_FAILURE);
+       }
+
+       bin = bin_info_create(&fdc, bin_path, SO_LOW_ADDR, SO_MEMSZ, true,
+               data_dir, NULL, BT_LOG_OUTPUT_LEVEL, NULL);
+       ok(bin, "bin_info_create successful (%s)", bin_path);
+
+       /* Test bin_info_has_address */
+       subtest_has_address(bin, func_foo_printf_addr);
+
+       /* Test function name lookup (with DWARF) */
+       subtest_lookup_function_name(bin, func_foo_printf_addr,
+                                    func_foo_printf_name);
+
+       /* Test source location lookup */
+       subtest_lookup_source_location(bin, func_foo_printf_addr,
+                                      opt_func_foo_printf_line_no,
+                                      FUNC_FOO_FILENAME);
+
+       /* Test source location lookup - inlined function */
+       subtest_lookup_source_location(bin, func_foo_tp_addr,
+                                      opt_func_foo_tp_line_no,
+                                      FUNC_FOO_FILENAME);
+
+       bin_info_destroy(bin);
+       bt_fd_cache_fini(&fdc);
+       g_free(data_dir);
+       g_free(bin_path);
+}
+
+int main(int argc, char **argv)
+{
+       int ret;
+       GError *error = NULL;
+       GOptionContext *context;
+       int status;
+
+       plan_tests(NR_TESTS);
+
+       context = g_option_context_new("- bin info test");
+       g_option_context_add_main_entries(context, entries, NULL);
+       if (!g_option_context_parse(context, &argc, &argv, &error)) {
+               fprintf(stderr, "option parsing failed: %s\n", error->message);
+               status = EXIT_FAILURE;
+               goto end;
+       }
+
+       g_snprintf(func_foo_printf_name, FUNC_FOO_NAME_LEN,
+                  FUNC_FOO_PRINTF_NAME_FMT, opt_func_foo_printf_offset);
+       func_foo_printf_addr =
+               SO_LOW_ADDR + opt_func_foo_addr + opt_func_foo_printf_offset;
+       func_foo_tp_addr =
+               SO_LOW_ADDR + opt_func_foo_addr + opt_func_foo_tp_offset;
+
+       if (build_id_to_bin()) {
+               fprintf(stderr, "Failed to parse / missing build id\n");
+               status = EXIT_FAILURE;
+               goto end;
+       }
+
+       ret = bin_info_init(BT_LOG_OUTPUT_LEVEL, NULL);
+       ok(ret == 0, "bin_info_init successful");
+
+       test_bin_info_elf(opt_debug_info_dir);
+       test_bin_info_bundled(opt_debug_info_dir);
+       test_bin_info_build_id(opt_debug_info_dir);
+       test_bin_info_debug_link(opt_debug_info_dir);
+
+       status = exit_status();
+
+end:
+       g_option_context_free(context);
+
+       return status;
+}
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-dwarf-i386-linux-gnu.sh b/tests/plugins/flt.lttng-utils.debug-info/test-dwarf-i386-linux-gnu.sh
new file mode 100755 (executable)
index 0000000..fed378e
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/flt.lttng-utils.debug-info"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/i386-linux-gnu"
+
+"$this_dir_build/test-dwarf" \
+       "$debug_info_data"
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-dwarf-powerpc-linux-gnu.sh b/tests/plugins/flt.lttng-utils.debug-info/test-dwarf-powerpc-linux-gnu.sh
new file mode 100755 (executable)
index 0000000..f2bbc20
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/flt.lttng-utils.debug-info"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/powerpc-linux-gnu"
+
+"$this_dir_build/test-dwarf" \
+       "$debug_info_data"
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-dwarf-powerpc64le-linux-gnu.sh b/tests/plugins/flt.lttng-utils.debug-info/test-dwarf-powerpc64le-linux-gnu.sh
new file mode 100755 (executable)
index 0000000..6bb2872
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/flt.lttng-utils.debug-info"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/powerpc64le-linux-gnu"
+
+"$this_dir_build/test-dwarf" \
+       "$debug_info_data"
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-dwarf-x86-64-linux-gnu.sh b/tests/plugins/flt.lttng-utils.debug-info/test-dwarf-x86-64-linux-gnu.sh
new file mode 100755 (executable)
index 0000000..c030695
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/flt.lttng-utils.debug-info"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/x86-64-linux-gnu"
+
+"$this_dir_build/test-dwarf" \
+       "$debug_info_data/"
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-dwarf.c b/tests/plugins/flt.lttng-utils.debug-info/test-dwarf.c
new file mode 100644 (file)
index 0000000..b74eabf
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2015 EfficiOS Inc. and Linux Foundation
+ * Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+ *
+ * Babeltrace bt_dwarf (DWARF utilities) tests
+ */
+
+#include <fcntl.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <lttng-utils/debug-info/dwarf.h>
+#include "tap/tap.h"
+
+#define NR_TESTS 17
+
+#define SO_NAME "libhello-so"
+#define DWARF_DIR_NAME "dwarf-full"
+#define ELF_DIR_NAME "elf-only"
+
+/*
+ * Test that we fail on an ELF file without DWARF.
+ */
+static
+void test_bt_no_dwarf(const char *data_dir)
+{
+       int fd;
+       char *path;
+       Dwarf *dwarf_info = NULL;
+
+       path = g_build_filename(data_dir, ELF_DIR_NAME, SO_NAME, NULL);
+       if (!path) {
+               diag("Failed to allocate memory for path");
+               exit(EXIT_FAILURE);
+       }
+
+       fd = open(path, O_RDONLY);
+       ok(fd >= 0, "Open ELF file %s", path);
+       if (fd < 0) {
+               skip(1, "dwarf_begin failed as expected");
+       } else {
+               dwarf_info = dwarf_begin(fd, DWARF_C_READ);
+               ok(!dwarf_info, "dwarf_begin failed as expected");
+       }
+
+       if (dwarf_info) {
+               dwarf_end(dwarf_info);
+       }
+
+       if (fd >= 0) {
+               close(fd);
+       }
+       g_free(path);
+}
+
+/*
+ * Test with a proper ELF file with DWARF.
+ */
+static
+void test_bt_dwarf(const char *data_dir)
+{
+       int fd, ret, tag;
+       char *path;
+       char *die_name = NULL;
+       struct bt_dwarf_cu *cu = NULL;
+       struct bt_dwarf_die *die = NULL;
+       Dwarf *dwarf_info = NULL;
+
+       path = g_build_filename(data_dir, DWARF_DIR_NAME, SO_NAME, NULL);
+       if (!path) {
+               diag("Failed to allocate memory for path");
+               exit(EXIT_FAILURE);
+       }
+
+       fd = open(path, O_RDONLY);
+       ok(fd >= 0, "Open DWARF file %s", path);
+       if (fd < 0) {
+               exit(EXIT_FAILURE);
+       }
+       dwarf_info = dwarf_begin(fd, DWARF_C_READ);
+       ok(dwarf_info, "dwarf_begin successful");
+       cu = bt_dwarf_cu_create(dwarf_info);
+       ok(cu, "bt_dwarf_cu_create successful");
+       ret = bt_dwarf_cu_next(cu);
+       ok(ret == 0, "bt_dwarf_cu_next successful");
+       die = bt_dwarf_die_create(cu);
+       ok(die, "bt_dwarf_die_create successful");
+       if (!die) {
+               exit(EXIT_FAILURE);
+       }
+       /*
+        * Test bt_dwarf_die_next twice, as the code path is different
+        * for DIEs at depth 0 (just created) and other depths.
+        */
+       ret = bt_dwarf_die_next(die);
+       ok(ret == 0, "bt_dwarf_die_next from root DIE successful");
+       ok(die->depth == 1,
+               "bt_dwarf_die_next from root DIE - correct depth value");
+       ret = bt_dwarf_die_next(die);
+       ok(ret == 0,
+               "bt_dwarf_die_next from non-root DIE successful");
+       ok(die->depth == 1,
+               "bt_dwarf_die_next from non-root DIE - correct depth value");
+
+       /* Reset DIE to test dwarf_child */
+       bt_dwarf_die_destroy(die);
+       die = bt_dwarf_die_create(cu);
+       if (!die) {
+               diag("Failed to create bt_dwarf_die");
+               exit(EXIT_FAILURE);
+       }
+
+       ret = bt_dwarf_die_child(die);
+       ok(ret == 0, "bt_dwarf_die_child successful");
+       ok(die->depth == 1, "bt_dwarf_die_child - correct depth value");
+
+       ret = bt_dwarf_die_get_tag(die, &tag);
+       ok(ret == 0, "bt_dwarf_die_get_tag successful");
+       ok(tag == DW_TAG_typedef, "bt_dwarf_die_get_tag - correct tag value");
+       ret = bt_dwarf_die_get_name(die, &die_name);
+       ok(ret == 0, "bt_dwarf_die_get_name successful");
+       ok(strcmp(die_name, "size_t") == 0,
+               "bt_dwarf_die_get_name - correct name value");
+
+       bt_dwarf_die_destroy(die);
+       bt_dwarf_cu_destroy(cu);
+       dwarf_end(dwarf_info);
+       free(die_name);
+       close(fd);
+       g_free(path);
+}
+
+int main(int argc, char **argv)
+{
+       const char *data_dir;
+
+       plan_tests(NR_TESTS);
+
+       if (argc != 2) {
+               return EXIT_FAILURE;
+       } else {
+               data_dir = argv[1];
+       }
+
+       test_bt_no_dwarf(data_dir);
+       test_bt_dwarf(data_dir);
+
+       return exit_status();
+}
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test-succeed.sh b/tests/plugins/flt.lttng-utils.debug-info/test-succeed.sh
new file mode 100755 (executable)
index 0000000..430bab3
--- /dev/null
@@ -0,0 +1,113 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
+# Copyright (C) 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
+#
+
+# This test validates that a `src.ctf.fs` component successfully reads
+# specific CTF traces and creates the expected messages.
+#
+# Such CTF traces to open either exist (in `tests/ctf-traces/succeed`)
+# or are generated by this test using local trace generators.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/flt.lttng-utils.debug-info"
+succeed_trace_dir="$BT_CTF_TRACES_PATH/succeed"
+expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
+binary_artefact_dir="$BT_TESTS_DATADIR/$this_dir_relative"
+data_dir="$BT_TESTS_DATADIR/$this_dir_relative"
+
+test_debug_info() {
+       local name="$1"
+       local local_args=(
+               "-c" "flt.lttng-utils.debug-info"
+               "-p" "target-prefix=\"$binary_artefact_dir/x86-64-linux-gnu/dwarf-full\""
+               "-c" "sink.text.details"
+               "-p" "with-trace-name=no,with-stream-name=no"
+       )
+
+       bt_diff_cli "$expect_dir/trace-$name.expect" "/dev/null" \
+               "$succeed_trace_dir/$name" "${local_args[@]}"
+       ok $? "Trace '$name' gives the expected output"
+}
+
+test_compare_to_ctf_fs() {
+       # Compare the `sink.text.details` output of a graph with and without a
+       # `flt.lttng-utils.debug-info` component. Both should be identical for
+       # traces without LTTng debugging fields.
+       local test_name=$1
+       shift 1
+       local cli_args=("$@")
+       local debug_info_cli_args=("-c" "flt.lttng-utils.debug-info")
+       local details_cli_args=(
+               "-c" "sink.text.details"
+               "--params" "with-trace-name=false,with-stream-name=false,with-uuid=false"
+       )
+       local actual_stdout
+       local actual_stderr
+       local expected_stdout
+       local expected_stderr
+       local ret=0
+
+       actual_stdout=$(mktemp -t test-debug-info-stdout-actual.XXXXXX)
+       actual_stderr=$(mktemp -t test-debug-info-stderr-actual.XXXXXX)
+       expected_stdout=$(mktemp -t test-debug-info-stdout-expected.XXXXXX)
+       expected_stderr=$(mktemp -t test-debug-info-stderr-expected.XXXXXX)
+
+       # Create expected files using a graph without a `debug-info` component.
+       bt_cli "$expected_stdout" "$expected_stderr" "${cli_args[@]}" \
+               "${details_cli_args[@]}"
+
+       # Read the same trace with a `debug-info` component in the graph.
+       bt_cli "$actual_stdout" "$actual_stderr" "${cli_args[@]}" \
+               "${details_cli_args[@]}" "${debug_info_cli_args[@]}"
+
+       bt_diff "$expected_stdout" "$actual_stdout"
+       ok $? "Input '$test_name' gives the expected stdout"
+
+       bt_diff "$expected_stderr" "$actual_stderr"
+       ok $? "Input '$test_name' gives the expected stderr"
+
+       rm -f "$actual_stdout"
+       rm -f "$actual_stderr"
+       rm -f "$expected_stdout"
+       rm -f "$expected_stderr"
+}
+
+test_compare_ctf_src_trace() {
+       local trace_name=$1
+       local trace_path="$succeed_trace_dir/$trace_name"
+       local cli_args=("$trace_path")
+
+       diag "Comparing output with and without 'flt.lttng-utils.debug-info' on '$trace_name'"
+       test_compare_to_ctf_fs "src.ctf.fs with $trace_name trace" "${cli_args[@]}"
+}
+
+test_compare_complete_src_trace() {
+
+       local source_name="src.test-debug-info.CompleteSrc"
+       local cli_args=("--plugin-path=$data_dir" "-c" "$source_name")
+       test_compare_to_ctf_fs "$source_name" "${cli_args[@]}"
+}
+
+plan_tests 9
+
+test_debug_info debug-info
+
+test_compare_ctf_src_trace smalltrace
+test_compare_ctf_src_trace 2packets
+test_compare_ctf_src_trace session-rotation
+
+test_compare_complete_src_trace
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_bin_info.c b/tests/plugins/flt.lttng-utils.debug-info/test_bin_info.c
deleted file mode 100644 (file)
index a398fae..0000000
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2015 EfficiOS Inc. and Linux Foundation
- * Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
- * Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
- *
- * Babeltrace SO info tests
- */
-
-#define BT_LOG_OUTPUT_LEVEL BT_LOG_WARNING
-#define BT_LOG_TAG "TEST/BIN-INFO"
-#include "logging/log.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-#include <glib.h>
-
-#include "common/macros.h"
-#include "common/assert.h"
-#include <lttng-utils/debug-info/bin-info.h>
-
-#include "tap/tap.h"
-
-#define NR_TESTS 57
-
-#define SO_NAME "libhello_so"
-#define DEBUG_NAME "libhello_so.debug"
-#define FUNC_FOO_FILENAME "./libhello.c"
-#define FUNC_FOO_PRINTF_NAME_FMT "foo+0x%" PRIx64
-#define FUNC_FOO_NAME_LEN 64
-
-#define DWARF_DIR_NAME "dwarf_full"
-#define ELF_DIR_NAME "elf_only"
-#define BUILDID_DIR_NAME "build_id"
-#define DEBUGLINK_DIR_NAME "debug_link"
-
-/* Lower bound of PIC address mapping */
-#define SO_LOW_ADDR 0x400000
-/* Size of PIC address mapping */
-#define SO_MEMSZ 0x800000
-/* An address outside the PIC mapping */
-#define SO_INV_ADDR 0x200000
-
-#define BUILD_ID_HEX_LEN 20
-
-static uint64_t opt_func_foo_addr;
-static uint64_t opt_func_foo_printf_offset;
-static uint64_t opt_func_foo_printf_line_no;
-static uint64_t opt_func_foo_tp_offset;
-static uint64_t opt_func_foo_tp_line_no;
-static uint64_t opt_debug_link_crc;
-static gchar *opt_build_id;
-static gchar *opt_debug_info_dir;
-
-static uint64_t func_foo_printf_addr;
-static uint64_t func_foo_tp_addr;
-static char func_foo_printf_name[FUNC_FOO_NAME_LEN];
-static uint8_t build_id[BUILD_ID_HEX_LEN];
-
-static GOptionEntry entries[] = {
-       {"foo-addr", 0, 0, G_OPTION_ARG_INT64, &opt_func_foo_addr,
-        "Offset to printf in foo", "0xX"},
-       {"printf-offset", 0, 0, G_OPTION_ARG_INT64, &opt_func_foo_printf_offset,
-        "Offset to printf in foo", "0xX"},
-       {"printf-lineno", 0, 0, G_OPTION_ARG_INT64,
-        &opt_func_foo_printf_line_no, "Line number to printf in foo", "N"},
-       {"tp-offset", 0, 0, G_OPTION_ARG_INT64, &opt_func_foo_tp_offset,
-        "Offset to tp in foo", "0xX"},
-       {"tp-lineno", 0, 0, G_OPTION_ARG_INT64, &opt_func_foo_tp_line_no,
-        "Line number to tp in foo", "N"},
-       {"debug-link-crc", 0, 0, G_OPTION_ARG_INT64, &opt_debug_link_crc,
-        "Debug link CRC", "0xX"},
-       {"build-id", 0, 0, G_OPTION_ARG_STRING, &opt_build_id, "Build ID",
-        "XXXXXXXXXXXXXXX"},
-       {"debug-info-dir", 0, 0, G_OPTION_ARG_STRING, &opt_debug_info_dir,
-        "Debug info directory", NULL},
-       {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}};
-
-static
-int build_id_to_bin(void)
-{
-       int ret, len, i;
-
-       if (!opt_build_id) {
-               goto error;
-       }
-
-       len = strnlen(opt_build_id, BUILD_ID_HEX_LEN * 2);
-       if (len != (BUILD_ID_HEX_LEN * 2)) {
-               goto error;
-       }
-
-       for (i = 0; i < (len / 2); i++) {
-               ret = sscanf(opt_build_id + 2 * i, "%02hhx", &build_id[i]);
-               if (ret != 1) {
-                       goto error;
-               }
-       }
-
-       if (i != BUILD_ID_HEX_LEN) {
-               goto error;
-       }
-
-       return 0;
-error:
-       return -1;
-}
-
-static
-void subtest_has_address(struct bin_info *bin, uint64_t addr)
-{
-       int ret;
-
-       ret = bin_info_has_address(bin, SO_LOW_ADDR - 1);
-       ok(ret == 0, "bin_info_has_address - address under SO's range");
-
-       ret = bin_info_has_address(bin, SO_LOW_ADDR);
-       ok(ret == 1, "bin_info_has_address - lower bound of SO's range");
-
-       ret = bin_info_has_address(bin, addr);
-       ok(ret == 1, "bin_info_has_address - address in SO's range");
-
-       ret = bin_info_has_address(bin, SO_LOW_ADDR + SO_MEMSZ - 1);
-       ok(ret == 1, "bin_info_has_address - upper bound of SO's range");
-
-       ret = bin_info_has_address(bin, SO_LOW_ADDR + SO_MEMSZ);
-       ok(ret == 0, "bin_info_has_address - address above SO's range");
-}
-
-static
-void subtest_lookup_function_name(struct bin_info *bin, uint64_t addr,
-                                        char *func_name)
-{
-       int ret;
-       char *_func_name = NULL;
-
-       ret = bin_info_lookup_function_name(bin, addr, &_func_name);
-       ok(ret == 0, "bin_info_lookup_function_name successful at 0x%" PRIx64, addr);
-       if (_func_name) {
-               ok(strcmp(_func_name, func_name) == 0,
-                  "bin_info_lookup_function_name - correct function name (%s == %s)",
-                  func_name, _func_name);
-               free(_func_name);
-               _func_name = NULL;
-       } else {
-               skip(1,
-                    "bin_info_lookup_function_name - function name is NULL");
-       }
-
-       /* Test function name lookup - erroneous address */
-       ret = bin_info_lookup_function_name(bin, SO_INV_ADDR, &_func_name);
-       ok(ret == -1 && !_func_name,
-          "bin_info_lookup_function_name - fail on invalid addr");
-       free(_func_name);
-}
-
-static
-void subtest_lookup_source_location(struct bin_info *bin, uint64_t addr,
-                                          uint64_t line_no, const char *filename)
-{
-       int ret;
-       struct source_location *src_loc = NULL;
-
-       ret = bin_info_lookup_source_location(bin, addr, &src_loc);
-       ok(ret == 0, "bin_info_lookup_source_location successful at 0x%" PRIx64,
-          addr);
-       if (src_loc) {
-               ok(src_loc->line_no == line_no,
-                  "bin_info_lookup_source_location - correct line_no (%" PRIu64 " == %" PRIu64 ")",
-                  line_no, src_loc->line_no);
-               ok(strcmp(src_loc->filename, filename) == 0,
-                  "bin_info_lookup_source_location - correct filename (%s == %s)",
-                  filename, src_loc->filename);
-               source_location_destroy(src_loc);
-               src_loc = NULL;
-       } else {
-               fail("bin_info_lookup_source_location - src_loc is NULL");
-               fail("bin_info_lookup_source_location - src_loc is NULL");
-       }
-
-       /* Test source location lookup - erroneous address */
-       ret = bin_info_lookup_source_location(bin, SO_INV_ADDR, &src_loc);
-       ok(ret == -1 && !src_loc,
-          "bin_info_lookup_source_location - fail on invalid addr");
-       if (src_loc) {
-               source_location_destroy(src_loc);
-       }
-}
-
-static
-void test_bin_info_build_id(const char *bin_info_dir)
-{
-       int ret;
-       char *data_dir, *bin_path;
-       struct bin_info *bin = NULL;
-       struct bt_fd_cache fdc;
-       uint8_t invalid_build_id[BUILD_ID_HEX_LEN] = {
-               0xa3, 0xfd, 0x8b, 0xff, 0x45, 0xe1, 0xa9, 0x32, 0x15, 0xdd,
-               0x6d, 0xaa, 0xd5, 0x53, 0x98, 0x7e, 0xaf, 0xd4, 0x0c, 0xbb
-       };
-
-       diag("bin-info tests - separate DWARF via build ID");
-
-       data_dir = g_build_filename(bin_info_dir, BUILDID_DIR_NAME, NULL);
-       bin_path =
-               g_build_filename(bin_info_dir, BUILDID_DIR_NAME, SO_NAME, NULL);
-
-       if (!data_dir || !bin_path) {
-               exit(EXIT_FAILURE);
-       }
-
-       ret = bt_fd_cache_init(&fdc, BT_LOG_OUTPUT_LEVEL);
-       if (ret != 0) {
-               diag("Failed to initialize FD cache");
-               exit(EXIT_FAILURE);
-       }
-
-       bin = bin_info_create(&fdc, bin_path, SO_LOW_ADDR, SO_MEMSZ, true,
-                             data_dir, NULL, BT_LOG_OUTPUT_LEVEL, NULL);
-       ok(bin, "bin_info_create successful (%s)", bin_path);
-
-       /* Test setting invalid build_id */
-       ret = bin_info_set_build_id(bin, invalid_build_id, BUILD_ID_HEX_LEN);
-       ok(ret == -1, "bin_info_set_build_id fail on invalid build_id");
-
-       /* Test setting correct build_id */
-       ret = bin_info_set_build_id(bin, build_id, BUILD_ID_HEX_LEN);
-       ok(ret == 0, "bin_info_set_build_id successful");
-
-       /* Test bin_info_has_address */
-       subtest_has_address(bin, func_foo_printf_addr);
-
-       /* Test function name lookup (with DWARF) */
-       subtest_lookup_function_name(bin, func_foo_printf_addr,
-                                    func_foo_printf_name);
-
-       /* Test source location lookup */
-       subtest_lookup_source_location(bin, func_foo_printf_addr,
-                                      opt_func_foo_printf_line_no,
-                                      FUNC_FOO_FILENAME);
-
-       bin_info_destroy(bin);
-       bt_fd_cache_fini(&fdc);
-       g_free(data_dir);
-       g_free(bin_path);
-}
-
-static
-void test_bin_info_debug_link(const char *bin_info_dir)
-{
-       int ret;
-       char *data_dir, *bin_path;
-       struct bin_info *bin = NULL;
-       struct bt_fd_cache fdc;
-
-       diag("bin-info tests - separate DWARF via debug link");
-
-       data_dir = g_build_filename(bin_info_dir, DEBUGLINK_DIR_NAME, NULL);
-       bin_path = g_build_filename(bin_info_dir, DEBUGLINK_DIR_NAME, SO_NAME,
-                                   NULL);
-
-       if (!data_dir || !bin_path) {
-               exit(EXIT_FAILURE);
-       }
-
-       ret = bt_fd_cache_init(&fdc, BT_LOG_OUTPUT_LEVEL);
-       if (ret != 0) {
-               diag("Failed to initialize FD cache");
-               exit(EXIT_FAILURE);
-       }
-
-       bin = bin_info_create(&fdc, bin_path, SO_LOW_ADDR, SO_MEMSZ, true,
-               data_dir, NULL, BT_LOG_OUTPUT_LEVEL, NULL);
-       ok(bin, "bin_info_create successful (%s)", bin_path);
-
-       /* Test setting debug link */
-       ret = bin_info_set_debug_link(bin, DEBUG_NAME, opt_debug_link_crc);
-       ok(ret == 0, "bin_info_set_debug_link successful");
-
-       /* Test bin_info_has_address */
-       subtest_has_address(bin, func_foo_printf_addr);
-
-       /* Test function name lookup (with DWARF) */
-       subtest_lookup_function_name(bin, func_foo_printf_addr,
-                                    func_foo_printf_name);
-
-       /* Test source location lookup */
-       subtest_lookup_source_location(bin, func_foo_printf_addr,
-                                      opt_func_foo_printf_line_no,
-                                      FUNC_FOO_FILENAME);
-
-       bin_info_destroy(bin);
-       bt_fd_cache_fini(&fdc);
-       g_free(data_dir);
-       g_free(bin_path);
-}
-
-static
-void test_bin_info_elf(const char *bin_info_dir)
-{
-       int ret;
-       char *data_dir, *bin_path;
-       struct bin_info *bin = NULL;
-       struct source_location *src_loc = NULL;
-       struct bt_fd_cache fdc;
-
-       diag("bin-info tests - ELF only");
-
-       data_dir = g_build_filename(bin_info_dir, ELF_DIR_NAME, NULL);
-       bin_path = g_build_filename(bin_info_dir, ELF_DIR_NAME, SO_NAME, NULL);
-
-       if (!data_dir || !bin_path) {
-               exit(EXIT_FAILURE);
-       }
-
-       ret = bt_fd_cache_init(&fdc, BT_LOG_OUTPUT_LEVEL);
-       if (ret != 0) {
-               diag("Failed to initialize FD cache");
-               exit(EXIT_FAILURE);
-       }
-
-       bin = bin_info_create(&fdc, bin_path, SO_LOW_ADDR, SO_MEMSZ, true,
-               data_dir, NULL, BT_LOG_OUTPUT_LEVEL, NULL);
-       ok(bin, "bin_info_create successful (%s)", bin_path);
-
-       /* Test bin_info_has_address */
-       subtest_has_address(bin, func_foo_printf_addr);
-
-       /* Test function name lookup (with ELF) */
-       subtest_lookup_function_name(bin, func_foo_printf_addr,
-                                    func_foo_printf_name);
-
-       /* Test source location location - should fail on ELF only file  */
-       ret = bin_info_lookup_source_location(bin, func_foo_printf_addr,
-                                             &src_loc);
-       ok(ret == -1,
-          "bin_info_lookup_source_location - fail on ELF only file");
-
-       source_location_destroy(src_loc);
-       bin_info_destroy(bin);
-       bt_fd_cache_fini(&fdc);
-       g_free(data_dir);
-       g_free(bin_path);
-}
-
-static
-void test_bin_info_bundled(const char *bin_info_dir)
-{
-       int ret;
-       char *data_dir, *bin_path;
-       struct bin_info *bin = NULL;
-       struct bt_fd_cache fdc;
-
-       diag("bin-info tests - DWARF bundled in SO file");
-
-       data_dir = g_build_filename(bin_info_dir, DWARF_DIR_NAME, NULL);
-       bin_path =
-               g_build_filename(bin_info_dir, DWARF_DIR_NAME, SO_NAME, NULL);
-
-       if (!data_dir || !bin_path) {
-               exit(EXIT_FAILURE);
-       }
-
-       ret = bt_fd_cache_init(&fdc, BT_LOG_OUTPUT_LEVEL);
-       if (ret != 0) {
-               diag("Failed to initialize FD cache");
-               exit(EXIT_FAILURE);
-       }
-
-       bin = bin_info_create(&fdc, bin_path, SO_LOW_ADDR, SO_MEMSZ, true,
-               data_dir, NULL, BT_LOG_OUTPUT_LEVEL, NULL);
-       ok(bin, "bin_info_create successful (%s)", bin_path);
-
-       /* Test bin_info_has_address */
-       subtest_has_address(bin, func_foo_printf_addr);
-
-       /* Test function name lookup (with DWARF) */
-       subtest_lookup_function_name(bin, func_foo_printf_addr,
-                                    func_foo_printf_name);
-
-       /* Test source location lookup */
-       subtest_lookup_source_location(bin, func_foo_printf_addr,
-                                      opt_func_foo_printf_line_no,
-                                      FUNC_FOO_FILENAME);
-
-       /* Test source location lookup - inlined function */
-       subtest_lookup_source_location(bin, func_foo_tp_addr,
-                                      opt_func_foo_tp_line_no,
-                                      FUNC_FOO_FILENAME);
-
-       bin_info_destroy(bin);
-       bt_fd_cache_fini(&fdc);
-       g_free(data_dir);
-       g_free(bin_path);
-}
-
-int main(int argc, char **argv)
-{
-       int ret;
-       GError *error = NULL;
-       GOptionContext *context;
-       int status;
-
-       plan_tests(NR_TESTS);
-
-       context = g_option_context_new("- bin info test");
-       g_option_context_add_main_entries(context, entries, NULL);
-       if (!g_option_context_parse(context, &argc, &argv, &error)) {
-               fprintf(stderr, "option parsing failed: %s\n", error->message);
-               status = EXIT_FAILURE;
-               goto end;
-       }
-
-       g_snprintf(func_foo_printf_name, FUNC_FOO_NAME_LEN,
-                  FUNC_FOO_PRINTF_NAME_FMT, opt_func_foo_printf_offset);
-       func_foo_printf_addr =
-               SO_LOW_ADDR + opt_func_foo_addr + opt_func_foo_printf_offset;
-       func_foo_tp_addr =
-               SO_LOW_ADDR + opt_func_foo_addr + opt_func_foo_tp_offset;
-
-       if (build_id_to_bin()) {
-               fprintf(stderr, "Failed to parse / missing build id\n");
-               status = EXIT_FAILURE;
-               goto end;
-       }
-
-       ret = bin_info_init(BT_LOG_OUTPUT_LEVEL, NULL);
-       ok(ret == 0, "bin_info_init successful");
-
-       test_bin_info_elf(opt_debug_info_dir);
-       test_bin_info_bundled(opt_debug_info_dir);
-       test_bin_info_build_id(opt_debug_info_dir);
-       test_bin_info_debug_link(opt_debug_info_dir);
-
-       status = exit_status();
-
-end:
-       g_option_context_free(context);
-
-       return status;
-}
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_bin_info_i386-linux-gnu b/tests/plugins/flt.lttng-utils.debug-info/test_bin_info_i386-linux-gnu
deleted file mode 100755 (executable)
index 2703f3e..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
-# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/flt.lttng-utils.debug-info"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/i386-linux-gnu"
-
-"$this_dir_build/test_bin_info" \
-       --foo-addr=0x1c8d \
-       --printf-offset=0xda \
-       --printf-lineno=36 \
-       --tp-offset=0x12 \
-       --tp-lineno=35 \
-       --debug-link-crc=0xdeead493 \
-       --debug-info-dir "$debug_info_data" \
-       --build-id cdd98cdd87f7fe64c13b6daad553987eafd40cbb
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_bin_info_powerpc-linux-gnu b/tests/plugins/flt.lttng-utils.debug-info/test_bin_info_powerpc-linux-gnu
deleted file mode 100755 (executable)
index d8f7ab4..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
-# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/flt.lttng-utils.debug-info"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/powerpc-linux-gnu"
-
-"$this_dir_build/test_bin_info" \
-       --foo-addr=0x23bc \
-       --printf-offset=0x114 \
-       --printf-lineno=36 \
-       --tp-offset=0x28 \
-       --tp-lineno=35 \
-       --debug-link-crc=0xd7b98958 \
-       --debug-info-dir "$debug_info_data" \
-       --build-id cdd98cdd87f7fe64c13b6daad553987eafd40cbb
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_bin_info_powerpc64le-linux-gnu b/tests/plugins/flt.lttng-utils.debug-info/test_bin_info_powerpc64le-linux-gnu
deleted file mode 100755 (executable)
index 60407c5..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
-# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/flt.lttng-utils.debug-info"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/powerpc64le-linux-gnu"
-
-"$this_dir_build/test_bin_info" \
-       --foo-addr=0x2e7c \
-       --printf-offset=0x190 \
-       --printf-lineno=36 \
-       --tp-offset=0x1c \
-       --tp-lineno=35 \
-       --debug-link-crc=0x9b8eb2ff \
-       --debug-info-dir "$debug_info_data" \
-       --build-id cdd98cdd87f7fe64c13b6daad553987eafd40cbb
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_bin_info_x86_64-linux-gnu b/tests/plugins/flt.lttng-utils.debug-info/test_bin_info_x86_64-linux-gnu
deleted file mode 100755 (executable)
index d0e59e2..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
-# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/flt.lttng-utils.debug-info"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/x86_64-linux-gnu"
-
-"$this_dir_build/test_bin_info" \
-       --foo-addr=0x2277 \
-       --printf-offset=0xf0 \
-       --printf-lineno=36 \
-       --tp-offset=0x89 \
-       --tp-lineno=35 \
-       --debug-link-crc=0x289a8fdc \
-       --debug-info-dir "$debug_info_data" \
-       --build-id cdd98cdd87f7fe64c13b6daad553987eafd40cbb
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_dwarf.c b/tests/plugins/flt.lttng-utils.debug-info/test_dwarf.c
deleted file mode 100644 (file)
index 99c584f..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (C) 2015 EfficiOS Inc. and Linux Foundation
- * Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
- *
- * Babeltrace bt_dwarf (DWARF utilities) tests
- */
-
-#include <fcntl.h>
-#include <glib.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-#include <lttng-utils/debug-info/dwarf.h>
-#include "tap/tap.h"
-
-#define NR_TESTS 17
-
-#define SO_NAME "libhello_so"
-#define DWARF_DIR_NAME "dwarf_full"
-#define ELF_DIR_NAME "elf_only"
-
-/*
- * Test that we fail on an ELF file without DWARF.
- */
-static
-void test_bt_no_dwarf(const char *data_dir)
-{
-       int fd;
-       char *path;
-       Dwarf *dwarf_info = NULL;
-
-       path = g_build_filename(data_dir, ELF_DIR_NAME, SO_NAME, NULL);
-       if (!path) {
-               diag("Failed to allocate memory for path");
-               exit(EXIT_FAILURE);
-       }
-
-       fd = open(path, O_RDONLY);
-       ok(fd >= 0, "Open ELF file %s", path);
-       if (fd < 0) {
-               skip(1, "dwarf_begin failed as expected");
-       } else {
-               dwarf_info = dwarf_begin(fd, DWARF_C_READ);
-               ok(!dwarf_info, "dwarf_begin failed as expected");
-       }
-
-       if (dwarf_info) {
-               dwarf_end(dwarf_info);
-       }
-
-       if (fd >= 0) {
-               close(fd);
-       }
-       g_free(path);
-}
-
-/*
- * Test with a proper ELF file with DWARF.
- */
-static
-void test_bt_dwarf(const char *data_dir)
-{
-       int fd, ret, tag;
-       char *path;
-       char *die_name = NULL;
-       struct bt_dwarf_cu *cu = NULL;
-       struct bt_dwarf_die *die = NULL;
-       Dwarf *dwarf_info = NULL;
-
-       path = g_build_filename(data_dir, DWARF_DIR_NAME, SO_NAME, NULL);
-       if (!path) {
-               diag("Failed to allocate memory for path");
-               exit(EXIT_FAILURE);
-       }
-
-       fd = open(path, O_RDONLY);
-       ok(fd >= 0, "Open DWARF file %s", path);
-       if (fd < 0) {
-               exit(EXIT_FAILURE);
-       }
-       dwarf_info = dwarf_begin(fd, DWARF_C_READ);
-       ok(dwarf_info, "dwarf_begin successful");
-       cu = bt_dwarf_cu_create(dwarf_info);
-       ok(cu, "bt_dwarf_cu_create successful");
-       ret = bt_dwarf_cu_next(cu);
-       ok(ret == 0, "bt_dwarf_cu_next successful");
-       die = bt_dwarf_die_create(cu);
-       ok(die, "bt_dwarf_die_create successful");
-       if (!die) {
-               exit(EXIT_FAILURE);
-       }
-       /*
-        * Test bt_dwarf_die_next twice, as the code path is different
-        * for DIEs at depth 0 (just created) and other depths.
-        */
-       ret = bt_dwarf_die_next(die);
-       ok(ret == 0, "bt_dwarf_die_next from root DIE successful");
-       ok(die->depth == 1,
-               "bt_dwarf_die_next from root DIE - correct depth value");
-       ret = bt_dwarf_die_next(die);
-       ok(ret == 0,
-               "bt_dwarf_die_next from non-root DIE successful");
-       ok(die->depth == 1,
-               "bt_dwarf_die_next from non-root DIE - correct depth value");
-
-       /* Reset DIE to test dwarf_child */
-       bt_dwarf_die_destroy(die);
-       die = bt_dwarf_die_create(cu);
-       if (!die) {
-               diag("Failed to create bt_dwarf_die");
-               exit(EXIT_FAILURE);
-       }
-
-       ret = bt_dwarf_die_child(die);
-       ok(ret == 0, "bt_dwarf_die_child successful");
-       ok(die->depth == 1, "bt_dwarf_die_child - correct depth value");
-
-       ret = bt_dwarf_die_get_tag(die, &tag);
-       ok(ret == 0, "bt_dwarf_die_get_tag successful");
-       ok(tag == DW_TAG_typedef, "bt_dwarf_die_get_tag - correct tag value");
-       ret = bt_dwarf_die_get_name(die, &die_name);
-       ok(ret == 0, "bt_dwarf_die_get_name successful");
-       ok(strcmp(die_name, "size_t") == 0,
-               "bt_dwarf_die_get_name - correct name value");
-
-       bt_dwarf_die_destroy(die);
-       bt_dwarf_cu_destroy(cu);
-       dwarf_end(dwarf_info);
-       free(die_name);
-       close(fd);
-       g_free(path);
-}
-
-int main(int argc, char **argv)
-{
-       const char *data_dir;
-
-       plan_tests(NR_TESTS);
-
-       if (argc != 2) {
-               return EXIT_FAILURE;
-       } else {
-               data_dir = argv[1];
-       }
-
-       test_bt_no_dwarf(data_dir);
-       test_bt_dwarf(data_dir);
-
-       return exit_status();
-}
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_dwarf_i386-linux-gnu b/tests/plugins/flt.lttng-utils.debug-info/test_dwarf_i386-linux-gnu
deleted file mode 100755 (executable)
index 03380c5..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
-# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/flt.lttng-utils.debug-info"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/i386-linux-gnu"
-
-"$this_dir_build/test_dwarf" \
-       "$debug_info_data"
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_dwarf_powerpc-linux-gnu b/tests/plugins/flt.lttng-utils.debug-info/test_dwarf_powerpc-linux-gnu
deleted file mode 100755 (executable)
index d60e08c..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
-# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/flt.lttng-utils.debug-info"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/powerpc-linux-gnu"
-
-"$this_dir_build/test_dwarf" \
-       "$debug_info_data"
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_dwarf_powerpc64le-linux-gnu b/tests/plugins/flt.lttng-utils.debug-info/test_dwarf_powerpc64le-linux-gnu
deleted file mode 100755 (executable)
index 8656272..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
-# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/flt.lttng-utils.debug-info"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/powerpc64le-linux-gnu"
-
-"$this_dir_build/test_dwarf" \
-       "$debug_info_data"
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_dwarf_x86_64-linux-gnu b/tests/plugins/flt.lttng-utils.debug-info/test_dwarf_x86_64-linux-gnu
deleted file mode 100755 (executable)
index 8a7a3eb..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
-# Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/flt.lttng-utils.debug-info"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-debug_info_data="$BT_TESTS_DATADIR/$this_dir_relative/x86_64-linux-gnu"
-
-"$this_dir_build/test_dwarf" \
-       "$debug_info_data/"
diff --git a/tests/plugins/flt.lttng-utils.debug-info/test_succeed b/tests/plugins/flt.lttng-utils.debug-info/test_succeed
deleted file mode 100755 (executable)
index 1df6fbb..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
-# Copyright (C) 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
-#
-
-# This test validates that a `src.ctf.fs` component successfully reads
-# specific CTF traces and creates the expected messages.
-#
-# Such CTF traces to open either exist (in `tests/ctf-traces/succeed`)
-# or are generated by this test using local trace generators.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/flt.lttng-utils.debug-info"
-succeed_trace_dir="$BT_CTF_TRACES_PATH/succeed"
-expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
-binary_artefact_dir="$BT_TESTS_DATADIR/$this_dir_relative"
-data_dir="$BT_TESTS_DATADIR/$this_dir_relative"
-
-test_debug_info() {
-       local name="$1"
-       local local_args=(
-               "-c" "flt.lttng-utils.debug-info"
-               "-p" "target-prefix=\"$binary_artefact_dir/x86_64-linux-gnu/dwarf_full\""
-               "-c" "sink.text.details"
-               "-p" "with-trace-name=no,with-stream-name=no"
-       )
-
-       bt_diff_cli "$expect_dir/trace-$name.expect" "/dev/null" \
-               "$succeed_trace_dir/$name" "${local_args[@]}"
-       ok $? "Trace '$name' gives the expected output"
-}
-
-test_compare_to_ctf_fs() {
-       # Compare the `sink.text.details` output of a graph with and without a
-       # `flt.lttng-utils.debug-info` component. Both should be identical for
-       # traces without LTTng debugging fields.
-       local test_name=$1
-       shift 1
-       local cli_args=("$@")
-       local debug_info_cli_args=("-c" "flt.lttng-utils.debug-info")
-       local details_cli_args=(
-               "-c" "sink.text.details"
-               "--params" "with-trace-name=false,with-stream-name=false,with-uuid=false"
-       )
-       local actual_stdout
-       local actual_stderr
-       local expected_stdout
-       local expected_stderr
-       local ret=0
-
-       actual_stdout=$(mktemp -t test_debug_info_stdout_actual.XXXXXX)
-       actual_stderr=$(mktemp -t test_debug_info_stderr_actual.XXXXXX)
-       expected_stdout=$(mktemp -t test_debug_info_stdout_expected.XXXXXX)
-       expected_stderr=$(mktemp -t test_debug_info_stderr_expected.XXXXXX)
-
-       # Create expected files using a graph without a `debug-info` component.
-       bt_cli "$expected_stdout" "$expected_stderr" "${cli_args[@]}" \
-               "${details_cli_args[@]}"
-
-       # Read the same trace with a `debug-info` component in the graph.
-       bt_cli "$actual_stdout" "$actual_stderr" "${cli_args[@]}" \
-               "${details_cli_args[@]}" "${debug_info_cli_args[@]}"
-
-       bt_diff "$expected_stdout" "$actual_stdout"
-       ok $? "Input '$test_name' gives the expected stdout"
-
-       bt_diff "$expected_stderr" "$actual_stderr"
-       ok $? "Input '$test_name' gives the expected stderr"
-
-       rm -f "$actual_stdout"
-       rm -f "$actual_stderr"
-       rm -f "$expected_stdout"
-       rm -f "$expected_stderr"
-}
-
-test_compare_ctf_src_trace() {
-       local trace_name=$1
-       local trace_path="$succeed_trace_dir/$trace_name"
-       local cli_args=("$trace_path")
-
-       diag "Comparing output with and without 'flt.lttng-utils.debug-info' on '$trace_name'"
-       test_compare_to_ctf_fs "src.ctf.fs with $trace_name trace" "${cli_args[@]}"
-}
-
-test_compare_complete_src_trace() {
-
-       local source_name="src.test_debug_info.CompleteSrc"
-       local cli_args=("--plugin-path=$data_dir" "-c" "$source_name")
-       test_compare_to_ctf_fs "$source_name" "${cli_args[@]}"
-}
-
-plan_tests 9
-
-test_debug_info debug-info
-
-test_compare_ctf_src_trace smalltrace
-test_compare_ctf_src_trace 2packets
-test_compare_ctf_src_trace session-rotation
-
-test_compare_complete_src_trace
index c550615dc070cd1fc121e41b0ca6c352b442f5a1..0996598806120428cc8656482afa8d1be1ed8ff6 100644 (file)
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: MIT
 
 dist_check_SCRIPTS = \
-       test_succeed
+       test-succeed.sh
diff --git a/tests/plugins/flt.utils.muxer/succeed/test-succeed.sh b/tests/plugins/flt.utils.muxer/succeed/test-succeed.sh
new file mode 100755 (executable)
index 0000000..dc6af63
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
+#
+
+# This file tests what happens when we mux messages.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+data_dir="$BT_TESTS_DATADIR/plugins/flt.utils.muxer"
+
+plan_tests 12
+
+function run_test
+{
+       local test_name="$1"
+       local local_args=(
+               "--plugin-path" "$data_dir"
+               "-c" "src.test-muxer.TheSourceOfConfusion"
+               "-p" "test-name=$test_name"
+               "-c" "sink.text.details"
+               "--params=compact=false,with-metadata=false"
+       )
+
+       stdout_expected="$data_dir/succeed/$test_name.expect"
+       bt_diff_cli "$stdout_expected" /dev/null "${local_args[@]}"
+       ok $? "$test_name"
+}
+
+
+test_cases=(
+       basic-timestamp-ordering
+       diff-event-class-id
+       diff-event-class-name
+       diff-inactivity-msg-cs
+       diff-stream-class-id
+       diff-stream-class-name
+       diff-stream-class-no-name
+       diff-stream-id
+       diff-stream-name
+       diff-stream-no-name
+       diff-trace-name
+       multi-iter-ordering
+)
+
+for i in "${test_cases[@]}"
+do
+       run_test "$i"
+done
diff --git a/tests/plugins/flt.utils.muxer/succeed/test_succeed b/tests/plugins/flt.utils.muxer/succeed/test_succeed
deleted file mode 100755 (executable)
index 578ac22..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
-#
-
-# This file tests what happens when we mux messages.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
-fi
-
-# shellcheck source=../../../utils/utils.sh
-source "$UTILSSH"
-
-data_dir="$BT_TESTS_DATADIR/plugins/flt.utils.muxer"
-
-plan_tests 12
-
-function run_test
-{
-       local test_name="$1"
-       local local_args=(
-               "--plugin-path" "$data_dir"
-               "-c" "src.test-muxer.TheSourceOfConfusion"
-               "-p" "test-name=$test_name"
-               "-c" "sink.text.details"
-               "--params=compact=false,with-metadata=false"
-       )
-
-       stdout_expected="$data_dir/succeed/$test_name.expect"
-       bt_diff_cli "$stdout_expected" /dev/null "${local_args[@]}"
-       ok $? "$test_name"
-}
-
-
-test_cases=(
-       basic_timestamp_ordering
-       diff_event_class_id
-       diff_event_class_name
-       diff_inactivity_msg_cs
-       diff_stream_class_id
-       diff_stream_class_name
-       diff_stream_class_no_name
-       diff_stream_id
-       diff_stream_name
-       diff_stream_no_name
-       diff_trace_name
-       multi_iter_ordering
-)
-
-for i in "${test_cases[@]}"
-do
-       run_test "$i"
-done
diff --git a/tests/plugins/flt.utils.muxer/test-clock-compatibility.cpp b/tests/plugins/flt.utils.muxer/test-clock-compatibility.cpp
new file mode 100644 (file)
index 0000000..ef05d1d
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2024 EfficiOS Inc.
+ */
+
+#include "cpp-common/bt2/component-class-dev.hpp"
+#include "cpp-common/bt2/component-class.hpp"
+#include "cpp-common/bt2/error.hpp"
+#include "cpp-common/bt2/graph.hpp"
+#include "cpp-common/bt2/plugin-load.hpp"
+#include "cpp-common/bt2c/call.hpp"
+#include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
+
+#include "tap/tap.h"
+
+namespace {
+
+/* The types of messages a `TestSourceIter` is instructed to send. */
+enum class MsgType
+{
+    /* Send stream beginning and stream end messages. */
+    Stream,
+
+    /* Send a message iterator inactivity message. */
+    MsgIterInactivity,
+};
+
+__attribute__((used)) const char *format_as(MsgType msgType) noexcept
+{
+    switch (msgType) {
+    case MsgType::Stream:
+        return "stream beginning/end";
+
+    case MsgType::MsgIterInactivity:
+        return "message iterator inactivity";
+    }
+
+    bt_common_abort();
+}
+
+using CreateClockClass = bt2::ClockClass::Shared (*)(bt2::SelfComponent);
+
+struct TestSourceData final
+{
+    /* The function to call to obtain a clock class. */
+    CreateClockClass createClockClass;
+
+    /* The type of messages to send. */
+    MsgType msgType;
+
+    /* If not empty, the clock snapshot to set on the message. */
+    bt2s::optional<std::uint64_t> clockSnapshot;
+};
+
+class TestSource;
+
+class TestSourceIter final : public bt2::UserMessageIterator<TestSourceIter, TestSource>
+{
+    friend bt2::UserMessageIterator<TestSourceIter, TestSource>;
+
+public:
+    explicit TestSourceIter(const bt2::SelfMessageIterator self,
+                            bt2::SelfMessageIteratorConfiguration,
+                            const bt2::SelfComponentOutputPort port) :
+        bt2::UserMessageIterator<TestSourceIter, TestSource> {self, "TEST-SRC-MSG-ITER"},
+        _mData {&port.data<const TestSourceData>()}, _mSelf {self}
+    {
+    }
+
+private:
+    void _next(bt2::ConstMessageArray& msgs)
+    {
+        if (_mDone) {
+            return;
+        }
+
+        const auto clockCls = _mData->createClockClass(_mSelf.component());
+
+        switch (_mData->msgType) {
+        case MsgType::Stream:
+        {
+            const auto traceCls = _mSelf.component().createTraceClass();
+            const auto streamCls = traceCls->createStreamClass();
+
+            if (clockCls) {
+                streamCls->defaultClockClass(*clockCls);
+            }
+
+            const auto stream = streamCls->instantiate(*traceCls->instantiate());
+
+            /* Create stream beginning message. */
+            msgs.append(bt2c::call([&] {
+                const auto streamBeginningMsg = this->_createStreamBeginningMessage(*stream);
+
+                /* Set clock snapshot if instructed to. */
+                if (_mData->clockSnapshot) {
+                    streamBeginningMsg->defaultClockSnapshot(*_mData->clockSnapshot);
+                }
+
+                return streamBeginningMsg;
+            }));
+
+            /*
+             * The iterator needs to send a stream end message to avoid
+             * a postcondition assertion failure, where it's ended but
+             * didn't end all streams.
+             *
+             * The stream end messages don't play a role in the test
+             * otherwise.
+             */
+            msgs.append(this->_createStreamEndMessage(*stream));
+            break;
+        }
+
+        case MsgType::MsgIterInactivity:
+            msgs.append(
+                this->_createMessageIteratorInactivityMessage(*clockCls, *_mData->clockSnapshot));
+            break;
+        }
+
+        _mDone = true;
+    }
+
+    bool _mDone = false;
+    const TestSourceData *_mData;
+    bt2::SelfMessageIterator _mSelf;
+};
+
+class TestSource final : public bt2::UserSourceComponent<TestSource, TestSourceIter, TestSourceData>
+{
+    friend class TestSourceIter;
+
+    using _ThisUserSourceComponent =
+        bt2::UserSourceComponent<TestSource, TestSourceIter, TestSourceData>;
+
+public:
+    static constexpr auto name = "test-source";
+
+    explicit TestSource(const bt2::SelfSourceComponent self, bt2::ConstMapValue,
+                        TestSourceData * const data) :
+        _ThisUserSourceComponent {self, "TEST-SRC"},
+        _mData {*data}
+    {
+        this->_addOutputPort("out", _mData);
+    }
+
+private:
+    TestSourceData _mData;
+};
+
+class ErrorTestCase final
+{
+public:
+    /* Intentionally not explicit */
+    ErrorTestCase(CreateClockClass createClockClass1Param, CreateClockClass createClockClass2Param,
+                  const char * const testName, const char * const expectedCauseMsg) :
+        _mCreateClockClass1 {createClockClass1Param},
+        _mCreateClockClass2 {createClockClass2Param}, _mTestName {testName}, _mExpectedCauseMsg {
+                                                                                 expectedCauseMsg}
+    {
+    }
+
+    void run() const noexcept;
+
+private:
+    void _runOne(MsgType msgType1, MsgType msgType2) const noexcept;
+
+    CreateClockClass _mCreateClockClass1;
+    CreateClockClass _mCreateClockClass2;
+    const char *_mTestName;
+    const char *_mExpectedCauseMsg;
+};
+
+bt2::ClockClass::Shared noClockClass(bt2::SelfComponent) noexcept
+{
+    return bt2::ClockClass::Shared {};
+}
+
+void ErrorTestCase::run() const noexcept
+{
+    static constexpr std::array<MsgType, 2> msgTypes {
+        MsgType::Stream,
+        MsgType::MsgIterInactivity,
+    };
+
+    for (const auto msgType1 : msgTypes) {
+        for (const auto msgType2 : msgTypes) {
+            /*
+             * It's not possible to create message iterator inactivity
+             * messages without a clock class. Skip those cases.
+             */
+            if ((msgType1 == MsgType::MsgIterInactivity && _mCreateClockClass1 == noClockClass) ||
+                (msgType2 == MsgType::MsgIterInactivity && _mCreateClockClass2 == noClockClass)) {
+                continue;
+            }
+
+            /*
+             * The test scenarios depend on the message with the first
+             * clock class going through the muxer first.
+             *
+             * Between a message with a clock snapshot and a message
+             * without a clock snapshot, the muxer always picks the
+             * message without a clock snapshot first.
+             *
+             * Message iterator inactivity messages always have a clock
+             * snapshot. Therefore, if:
+             *
+             * First message:
+             *     A message iterator inactivity message (always has a
+             *     clock snapshot).
+             *
+             * Second message:
+             *     Doesn't have a clock class (never has a clock
+             *     snapshot).
+             *
+             * Then there's no way for the first message to go through
+             * first.
+             *
+             * Skip those cases.
+             */
+            if (msgType1 == MsgType::MsgIterInactivity && _mCreateClockClass2 == noClockClass) {
+                continue;
+            }
+
+            this->_runOne(msgType1, msgType2);
+        }
+    }
+}
+
+std::string makeSpecTestName(const char * const testName, const MsgType msgType1,
+                             const MsgType msgType2)
+{
+    return fmt::format("{} ({}, {})", testName, msgType1, msgType2);
+}
+
+void ErrorTestCase::_runOne(const MsgType msgType1, const MsgType msgType2) const noexcept
+{
+    const auto specTestName = makeSpecTestName(_mTestName, msgType1, msgType2);
+    const auto srcCompCls = bt2::SourceComponentClass::create<TestSource>();
+    const auto graph = bt2::Graph::create(0);
+
+    {
+        /*
+         * The test scenarios depend on the message with the first clock class going through the
+         * muxer first. Between a message with a clock snapshot and a message without a clock
+         * snapshot, the muxer always picks the message without a clock snapshot first.
+         *
+         * Therefore, for the first message, only set a clock snapshot when absolutely necessary,
+         * that is when the message type is "message iterator inactivity".
+         *
+         * For the second message, always set a clock snapshot when possible, that is when a clock
+         * class is defined for that message.
+         */
+        const auto srcComp1 =
+            graph->addComponent(*srcCompCls, "source-1",
+                                TestSourceData {_mCreateClockClass1, msgType1,
+                                                msgType1 == MsgType::MsgIterInactivity ?
+                                                    bt2s::optional<std::uint64_t> {10} :
+                                                    bt2s::nullopt});
+        const auto srcComp2 =
+            graph->addComponent(*srcCompCls, "source-2",
+                                TestSourceData {_mCreateClockClass2, msgType2,
+                                                _mCreateClockClass2 != noClockClass ?
+                                                    bt2s::optional<std::uint64_t> {20} :
+                                                    bt2s::nullopt});
+
+        const auto utilsPlugin = bt2::findPlugin("utils");
+
+        BT_ASSERT(utilsPlugin);
+
+        /* Add muxer component */
+        const auto muxerComp = bt2c::call([&] {
+            const auto muxerCompCls = utilsPlugin->filterComponentClasses()["muxer"];
+
+            BT_ASSERT(muxerCompCls);
+
+            return graph->addComponent(*muxerCompCls, "the-muxer");
+        });
+
+        /* Add dummy sink component */
+        const auto sinkComp = bt2c::call([&] {
+            const auto dummySinkCompCls = utilsPlugin->sinkComponentClasses()["dummy"];
+
+            BT_ASSERT(dummySinkCompCls);
+
+            return graph->addComponent(*dummySinkCompCls, "the-sink");
+        });
+
+        /* Connect ports */
+        graph->connectPorts(*srcComp1.outputPorts()["out"], *muxerComp.inputPorts()["in0"]);
+        graph->connectPorts(*srcComp2.outputPorts()["out"], *muxerComp.inputPorts()["in1"]);
+        graph->connectPorts(*muxerComp.outputPorts()["out"], *sinkComp.inputPorts()["in"]);
+    }
+
+    const auto thrown = bt2c::call([&graph] {
+        try {
+            graph->run();
+        } catch (const bt2::Error&) {
+            return true;
+        }
+
+        return false;
+    });
+
+    ok(thrown, "%s - `bt2::Error` thrown", specTestName.c_str());
+
+    const auto error = bt2::takeCurrentThreadError();
+
+    ok(error, "%s - current thread has an error", specTestName.c_str());
+    ok(error.length() > 0, "%s - error has at least one cause", specTestName.c_str());
+
+    const auto cause = error[0];
+
+    if (!ok(cause.message().startsWith(_mExpectedCauseMsg), "%s - cause's message is expected",
+            specTestName.c_str())) {
+        diag("expected: %s", _mExpectedCauseMsg);
+        diag("actual: %s", cause.message().data());
+    }
+
+    ok(cause.actorTypeIsMessageIterator(), "%s - cause's actor type is message iterator",
+       specTestName.c_str());
+    ok(cause.asMessageIterator().componentName() == "the-muxer",
+       "%s - causes's component name is `the-muxer`", specTestName.c_str());
+}
+
+const bt2c::Uuid uuidA {"f00aaf65-ebec-4eeb-85b2-fc255cf1aa8a"};
+const bt2c::Uuid uuidB {"03482981-a77b-4d7b-94c4-592bf9e91785"};
+
+const ErrorTestCase errorTestCases[] = {
+    {noClockClass,
+     [](const bt2::SelfComponent self) {
+         return self.createClockClass();
+     },
+     "no clock class followed by clock class", "Expecting no clock class, but got one"},
+
+    {[](const bt2::SelfComponent self) {
+         return self.createClockClass();
+     },
+     noClockClass, "clock class with Unix epoch origin followed by no clock class",
+     "Expecting a clock class, but got none"},
+
+    {[](const bt2::SelfComponent self) {
+         return self.createClockClass();
+     },
+     [](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false);
+         return clockCls;
+     },
+     "clock class with Unix epoch origin followed by clock class with other origin",
+     "Expecting a clock class having a Unix epoch origin, but got one not having a Unix epoch origin"},
+
+    {[](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false).uuid(uuidA);
+         return clockCls;
+     },
+     noClockClass, "clock class with other origin and a UUID followed by no clock class",
+     "Expecting a clock class, but got none"},
+
+    {[](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false).uuid(uuidA);
+         return clockCls;
+     },
+     [](const bt2::SelfComponent self) {
+         return self.createClockClass();
+     },
+     "clock class with other origin and a UUID followed by clock class with Unix epoch origin",
+     "Expecting a clock class not having a Unix epoch origin, but got one having a Unix epoch origin"},
+
+    {[](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false).uuid(uuidA);
+         return clockCls;
+     },
+     [](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false);
+         return clockCls;
+     },
+     "clock class with other origin and a UUID followed by clock class with other origin and no UUID",
+     "Expecting a clock class with a UUID, but got one without a UUID"},
+
+    {[](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false).uuid(uuidA);
+         return clockCls;
+     },
+     [](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false).uuid(uuidB);
+         return clockCls;
+     },
+     "clock class with other origin and a UUID followed by clock class with other origin and another UUID",
+     "Expecting a clock class with a specific UUID, but got one with a different UUID"},
+
+    {[](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false);
+         return clockCls;
+     },
+     noClockClass, "clock class with other origin and no UUID followed by no clock class",
+     "Expecting a clock class, but got none"},
+
+    {[](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false);
+         return clockCls;
+     },
+
+     [](const bt2::SelfComponent self) {
+         const auto clockCls = self.createClockClass();
+
+         clockCls->originIsUnixEpoch(false);
+         return clockCls;
+     },
+     "clock class with other origin and no UUID followed by different clock class",
+     "Unexpected clock class"},
+};
+
+} /* namespace */
+
+int main()
+{
+    plan_tests(150);
+
+    for (auto& errorTestCase : errorTestCases) {
+        errorTestCase.run();
+    }
+
+    return exit_status();
+}
diff --git a/tests/plugins/flt.utils.muxer/test-clock-compatibility.sh b/tests/plugins/flt.utils.muxer/test-clock-compatibility.sh
new file mode 100755 (executable)
index 0000000..e3e2cca
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2024 EfficiOS Inc.
+#
+
+if [[ -n "${BT_TESTS_SRCDIR:-}" ]]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+bt_run_in_py_env "${BT_TESTS_BUILDDIR}/plugins/flt.utils.muxer/test-clock-compatibility"
index 47803073d4aef1ef5e0df4ec4da78f65c4bacaa1..5c369a4339fd8fefb7fa79bbdb60620f71af6850 100644 (file)
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: MIT
 
 dist_check_SCRIPTS = \
-       test_trimming
+       test-trimming.sh
diff --git a/tests/plugins/flt.utils.trimmer/test-trimming.sh b/tests/plugins/flt.utils.trimmer/test-trimming.sh
new file mode 100755 (executable)
index 0000000..f46fe31
--- /dev/null
@@ -0,0 +1,625 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+
+# This file tests what happens when we trim at different points in the message
+# flow.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+data_dir="$BT_TESTS_DATADIR/plugins/flt.utils.trimmer"
+temp_stdout_expected=$(mktemp)
+temp_stderr_expected="/dev/null"
+
+plan_tests 56
+
+function run_test
+{
+       local begin_time="$1"
+       local end_time="$2"
+       # with_stream_msgs_cs and with_packet_msgs are set to "true" or "false"
+       # by the tests.
+       local local_args=(
+               "--plugin-path" "$data_dir"
+               "-c" "src.test-trimmer.TheSourceOfAllEvil"
+               "-p" "with-stream-msgs-cs=$with_stream_msgs_cs"
+               "-p" "with-packet-msgs=$with_packet_msgs"
+               "-c" "sink.text.details"
+               "--params=compact=true,with-metadata=false"
+       )
+
+       if [ "$with_stream_msgs_cs" = "true" ]; then
+               test_name="with stream message clock snapshots"
+       else
+               test_name="without stream message clock snapshots"
+       fi
+
+       if [ "$with_packet_msgs" = "true" ]; then
+               test_name="$test_name, with packet messages"
+       else
+               test_name="$test_name, without packet messages"
+       fi
+
+       if [ -n "$begin_time" ]; then
+               local_args+=("--begin=$begin_time")
+               test_name="$test_name, with --begin=$begin_time"
+       else
+               test_name="$test_name, without --begin"
+       fi
+
+       if [ -n "$end_time" ]; then
+               local_args+=("--end=$end_time")
+               test_name="$test_name, with --end=$end_time"
+       else
+               test_name="$test_name, without --end"
+       fi
+
+       bt_diff_cli "$temp_stdout_expected" "$temp_stderr_expected" "${local_args[@]}"
+       ok $? "$test_name"
+}
+
+function test_with_stream_msg_cs_with_packets {
+       with_stream_msgs_cs="true"
+       with_packet_msgs="true"
+
+       # Baseline (without trimming)
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+       run_test "" ""
+
+       # Trim begin at a time before what the clock class can represent
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+       run_test 50 ""
+
+       # Trim begin before stream beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+       run_test 10050 ""
+
+       # Trim begin before packet beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [150 10,150,000,000,000] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+       run_test 10150 ""
+
+       # Trim begin before first event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [250 10,250,000,000,000] {0 0 0} Stream beginning
+       [250 10,250,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+       run_test 10250 ""
+
+       # Trim begin before second event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [350 10,350,000,000,000] {0 0 0} Stream beginning
+       [350 10,350,000,000,000] {0 0 0} Packet beginning
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test 10350 ""
+
+       # Trim begin before packet end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [850 10,850,000,000,000] {0 0 0} Stream beginning
+       [850 10,850,000,000,000] {0 0 0} Packet beginning
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test 10850 ""
+
+       # Trim begin after everything
+       cat <<- 'END' > "$temp_stdout_expected"
+       END
+
+       run_test 11050 ""
+
+       # Trim end after stream end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 11050
+
+       # Trim end after packet end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [950 10,950,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10950
+
+       # Trim end after second event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [450 10,450,000,000,000] {0 0 0} Packet end
+       [450 10,450,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10450
+
+       # Trim end after first event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [350 10,350,000,000,000] {0 0 0} Packet end
+       [350 10,350,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10350
+
+       # Trim end after packet beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [250 10,250,000,000,000] {0 0 0} Packet end
+       [250 10,250,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10250
+
+       # Trim end after stream beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [150 10,150,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10150
+
+       # Trim end before everything
+       cat <<- 'END' > "$temp_stdout_expected"
+       END
+
+       run_test "" 10050
+
+       # Trim end at a time before what the clock class can represent
+       cat <<- 'END' > "$temp_stdout_expected"
+       END
+
+       run_test "" 50
+}
+
+function test_without_stream_msg_cs_with_packets {
+       with_stream_msgs_cs="false"
+       with_packet_msgs="true"
+
+       # Baseline (without trimming)
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [Unknown] {0 0 0} Stream end
+       END
+       run_test "" ""
+
+       # Trim begin at a time before what the clock class can represent
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [Unknown] {0 0 0} Stream end
+       END
+       run_test 50 ""
+
+       # Trim begin before stream beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [Unknown] {0 0 0} Stream end
+       END
+       run_test 10050 ""
+
+       # Trim begin before packet beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [Unknown] {0 0 0} Stream end
+       END
+       run_test 10150 ""
+
+       # Trim begin before first event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [250 10,250,000,000,000] {0 0 0} Stream beginning
+       [250 10,250,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [Unknown] {0 0 0} Stream end
+       END
+       run_test 10250 ""
+
+       # Trim begin before second event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [350 10,350,000,000,000] {0 0 0} Stream beginning
+       [350 10,350,000,000,000] {0 0 0} Packet beginning
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test 10350 ""
+
+       # Trim begin before packet end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [850 10,850,000,000,000] {0 0 0} Stream beginning
+       [850 10,850,000,000,000] {0 0 0} Packet beginning
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test 10850 ""
+
+       # Trim begin after everything
+       cat <<- 'END' > "$temp_stdout_expected"
+       END
+
+       run_test 11050 ""
+
+       # Trim end after stream end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test "" 11050
+
+       # Trim end after packet end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [900 10,900,000,000,000] {0 0 0} Packet end
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test "" 10950
+
+       # Trim end after second event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [450 10,450,000,000,000] {0 0 0} Packet end
+       [450 10,450,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10450
+
+       # Trim end after first event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [350 10,350,000,000,000] {0 0 0} Packet end
+       [350 10,350,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10350
+
+       # Trim end after packet beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [200 10,200,000,000,000] {0 0 0} Packet beginning
+       [250 10,250,000,000,000] {0 0 0} Packet end
+       [250 10,250,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10250
+
+       # Trim end after stream beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test "" 10150
+
+       # Trim end before everything
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test "" 10050
+
+       # Trim end at a time before what the clock class can represent
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test "" 50
+}
+
+function test_with_stream_msg_cs_without_packets {
+       with_stream_msgs_cs="true"
+       with_packet_msgs="false"
+
+       # Baseline (without trimming)
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+       run_test "" ""
+
+       # Trim begin at a time before what the clock class can represent
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+       run_test 50 ""
+
+       # Trim begin before stream beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+       run_test 10050 ""
+
+       # Trim begin before first event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [250 10,250,000,000,000] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+       run_test 10250 ""
+
+       # Trim begin before second event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [350 10,350,000,000,000] {0 0 0} Stream beginning
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test 10350 ""
+
+       # Trim begin before packet end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [850 10,850,000,000,000] {0 0 0} Stream beginning
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test 10850 ""
+
+       # Trim begin after everything
+       cat <<- 'END' > "$temp_stdout_expected"
+       END
+
+       run_test 11050 ""
+
+       # Trim end after stream end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [1000 11,000,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 11050
+
+       # Trim end after packet end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [950 10,950,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10950
+
+       # Trim end after second event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [450 10,450,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10450
+
+       # Trim end after first event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [350 10,350,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10350
+
+       # Trim end after packet beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [250 10,250,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10250
+
+       # Trim end after stream beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [100 10,100,000,000,000] {0 0 0} Stream beginning
+       [150 10,150,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10150
+
+       # Trim end before everything
+       cat <<- 'END' > "$temp_stdout_expected"
+       END
+
+       run_test "" 10050
+
+       # Trim end at a time before what the clock class can represent
+       cat <<- 'END' > "$temp_stdout_expected"
+       END
+
+       run_test "" 50
+}
+
+function test_without_stream_msg_cs_without_packets {
+       with_stream_msgs_cs="false"
+       with_packet_msgs="false"
+
+       # Baseline (without trimming)
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [Unknown] {0 0 0} Stream end
+       END
+       run_test "" ""
+
+       # Trim begin at a time before what the clock class can represent
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [Unknown] {0 0 0} Stream end
+       END
+       run_test 50 ""
+
+       # Trim begin before stream beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [Unknown] {0 0 0} Stream end
+       END
+       run_test 10050 ""
+
+       # Trim begin before second event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [350 10,350,000,000,000] {0 0 0} Stream beginning
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test 10350 ""
+
+       # Trim begin after everything
+       cat <<- 'END' > "$temp_stdout_expected"
+       END
+
+       run_test 11050 ""
+
+       # Trim end after stream end
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test "" 11050
+
+       # Trim end after first event
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
+       [350 10,350,000,000,000] {0 0 0} Stream end
+       END
+
+       run_test "" 10350
+
+       # Trim end after stream beginning
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test "" 10150
+
+       # Trim end at a time before what the clock class can represent
+       cat <<- 'END' > "$temp_stdout_expected"
+       [Unknown] {0 0 0} Stream beginning
+       [Unknown] {0 0 0} Stream end
+       END
+
+       run_test "" 50
+}
+
+test_with_stream_msg_cs_with_packets
+test_without_stream_msg_cs_with_packets
+test_with_stream_msg_cs_without_packets
+test_without_stream_msg_cs_without_packets
+
+# Do not `rm` $temp_stderr_expected because it's set to `/dev/null` right now
+# and that would print an error.
+rm -f "$temp_stdout_expected"
diff --git a/tests/plugins/flt.utils.trimmer/test_trimming b/tests/plugins/flt.utils.trimmer/test_trimming
deleted file mode 100755 (executable)
index f46fe31..0000000
+++ /dev/null
@@ -1,625 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-# This file tests what happens when we trim at different points in the message
-# flow.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-data_dir="$BT_TESTS_DATADIR/plugins/flt.utils.trimmer"
-temp_stdout_expected=$(mktemp)
-temp_stderr_expected="/dev/null"
-
-plan_tests 56
-
-function run_test
-{
-       local begin_time="$1"
-       local end_time="$2"
-       # with_stream_msgs_cs and with_packet_msgs are set to "true" or "false"
-       # by the tests.
-       local local_args=(
-               "--plugin-path" "$data_dir"
-               "-c" "src.test-trimmer.TheSourceOfAllEvil"
-               "-p" "with-stream-msgs-cs=$with_stream_msgs_cs"
-               "-p" "with-packet-msgs=$with_packet_msgs"
-               "-c" "sink.text.details"
-               "--params=compact=true,with-metadata=false"
-       )
-
-       if [ "$with_stream_msgs_cs" = "true" ]; then
-               test_name="with stream message clock snapshots"
-       else
-               test_name="without stream message clock snapshots"
-       fi
-
-       if [ "$with_packet_msgs" = "true" ]; then
-               test_name="$test_name, with packet messages"
-       else
-               test_name="$test_name, without packet messages"
-       fi
-
-       if [ -n "$begin_time" ]; then
-               local_args+=("--begin=$begin_time")
-               test_name="$test_name, with --begin=$begin_time"
-       else
-               test_name="$test_name, without --begin"
-       fi
-
-       if [ -n "$end_time" ]; then
-               local_args+=("--end=$end_time")
-               test_name="$test_name, with --end=$end_time"
-       else
-               test_name="$test_name, without --end"
-       fi
-
-       bt_diff_cli "$temp_stdout_expected" "$temp_stderr_expected" "${local_args[@]}"
-       ok $? "$test_name"
-}
-
-function test_with_stream_msg_cs_with_packets {
-       with_stream_msgs_cs="true"
-       with_packet_msgs="true"
-
-       # Baseline (without trimming)
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-       run_test "" ""
-
-       # Trim begin at a time before what the clock class can represent
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-       run_test 50 ""
-
-       # Trim begin before stream beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-       run_test 10050 ""
-
-       # Trim begin before packet beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [150 10,150,000,000,000] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-       run_test 10150 ""
-
-       # Trim begin before first event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [250 10,250,000,000,000] {0 0 0} Stream beginning
-       [250 10,250,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-       run_test 10250 ""
-
-       # Trim begin before second event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [350 10,350,000,000,000] {0 0 0} Stream beginning
-       [350 10,350,000,000,000] {0 0 0} Packet beginning
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test 10350 ""
-
-       # Trim begin before packet end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [850 10,850,000,000,000] {0 0 0} Stream beginning
-       [850 10,850,000,000,000] {0 0 0} Packet beginning
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test 10850 ""
-
-       # Trim begin after everything
-       cat <<- 'END' > "$temp_stdout_expected"
-       END
-
-       run_test 11050 ""
-
-       # Trim end after stream end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 11050
-
-       # Trim end after packet end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [950 10,950,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10950
-
-       # Trim end after second event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [450 10,450,000,000,000] {0 0 0} Packet end
-       [450 10,450,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10450
-
-       # Trim end after first event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [350 10,350,000,000,000] {0 0 0} Packet end
-       [350 10,350,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10350
-
-       # Trim end after packet beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [250 10,250,000,000,000] {0 0 0} Packet end
-       [250 10,250,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10250
-
-       # Trim end after stream beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [150 10,150,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10150
-
-       # Trim end before everything
-       cat <<- 'END' > "$temp_stdout_expected"
-       END
-
-       run_test "" 10050
-
-       # Trim end at a time before what the clock class can represent
-       cat <<- 'END' > "$temp_stdout_expected"
-       END
-
-       run_test "" 50
-}
-
-function test_without_stream_msg_cs_with_packets {
-       with_stream_msgs_cs="false"
-       with_packet_msgs="true"
-
-       # Baseline (without trimming)
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [Unknown] {0 0 0} Stream end
-       END
-       run_test "" ""
-
-       # Trim begin at a time before what the clock class can represent
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [Unknown] {0 0 0} Stream end
-       END
-       run_test 50 ""
-
-       # Trim begin before stream beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [Unknown] {0 0 0} Stream end
-       END
-       run_test 10050 ""
-
-       # Trim begin before packet beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [Unknown] {0 0 0} Stream end
-       END
-       run_test 10150 ""
-
-       # Trim begin before first event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [250 10,250,000,000,000] {0 0 0} Stream beginning
-       [250 10,250,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [Unknown] {0 0 0} Stream end
-       END
-       run_test 10250 ""
-
-       # Trim begin before second event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [350 10,350,000,000,000] {0 0 0} Stream beginning
-       [350 10,350,000,000,000] {0 0 0} Packet beginning
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test 10350 ""
-
-       # Trim begin before packet end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [850 10,850,000,000,000] {0 0 0} Stream beginning
-       [850 10,850,000,000,000] {0 0 0} Packet beginning
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test 10850 ""
-
-       # Trim begin after everything
-       cat <<- 'END' > "$temp_stdout_expected"
-       END
-
-       run_test 11050 ""
-
-       # Trim end after stream end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test "" 11050
-
-       # Trim end after packet end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [900 10,900,000,000,000] {0 0 0} Packet end
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test "" 10950
-
-       # Trim end after second event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [450 10,450,000,000,000] {0 0 0} Packet end
-       [450 10,450,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10450
-
-       # Trim end after first event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [350 10,350,000,000,000] {0 0 0} Packet end
-       [350 10,350,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10350
-
-       # Trim end after packet beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [200 10,200,000,000,000] {0 0 0} Packet beginning
-       [250 10,250,000,000,000] {0 0 0} Packet end
-       [250 10,250,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10250
-
-       # Trim end after stream beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test "" 10150
-
-       # Trim end before everything
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test "" 10050
-
-       # Trim end at a time before what the clock class can represent
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test "" 50
-}
-
-function test_with_stream_msg_cs_without_packets {
-       with_stream_msgs_cs="true"
-       with_packet_msgs="false"
-
-       # Baseline (without trimming)
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-       run_test "" ""
-
-       # Trim begin at a time before what the clock class can represent
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-       run_test 50 ""
-
-       # Trim begin before stream beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-       run_test 10050 ""
-
-       # Trim begin before first event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [250 10,250,000,000,000] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-       run_test 10250 ""
-
-       # Trim begin before second event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [350 10,350,000,000,000] {0 0 0} Stream beginning
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test 10350 ""
-
-       # Trim begin before packet end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [850 10,850,000,000,000] {0 0 0} Stream beginning
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test 10850 ""
-
-       # Trim begin after everything
-       cat <<- 'END' > "$temp_stdout_expected"
-       END
-
-       run_test 11050 ""
-
-       # Trim end after stream end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [1000 11,000,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 11050
-
-       # Trim end after packet end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [950 10,950,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10950
-
-       # Trim end after second event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [450 10,450,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10450
-
-       # Trim end after first event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [350 10,350,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10350
-
-       # Trim end after packet beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [250 10,250,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10250
-
-       # Trim end after stream beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [100 10,100,000,000,000] {0 0 0} Stream beginning
-       [150 10,150,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10150
-
-       # Trim end before everything
-       cat <<- 'END' > "$temp_stdout_expected"
-       END
-
-       run_test "" 10050
-
-       # Trim end at a time before what the clock class can represent
-       cat <<- 'END' > "$temp_stdout_expected"
-       END
-
-       run_test "" 50
-}
-
-function test_without_stream_msg_cs_without_packets {
-       with_stream_msgs_cs="false"
-       with_packet_msgs="false"
-
-       # Baseline (without trimming)
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [Unknown] {0 0 0} Stream end
-       END
-       run_test "" ""
-
-       # Trim begin at a time before what the clock class can represent
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [Unknown] {0 0 0} Stream end
-       END
-       run_test 50 ""
-
-       # Trim begin before stream beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [Unknown] {0 0 0} Stream end
-       END
-       run_test 10050 ""
-
-       # Trim begin before second event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [350 10,350,000,000,000] {0 0 0} Stream beginning
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test 10350 ""
-
-       # Trim begin after everything
-       cat <<- 'END' > "$temp_stdout_expected"
-       END
-
-       run_test 11050 ""
-
-       # Trim end after stream end
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [400 10,400,000,000,000] {0 0 0} Event `event 2` (1)
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test "" 11050
-
-       # Trim end after first event
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [300 10,300,000,000,000] {0 0 0} Event `event 1` (0)
-       [350 10,350,000,000,000] {0 0 0} Stream end
-       END
-
-       run_test "" 10350
-
-       # Trim end after stream beginning
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test "" 10150
-
-       # Trim end at a time before what the clock class can represent
-       cat <<- 'END' > "$temp_stdout_expected"
-       [Unknown] {0 0 0} Stream beginning
-       [Unknown] {0 0 0} Stream end
-       END
-
-       run_test "" 50
-}
-
-test_with_stream_msg_cs_with_packets
-test_without_stream_msg_cs_with_packets
-test_with_stream_msg_cs_without_packets
-test_without_stream_msg_cs_without_packets
-
-# Do not `rm` $temp_stderr_expected because it's set to `/dev/null` right now
-# and that would print an error.
-rm -f "$temp_stdout_expected"
index fb488d066b5624cc01c37b6881620495ec1b9d17..557a355e3f5167d1bdf499dbae43b6c6038b6588 100644 (file)
@@ -3,5 +3,5 @@
 SUBDIRS = succeed
 
 dist_check_SCRIPTS = \
-       test_assume_single_trace \
-       test_stream_names
+       test-assume-single-trace.sh \
+       test-stream-names.sh
index 1f7d46cffab7fc5619a3bc4cf0d885c56cd692b5..b4332093fb32b32a3a47cf94dc5ed80776d1be6f 100644 (file)
@@ -1,12 +1,12 @@
 # SPDX-License-Identifier: MIT
 
-dist_check_SCRIPTS = test_succeed
+dist_check_SCRIPTS = test-succeed.sh
 
 # CTF trace generators
 GEN_TRACE_LDADD = \
        $(top_builddir)/src/ctf-writer/libbabeltrace2-ctf-writer.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/logging/liblogging.la
 
 gen_trace_float_SOURCES = gen-trace-float.c
 gen_trace_float_LDADD = $(GEN_TRACE_LDADD)
diff --git a/tests/plugins/sink.ctf.fs/succeed/test-succeed.sh b/tests/plugins/sink.ctf.fs/succeed/test-succeed.sh
new file mode 100755 (executable)
index 0000000..6d47c09
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
+#
+
+# This test validates that a `src.ctf.fs` component successfully reads
+# specific CTF traces and creates the expected messages.
+#
+# Such CTF traces to open either exist (in `tests/ctf-traces/succeed`)
+# or are generated by this test using local trace generators.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/sink.ctf.fs/succeed"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
+succeed_traces="$BT_CTF_TRACES_PATH/succeed"
+
+test_ctf_single() {
+       local name="$1"
+       local in_trace_dir="$2"
+       local temp_out_trace_dir
+
+       temp_out_trace_dir="$(mktemp -d)"
+
+       diag "Converting trace '$name' to CTF through 'sink.ctf.fs'"
+       "$BT_TESTS_BT2_BIN" >/dev/null "$in_trace_dir" -o ctf -w "$temp_out_trace_dir"
+       ret=$?
+       ok $ret "'sink.ctf.fs' component succeeds with input trace '$name'"
+       converted_test_name="Converted trace '$name' gives the expected output"
+
+       if [ $ret -eq 0 ]; then
+               bt_diff_details_ctf_single "$expect_dir/trace-$name.expect" \
+                       "$temp_out_trace_dir" \
+                       '-p' 'with-uuid=no,with-trace-name=no,with-stream-name=no'
+               ok $? "$converted_test_name"
+       else
+               fail "$converted_test_name"
+       fi
+
+       rm -rf "$temp_out_trace_dir"
+}
+
+test_ctf_existing_single() {
+       local name="$1"
+       local trace_dir="$succeed_traces/$name"
+
+       test_ctf_single "$name" "$trace_dir"
+}
+
+test_ctf_gen_single() {
+       local name="$1"
+       local temp_gen_trace_dir
+
+       temp_gen_trace_dir="$(mktemp -d)"
+
+       diag "Generating trace '$name'"
+
+       if ! "$this_dir_build/gen-trace-$name" "$temp_gen_trace_dir"; then
+               # this is not part of the test itself; it must not fail
+               echo "ERROR: \"$this_dir_build/gen-trace-$name" "$temp_gen_trace_dir\" failed" >&2
+               rm -rf "$temp_gen_trace_dir"
+               exit 1
+       fi
+
+       test_ctf_single "$name" "$temp_gen_trace_dir"
+       rm -rf "$temp_gen_trace_dir"
+}
+
+plan_tests 14
+
+test_ctf_gen_single float
+test_ctf_gen_single double
+test_ctf_existing_single meta-variant-no-underscore
+test_ctf_existing_single meta-variant-one-underscore
+test_ctf_existing_single meta-variant-reserved-keywords
+test_ctf_existing_single meta-variant-same-with-underscore
+test_ctf_existing_single meta-variant-two-underscores
diff --git a/tests/plugins/sink.ctf.fs/succeed/test_succeed b/tests/plugins/sink.ctf.fs/succeed/test_succeed
deleted file mode 100755 (executable)
index 6d47c09..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
-#
-
-# This test validates that a `src.ctf.fs` component successfully reads
-# specific CTF traces and creates the expected messages.
-#
-# Such CTF traces to open either exist (in `tests/ctf-traces/succeed`)
-# or are generated by this test using local trace generators.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
-fi
-
-# shellcheck source=../../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/sink.ctf.fs/succeed"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
-succeed_traces="$BT_CTF_TRACES_PATH/succeed"
-
-test_ctf_single() {
-       local name="$1"
-       local in_trace_dir="$2"
-       local temp_out_trace_dir
-
-       temp_out_trace_dir="$(mktemp -d)"
-
-       diag "Converting trace '$name' to CTF through 'sink.ctf.fs'"
-       "$BT_TESTS_BT2_BIN" >/dev/null "$in_trace_dir" -o ctf -w "$temp_out_trace_dir"
-       ret=$?
-       ok $ret "'sink.ctf.fs' component succeeds with input trace '$name'"
-       converted_test_name="Converted trace '$name' gives the expected output"
-
-       if [ $ret -eq 0 ]; then
-               bt_diff_details_ctf_single "$expect_dir/trace-$name.expect" \
-                       "$temp_out_trace_dir" \
-                       '-p' 'with-uuid=no,with-trace-name=no,with-stream-name=no'
-               ok $? "$converted_test_name"
-       else
-               fail "$converted_test_name"
-       fi
-
-       rm -rf "$temp_out_trace_dir"
-}
-
-test_ctf_existing_single() {
-       local name="$1"
-       local trace_dir="$succeed_traces/$name"
-
-       test_ctf_single "$name" "$trace_dir"
-}
-
-test_ctf_gen_single() {
-       local name="$1"
-       local temp_gen_trace_dir
-
-       temp_gen_trace_dir="$(mktemp -d)"
-
-       diag "Generating trace '$name'"
-
-       if ! "$this_dir_build/gen-trace-$name" "$temp_gen_trace_dir"; then
-               # this is not part of the test itself; it must not fail
-               echo "ERROR: \"$this_dir_build/gen-trace-$name" "$temp_gen_trace_dir\" failed" >&2
-               rm -rf "$temp_gen_trace_dir"
-               exit 1
-       fi
-
-       test_ctf_single "$name" "$temp_gen_trace_dir"
-       rm -rf "$temp_gen_trace_dir"
-}
-
-plan_tests 14
-
-test_ctf_gen_single float
-test_ctf_gen_single double
-test_ctf_existing_single meta-variant-no-underscore
-test_ctf_existing_single meta-variant-one-underscore
-test_ctf_existing_single meta-variant-reserved-keywords
-test_ctf_existing_single meta-variant-same-with-underscore
-test_ctf_existing_single meta-variant-two-underscores
diff --git a/tests/plugins/sink.ctf.fs/test-assume-single-trace.sh b/tests/plugins/sink.ctf.fs/test-assume-single-trace.sh
new file mode 100755 (executable)
index 0000000..37f5270
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020 EfficiOS Inc.
+#
+
+# This file tests the assume-single-trace parameter of sink.ctf.fs.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+# Directory containing the Python test source.
+data_dir="$BT_TESTS_DATADIR/plugins/sink.ctf.fs/assume-single-trace"
+
+temp_stdout=$(mktemp)
+temp_expected_stdout=$(mktemp)
+temp_stderr=$(mktemp)
+temp_output_dir=$(mktemp -d)
+
+trace_dir="$temp_output_dir/the-trace"
+
+if [ "$BT_TESTS_ENABLE_PYTHON_PLUGINS" != "1" ]; then
+       plan_skip_all "This test requires the Python plugin provider"
+       exit
+fi
+
+plan_tests 7
+
+bt_cli "$temp_stdout" "$temp_stderr" \
+       "--plugin-path=${data_dir}" \
+       -c src.foo.TheSource \
+       -c sink.ctf.fs -p "path=\"${trace_dir}\"" -p 'assume-single-trace=true'
+ok "$?" "run sink.ctf.fs with assume-single-trace=true"
+
+# Check stdout.
+if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
+       echo "Created CTF trace \`$(cygpath -m "${trace_dir}")\`." > "$temp_expected_stdout"
+else
+       echo "Created CTF trace \`${trace_dir}\`." > "$temp_expected_stdout"
+fi
+bt_diff "$temp_expected_stdout" "$temp_stdout"
+ok "$?" "expected message on stdout"
+
+# Check stderr.
+bt_diff "/dev/null" "$temp_stderr"
+ok "$?" "stderr is empty"
+
+# Verify only the expected files exist.
+files=("$trace_dir"/*)
+num_files=${#files[@]}
+is "$num_files" "2" "expected number of files in output directory"
+
+test -f "$trace_dir/metadata"
+ok "$?" "metadata file exists"
+
+test -f "$trace_dir/the-stream"
+ok "$?" "the-stream file exists"
+
+# Read back the output trace to make sure it's properly formed.
+echo "the-event: " > "$temp_expected_stdout"
+bt_diff_cli "$temp_expected_stdout" /dev/null "$trace_dir"
+ok "$?" "read back output trace"
+
+rm -f "$temp_stdout"
+rm -f "$temp_stderr"
+rm -f "$temp_expected_stdout"
+rm -f "$trace_dir/metadata"
+rm -f "$trace_dir/the-stream"
+rmdir "$trace_dir"
+rmdir "$temp_output_dir"
diff --git a/tests/plugins/sink.ctf.fs/test-stream-names.sh b/tests/plugins/sink.ctf.fs/test-stream-names.sh
new file mode 100755 (executable)
index 0000000..e684376
--- /dev/null
@@ -0,0 +1,94 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020 EfficiOS Inc.
+#
+
+# This file tests corner cases related to stream names:
+#
+#   - two streams with the same name
+#   - a stream named "metadata"
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+# Directory containing the Python test source.
+data_dir="$BT_TESTS_DATADIR/plugins/sink.ctf.fs/stream-names"
+
+temp_stdout=$(mktemp)
+temp_expected_stdout=$(mktemp)
+temp_stderr=$(mktemp)
+temp_output_dir=$(mktemp -d)
+trace_dir="$temp_output_dir/trace"
+
+if [ "$BT_TESTS_ENABLE_PYTHON_PLUGINS" != "1" ]; then
+       plan_skip_all "This test requires the Python plugin provider"
+       exit
+fi
+
+plan_tests 9
+
+bt_cli "$temp_stdout" "$temp_stderr" \
+       "--plugin-path=${data_dir}" \
+       -c src.foo.TheSource \
+       -c sink.ctf.fs -p "path=\"${temp_output_dir}\""
+ok "$?" "run babeltrace"
+
+# Check stdout.
+if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
+       # shellcheck disable=SC2028
+       echo "Created CTF trace \`$(cygpath -m "${temp_output_dir}")\\trace\`." > "$temp_expected_stdout"
+else
+       echo "Created CTF trace \`${trace_dir}\`." > "$temp_expected_stdout"
+fi
+bt_diff "$temp_expected_stdout" "$temp_stdout"
+ok "$?" "expected message on stdout"
+
+# Check stderr.
+bt_diff "/dev/null" "$temp_stderr"
+ok "$?" "stderr is empty"
+
+# Verify only the expected files exist.
+files=("$trace_dir"/*)
+num_files=${#files[@]}
+is "$num_files" "4" "expected number of files in output directory"
+
+test -f "$trace_dir/metadata"
+ok "$?" "metadata file exists"
+
+test -f "$trace_dir/metadata-0"
+ok "$?" "metadata-0 file exists"
+
+test -f "$trace_dir/the-stream"
+ok "$?" "the-stream file exists"
+
+test -f "$trace_dir/the-stream-0"
+ok "$?" "the-stream-0 file exists"
+
+# Read back the output trace to make sure it's properly formed.
+cat <<- 'END' > "$temp_expected_stdout"
+the-event: 
+the-event: 
+the-event: 
+END
+bt_diff_cli "$temp_expected_stdout" /dev/null "$trace_dir"
+ok "$?" "read back output trace"
+
+rm -f "$temp_stdout"
+rm -f "$temp_stderr"
+rm -f "$temp_expected_stdout"
+rm -f "$trace_dir/metadata"
+rm -f "$trace_dir/metadata-0"
+rm -f "$trace_dir/the-stream"
+rm -f "$trace_dir/the-stream-0"
+rmdir "$trace_dir"
+rmdir "$temp_output_dir"
diff --git a/tests/plugins/sink.ctf.fs/test_assume_single_trace b/tests/plugins/sink.ctf.fs/test_assume_single_trace
deleted file mode 100755 (executable)
index 69dfb36..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2020 EfficiOS Inc.
-#
-
-# This file tests the assume-single-trace parameter of sink.ctf.fs.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-# Directory containing the Python test source.
-data_dir="$BT_TESTS_DATADIR/plugins/sink.ctf.fs/assume-single-trace"
-
-temp_stdout=$(mktemp)
-temp_expected_stdout=$(mktemp)
-temp_stderr=$(mktemp)
-temp_output_dir=$(mktemp -d)
-
-trace_dir="$temp_output_dir/the-trace"
-
-plan_tests 7
-
-bt_cli "$temp_stdout" "$temp_stderr" \
-       "--plugin-path=${data_dir}" \
-       -c src.foo.TheSource \
-       -c sink.ctf.fs -p "path=\"${trace_dir}\"" -p 'assume-single-trace=true'
-ok "$?" "run sink.ctf.fs with assume-single-trace=true"
-
-# Check stdout.
-if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
-       echo "Created CTF trace \`$(cygpath -m "${trace_dir}")\`." > "$temp_expected_stdout"
-else
-       echo "Created CTF trace \`${trace_dir}\`." > "$temp_expected_stdout"
-fi
-bt_diff "$temp_expected_stdout" "$temp_stdout"
-ok "$?" "expected message on stdout"
-
-# Check stderr.
-bt_diff "/dev/null" "$temp_stderr"
-ok "$?" "stderr is empty"
-
-# Verify only the expected files exist.
-files=("$trace_dir"/*)
-num_files=${#files[@]}
-is "$num_files" "2" "expected number of files in output directory"
-
-test -f "$trace_dir/metadata"
-ok "$?" "metadata file exists"
-
-test -f "$trace_dir/the-stream"
-ok "$?" "the-stream file exists"
-
-# Read back the output trace to make sure it's properly formed.
-echo "the-event: " > "$temp_expected_stdout"
-bt_diff_cli "$temp_expected_stdout" /dev/null "$trace_dir"
-ok "$?" "read back output trace"
-
-rm -f "$temp_stdout"
-rm -f "$temp_stderr"
-rm -f "$temp_expected_stdout"
-rm -f "$trace_dir/metadata"
-rm -f "$trace_dir/the-stream"
-rmdir "$trace_dir"
-rmdir "$temp_output_dir"
diff --git a/tests/plugins/sink.ctf.fs/test_stream_names b/tests/plugins/sink.ctf.fs/test_stream_names
deleted file mode 100755 (executable)
index e5993c7..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2020 EfficiOS Inc.
-#
-
-# This file tests corner cases related to stream names:
-#
-#   - two streams with the same name
-#   - a stream named "metadata"
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-# Directory containing the Python test source.
-data_dir="$BT_TESTS_DATADIR/plugins/sink.ctf.fs/stream-names"
-
-temp_stdout=$(mktemp)
-temp_expected_stdout=$(mktemp)
-temp_stderr=$(mktemp)
-temp_output_dir=$(mktemp -d)
-trace_dir="$temp_output_dir/trace"
-
-plan_tests 9
-
-bt_cli "$temp_stdout" "$temp_stderr" \
-       "--plugin-path=${data_dir}" \
-       -c src.foo.TheSource \
-       -c sink.ctf.fs -p "path=\"${temp_output_dir}\""
-ok "$?" "run babeltrace"
-
-# Check stdout.
-if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
-       # shellcheck disable=SC2028
-       echo "Created CTF trace \`$(cygpath -m "${temp_output_dir}")\\trace\`." > "$temp_expected_stdout"
-else
-       echo "Created CTF trace \`${trace_dir}\`." > "$temp_expected_stdout"
-fi
-bt_diff "$temp_expected_stdout" "$temp_stdout"
-ok "$?" "expected message on stdout"
-
-# Check stderr.
-bt_diff "/dev/null" "$temp_stderr"
-ok "$?" "stderr is empty"
-
-# Verify only the expected files exist.
-files=("$trace_dir"/*)
-num_files=${#files[@]}
-is "$num_files" "4" "expected number of files in output directory"
-
-test -f "$trace_dir/metadata"
-ok "$?" "metadata file exists"
-
-test -f "$trace_dir/metadata-0"
-ok "$?" "metadata-0 file exists"
-
-test -f "$trace_dir/the-stream"
-ok "$?" "the-stream file exists"
-
-test -f "$trace_dir/the-stream-0"
-ok "$?" "the-stream-0 file exists"
-
-# Read back the output trace to make sure it's properly formed.
-cat <<- 'END' > "$temp_expected_stdout"
-the-event: 
-the-event: 
-the-event: 
-END
-bt_diff_cli "$temp_expected_stdout" /dev/null "$trace_dir"
-ok "$?" "read back output trace"
-
-rm -f "$temp_stdout"
-rm -f "$temp_stderr"
-rm -f "$temp_expected_stdout"
-rm -f "$trace_dir/metadata"
-rm -f "$trace_dir/metadata-0"
-rm -f "$trace_dir/the-stream"
-rm -f "$trace_dir/the-stream-0"
-rmdir "$trace_dir"
-rmdir "$temp_output_dir"
diff --git a/tests/plugins/sink.text.details/succeed/test-succeed.sh b/tests/plugins/sink.text.details/succeed/test-succeed.sh
new file mode 100755 (executable)
index 0000000..9c21155
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/sink.text.details/succeed"
+expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
+
+test_details() {
+       local test_name="$1"
+       local trace_name="$2"
+       shift 2
+       local details_args=("$@")
+       local trace_dir="$BT_CTF_TRACES_PATH/succeed/$trace_name"
+       local expect_path="$expect_dir/$test_name.expect"
+
+       bt_diff_cli "$expect_path" /dev/null \
+               "$trace_dir" -p trace-name=the-trace \
+               -c sink.text.details "${details_args[@]+${details_args[@]}}"
+       ok $? "'$test_name' test has the expected output"
+}
+
+# This is used for the moment because the source is `src.ctf.fs` and
+# such a component can make its stream names contain absolute paths.
+test_details_no_stream_name() {
+       local test_name="$1"
+       local trace_name="$2"
+       shift 2
+       local details_args=("$@")
+
+       test_details "$test_name" "$trace_name" \
+               "${details_args[@]+${details_args[@]}}" -p with-stream-name=no
+}
+
+plan_tests 12
+
+test_details_no_stream_name default wk-heartbeat-u
+test_details_no_stream_name default-compact wk-heartbeat-u -p compact=yes
+test_details_no_stream_name default-compact-without-metadata wk-heartbeat-u -p compact=yes,with-metadata=no
+test_details_no_stream_name default-compact-without-time wk-heartbeat-u -p compact=yes,with-time=no
+test_details_no_stream_name default-without-data wk-heartbeat-u -p with-data=no
+test_details_no_stream_name default-without-data-without-metadata wk-heartbeat-u -p with-data=no,with-metadata=no
+test_details_no_stream_name default-without-metadata wk-heartbeat-u -p with-metadata=no
+test_details_no_stream_name default-without-names wk-heartbeat-u -p with-stream-name=no,with-trace-name=no,with-stream-class-name=no
+test_details_no_stream_name default-without-time wk-heartbeat-u -p with-time=no
+test_details_no_stream_name default-without-trace-name wk-heartbeat-u -p with-trace-name=no
+test_details_no_stream_name default-without-uuid wk-heartbeat-u -p with-uuid=no
+test_details_no_stream_name no-packet-context no-packet-context
diff --git a/tests/plugins/sink.text.details/succeed/test_succeed b/tests/plugins/sink.text.details/succeed/test_succeed
deleted file mode 100755 (executable)
index 9c21155..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
-fi
-
-# shellcheck source=../../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/sink.text.details/succeed"
-expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
-
-test_details() {
-       local test_name="$1"
-       local trace_name="$2"
-       shift 2
-       local details_args=("$@")
-       local trace_dir="$BT_CTF_TRACES_PATH/succeed/$trace_name"
-       local expect_path="$expect_dir/$test_name.expect"
-
-       bt_diff_cli "$expect_path" /dev/null \
-               "$trace_dir" -p trace-name=the-trace \
-               -c sink.text.details "${details_args[@]+${details_args[@]}}"
-       ok $? "'$test_name' test has the expected output"
-}
-
-# This is used for the moment because the source is `src.ctf.fs` and
-# such a component can make its stream names contain absolute paths.
-test_details_no_stream_name() {
-       local test_name="$1"
-       local trace_name="$2"
-       shift 2
-       local details_args=("$@")
-
-       test_details "$test_name" "$trace_name" \
-               "${details_args[@]+${details_args[@]}}" -p with-stream-name=no
-}
-
-plan_tests 12
-
-test_details_no_stream_name default wk-heartbeat-u
-test_details_no_stream_name default-compact wk-heartbeat-u -p compact=yes
-test_details_no_stream_name default-compact-without-metadata wk-heartbeat-u -p compact=yes,with-metadata=no
-test_details_no_stream_name default-compact-without-time wk-heartbeat-u -p compact=yes,with-time=no
-test_details_no_stream_name default-without-data wk-heartbeat-u -p with-data=no
-test_details_no_stream_name default-without-data-without-metadata wk-heartbeat-u -p with-data=no,with-metadata=no
-test_details_no_stream_name default-without-metadata wk-heartbeat-u -p with-metadata=no
-test_details_no_stream_name default-without-names wk-heartbeat-u -p with-stream-name=no,with-trace-name=no,with-stream-class-name=no
-test_details_no_stream_name default-without-time wk-heartbeat-u -p with-time=no
-test_details_no_stream_name default-without-trace-name wk-heartbeat-u -p with-trace-name=no
-test_details_no_stream_name default-without-uuid wk-heartbeat-u -p with-uuid=no
-test_details_no_stream_name no-packet-context no-packet-context
index 2eb912ff4e2f76a12b70933631f223a83ff06980..4760ef5e5e19456998cee0f9735448eec9d1393a 100644 (file)
@@ -1,3 +1,3 @@
 dist_check_SCRIPTS = \
-       test_pretty_python \
-       test_enum
+       test-pretty-python.sh \
+       test-enum.sh
diff --git a/tests/plugins/sink.text.pretty/test-enum.sh b/tests/plugins/sink.text.pretty/test-enum.sh
new file mode 100755 (executable)
index 0000000..fb68ca4
--- /dev/null
@@ -0,0 +1,267 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020 Geneviève Bastien <gbastien@versatic.net>
+#
+# This file tests pretty printing in details some event classes that are
+# not all covered by the main babeltrace tests with traces.
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+data_dir="$BT_TESTS_DATADIR/plugins/sink.text.pretty"
+temp_stdout_expected_file=$(mktemp -t test-pretty-expected-stdout.XXXXXX)
+
+plan_tests 31
+
+function compare_enum_sorted
+{
+       local expected_file="$1"
+       local actual_file="$2"
+
+       # The order in which enum labels are printed by a `sink.text.pretty`
+       # component directly depends on the order in which mappings were added
+       # to the enum field class in the source component. This order should
+       # not be relied on when testing. Relying on it caused problems with
+       # Python component classes because different versions of Python sort
+       # data structures differently (e.g. dictionaries are insertion sorted
+       # since Python 3.7).
+
+       bt_run_in_py_env "${BT_TESTS_PYTHON_BIN}" "${BT_TESTS_SRCDIR}/utils/python/split_sort_compare.py" \
+               "$(cat "$expected_file")" "$(cat "$actual_file")"
+}
+
+function run_test
+{
+       local test_name=$1
+       local expected_to_fail="$2"
+       local value="$3"
+       local expected_stdout_file="$4"
+       local actual_stdout_file
+       local actual_stderr_file
+       local ret=0
+       local local_args=(
+               "--plugin-path" "$data_dir"
+               "-c" "src.test-pretty.TheSourceOfProblems"
+               "-p" "enum-values=\"$enum_values\""
+               "-p" "value=$value"
+               "-p" "enum-signed=$enum_signed"
+               "-c" "sink.text.pretty"
+               "-p" "print-enum-flags=true"
+       )
+
+       actual_stdout_file="$(mktemp -t actual-pretty-stdout.XXXXXX)"
+       actual_stderr_file="$(mktemp -t actual-pretty-stderr.XXXXXX)"
+
+       bt_cli "$actual_stdout_file" "$actual_stderr_file" "${local_args[@]}"
+
+       compare_enum_sorted "$expected_stdout_file" "$actual_stdout_file"
+       ret_stdout=$?
+
+       bt_diff /dev/null "$actual_stderr_file"
+       ret_stderr=$?
+
+       if ((ret_stdout != 0 || ret_stderr != 0)); then
+               ret=1
+       fi
+
+       rm -f "$actual_stdout_file" "$actual_stderr_file"
+
+       if [ "$expected_to_fail" = "1" ]; then
+               isnt $ret 0 "$test_name signed=$enum_signed with value=$value doesn't match as expected"
+       else
+               ok $ret "$test_name signed=$enum_signed with value=$value matches"
+       fi
+
+}
+
+function test_normal_enum {
+       test_name="Normal enum"
+       enum_signed="$1"
+       enum_values="single,1,1 single2,2,2 single3,4,4 range,4,8 range2,15,20"
+
+       # Hit a single value
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "single" : container = 1 ) }
+       END
+       run_test "$test_name" 0 1 "$temp_stdout_expected_file"
+
+       # Hit a single range
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "range" : container = 7 ) }
+       END
+       run_test "$test_name" 0 7 "$temp_stdout_expected_file"
+
+       # Hit a range and a value
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( { "single3", "range" } : container = 4 ) }
+       END
+       run_test "$test_name" 0 4 "$temp_stdout_expected_file"
+
+       # Hit a range and a value
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( { "NOT_A_LABEL", "DOESNT_EXIST" } : container = 4 ) }
+       END
+       run_test "$test_name" 1 4 "$temp_stdout_expected_file"
+
+       # Unknown
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = 21 ) }
+       END
+       run_test "$test_name" 0 21 "$temp_stdout_expected_file"
+
+       # Unknown but with bits with a value, but range larger than 1 element
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = 12 ) }
+       END
+       run_test "$test_name" 0 12 "$temp_stdout_expected_file"
+
+       # Unknown value of 0
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = 0 ) }
+       END
+       run_test "$test_name" 0 0 "$temp_stdout_expected_file"
+}
+
+function test_normal_enum_negative {
+       test_name="Normal enum with negative value"
+       enum_signed="true"
+       enum_values="zero,0,0 single,1,1 single2,2,2 single3,4,4 range,4,8 negative,-1,-1 rangeNegative,-6,-2"
+
+       # Hit a single negative value
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "negative" : container = -1 ) }
+       END
+       run_test "$test_name" 0 -1 "$temp_stdout_expected_file"
+
+       # Hit a single negative range
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "rangeNegative" : container = -6 ) }
+       END
+       run_test "$test_name" 0 -6 "$temp_stdout_expected_file"
+
+       # Unknown
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = -7 ) }
+       END
+       run_test "$test_name" 0 -7 "$temp_stdout_expected_file"
+
+       # value of 0
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "zero" : container = 0 ) }
+       END
+       run_test "$test_name" 0 0 "$temp_stdout_expected_file"
+}
+
+function test_bit_flag_enum {
+       test_name="Bit flag enum"
+       enum_signed="false"
+       enum_values="bit0,1,1 bit0bis,1,1 bit1,2,2 bit3,4,4 bit4,8,8 bit5,16,16 bit5,32,32"
+
+       # Single value hit
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit1" : container = 2 ) }
+       END
+       run_test "$test_name" 0 2 "$temp_stdout_expected_file"
+
+       # Multiple flags set
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit3" | "bit4" : container = 12 ) }
+       END
+       run_test "$test_name" 0 12 "$temp_stdout_expected_file"
+
+       # Some unknown bit
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = 68 ) }
+       END
+       run_test "$test_name" 0 68 "$temp_stdout_expected_file"
+
+       # Multiple labels for bit 0
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( { "bit0", "bit0bis" } : container = 1 ) }
+       END
+       run_test "$test_name" 0 1 "$temp_stdout_expected_file"
+
+       # Two labels for bit 0 and one label for bit 1
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( { "bit0", "bit0bis" } | "bit1" : container = 3 ) }
+       END
+       run_test "$test_name" 0 3 "$temp_stdout_expected_file"
+
+       # Single label for bit 0
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit5" | "bit5" : container = 48 ) }
+       END
+       run_test "$test_name" 0 48 "$temp_stdout_expected_file"
+
+       # negative value
+       enum_signed="true"
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = -1 ) }
+       END
+       run_test "$test_name" 0 -1 "$temp_stdout_expected_file"
+}
+
+function test_mixed_enum {
+       test_name="Mixed enum bits at beginning"
+       enum_signed="false"
+       enum_values="bit0,1,1 bit1,2,2 bit2,4,4 bit3,8,8 bit4,16,16 range,32,44 singleValue,45,45"
+
+       # Value with bit fields
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit0" | "bit1" | "bit2" | "bit3" | "bit4" : container = 31 ) }
+       END
+       run_test "$test_name" 0 31 "$temp_stdout_expected_file"
+
+       # A value with some bit flags set, but within another range
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "range" : container = 36 ) }
+       END
+       run_test "$test_name" 0 36 "$temp_stdout_expected_file"
+
+       # A value with some bit flags set, but corresponding to another value
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "singleValue" : container = 45 ) }
+       END
+       run_test "$test_name" 0 45 "$temp_stdout_expected_file"
+
+       # Value above the ranges
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( <unknown> : container = 46 ) }
+       END
+       run_test "$test_name" 0 46 "$temp_stdout_expected_file"
+
+       # Since low values are often powers of 2, they may be considered bit flags too
+       test_name="Mixed enum bits at end"
+       enum_signed="false"
+       enum_values="val1,1,1 val2,2,2 val3,3,3 val4,4,4 val5,5,5 bit3,8,8 bit4,16,16 bit5,32,32"
+
+       # Value with bit fields
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "bit4" : container = 16 ) }
+       END
+       run_test "$test_name" 0 16 "$temp_stdout_expected_file"
+
+       # Value with a bit field set both at beginning and end
+       cat <<- 'END' > "$temp_stdout_expected_file"
+       with_enum: { enum_field = ( "val1" | "bit4" : container = 17 ) }
+       END
+       run_test "$test_name" 0 17 "$temp_stdout_expected_file"
+}
+
+# Enumerations tests
+test_normal_enum "false"
+test_normal_enum "true"
+test_normal_enum_negative
+test_bit_flag_enum
+test_mixed_enum
+
+rm -f "$temp_stdout_expected_file"
diff --git a/tests/plugins/sink.text.pretty/test-pretty-python.sh b/tests/plugins/sink.text.pretty/test-pretty-python.sh
new file mode 100755 (executable)
index 0000000..d967073
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+bt_run_py_test "${BT_TESTS_SRCDIR}/plugins/sink.text.pretty" "test_*"
diff --git a/tests/plugins/sink.text.pretty/test_enum b/tests/plugins/sink.text.pretty/test_enum
deleted file mode 100755 (executable)
index 1d6b723..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2020 Geneviève Bastien <gbastien@versatic.net>
-#
-# This file tests pretty printing in details some event classes that are
-# not all covered by the main babeltrace tests with traces.
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-data_dir="$BT_TESTS_DATADIR/plugins/sink.text.pretty"
-temp_stdout_expected_file=$(mktemp -t test_pretty_expected_stdout.XXXXXX)
-
-plan_tests 31
-
-function compare_enum_sorted
-{
-       local expected_file="$1"
-       local actual_file="$2"
-
-       # The order in which enum labels are printed by a `sink.text.pretty`
-       # component directly depends on the order in which mappings were added
-       # to the enum field class in the source component. This order should
-       # not be relied on when testing. Relying on it caused problems with
-       # Python component classes because different versions of Python sort
-       # data structures differently (e.g. dictionaries are insertion sorted
-       # since Python 3.7).
-
-       run_python_bt2 "${BT_TESTS_PYTHON_BIN}" "${BT_TESTS_SRCDIR}/utils/python/split_sort_compare.py" \
-               "$(cat "$expected_file")" "$(cat "$actual_file")"
-}
-
-function run_test
-{
-       local test_name=$1
-       local expected_to_fail="$2"
-       local value="$3"
-       local expected_stdout_file="$4"
-       local actual_stdout_file
-       local actual_stderr_file
-       local ret=0
-       local local_args=(
-               "--plugin-path" "$data_dir"
-               "-c" "src.test-pretty.TheSourceOfProblems"
-               "-p" "enum-values=\"$enum_values\""
-               "-p" "value=$value"
-               "-p" "enum-signed=$enum_signed"
-               "-c" "sink.text.pretty"
-               "-p" "print-enum-flags=true"
-       )
-
-       actual_stdout_file="$(mktemp -t actual_pretty_stdout.XXXXXX)"
-       actual_stderr_file="$(mktemp -t actual_pretty_stderr.XXXXXX)"
-
-       bt_cli "$actual_stdout_file" "$actual_stderr_file" "${local_args[@]}"
-
-       compare_enum_sorted "$expected_stdout_file" "$actual_stdout_file"
-       ret_stdout=$?
-
-       bt_diff /dev/null "$actual_stderr_file"
-       ret_stderr=$?
-
-       if ((ret_stdout != 0 || ret_stderr != 0)); then
-               ret=1
-       fi
-
-       rm -f "$actual_stdout_file" "$actual_stderr_file"
-
-       if [ "$expected_to_fail" = "1" ]; then
-               isnt $ret 0 "$test_name signed=$enum_signed with value=$value doesn't match as expected"
-       else
-               ok $ret "$test_name signed=$enum_signed with value=$value matches"
-       fi
-
-}
-
-function test_normal_enum {
-       test_name="Normal enum"
-       enum_signed="$1"
-       enum_values="single,1,1 single2,2,2 single3,4,4 range,4,8 range2,15,20"
-
-       # Hit a single value
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "single" : container = 1 ) }
-       END
-       run_test "$test_name" 0 1 "$temp_stdout_expected_file"
-
-       # Hit a single range
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "range" : container = 7 ) }
-       END
-       run_test "$test_name" 0 7 "$temp_stdout_expected_file"
-
-       # Hit a range and a value
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( { "single3", "range" } : container = 4 ) }
-       END
-       run_test "$test_name" 0 4 "$temp_stdout_expected_file"
-
-       # Hit a range and a value
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( { "NOT_A_LABEL", "DOESNT_EXIST" } : container = 4 ) }
-       END
-       run_test "$test_name" 1 4 "$temp_stdout_expected_file"
-
-       # Unknown
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( <unknown> : container = 21 ) }
-       END
-       run_test "$test_name" 0 21 "$temp_stdout_expected_file"
-
-       # Unknown but with bits with a value, but range larger than 1 element
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( <unknown> : container = 12 ) }
-       END
-       run_test "$test_name" 0 12 "$temp_stdout_expected_file"
-
-       # Unknown value of 0
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( <unknown> : container = 0 ) }
-       END
-       run_test "$test_name" 0 0 "$temp_stdout_expected_file"
-}
-
-function test_normal_enum_negative {
-       test_name="Normal enum with negative value"
-       enum_signed="true"
-       enum_values="zero,0,0 single,1,1 single2,2,2 single3,4,4 range,4,8 negative,-1,-1 rangeNegative,-6,-2"
-
-       # Hit a single negative value
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "negative" : container = -1 ) }
-       END
-       run_test "$test_name" 0 -1 "$temp_stdout_expected_file"
-
-       # Hit a single negative range
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "rangeNegative" : container = -6 ) }
-       END
-       run_test "$test_name" 0 -6 "$temp_stdout_expected_file"
-
-       # Unknown
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( <unknown> : container = -7 ) }
-       END
-       run_test "$test_name" 0 -7 "$temp_stdout_expected_file"
-
-       # value of 0
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "zero" : container = 0 ) }
-       END
-       run_test "$test_name" 0 0 "$temp_stdout_expected_file"
-}
-
-function test_bit_flag_enum {
-       test_name="Bit flag enum"
-       enum_signed="false"
-       enum_values="bit0,1,1 bit0bis,1,1 bit1,2,2 bit3,4,4 bit4,8,8 bit5,16,16 bit5,32,32"
-
-       # Single value hit
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "bit1" : container = 2 ) }
-       END
-       run_test "$test_name" 0 2 "$temp_stdout_expected_file"
-
-       # Multiple flags set
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "bit3" | "bit4" : container = 12 ) }
-       END
-       run_test "$test_name" 0 12 "$temp_stdout_expected_file"
-
-       # Some unknown bit
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( <unknown> : container = 68 ) }
-       END
-       run_test "$test_name" 0 68 "$temp_stdout_expected_file"
-
-       # Multiple labels for bit 0
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( { "bit0", "bit0bis" } : container = 1 ) }
-       END
-       run_test "$test_name" 0 1 "$temp_stdout_expected_file"
-
-       # Two labels for bit 0 and one label for bit 1
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( { "bit0", "bit0bis" } | "bit1" : container = 3 ) }
-       END
-       run_test "$test_name" 0 3 "$temp_stdout_expected_file"
-
-       # Single label for bit 0
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "bit5" | "bit5" : container = 48 ) }
-       END
-       run_test "$test_name" 0 48 "$temp_stdout_expected_file"
-
-       # negative value
-       enum_signed="true"
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( <unknown> : container = -1 ) }
-       END
-       run_test "$test_name" 0 -1 "$temp_stdout_expected_file"
-}
-
-function test_mixed_enum {
-       test_name="Mixed enum bits at beginning"
-       enum_signed="false"
-       enum_values="bit0,1,1 bit1,2,2 bit2,4,4 bit3,8,8 bit4,16,16 range,32,44 singleValue,45,45"
-
-       # Value with bit fields
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "bit0" | "bit1" | "bit2" | "bit3" | "bit4" : container = 31 ) }
-       END
-       run_test "$test_name" 0 31 "$temp_stdout_expected_file"
-
-       # A value with some bit flags set, but within another range
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "range" : container = 36 ) }
-       END
-       run_test "$test_name" 0 36 "$temp_stdout_expected_file"
-
-       # A value with some bit flags set, but corresponding to another value
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "singleValue" : container = 45 ) }
-       END
-       run_test "$test_name" 0 45 "$temp_stdout_expected_file"
-
-       # Value above the ranges
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( <unknown> : container = 46 ) }
-       END
-       run_test "$test_name" 0 46 "$temp_stdout_expected_file"
-
-       # Since low values are often powers of 2, they may be considered bit flags too
-       test_name="Mixed enum bits at end"
-       enum_signed="false"
-       enum_values="val1,1,1 val2,2,2 val3,3,3 val4,4,4 val5,5,5 bit3,8,8 bit4,16,16 bit5,32,32"
-
-       # Value with bit fields
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "bit4" : container = 16 ) }
-       END
-       run_test "$test_name" 0 16 "$temp_stdout_expected_file"
-
-       # Value with a bit field set both at beginning and end
-       cat <<- 'END' > "$temp_stdout_expected_file"
-       with_enum: { enum_field = ( "val1" | "bit4" : container = 17 ) }
-       END
-       run_test "$test_name" 0 17 "$temp_stdout_expected_file"
-}
-
-# Enumerations tests
-test_normal_enum "false"
-test_normal_enum "true"
-test_normal_enum_negative
-test_bit_flag_enum
-test_mixed_enum
-
-rm -f "$temp_stdout_expected_file"
index 0e0cd218e35f7af5230ca34a7461f577e98407c6..f04a04dc7182aaee9bc7bb4781e2d258a51fcecd 100644 (file)
@@ -3,6 +3,7 @@
 # Copyright (C) 2020 EfficiOS, Inc.
 
 import unittest
+
 import bt2
 
 
diff --git a/tests/plugins/sink.text.pretty/test_pretty_python b/tests/plugins/sink.text.pretty/test_pretty_python
deleted file mode 100755 (executable)
index fe395e7..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-run_python_bt2_test "${BT_TESTS_SRCDIR}/plugins/sink.text.pretty" "test_*"
index 745d8e729e01ddf7ae9872264ad651e8513fef95..8c3673534a9e782e16b826a713adc8d9a229a130 100644 (file)
@@ -3,10 +3,11 @@
 SUBDIRS = succeed
 
 dist_check_SCRIPTS = \
-       fail/test_fail \
-       query/test_query_metadata_info \
-       query/test_query_support_info \
+       fail/test-fail.sh \
+       query/test-query-metadata-info.sh \
+       query/test-query-support-info.sh \
        query/test_query_support_info.py \
-       query/test_query_trace_info \
+       query/test-query-trace-info.sh \
        query/test_query_trace_info.py \
-       test_deterministic_ordering
+       test-deterministic-ordering.sh \
+       field/test-field.sh
diff --git a/tests/plugins/src.ctf.fs/fail/test-fail.sh b/tests/plugins/src.ctf.fs/fail/test-fail.sh
new file mode 100755 (executable)
index 0000000..8e01469
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 EfficiOS Inc.
+#
+
+# This test validates that a `src.ctf.fs` component handles gracefully invalid
+# CTF traces and produces the expected error message.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+fail_trace_dir="$BT_CTF_TRACES_PATH/fail"
+
+stdout_file=$(mktemp -t test-ctf-fail-stdout.XXXXXX)
+stderr_file=$(mktemp -t test-ctf-fail-stderr.XXXXXX)
+data_dir="${BT_TESTS_SRCDIR}/data/plugins/src.ctf.fs/fail"
+
+test_fail() {
+       local name="$1"
+       local expected_stdout_file="$2"
+       local expected_error_msg="$3"
+
+       bt_cli "${stdout_file}" "${stderr_file}" \
+               -c sink.text.details -p "with-trace-name=no,with-stream-name=no" "${fail_trace_dir}/${name}"
+       isnt $? 0 "Trace ${name}: babeltrace exits with an error"
+
+       bt_diff "${expected_stdout_file}" "${stdout_file}"
+       ok $? "Trace ${name}: babeltrace produces the expected stdout"
+
+       # The expected error message will likely be found in the error stream
+       # even if Babeltrace aborts (e.g. hits an assert).  Check that the
+       # Babeltrace CLI finishes gracefully by checking that the error stream
+       # contains an error stack printed by the CLI.
+       bt_grep_ok \
+               "^CAUSED BY " \
+               "$stderr_file" \
+               "Trace $name: babeltrace produces an error stack"
+
+       bt_grep_ok \
+               "$expected_error_msg" \
+               "$stderr_file" \
+               "Trace $name: babeltrace produces the expected error message"
+}
+
+
+plan_tests 20
+
+test_fail \
+       "invalid-packet-size/trace" \
+       "/dev/null" \
+       "Failed to index CTF stream file '.*channel0_3'"
+
+test_fail \
+       "valid-events-then-invalid-events" \
+       "${data_dir}/valid-events-then-invalid-events.expect" \
+       "No event class with ID of event class ID to use in stream class: .*stream-class-id=0, event-class-id=255"
+
+test_fail \
+       "metadata-syntax-error" \
+       "/dev/null" \
+       "^  At line 3 in metadata stream: syntax error, unexpected CTF_RSBRAC: token=\"]\""
+
+test_fail \
+       "invalid-sequence-length-field-class" \
+       "/dev/null" \
+       "Sequence field class's length field class is not an unsigned integer field class: "
+
+test_fail \
+       "invalid-variant-selector-field-class" \
+       "/dev/null" \
+       "Variant field class's tag field class is not an enumeration field class: "
+
+rm -f "${stdout_file}" "${stderr_file}"
diff --git a/tests/plugins/src.ctf.fs/fail/test_fail b/tests/plugins/src.ctf.fs/fail/test_fail
deleted file mode 100755 (executable)
index 1c75a0d..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 EfficiOS Inc.
-#
-
-# This test validates that a `src.ctf.fs` component handles gracefully invalid
-# CTF traces and produces the expected error message.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
-fi
-
-# shellcheck source=../../../utils/utils.sh
-source "$UTILSSH"
-
-fail_trace_dir="$BT_CTF_TRACES_PATH/fail"
-
-stdout_file=$(mktemp -t test_ctf_fail_stdout.XXXXXX)
-stderr_file=$(mktemp -t test_ctf_fail_stderr.XXXXXX)
-data_dir="${BT_TESTS_SRCDIR}/data/plugins/src.ctf.fs/fail"
-
-test_fail() {
-       local name="$1"
-       local expected_stdout_file="$2"
-       local expected_error_msg="$3"
-
-       bt_cli "${stdout_file}" "${stderr_file}" \
-               -c sink.text.details -p "with-trace-name=no,with-stream-name=no" "${fail_trace_dir}/${name}"
-       isnt $? 0 "Trace ${name}: babeltrace exits with an error"
-
-       bt_diff "${expected_stdout_file}" "${stdout_file}"
-       ok $? "Trace ${name}: babeltrace produces the expected stdout"
-
-       # The expected error message will likely be found in the error stream
-       # even if Babeltrace aborts (e.g. hits an assert).  Check that the
-       # Babeltrace CLI finishes gracefully by checking that the error stream
-       # contains an error stack printed by the CLI.
-       grep --silent "^CAUSED BY " "${stderr_file}"
-       ok $? "Trace ${name}: babeltrace produces an error stack"
-
-       grep --silent "${expected_error_msg}" "${stderr_file}"
-       ok $? "Trace ${name}: babeltrace produces the expected error message"
-}
-
-
-plan_tests 12
-
-test_fail \
-       "invalid-packet-size/trace" \
-       "/dev/null" \
-       "Failed to index CTF stream file '.*channel0_3'"
-
-test_fail \
-       "valid-events-then-invalid-events" \
-       "${data_dir}/valid-events-then-invalid-events.expect" \
-       "No event class with ID of event class ID to use in stream class: .*stream-class-id=0, event-class-id=255"
-
-test_fail \
-       "metadata-syntax-error" \
-       "/dev/null" \
-       "^  At line 3 in metadata stream: syntax error, unexpected CTF_RSBRAC: token=\"]\""
-
-rm -f "${stdout_file}" "${stderr_file}"
diff --git a/tests/plugins/src.ctf.fs/field/test-field.sh b/tests/plugins/src.ctf.fs/field/test-field.sh
new file mode 100755 (executable)
index 0000000..df035d3
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2023 Efficios, Inc.
+
+SH_TAP=1
+
+if [[ -n ${BT_TESTS_SRCDIR:-} ]]; then
+        UTILSSH=$BT_TESTS_SRCDIR/utils/utils.sh
+else
+        UTILSSH=$(dirname "$0")/../../../utils/utils.sh
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+# Directory containing the plugin
+data_dir=$BT_TESTS_DATADIR/plugins/src.ctf.fs/field
+
+test_pass() {
+    local -r mp_path=$1
+    local -r output_dir=$(mktemp -d)
+
+    run_python "$BT_TESTS_PYTHON_BIN" "$data_dir/data_from_mp.py" "$mp_path" "$output_dir"
+
+    local -r res_path=$(mktemp)
+
+    bt_cli "$res_path" /dev/null --plugin-path="$data_dir" \
+        -c sink.test-text.single "$output_dir/trace"
+    bt_diff "$res_path" "$output_dir/expect"
+    ok $? "$mp_path"
+    rm -rf "$output_dir" "$res_path"
+}
+
+plan_tests 6
+
+for mp_path in "$data_dir"/ctf-1/pass-*.mp; do
+    test_pass "$mp_path"
+done
diff --git a/tests/plugins/src.ctf.fs/query/test-query-metadata-info.sh b/tests/plugins/src.ctf.fs/query/test-query-metadata-info.sh
new file mode 100755 (executable)
index 0000000..6f4545b
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+# Copyright (C) 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
+#
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/src.ctf.fs/query"
+succeed_trace_dir="$BT_CTF_TRACES_PATH/succeed"
+expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
+
+test_query_metadata_info() {
+       local name="$1"
+       local ret=0
+       local trace_path="$succeed_trace_dir/$name"
+       local expected_stdout="$expect_dir/metadata-info-$name.expect"
+       local temp_stdout_output_file
+       local temp_stderr_output_file
+       local query=("query" "src.ctf.fs" "metadata-info" "--params" "path=\"$trace_path\"")
+
+       temp_stdout_output_file="$(mktemp -t actual-stdout.XXXXXX)"
+       temp_stderr_output_file="$(mktemp -t actual-stderr.XXXXXX)"
+
+       bt_cli "$temp_stdout_output_file" "$temp_stderr_output_file" \
+               "${query[@]}"
+
+       bt_diff "$expected_stdout" "$temp_stdout_output_file"
+       ret_stdout=$?
+
+       bt_diff /dev/null "$temp_stderr_output_file"
+       ret_stderr=$?
+
+       if ((ret_stdout != 0 || ret_stderr != 0)); then
+               ret=1
+       fi
+
+       ok $ret "Trace '$name' \`metadata-info\` query gives the expected output"
+       rm -f "$temp_stdout_output_file" "$temp_stderr_output_file"
+}
+
+test_non_existent_trace_dir() {
+       local empty_dir
+       local stdout_file
+       local stderr_file
+       local query
+
+       empty_dir=$(mktemp -d)
+       stdout_file="$(mktemp -t actual-stdout.XXXXXX)"
+       stderr_file="$(mktemp -t actual-stderr.XXXXXX)"
+       query=("query" "src.ctf.fs" "metadata-info" "--params" "path=\"$empty_dir\"")
+
+       bt_cli "$stdout_file" "$stderr_file" \
+               "${query[@]}"
+       isnt $? 0 "non existent trace dir: babeltrace exits with an error"
+
+       bt_diff "/dev/null" "${stdout_file}"
+       ok $? "non existent trace dir: babeltrace produces the expected stdout"
+
+       bt_grep_ok \
+               "^CAUSED BY " \
+               "${stderr_file}" \
+               "non existent trace dir: babeltrace produces an error stack"
+
+       bt_grep_ok \
+               "Failed to open metadata file: No such file or directory: path=\".*metadata\"" \
+               "$stderr_file" \
+               "non existent trace dir: babeltrace produces the expected error message"
+
+       rm -f "${stdout_file}" "${stderr_file}"
+       rmdir "${empty_dir}"
+}
+
+plan_tests 7
+test_query_metadata_info succeed1
+test_non_existent_trace_dir
+test_query_metadata_info lf-metadata
+test_query_metadata_info crlf-metadata
diff --git a/tests/plugins/src.ctf.fs/query/test-query-support-info.sh b/tests/plugins/src.ctf.fs/query/test-query-support-info.sh
new file mode 100755 (executable)
index 0000000..4c45802
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+bt_run_py_test "${BT_TESTS_SRCDIR}/plugins/src.ctf.fs/query" test_query_support_info.py
diff --git a/tests/plugins/src.ctf.fs/query/test-query-trace-info.sh b/tests/plugins/src.ctf.fs/query/test-query-trace-info.sh
new file mode 100755 (executable)
index 0000000..1c5b656
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+bt_run_py_test "${BT_TESTS_SRCDIR}/plugins/src.ctf.fs/query" test_query_trace_info.py
diff --git a/tests/plugins/src.ctf.fs/query/test_query_metadata_info b/tests/plugins/src.ctf.fs/query/test_query_metadata_info
deleted file mode 100755 (executable)
index 6db9009..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-# Copyright (C) 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
-#
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
-fi
-
-# shellcheck source=../../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/src.ctf.fs/query"
-succeed_trace_dir="$BT_CTF_TRACES_PATH/succeed"
-expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
-
-test_query_metadata_info() {
-       local name="$1"
-       local ret=0
-       local trace_path="$succeed_trace_dir/$name"
-       local expected_stdout="$expect_dir/metadata-info-$name.expect"
-       local temp_stdout_output_file
-       local temp_stderr_output_file
-       local query=("query" "src.ctf.fs" "metadata-info" "--params" "path=\"$trace_path\"")
-
-       temp_stdout_output_file="$(mktemp -t actual_stdout.XXXXXX)"
-       temp_stderr_output_file="$(mktemp -t actual_stderr.XXXXXX)"
-
-       bt_cli "$temp_stdout_output_file" "$temp_stderr_output_file" \
-               "${query[@]}"
-
-       bt_diff "$expected_stdout" "$temp_stdout_output_file"
-       ret_stdout=$?
-
-       bt_diff /dev/null "$temp_stderr_output_file"
-       ret_stderr=$?
-
-       if ((ret_stdout != 0 || ret_stderr != 0)); then
-               ret=1
-       fi
-
-       ok $ret "Trace '$name' \`metadata-info\` query gives the expected output"
-       rm -f "$temp_stdout_output_file" "$temp_stderr_output_file"
-}
-
-plan_tests 1
-test_query_metadata_info succeed1
diff --git a/tests/plugins/src.ctf.fs/query/test_query_support_info b/tests/plugins/src.ctf.fs/query/test_query_support_info
deleted file mode 100755 (executable)
index 5104462..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
-fi
-
-# shellcheck source=../../../utils/utils.sh
-source "$UTILSSH"
-
-run_python_bt2_test "${BT_TESTS_SRCDIR}/plugins/src.ctf.fs/query" test_query_support_info.py
index 527a5daec1c1f4d8ac6743145a3930dcd7e9aef6..683312f59746fdbfff29d80923531f3f7277a973 100644 (file)
@@ -3,10 +3,10 @@
 # Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
 #
 
-import unittest
-import bt2
 import os
+import unittest
 
+import bt2
 
 session_rotation_trace_path = os.path.join(
     os.environ["BT_CTF_TRACES_PATH"], "succeed", "session-rotation"
diff --git a/tests/plugins/src.ctf.fs/query/test_query_trace_info b/tests/plugins/src.ctf.fs/query/test_query_trace_info
deleted file mode 100755 (executable)
index 49f316b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
-fi
-
-# shellcheck source=../../../utils/utils.sh
-source "$UTILSSH"
-
-run_python_bt2_test "${BT_TESTS_SRCDIR}/plugins/src.ctf.fs/query" test_query_trace_info.py
index ee906356bdfff0a5360abff8b8d62ebd61a26932..18a17120e9027efd500cf1e21cb7b8dddb3a73de 100644 (file)
@@ -3,11 +3,11 @@
 # Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
 #
 
-import unittest
-import bt2
 import os
 import re
+import unittest
 
+import bt2
 
 test_ctf_traces_path = os.environ["BT_CTF_TRACES_PATH"]
 
index 2cfbe2d93f9965afd797e9bf1ecd3e23427e40c3..f424a93d389c9c084a4e0f93d35b34b41080db93 100644 (file)
@@ -1,12 +1,12 @@
 # SPDX-License-Identifier: MIT
 
-dist_check_SCRIPTS = test_succeed
+dist_check_SCRIPTS = test-succeed.sh
 
 # CTF trace generators
 GEN_TRACE_LDADD = \
        $(top_builddir)/src/ctf-writer/libbabeltrace2-ctf-writer.la \
-       $(top_builddir)/src/common/libbabeltrace2-common.la \
-       $(top_builddir)/src/logging/libbabeltrace2-logging.la
+       $(top_builddir)/src/common/libcommon.la \
+       $(top_builddir)/src/logging/liblogging.la
 
 gen_trace_simple_SOURCES = gen-trace-simple.c
 gen_trace_simple_LDADD = $(GEN_TRACE_LDADD)
diff --git a/tests/plugins/src.ctf.fs/succeed/test-succeed.sh b/tests/plugins/src.ctf.fs/succeed/test-succeed.sh
new file mode 100755 (executable)
index 0000000..5a00601
--- /dev/null
@@ -0,0 +1,141 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
+#
+
+# This test validates that a `src.ctf.fs` component successfully reads
+# specific CTF traces and creates the expected messages.
+#
+# Such CTF traces to open either exist (in `tests/ctf-traces/succeed`)
+# or are generated by this test using local trace generators.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
+fi
+
+# shellcheck source=../../../utils/utils.sh
+source "$UTILSSH"
+
+this_dir_relative="plugins/src.ctf.fs/succeed"
+this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
+succeed_trace_dir="$BT_CTF_TRACES_PATH/succeed"
+expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
+
+test_ctf_common_details_args=("-p" "with-trace-name=no,with-stream-name=no")
+
+test_ctf_gen_single() {
+       name="$1"
+
+       diag "Generating trace '$name'"
+       bt_diff_details_ctf_gen_single "$this_dir_build/gen-trace-$name" \
+               "$expect_dir/trace-$name.expect" \
+               "${test_ctf_common_details_args[@]}" "-p" "with-uuid=no"
+       ok $? "Generated trace '$name' gives the expected output"
+}
+
+test_ctf_single() {
+       name="$1"
+
+       bt_diff_details_ctf_single "$expect_dir/trace-$name.expect" \
+               "$succeed_trace_dir/$name" "${test_ctf_common_details_args[@]}"
+       ok $? "Trace '$name' gives the expected output"
+}
+
+test_packet_end() {
+       local name="$1"
+       local expected_stdout="$expect_dir/trace-$name.expect"
+       local ret=0
+       local ret_stdout
+       local ret_stderr
+       local details_comp=("-c" "sink.text.details")
+       local details_args=("-p" "with-trace-name=no,with-stream-name=no,with-metadata=no,compact=yes")
+       local temp_stdout_output_file
+       local temp_greped_stdout_output_file
+       local temp_stderr_output_file
+
+       temp_stdout_output_file="$(mktemp -t actual-stdout.XXXXXX)"
+       temp_greped_stdout_output_file="$(mktemp -t greped-stdout.XXXXXX)"
+       temp_stderr_output_file="$(mktemp -t actual-stderr.XXXXXX)"
+
+       bt_cli "$temp_stdout_output_file" "$temp_stderr_output_file" \
+               "$succeed_trace_dir/$name" "${details_comp[@]}" \
+               "${details_args[@]}"
+
+       bt_grep "Packet end" "$temp_stdout_output_file" > "$temp_greped_stdout_output_file"
+
+       bt_diff "$expected_stdout" "$temp_greped_stdout_output_file"
+       ret_stdout=$?
+
+       bt_diff /dev/null "$temp_stderr_output_file"
+       ret_stderr=$?
+
+       if ((ret_stdout != 0 || ret_stderr != 0)); then
+               ret=1
+       fi
+
+       ok $ret "Trace '$name' gives the expected output"
+       rm -f "$temp_stdout_output_file" "$temp_stderr_output_file" "$temp_greped_stdout_output_file"
+}
+
+test_force_origin_unix_epoch() {
+       local name1="$1"
+       local name2="$2"
+       local expected_stdout="$expect_dir/trace-$name1-$name2.expect"
+       local ret=0
+       local ret_stdout
+       local ret_stderr
+       local src_ctf_fs_args=("-p" "force-clock-class-origin-unix-epoch=true")
+       local details_comp=("-c" "sink.text.details")
+       local details_args=("-p" "with-trace-name=no,with-stream-name=no,with-metadata=yes,compact=yes")
+       local temp_stdout_output_file
+       local temp_stderr_output_file
+
+       temp_stdout_output_file="$(mktemp -t actual-stdout.XXXXXX)"
+       temp_stderr_output_file="$(mktemp -t actual-stderr.XXXXXX)"
+
+       bt_cli "$temp_stdout_output_file" "$temp_stderr_output_file" \
+               "$succeed_trace_dir/$name1" "${src_ctf_fs_args[@]}" \
+               "$succeed_trace_dir/$name2" "${src_ctf_fs_args[@]}" \
+               "${details_comp[@]}" "${details_args[@]}"
+
+       bt_diff "$expected_stdout" "$temp_stdout_output_file"
+       ret_stdout=$?
+
+       if ((ret_stdout != 0)); then
+               ret=1
+       fi
+
+       ok $ret "Trace '$name1' and '$name2' give the expected stdout"
+
+       bt_diff /dev/null "$temp_stderr_output_file"
+       ret_stderr=$?
+
+       if ((ret_stderr != 0)); then
+               ret=1
+       fi
+
+       ok $ret "Trace '$name1' and '$name2' give the expected stderr"
+
+       rm -f "$temp_stdout_output_file" "$temp_stderr_output_file"
+}
+
+plan_tests 13
+
+test_force_origin_unix_epoch 2packets barectf-event-before-packet
+test_ctf_gen_single simple
+test_ctf_single smalltrace
+test_ctf_single 2packets
+test_ctf_single barectf-event-before-packet
+test_ctf_single session-rotation
+test_ctf_single lttng-tracefile-rotation
+test_ctf_single array-align-elem
+test_ctf_single struct-array-align-elem
+test_ctf_single meta-ctx-sequence
+test_packet_end lttng-event-after-packet
+test_packet_end lttng-crash
diff --git a/tests/plugins/src.ctf.fs/succeed/test_succeed b/tests/plugins/src.ctf.fs/succeed/test_succeed
deleted file mode 100755 (executable)
index 81d8759..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
-#
-
-# This test validates that a `src.ctf.fs` component successfully reads
-# specific CTF traces and creates the expected messages.
-#
-# Such CTF traces to open either exist (in `tests/ctf-traces/succeed`)
-# or are generated by this test using local trace generators.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../../utils/utils.sh"
-fi
-
-# shellcheck source=../../../utils/utils.sh
-source "$UTILSSH"
-
-this_dir_relative="plugins/src.ctf.fs/succeed"
-this_dir_build="$BT_TESTS_BUILDDIR/$this_dir_relative"
-succeed_trace_dir="$BT_CTF_TRACES_PATH/succeed"
-expect_dir="$BT_TESTS_DATADIR/$this_dir_relative"
-
-test_ctf_common_details_args=("-p" "with-trace-name=no,with-stream-name=no")
-
-test_ctf_gen_single() {
-       name="$1"
-
-       diag "Generating trace '$name'"
-       bt_diff_details_ctf_gen_single "$this_dir_build/gen-trace-$name" \
-               "$expect_dir/trace-$name.expect" \
-               "${test_ctf_common_details_args[@]}" "-p" "with-uuid=no"
-       ok $? "Generated trace '$name' gives the expected output"
-}
-
-test_ctf_single() {
-       name="$1"
-
-       bt_diff_details_ctf_single "$expect_dir/trace-$name.expect" \
-               "$succeed_trace_dir/$name" "${test_ctf_common_details_args[@]}"
-       ok $? "Trace '$name' gives the expected output"
-}
-
-test_packet_end() {
-       local name="$1"
-       local expected_stdout="$expect_dir/trace-$name.expect"
-       local ret=0
-       local ret_stdout
-       local ret_stderr
-       local details_comp=("-c" "sink.text.details")
-       local details_args=("-p" "with-trace-name=no,with-stream-name=no,with-metadata=no,compact=yes")
-       local temp_stdout_output_file
-       local temp_greped_stdout_output_file
-       local temp_stderr_output_file
-
-       temp_stdout_output_file="$(mktemp -t actual_stdout.XXXXXX)"
-       temp_greped_stdout_output_file="$(mktemp -t greped_stdout.XXXXXX)"
-       temp_stderr_output_file="$(mktemp -t actual_stderr.XXXXXX)"
-
-       bt_cli "$temp_stdout_output_file" "$temp_stderr_output_file" \
-               "$succeed_trace_dir/$name" "${details_comp[@]}" \
-               "${details_args[@]}"
-
-       "$BT_TESTS_GREP_BIN" "Packet end" "$temp_stdout_output_file" > "$temp_greped_stdout_output_file"
-
-       bt_diff "$expected_stdout" "$temp_greped_stdout_output_file"
-       ret_stdout=$?
-
-       bt_diff /dev/null "$temp_stderr_output_file"
-       ret_stderr=$?
-
-       if ((ret_stdout != 0 || ret_stderr != 0)); then
-               ret=1
-       fi
-
-       ok $ret "Trace '$name' gives the expected output"
-       rm -f "$temp_stdout_output_file" "$temp_stderr_output_file" "$temp_greped_stdout_output_file"
-}
-
-test_force_origin_unix_epoch() {
-       local name1="$1"
-       local name2="$2"
-       local expected_stdout="$expect_dir/trace-$name1-$name2.expect"
-       local ret=0
-       local ret_stdout
-       local ret_stderr
-       local src_ctf_fs_args=("-p" "force-clock-class-origin-unix-epoch=true")
-       local details_comp=("-c" "sink.text.details")
-       local details_args=("-p" "with-trace-name=no,with-stream-name=no,with-metadata=yes,compact=yes")
-       local temp_stdout_output_file
-       local temp_stderr_output_file
-
-       temp_stdout_output_file="$(mktemp -t actual_stdout.XXXXXX)"
-       temp_stderr_output_file="$(mktemp -t actual_stderr.XXXXXX)"
-
-       bt_cli "$temp_stdout_output_file" "$temp_stderr_output_file" \
-               "$succeed_trace_dir/$name1" "${src_ctf_fs_args[@]}" \
-               "$succeed_trace_dir/$name2" "${src_ctf_fs_args[@]}" \
-               "${details_comp[@]}" "${details_args[@]}"
-
-       bt_diff "$expected_stdout" "$temp_stdout_output_file"
-       ret_stdout=$?
-
-       if ((ret_stdout != 0)); then
-               ret=1
-       fi
-
-       ok $ret "Trace '$name1' and '$name2' give the expected stdout"
-
-       bt_diff /dev/null "$temp_stderr_output_file"
-       ret_stderr=$?
-
-       if ((ret_stderr != 0)); then
-               ret=1
-       fi
-
-       ok $ret "Trace '$name1' and '$name2' give the expected stderr"
-
-       rm -f "$temp_stdout_output_file" "$temp_stderr_output_file"
-}
-
-plan_tests 13
-
-test_force_origin_unix_epoch 2packets barectf-event-before-packet
-test_ctf_gen_single simple
-test_ctf_single smalltrace
-test_ctf_single 2packets
-test_ctf_single barectf-event-before-packet
-test_ctf_single session-rotation
-test_ctf_single lttng-tracefile-rotation
-test_ctf_single array-align-elem
-test_ctf_single struct-array-align-elem
-test_ctf_single meta-ctx-sequence
-test_packet_end lttng-event-after-packet
-test_packet_end lttng-crash
diff --git a/tests/plugins/src.ctf.fs/test-deterministic-ordering.sh b/tests/plugins/src.ctf.fs/test-deterministic-ordering.sh
new file mode 100755 (executable)
index 0000000..f66247b
--- /dev/null
@@ -0,0 +1,108 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Efficios, Inc.
+#
+
+# Test the deterministic behavior of the src.ctf.fs component versus the
+# ordering of the given input paths.
+#
+# In presence of multiple copies of the same packet, we want it to pick the
+# copy of the packet to read in a deterministic fashion.
+#
+# This test is written assuming the specific implementation of the src.ctf.fs
+# component class, which sorts its input paths lexicographically.
+#
+# There are three traces (a-corrupted, b-not-corrupted and c-corrupted) with the
+# same UUID and the same packet, except that this packet is corrupted in
+# a-corrupted and c-corrupted. In these cases, there is an event with an
+# invalid id.  When reading these corrupted packets, we expect babeltrace to
+# emit an error.
+#
+# When reading a-corrupted and b-not-corrupted together, the copy of the packet
+# from a-corrupted is read, and babeltrace exits with an error.
+#
+# When reading b-not-corrupted and c-corrupted together, the copy of the packet
+# from b-not-corrupted is read, and babeltrace executes successfully.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+traces_dir="${BT_CTF_TRACES_PATH}/deterministic-ordering"
+trace_a_corrupted="${traces_dir}/a-corrupted"
+trace_b_not_corrupted="${traces_dir}/b-not-corrupted"
+trace_c_corrupted="${traces_dir}/c-corrupted"
+
+if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
+       # The MSYS2 shell makes a mess trying to convert the Unix-like paths
+       # to Windows-like paths, so just disable the automatic conversion and
+       # do it by hand.
+       export MSYS2_ARG_CONV_EXCL="*"
+       trace_a_corrupted=$(cygpath -m "${trace_a_corrupted}")
+       trace_b_not_corrupted=$(cygpath -m "${trace_b_not_corrupted}")
+       trace_c_corrupted=$(cygpath -m "${trace_c_corrupted}")
+fi
+
+stdout_file=$(mktemp -t test-deterministic-ordering-stdout.XXXXXX)
+stderr_file=$(mktemp -t test-deterministic-ordering-stderr.XXXXXX)
+
+expect_failure() {
+       local test_name
+       local inputs
+
+       test_name="$1"
+       inputs="$2"
+
+       bt_cli "${stdout_file}" "${stderr_file}" \
+               -c src.ctf.fs -p "inputs=[${inputs}]"
+       isnt 0 "$?" "${test_name}: exit status is not 0"
+
+       bt_grep_ok \
+               "^ERROR: " \
+               "${stderr_file}" \
+               "${test_name}: error stack is produced"
+
+       bt_grep_ok \
+               "No event class with ID of event class ID to use in stream class" \
+               "$stderr_file" \
+               "$test_name: expected error message is present"
+}
+
+expect_success() {
+       local test_name
+       local inputs
+
+       test_name="$1"
+       inputs="$2"
+
+       bt_cli "${stdout_file}" "${stderr_file}" \
+               -c src.ctf.fs -p "inputs=[${inputs}]" \
+               -c sink.text.details -p 'with-trace-name=no,with-stream-name=no,with-metadata=no,compact=yes'
+       ok "$?" "${test_name}: exit status is 0"
+
+       bt_diff "${traces_dir}/b-c.expect" "${stdout_file}"
+       ok "$?" "${test_name}: expected output is produced"
+}
+
+plan_tests 10
+
+# Trace with corrupted packet comes first lexicographically, expect a failure.
+
+expect_failure "ab" "\"${trace_a_corrupted}\",\"${trace_b_not_corrupted}\""
+expect_failure "ba" "\"${trace_b_not_corrupted}\",\"${trace_a_corrupted}\""
+
+# Trace with non-corrupted packet comes first lexicographically, expect a success.
+
+expect_success "bc" "\"${trace_b_not_corrupted}\",\"${trace_c_corrupted}\""
+expect_success "cb" "\"${trace_c_corrupted}\",\"${trace_b_not_corrupted}\""
+
+rm -f "${stdout_file}" "${stderr_file}"
diff --git a/tests/plugins/src.ctf.fs/test_deterministic_ordering b/tests/plugins/src.ctf.fs/test_deterministic_ordering
deleted file mode 100755 (executable)
index 9eb8b8f..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Efficios, Inc.
-#
-
-# Test the deterministic behavior of the src.ctf.fs component versus the
-# ordering of the given input paths.
-#
-# In presence of multiple copies of the same packet, we want it to pick the
-# copy of the packet to read in a deterministic fashion.
-#
-# This test is written assuming the specific implementation of the src.ctf.fs
-# component class, which sorts its input paths lexicographically.
-#
-# There are three traces (a-corrupted, b-not-corrupted and c-corrupted) with the
-# same UUID and the same packet, except that this packet is corrupted in
-# a-corrupted and c-corrupted. In these cases, there is an event with an
-# invalid id.  When reading these corrupted packets, we expect babeltrace to
-# emit an error.
-#
-# When reading a-corrupted and b-not-corrupted together, the copy of the packet
-# from a-corrupted is read, and babeltrace exits with an error.
-#
-# When reading b-not-corrupted and c-corrupted together, the copy of the packet
-# from b-not-corrupted is read, and babeltrace executes successfully.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-traces_dir="${BT_CTF_TRACES_PATH}/deterministic-ordering"
-trace_a_corrupted="${traces_dir}/a-corrupted"
-trace_b_not_corrupted="${traces_dir}/b-not-corrupted"
-trace_c_corrupted="${traces_dir}/c-corrupted"
-
-if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
-       # The MSYS2 shell makes a mess trying to convert the Unix-like paths
-       # to Windows-like paths, so just disable the automatic conversion and
-       # do it by hand.
-       export MSYS2_ARG_CONV_EXCL="*"
-       trace_a_corrupted=$(cygpath -m "${trace_a_corrupted}")
-       trace_b_not_corrupted=$(cygpath -m "${trace_b_not_corrupted}")
-       trace_c_corrupted=$(cygpath -m "${trace_c_corrupted}")
-fi
-
-stdout_file=$(mktemp -t test_deterministic_ordering_stdout.XXXXXX)
-stderr_file=$(mktemp -t test_deterministic_ordering_stderr.XXXXXX)
-
-expect_failure() {
-       local test_name
-       local inputs
-
-       test_name="$1"
-       inputs="$2"
-
-       bt_cli "${stdout_file}" "${stderr_file}" \
-               -c src.ctf.fs -p "inputs=[${inputs}]"
-       isnt 0 "$?" "${test_name}: exit status is not 0"
-
-       grep --silent "^ERROR: " "${stderr_file}"
-       ok "$?" "${test_name}: error stack is produced"
-
-       grep --silent "No event class with ID of event class ID to use in stream class" "${stderr_file}"
-       ok "$?" "${test_name}: expected error message is present"
-}
-
-expect_success() {
-       local test_name
-       local inputs
-
-       test_name="$1"
-       inputs="$2"
-
-       bt_cli "${stdout_file}" "${stderr_file}" \
-               -c src.ctf.fs -p "inputs=[${inputs}]" \
-               -c sink.text.details -p 'with-trace-name=no,with-stream-name=no,with-metadata=no,compact=yes'
-       ok "$?" "${test_name}: exit status is 0"
-
-       bt_diff "${traces_dir}/b-c.expect" "${stdout_file}"
-       ok "$?" "${test_name}: expected output is produced"
-}
-
-plan_tests 10
-
-# Trace with corrupted packet comes first lexicographically, expect a failure.
-
-expect_failure "ab" "\"${trace_a_corrupted}\",\"${trace_b_not_corrupted}\""
-expect_failure "ba" "\"${trace_b_not_corrupted}\",\"${trace_a_corrupted}\""
-
-# Trace with non-corrupted packet comes first lexicographically, expect a success.
-
-expect_success "bc" "\"${trace_b_not_corrupted}\",\"${trace_c_corrupted}\""
-expect_success "cb" "\"${trace_c_corrupted}\",\"${trace_b_not_corrupted}\""
-
-rm -f "${stdout_file}" "${stderr_file}"
diff --git a/tests/plugins/src.ctf.lttng-live/test-live.sh b/tests/plugins/src.ctf.lttng-live/test-live.sh
new file mode 100755 (executable)
index 0000000..6516120
--- /dev/null
@@ -0,0 +1,429 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
+#
+
+# This test validates that a `src.ctf.lttng-live` component successfully does
+# various tasks that a `src.ctf.lttng-live` component is expected to do, like
+# listing tracing sessions and receiving live traces / producing the expected
+# messages out of it.
+#
+# A mock LTTng live server is used to feed data to the component.
+
+SH_TAP=1
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
+fi
+
+# shellcheck source=../../utils/utils.sh
+source "$UTILSSH"
+
+function cleanup ()
+{
+       # Disable trap for SIGTERM since the following kill to the
+       # pidgroup will be SIGTERM. Otherwise it loops.
+       # The '-' before the pid number ($$) indicates 'kill' to signal the
+       # whole process group.
+       trap - SIGTERM && kill -- -$$
+}
+
+# Ensure that background child jobs are killed on SIGINT/SIGTERM
+trap cleanup SIGINT SIGTERM
+
+this_dir_relative="plugins/src.ctf.lttng-live"
+test_data_dir="$BT_TESTS_DATADIR/$this_dir_relative"
+trace_dir="$BT_CTF_TRACES_PATH"
+
+if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
+       # Same as the above, but in Windows form (C:\foo\bar) instead of Unix form
+       # (/c/foo/bar).
+       trace_dir_native=$(cygpath -w "${trace_dir}")
+else
+       trace_dir_native="${trace_dir}"
+fi
+
+lttng_live_server() {
+       local pid_file="$1"
+       local retcode_file="$2"
+       shift 2
+       local server_args=("$@")
+
+       local server_script="$test_data_dir/lttng_live_server.py"
+
+       # start server
+       diag "$BT_TESTS_PYTHON_BIN $server_script ${server_args[*]}"
+       bt_run_in_py_utils_env "$BT_TESTS_PYTHON_BIN" "$server_script" "${server_args[@]}" 1>&2 &
+
+       # write PID to file
+       echo $! > "$pid_file"
+
+       # wait for server to exit
+       wait
+
+       # write return code to file
+       echo $? > "$retcode_file"
+}
+
+kill_lttng_live_server() {
+       local pid_file="$1"
+
+       if [ ! -s "$pid_file" ]; then
+               return
+       fi
+
+       kill -9 "$(cat "$pid_file")"
+}
+
+get_cli_output_with_lttng_live_server() {
+       local cli_args_template="$1"
+       local cli_stdout_file="$2"
+       local cli_stderr_file="$3"
+       local port_file="$4"
+       local trace_path_prefix="$5"
+       shift 5
+       local server_args=("$@")
+
+       local i
+       local ret
+       local port
+       local cli_args
+       local server_pid_file
+       local server_retcode_file
+
+       server_args+=(--port-file "$port_file" --trace-path-prefix "$trace_path_prefix")
+       server_pid_file="$(mktemp -t test-live-server-pid.XXXXXX)"
+       server_retcode_file="$(mktemp -t test-live-server-ret.XXXXX)"
+
+       diag "Starting LTTng live server mockup"
+
+       # This starts the server, which eventually writes its listening
+       # port number to the `$port_file` file. The lttng_live_server()
+       # function itself writes the server's PID to the
+       # `$server_pid_file` file. When the server exits,
+       # lttng_live_server() writes its return code to the
+       # `$server_retcode_file` file.
+       lttng_live_server "$server_pid_file" "$server_retcode_file" "${server_args[@]}" &
+
+       # Get port number
+       i=0
+       while [ ! -s "$port_file" ]; do
+               sleep .1
+
+               # Timeout of 30 seconds
+               if [ "$i" -eq "300" ]; then
+                       # too long, kill it
+                       kill_lttng_live_server "$server_pid_file"
+                       wait
+                       rm -f "$server_pid_file"
+                       rm -f "$server_retcode_file"
+                       return 1
+               fi
+
+               i=$((i + 1))
+       done
+
+       port=$(<"$port_file")
+
+       diag "LTTng live port is $port"
+
+       cli_args=${cli_args_template//@PORT@/$port}
+
+       # Split argument string by spaces into an array.
+       IFS=' ' read -ra cli_args <<< "$cli_args"
+
+       if ! bt_cli "$cli_stdout_file" "$cli_stderr_file" "${cli_args[@]}"; then
+               # CLI failed: cancel everything else
+               kill_lttng_live_server "$server_pid_file"
+               wait
+               rm -f "$server_pid_file"
+               rm -f "$server_retcode_file"
+               return 1
+       fi
+
+       # get server's return code
+       i=0
+       while [ ! -s "$server_retcode_file" ]; do
+               sleep .1
+
+               # Timeout of 30 seconds
+               if [ "$i" -eq "300" ]; then
+                       # too long, kill it
+                       kill_lttng_live_server "$server_pid_file"
+                       wait
+                       rm -f "$server_pid_file"
+                       rm -f "$server_retcode_file"
+                       return 1
+               fi
+
+               i=$((i + 1))
+       done
+
+       wait
+
+       ret=$(<"$server_retcode_file")
+
+       rm -f "$server_pid_file"
+       rm -f "$server_retcode_file"
+       return "$ret"
+}
+
+run_test() {
+       local test_text="$1"
+       local cli_args_template="$2"
+       local expected_stdout="$3"
+       local expected_stderr="$4"
+       local trace_path_prefix="$5"
+       shift 5
+       local server_args=("$@")
+
+       local cli_stderr
+       local cli_stdout
+       local port_file
+       local port
+
+       cli_stderr="$(mktemp -t test-live-stderr.XXXXXX)"
+       cli_stdout="$(mktemp -t test-live-stdout.XXXXXX)"
+       port_file="$(mktemp -t test-live-server-port.XXXXXX)"
+
+       get_cli_output_with_lttng_live_server "$cli_args_template" "$cli_stdout" \
+               "$cli_stderr" "$port_file" "$trace_path_prefix" "${server_args[@]}"
+       port=$(<"$port_file")
+
+       bt_diff "$expected_stdout" "$cli_stdout"
+       ok $? "$test_text - stdout"
+       bt_diff "$expected_stderr" "$cli_stderr"
+       ok $? "$test_text - stderr"
+
+       rm -f "$cli_stderr"
+       rm -f "$cli_stdout"
+       rm -f "$port_file"
+}
+
+test_list_sessions() {
+       # Test the basic listing of sessions.
+       # Ensure that a multi-domain trace is seen as a single session.
+       # run_test() is not used here because the port is needed to craft the
+       # expected output.
+
+       local port
+       local port_file
+       local tmp_stdout_expected
+       local template_expected
+
+       local test_text="CLI prints the expected session list"
+       local cli_args_template="-i lttng-live net://localhost:@PORT@"
+       local server_args=("$test_data_dir/list-sessions.json")
+
+       template_expected=$(<"$test_data_dir/cli-list-sessions.expect")
+       cli_stderr="$(mktemp -t test-live-list-sessions-stderr.XXXXXX)"
+       cli_stdout="$(mktemp -t test-live-list-sessions-stdout.XXXXXX)"
+       port_file="$(mktemp -t test-live-list-sessions-server-port.XXXXXX)"
+       tmp_stdout_expected="$(mktemp -t test-live-list-sessions-stdout-expected.XXXXXX)"
+
+       get_cli_output_with_lttng_live_server "$cli_args_template" "$cli_stdout" \
+               "$cli_stderr" "$port_file" "$trace_dir_native" "${server_args[@]}"
+       port=$(<"$port_file")
+
+       # Craft the expected output. This is necessary since the port number
+       # (random) of a "relayd" is present in the output.
+       template_expected=${template_expected//@PORT@/$port}
+
+       echo "$template_expected" > "$tmp_stdout_expected"
+
+       bt_diff "$tmp_stdout_expected" "$cli_stdout"
+       ok $? "$test_text - stdout"
+       bt_diff "/dev/null" "$cli_stderr"
+       ok $? "$test_text - stderr"
+
+       rm -f "$cli_stderr"
+       rm -f "$cli_stdout"
+       rm -f "$port_file"
+       rm -f "$tmp_stdout_expected"
+}
+
+test_base() {
+       # Attach and consume data from a multi packets ust session with no
+       # discarded events.
+       local test_text="CLI attach and fetch from single-domains session - no discarded events"
+       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/trace-with-index -c sink.text.details"
+       local server_args=("$test_data_dir/base.json")
+       local expected_stdout="${test_data_dir}/cli-base.expect"
+       local expected_stderr="/dev/null"
+
+       run_test "$test_text" "$cli_args_template" "$expected_stdout" \
+               "$expected_stderr" "$trace_dir_native" "${server_args[@]}"
+}
+
+test_multi_domains() {
+       # Attach and consume data from a multi-domains session with discarded
+       # events.
+       local test_text="CLI attach and fetch from multi-domains session - discarded events"
+       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/multi-domains -c sink.text.details"
+       local server_args=("${test_data_dir}/multi-domains.json")
+       local expected_stdout="$test_data_dir/cli-multi-domains.expect"
+       local expected_stderr="/dev/null"
+
+       run_test "$test_text" "$cli_args_template" "$expected_stdout" \
+               "$expected_stderr" "$trace_dir_native" "${server_args[@]}"
+}
+
+test_rate_limited() {
+       # Attach and consume data from a multi packets ust session with no
+       # discarded events. Enforce a server side limit on the stream data
+       # requests size. Ensure that babeltrace respect the returned size and that
+       # many requests per packet works as expected.
+       # The packet size of the test trace is 4k. Limit requests to 1k.
+       local test_text="CLI many requests per packet"
+       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/trace-with-index -c sink.text.details"
+       local server_args=(--max-query-data-response-size 1024 "$test_data_dir/rate-limited.json")
+       local expected_stdout="${test_data_dir}/cli-base.expect"
+       local expected_stderr="/dev/null"
+
+       run_test "$test_text" "$cli_args_template" "$expected_stdout" \
+               "$expected_stderr" "$trace_dir_native" "${server_args[@]}"
+}
+
+test_compare_to_ctf_fs() {
+       # Compare the details text sink or ctf.fs and ctf.lttng-live to ensure
+       # that the trace is parsed the same way.
+       # Do the same with the session swapped on the relayd side. This validate
+       # that ordering is consistent between live and ctf fs.
+       local test_text="CLI src.ctf.fs vs src.ctf.lttng-live"
+       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/multi-domains -c sink.text.details --params with-trace-name=false,with-stream-name=false"
+       local server_args=("$test_data_dir/multi-domains.json")
+       local server_args_inverse=("$test_data_dir/multi-domains-inverse.json")
+       local expected_stdout
+       local expected_stderr
+
+       expected_stdout="$(mktemp -t test-live-compare-stdout-expected.XXXXXX)"
+       expected_stderr="$(mktemp -t test-live-compare-stderr-expected.XXXXXX)"
+
+       bt_cli "$expected_stdout" "$expected_stderr" "${trace_dir}/succeed/multi-domains" -c sink.text.details --params "with-trace-name=false,with-stream-name=false"
+       bt_remove_cr "${expected_stdout}"
+       bt_remove_cr "${expected_stderr}"
+       run_test "$test_text" "$cli_args_template" "$expected_stdout" \
+               "$expected_stderr" "$trace_dir_native" "${server_args[@]}"
+       diag "Inverse session order from lttng-relayd"
+       run_test "$test_text" "$cli_args_template" "$expected_stdout" \
+               "$expected_stderr" "$trace_dir_native" "${server_args_inverse[@]}"
+
+       rm -f "$expected_stdout"
+       rm -f "$expected_stderr"
+}
+
+test_inactivity_discarded_packet() {
+       # Attach and consume data from a multi-packet trace with discarded
+       # packets and emit an inactivity beacon during the discarded packets
+       # period.
+       #
+       # | pkt seq:0 |<-------discarded packets------>| pkt seq:8 |
+       # 0          20                                121       140
+       #
+       # This test was introduced to cover the following bug:
+       #
+       # When reading this type of trace locally, the CTF source is expected
+       # to introduce a "Discarded packets" message between packets 0 and 8.
+       # The timestamps of this message are [pkt0.end_ts, pkt8.begin_ts].
+       #
+       # In the context of a live source, the tracer could report an inactivity
+       # period during the interval of the "Discarded packets" message.
+       # Those messages eventually translate into a
+       # "Iterator inactivity" message with a timestamp set at the end of the
+       # inactivity period.
+       #
+       # If the tracer reports an inactivity period that ends at a point
+       # between pkt0 and pkt7 (resulting in an "Iterator inactivity" message),
+       # the live source could send a "Discarded packets" message that starts
+       # before the preceding "Iterator inactivity" message. This would break
+       # the monotonicity constraint of the graph.
+       local test_text="CLI attach and fetch from single-domains session - inactivity discarded packet"
+       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/7-lost-between-2-with-index -c sink.text.details"
+       local server_args=("$test_data_dir/inactivity-discarded-packet.json")
+       local expected_stdout="$test_data_dir/inactivity-discarded-packet.expect"
+       local expected_stderr="/dev/null"
+
+       run_test "$test_text" "$cli_args_template" "$expected_stdout" \
+               "$expected_stderr" "$trace_dir_native" "${server_args[@]}"
+}
+
+test_split_metadata() {
+       # Consume a metadata stream sent in two parts. This testcase tests the
+       # behaviour of Babeltrace when the tracing session was cleared (lttng
+       # clear) but the metadata is not yet available to the relay. In such
+       # cases, when asked for metadata, the relay will return the
+       # `LTTNG_VIEWER_METADATA_OK` status and a data length of 0. The viewer
+       # need to consider such case as a request to retry fetching metadata.
+       #
+       # This testcase emulates such behaviour by adding empty metadata
+       # packets.
+
+       local test_text="CLI attach and fetch from single-domain session - Receive metadata in two sections separated by a empty section"
+       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/split-metadata -c sink.text.details"
+       local server_args=("$test_data_dir/split-metadata.json")
+       local expected_stdout="${test_data_dir}/split-metadata.expect"
+       local expected_stderr="/dev/null"
+
+       run_test "$test_text" "$cli_args_template" "$expected_stdout" \
+               "$expected_stderr" "$trace_dir_native" "${server_args[@]}"
+}
+
+test_stored_values() {
+       # Split metadata, where the new metadata requires additional stored
+       # value slots in CTF message iterators.
+       local test_text="split metadata requiring additional stored values"
+       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/stored-values -c sink.text.details"
+       local server_args=("$test_data_dir/stored-values.json")
+       local expected_stdout="${test_data_dir}/stored-values.expect"
+       local expected_stderr="/dev/null"
+       local tmp_dir
+
+       tmp_dir=$(mktemp -d -t 'test-stored-value.XXXXXXX')
+
+       # Generate test trace.
+       bt_gen_mctf_trace "${trace_dir}/live/stored-values.mctf" "$tmp_dir/stored-values"
+
+       run_test "$test_text" "$cli_args_template" "$expected_stdout" \
+               "$expected_stderr" "$tmp_dir" "${server_args[@]}"
+
+       rm -rf "$tmp_dir"
+}
+
+test_live_new_stream_during_inactivity() {
+       # Announce a new stream while an existing stream is inactive.
+       # This requires the live consumer to check for new announced streams
+       # when it receives inactivity beacons.
+       local test_text="new stream announced while an existing stream is inactive"
+       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/new-streams -c sink.text.details"
+       local server_args=("$test_data_dir/new-streams.json")
+       local expected_stdout="${test_data_dir}/new-streams.expect"
+       local expected_stderr="/dev/null"
+       local tmp_dir
+
+       tmp_dir=$(mktemp -d -t 'test-new-streams.XXXXXXX')
+
+       # Generate test trace.
+       bt_gen_mctf_trace "${trace_dir}/live/new-streams/first-trace.mctf" "$tmp_dir/first-trace"
+       bt_gen_mctf_trace "${trace_dir}/live/new-streams/second-trace.mctf" "$tmp_dir/second-trace"
+
+       run_test "$test_text" "$cli_args_template" "$expected_stdout" \
+               "$expected_stderr" "$tmp_dir" "${server_args[@]}"
+
+       rm -rf "$tmp_dir"
+}
+
+plan_tests 20
+
+test_list_sessions
+test_base
+test_multi_domains
+test_rate_limited
+test_compare_to_ctf_fs
+test_inactivity_discarded_packet
+test_split_metadata
+test_stored_values
+test_live_new_stream_during_inactivity
diff --git a/tests/plugins/src.ctf.lttng-live/test_live b/tests/plugins/src.ctf.lttng-live/test_live
deleted file mode 100755 (executable)
index e1766e1..0000000
+++ /dev/null
@@ -1,405 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
-#
-
-# This test validates that a `src.ctf.fs` component successfully reads
-# specific CTF traces and creates the expected messages.
-#
-# Such CTF traces to open either exist (in `tests/ctf-traces/`)
-# or are generated by this test using local trace generators.
-
-SH_TAP=1
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../../utils/utils.sh"
-fi
-
-# shellcheck source=../../utils/utils.sh
-source "$UTILSSH"
-
-function cleanup ()
-{
-       # Disable trap for SIGTERM since the following kill to the
-       # pidgroup will be SIGTERM. Otherwise it loops.
-       # The '-' before the pid number ($$) indicates 'kill' to signal the
-       # whole process group.
-       trap - SIGTERM && kill -- -$$
-}
-
-# Ensure that background child jobs are killed on SIGINT/SIGTERM
-trap cleanup SIGINT SIGTERM
-
-this_dir_relative="plugins/src.ctf.lttng-live"
-test_data_dir="$BT_TESTS_DATADIR/$this_dir_relative"
-trace_dir="$BT_CTF_TRACES_PATH"
-
-if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
-       # Same as the above, but in Windows form (C:\foo\bar) instead of Unix form
-       # (/c/foo/bar).
-       trace_dir_native=$(cygpath -w "${trace_dir}")
-else
-       trace_dir_native="${trace_dir}"
-fi
-
-lttng_live_server() {
-       local port_file="$1"
-       local pid_file="$2"
-       local retcode_file="$3"
-       local server_args="$4"
-       local server_script="$test_data_dir/lttng_live_server.py"
-
-       # start server
-       diag "$BT_TESTS_PYTHON_BIN $server_script --port-file $port_file --trace-path-prefix $trace_dir_native $server_args"
-       echo "$server_args" | xargs "$BT_TESTS_PYTHON_BIN" "$server_script" \
-               --port-file "$port_file" \
-               --trace-path-prefix "$trace_dir_native" &
-
-       # write PID to file
-       echo $! > "$pid_file"
-
-       # wait for server to exit
-       wait
-
-       # write return code to file
-       echo $? > "$retcode_file"
-}
-
-kill_lttng_live_server() {
-       local pid_file="$1"
-
-       if [ ! -s "$pid_file" ]; then
-               return
-       fi
-
-       kill -9 "$(cat "$pid_file")"
-}
-
-get_cli_output_with_lttng_live_server() {
-       local cli_args_template="$1"
-       local sessions_file="$2"
-       local cli_stdout_file="$3"
-       local cli_stderr_file="$4"
-       local port_file="$5"
-
-       local i
-       local ret
-       local port
-       local cli_args
-       local server_pid_file
-       local server_retcode_file
-
-       server_pid_file="$(mktemp -t test_live_server_pid.XXXXXX)"
-       server_retcode_file="$(mktemp -t test_live_server_ret.XXXXX)"
-
-       diag "Starting LTTng live server mockup"
-
-       # This starts the server, which eventually writes its listening
-       # port number to the `$port_file` file. The lttng_live_server()
-       # function itself writes the server's PID to the
-       # `$server_pid_file` file. When the server exits,
-       # lttng_live_server() writes its return code to the
-       # `$server_retcode_file` file.
-       lttng_live_server "$port_file" "$server_pid_file" \
-               "$server_retcode_file" "$sessions_file" &
-
-       # Get port number
-       i=0
-       while [ ! -s "$port_file" ]; do
-               sleep .1
-
-               # Timeout of 30 seconds
-               if [ "$i" -eq "300" ]; then
-                       # too long, kill it
-                       kill_lttng_live_server "$server_pid_file"
-                       wait
-                       rm -f "$server_pid_file"
-                       rm -f "$server_retcode_file"
-                       return 1
-               fi
-
-               i=$((i + 1))
-       done
-
-       port=$(<"$port_file")
-
-       diag "LTTng live port is $port"
-
-       cli_args=${cli_args_template//@PORT@/$port}
-
-       # Split argument string by spaces into an array.
-       IFS=' ' read -ra cli_args <<< "$cli_args"
-
-       if ! bt_cli "$cli_stdout_file" "$cli_stderr_file" "${cli_args[@]}"; then
-               # CLI failed: cancel everything else
-               kill_lttng_live_server "$server_pid_file"
-               wait
-               rm -f "$server_pid_file"
-               rm -f "$server_retcode_file"
-               return 1
-       fi
-
-       # get server's return code
-       i=0
-       while [ ! -s "$server_retcode_file" ]; do
-               sleep .1
-
-               # Timeout of 30 seconds
-               if [ "$i" -eq "300" ]; then
-                       # too long, kill it
-                       kill_lttng_live_server "$server_pid_file"
-                       wait
-                       rm -f "$server_pid_file"
-                       rm -f "$server_retcode_file"
-                       return 1
-               fi
-
-               i=$((i + 1))
-       done
-
-       wait
-
-       ret=$(<"$server_retcode_file")
-
-       rm -f "$server_pid_file"
-       rm -f "$server_retcode_file"
-       return "$ret"
-}
-
-run_test() {
-       local test_text="$1"
-       local cli_args_template="$2"
-       local server_args="$3"
-       local expected_stdout="$4"
-       local expected_stderr="$5"
-
-       local cli_stderr
-       local cli_stdout
-       local port_file
-       local port
-
-       cli_stderr="$(mktemp -t test_live_stderr.XXXXXX)"
-       cli_stdout="$(mktemp -t test_live_stdout.XXXXXX)"
-       port_file="$(mktemp -t test_live_server_port.XXXXXX)"
-
-       get_cli_output_with_lttng_live_server "$cli_args_template" "$server_args" "$cli_stdout" "$cli_stderr" "$port_file"
-       port=$(<"$port_file")
-
-       bt_diff "$expected_stdout" "$cli_stdout"
-       ok $? "$test_text - stdout"
-       bt_diff "$expected_stderr" "$cli_stderr"
-       ok $? "$test_text - stderr"
-
-       rm -f "$cli_stderr"
-       rm -f "$cli_stdout"
-       rm -f "$port_file"
-}
-
-test_list_sessions() {
-       # Test the basic listing of sessions.
-       # Ensure that a multi-domain trace is seen as a single session.
-       # run_test() is not used here because the port is needed to craft the
-       # expected output.
-
-       local port
-       local port_file
-       local tmp_stdout_expected
-       local template_expected
-
-       local test_text="CLI prints the expected session list"
-       local cli_args_template="-i lttng-live net://localhost:@PORT@"
-       local sessions_file="$test_data_dir/list_sessions.json"
-       local server_args="--sessions-filename '$sessions_file'"
-
-       template_expected=$(<"$test_data_dir/cli-list-sessions.expect")
-       cli_stderr="$(mktemp -t test_live_list_sessions_stderr.XXXXXX)"
-       cli_stdout="$(mktemp -t test_live_list_sessions_stdout.XXXXXX)"
-       empty_file="$(mktemp -t test_live_list_sessions_empty.XXXXXX)"
-       port_file="$(mktemp -t test_live_list_sessions_server_port.XXXXXX)"
-       tmp_stdout_expected="$(mktemp -t test_live_list_sessions_stdout_expected.XXXXXX)"
-
-       get_cli_output_with_lttng_live_server "$cli_args_template" "$server_args" "$cli_stdout" "$cli_stderr" "$port_file"
-       port=$(<"$port_file")
-
-       # Craft the expected output. This is necessary since the port number
-       # (random) of a "relayd" is present in the output.
-       template_expected=${template_expected//@PORT@/$port}
-
-       echo "$template_expected" > "$tmp_stdout_expected"
-
-       bt_diff "$tmp_stdout_expected" "$cli_stdout"
-       ok $? "$test_text - stdout"
-       bt_diff "$empty_file" "$cli_stderr"
-       ok $? "$test_text - stderr"
-
-       rm -f "$cli_stderr"
-       rm -f "$cli_stdout"
-       rm -f "$empty_file"
-       rm -f "$port_file"
-       rm -f "$tmp_stdout_expected"
-}
-
-test_base() {
-       # Attach and consume data from a multi packets ust session with no
-       # discarded events.
-       local test_text="CLI attach and fetch from single-domains session - no discarded events"
-       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/trace-with-index -c sink.text.details"
-       local sessions_file="$test_data_dir/base.json"
-       local server_args="--sessions-filename '$sessions_file'"
-       local expected_stdout="${test_data_dir}/cli-base.expect"
-       local expected_stderr
-
-       # Empty file for stderr expected
-       expected_stderr="$(mktemp -t test_live_base_stderr_expected.XXXXXX)"
-
-       run_test "$test_text" "$cli_args_template" "$server_args" "$expected_stdout" "$expected_stderr"
-
-       rm -f "$expected_stderr"
-}
-
-test_multi_domains() {
-       # Attach and consume data from a multi-domains session with discarded
-       # events.
-       local test_text="CLI attach and fetch from multi-domains session - discarded events"
-       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/multi-domains -c sink.text.details"
-       local sessions_file="${test_data_dir}/multi_domains.json"
-       local server_args="--sessions-filename '$sessions_file'"
-       local expected_stdout="$test_data_dir/cli-multi-domains.expect"
-       local expected_stderr
-
-       # Empty file for stderr expected
-       expected_stderr="$(mktemp -t test_live_multi_domains_stderr_expected.XXXXXX)"
-
-       run_test "$test_text" "$cli_args_template" "$server_args" "$expected_stdout" "$expected_stderr"
-
-       rm -f "$expected_stderr"
-}
-
-test_rate_limited() {
-       # Attach and consume data from a multi packets ust session with no
-       # discarded events. Enforce a server side limit on the stream data
-       # requests size. Ensure that babeltrace respect the returned size and that
-       # many requests per packet works as expected.
-       # The packet size of the test trace is 4k. Limit requests to 1k.
-       local test_text="CLI many requests per packet"
-       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/trace-with-index -c sink.text.details"
-       local sessions_file="$test_data_dir/rate_limited.json"
-       local server_args="--max-query-data-response-size 1024 --sessions-filename '$sessions_file'"
-       local expected_stdout="${test_data_dir}/cli-base.expect"
-       local expected_stderr
-
-       # Empty file for stderr expected
-       expected_stderr="$(mktemp -t test_live_rate_limited_stderr_expected.XXXXXX)"
-
-       run_test "$test_text" "$cli_args_template" "$server_args" "$expected_stdout" "$expected_stderr"
-
-       rm -f "$expected_stderr"
-}
-
-test_compare_to_ctf_fs() {
-       # Compare the details text sink or ctf.fs and ctf.lttng-live to ensure
-       # that the trace is parsed the same way.
-       # Do the same with the session swapped on the relayd side. This validate
-       # that ordering is consistent between live and ctf fs.
-       local test_text="CLI src.ctf.fs vs src.ctf.lttng-live"
-       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/multi-domains -c sink.text.details --params with-trace-name=false,with-stream-name=false"
-       local sessions_file="$test_data_dir/multi_domains.json"
-       local sessions_file_inverse="$test_data_dir/multi_domains_inverse.json"
-       local server_args="--sessions-filename '$sessions_file'"
-       local server_args_inverse="--sessions-filename '$sessions_file_inverse'"
-       local expected_stdout
-       local expected_stderr
-
-       expected_stdout="$(mktemp -t test_live_compare_stdout_expected.XXXXXX)"
-       expected_stderr="$(mktemp -t test_live_compare_stderr_expected.XXXXXX)"
-
-       bt_cli "$expected_stdout" "$expected_stderr" "${trace_dir}/succeed/multi-domains" -c sink.text.details --params "with-trace-name=false,with-stream-name=false"
-       bt_remove_cr "${expected_stdout}"
-       bt_remove_cr "${expected_stderr}"
-       run_test "$test_text" "$cli_args_template" "$server_args" "$expected_stdout" "$expected_stderr"
-       diag "Inverse session order from lttng-relayd"
-       run_test "$test_text" "$cli_args_template" "$server_args_inverse" "$expected_stdout" "$expected_stderr"
-
-       rm -f "$expected_stdout"
-       rm -f "$expected_stderr"
-}
-
-test_inactivity_discarded_packet() {
-       # Attach and consume data from a multi-packet trace with discarded
-       # packets and emit an inactivity beacon during the discarded packets
-       # period.
-       #
-       # | pkt seq:0 |<-------discarded packets------>| pkt seq:8 |
-       # 0          20                                121       140
-       #
-       # This test was introduced to cover the following bug:
-       #
-       # When reading this type of trace locally, the CTF source is expected
-       # to introduce a "Discarded packets" message between packets 0 and 8.
-       # The timestamps of this message are [pkt0.end_ts, pkt8.begin_ts].
-       #
-       # In the context of a live source, the tracer could report an inactivity
-       # period during the interval of the "Discarded packets" message.
-       # Those messages eventually translate into a
-       # "Iterator inactivity" message with a timestamp set at the end of the
-       # inactivity period.
-       #
-       # If the tracer reports an inactivity period that ends at a point
-       # between pkt0 and pkt7 (resulting in an "Iterator inactivity" message),
-       # the live source could send a "Discarded packets" message that starts
-       # before the preceding "Iterator inactivity" message. This would break
-       # the monotonicity constraint of the graph.
-       local test_text="CLI attach and fetch from single-domains session - inactivity discarded packet"
-       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/7_lost_between_2_with_index -c sink.text.details"
-       local sessions_file="$test_data_dir/inactivity_discarded_packet.json"
-       local server_args="--sessions-filename '$sessions_file'"
-       local expected_stdout="$test_data_dir/inactivity_discarded_packet.expect"
-       local expected_stderr
-
-       # Empty file for stderr expected
-       expected_stderr="$(mktemp -t test_live_inactivity_discarded_packet_stderr_expected.XXXXXX)"
-
-       run_test "$test_text" "$cli_args_template" "$server_args" "$expected_stdout" "$expected_stderr"
-
-       rm -f "$expected_stderr"
-}
-
-test_split_metadata() {
-       # Consume a metadata stream sent in two parts. This testcase tests the
-       # behaviour of Babeltrace when the tracing session was cleared (lttng
-       # clear) but the metadata is not yet available to the relay. In such
-       # cases, when asked for metadata, the relay will return the
-       # `LTTNG_VIEWER_METADATA_OK` status and a data length of 0. The viewer
-       # need to consider such case as a request to retry fetching metadata.
-       #
-       # This testcase emulates such behaviour by adding empty metadata
-       # packets.
-
-       local test_text="CLI attach and fetch from single-domain session - Receive metadata in two sections separated by a empty section"
-       local cli_args_template="-i lttng-live net://localhost:@PORT@/host/hostname/split_metadata -c sink.text.details"
-       local sessions_file="$test_data_dir/split_metadata.json"
-       local server_args="--sessions-filename '$sessions_file'"
-       local expected_stdout="${test_data_dir}/split_metadata.expect"
-       local expected_stderr
-
-       # Empty file for stderr expected
-       expected_stderr="$(mktemp -t test_live_split_metadata_stderr_expected.XXXXXX)"
-
-       run_test "$test_text" "$cli_args_template" "$server_args" "$expected_stdout" "$expected_stderr"
-
-       rm -f "$expected_stderr"
-}
-
-plan_tests 16
-
-test_list_sessions
-test_base
-test_multi_domains
-test_rate_limited
-test_compare_to_ctf_fs
-test_inactivity_discarded_packet
-test_split_metadata
diff --git a/tests/python-plugin-provider/test-python-plugin-provider.sh b/tests/python-plugin-provider/test-python-plugin-provider.sh
new file mode 100755 (executable)
index 0000000..8fba060
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+
+if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
+       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
+else
+       UTILSSH="$(dirname "$0")/../utils/utils.sh"
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$UTILSSH"
+
+export PYTHON_PLUGIN_PROVIDER_TEST_PLUGIN_PATH="${BT_TESTS_SRCDIR}/python-plugin-provider/bt_plugin_test_python_plugin_provider.py"
+
+bt_run_py_test \
+       "${BT_TESTS_SRCDIR}/python-plugin-provider" \
+       test_python_plugin_provider.py
diff --git a/tests/python-plugin-provider/test_python_plugin_provider b/tests/python-plugin-provider/test_python_plugin_provider
deleted file mode 100755 (executable)
index 542c174..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
-#
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-export PYTHON_PLUGIN_PROVIDER_TEST_PLUGIN_PATH="${BT_TESTS_SRCDIR}/python-plugin-provider/bt_plugin_test_python_plugin_provider.py"
-
-run_python_bt2_test \
-       "${BT_TESTS_SRCDIR}/python-plugin-provider" \
-       test_python_plugin_provider.py
index c48d2dc985180779998a8687c0e0a29912ea3f2a..7f9f46d906553a8cf95b1c86be163c59b5635730 100644 (file)
@@ -3,9 +3,10 @@
 # Copyright (C) 2019 EfficiOS Inc.
 #
 
+import os
 import unittest
+
 import bt2
-import os
 
 
 class PythonPluginProviderTestCase(unittest.TestCase):
index acfbfec5049f4a510bd22b3e86672a96f08a7da6..e7065be2a4251174a4b005a9c238be5742ed750c 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-FileCopyrightText: 2013-2023 EfficiOS, Inc.
 # SPDX-License-Identifier: MIT
 
 SUBDIRS = tap
@@ -9,6 +10,6 @@ libtestcommon_la_SOURCES = common.c common.h
 EXTRA_DIST = python
 
 dist_check_SCRIPTS = \
-       run_python_bt2 \
+       run-in-py-env.sh \
        tap-driver.sh \
        utils.sh
index ee7780f4046e1cd9d33871a444837964182558c0..8c5e850722d545017fb4deccb9226f5d0dedd465 100644 (file)
 #define RMDIR_NFDOPEN 8
 
 static
-int nftw_recursive_rmdir(const char *file, const struct stat *sb, int flag,
-               struct FTW *s)
+int nftw_recursive_rmdir(const char *file,
+               const struct stat *sb __attribute__((unused)),
+               int flag,
+               struct FTW *s __attribute__((unused)))
 {
        switch (flag) {
        case FTW_F:
index 41641099c30ad058a58df318e45287a5894d5e71..cbd96cf14624d6d7686edb0f238d64c22528d177 100644 (file)
@@ -7,11 +7,9 @@
  * Lib BabelTrace - Common function to all tests
  */
 
-#ifndef _TESTS_COMMON_H
-#define _TESTS_COMMON_H
-
-struct bt_context;
+#ifndef TESTS_UTILS_COMMON_H
+#define TESTS_UTILS_COMMON_H
 
 void recursive_rmdir(const char *path);
 
-#endif /* _TESTS_COMMON_H */
+#endif /* TESTS_UTILS_COMMON_H */
index ab5b47e783714804a7ed747e165cddd328c88cc3..77e0c443ec9f00b8e96f3d27e48eba5091efc226 100644 (file)
@@ -1,55 +1,50 @@
-#!/bin/bash
-#
+# SPDX-FileCopyrightText: 2021-2022 EfficiOS, Inc.
 # SPDX-License-Identifier: GPL-2.0-only
-#
-
-### Base paths to test suite ###
-if [ -z "${BT_TESTS_SRCDIR:-}" ]; then
-       BT_TESTS_SRCDIR="@abs_top_srcdir@/tests"
-fi
-export BT_TESTS_SRCDIR
-
-if [ -z "${BT_TESTS_BUILDDIR:-}" ]; then
-       BT_TESTS_BUILDDIR="@abs_top_builddir@/tests"
-fi
-export BT_TESTS_BUILDDIR
-
-
-### External Tools ###
-if [ -z "${BT_TESTS_AWK_BIN:-}" ]; then
-       BT_TESTS_AWK_BIN="@AWK@"
-fi
-export BT_TESTS_AWK_BIN
-
-if [ -z "${BT_TESTS_GREP_BIN:-}" ]; then
-       BT_TESTS_GREP_BIN="@GREP@"
-fi
-export BT_TESTS_GREP_BIN
-
-if [ -z "${BT_TESTS_PYTHON_BIN:-}" ]; then
-       BT_TESTS_PYTHON_BIN="@PYTHON@"
-fi
-export BT_TESTS_PYTHON_BIN
-
-if [ -z "${BT_TESTS_PYTHON_CONFIG_BIN:-}" ]; then
-       BT_TESTS_PYTHON_CONFIG_BIN="@PYTHON_CONFIG@"
-fi
-export BT_TESTS_PYTHON_CONFIG_BIN
-
-if [ -z "${BT_TESTS_SED_BIN:-}" ]; then
-       BT_TESTS_SED_BIN="@SED@"
-fi
-export BT_TESTS_SED_BIN
-
-if [ -z "${BT_TESTS_CC_BIN:-}" ]; then
-       BT_TESTS_CC_BIN="@CC@"
-fi
-export BT_TESTS_CC_BIN
-
-
-### Optional features ###
-
-if [ -z "${BT_TESTS_ENABLE_ASAN:-}" ]; then
-       BT_TESTS_ENABLE_ASAN="@ENABLE_ASAN@"
-fi
-export BT_TESTS_ENABLE_ASAN
+
+# shellcheck shell=bash
+
+# Sets the variable named `$1` to `$2` if it's not set (or empty), and
+# exports it.
+_set_var_def() {
+       local -r varname=$1
+       local -r val=$2
+
+       if [[ -z $(eval echo "\${$varname:-}") ]]; then
+               eval "$varname='$val'"
+       fi
+
+       export "${varname?}"
+}
+
+# Base source directory of tests, if not set
+_set_var_def BT_TESTS_SRCDIR '@abs_top_srcdir@/tests'
+
+# Base build directory of tests, if not set
+_set_var_def BT_TESTS_BUILDDIR '@abs_top_builddir@/tests'
+
+# Name of the `awk` command to use when testing, if not set
+_set_var_def BT_TESTS_AWK_BIN '@AWK@'
+
+# Name of the `grep` command to use when testing, if not set
+_set_var_def BT_TESTS_GREP_BIN '@GREP@'
+
+# Name of the `python3` command to use when testing, if not set
+_set_var_def BT_TESTS_PYTHON_BIN '@PYTHON@'
+
+# Name of the `python3-config` command to use when testing, if not set
+_set_var_def BT_TESTS_PYTHON_CONFIG_BIN '@PYTHON_CONFIG@'
+
+# Name of the `sed` command to use when testing, if not set
+_set_var_def BT_TESTS_SED_BIN '@SED@'
+
+# Name of the `cc` command to use when testing, if not set
+_set_var_def BT_TESTS_CC_BIN '@CC@'
+
+# `1` if AddressSanitizer is used, if not set
+_set_var_def BT_TESTS_ENABLE_ASAN '@ENABLE_ASAN@'
+
+# `1` to run tests which depend on Python plugin support, if not set
+_set_var_def BT_TESTS_ENABLE_PYTHON_PLUGINS '@ENABLE_PYTHON_PLUGINS@'
+
+# No more
+unset -f _set_var_def
index 90d61ffdc5e75dde47a63de4fb711324df40ae02..4ff24987a4e672953e885430fe0e0c2c53bdfe4d 100644 (file)
@@ -19,12 +19,13 @@ def to_string(p):
         )
     elif type(p) is bt2._UnsignedIntegerValueConst:
         s = str(p) + "u"
+    elif type(p) is bt2._RealValueConst:
+        s = "{:.7f}".format(float(p))
     elif (
         type(p)
         in (
             bt2._StringValueConst,
             bt2._SignedIntegerValueConst,
-            bt2._RealValueConst,
             bt2._BoolValueConst,
         )
         or p is None
diff --git a/tests/utils/python/mctf.py b/tests/utils/python/mctf.py
new file mode 100644 (file)
index 0000000..bd8cc0f
--- /dev/null
@@ -0,0 +1,153 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2023 EfficiOS Inc.
+#
+# pyright: strict, reportTypeCommentUsage=false
+
+import os
+import sys
+import typing
+import argparse
+from typing import Any, List, Union
+
+import normand
+import moultipart
+
+
+class ErrorCause:
+    def __init__(self, what: str, line_no: int, col_no: int):
+        self._what = what
+        self._line_no = line_no
+        self._col_no = col_no
+
+    @property
+    def what(self):
+        return self._what
+
+    @property
+    def line_no(self):
+        return self._line_no
+
+    @property
+    def col_no(self):
+        return self._col_no
+
+
+class Error(RuntimeError):
+    def __init__(self, causes: List[ErrorCause]):
+        self._causes = causes
+
+    @property
+    def causes(self):
+        return self._causes
+
+
+def _write_file(
+    name: str, base_dir: str, content: Union[str, bytearray], verbose: bool
+):
+    path = os.path.join(base_dir, name)
+
+    if verbose:
+        print("Writing `{}`.".format(os.path.normpath(path)))
+
+    os.makedirs(os.path.normpath(os.path.dirname(path)), exist_ok=True)
+
+    with open(path, "w" if isinstance(content, str) else "wb") as f:
+        f.write(content)
+
+
+def _normand_parse(
+    part: moultipart.Part, init_vars: normand.VariablesT, init_labels: normand.LabelsT
+):
+    try:
+        return normand.parse(
+            part.content, init_variables=init_vars, init_labels=init_labels
+        )
+    except normand.ParseError as e:
+        raise Error(
+            [
+                ErrorCause(
+                    msg.text,
+                    msg.text_location.line_no + part.first_content_line_no - 1,
+                    msg.text_location.col_no,
+                )
+                for msg in e.messages
+            ]
+        ) from e
+
+
+def _generate_from_part(
+    part: moultipart.Part,
+    base_dir: str,
+    verbose: bool,
+    normand_vars: normand.VariablesT,
+    normand_labels: normand.LabelsT,
+):
+    content = part.content
+
+    if part.header_info != "metadata":
+        res = _normand_parse(part, normand_vars, normand_labels)
+        content = res.data
+        normand_vars = res.variables
+        normand_labels = res.labels
+
+    _write_file(part.header_info, base_dir, content, verbose)
+    return normand_vars, normand_labels
+
+
+def generate(input_path: str, base_dir: str, verbose: bool):
+    with open(input_path) as input_file:
+        variables = {}  # type: normand.VariablesT
+        labels = {}  # type: normand.LabelsT
+
+        for part in moultipart.parse(input_file):
+            variables, labels = _generate_from_part(
+                part, base_dir, verbose, variables, labels
+            )
+
+
+def _parse_cli_args():
+    argparser = argparse.ArgumentParser()
+    argparser.add_argument(
+        "input_path", metavar="PATH", type=str, help="moultipart input file name"
+    )
+    argparser.add_argument(
+        "--base-dir", type=str, help="base directory of generated files", default=""
+    )
+    argparser.add_argument(
+        "--verbose", "-v", action="store_true", help="increase verbosity"
+    )
+    return argparser.parse_args()
+
+
+def _run_cli(args: Any):
+    generate(
+        typing.cast(str, args.input_path),
+        typing.cast(str, args.base_dir),
+        typing.cast(bool, args.verbose),
+    )
+
+
+def _try_run_cli():
+    args = _parse_cli_args()
+
+    try:
+        _run_cli(args)
+    except Error as exc:
+        print("Failed to process Normand part:", file=sys.stderr)
+
+        for cause in reversed(exc.causes):
+            print(
+                "  {}:{}:{} - {}{}".format(
+                    os.path.abspath(args.input_path),
+                    cause.line_no,
+                    cause.col_no,
+                    cause.what,
+                    "." if cause.what[-1] not in ".:;" else "",
+                ),
+                file=sys.stderr,
+            )
+
+
+if __name__ == "__main__":
+    _try_run_cli()
diff --git a/tests/utils/python/moultipart.py b/tests/utils/python/moultipart.py
new file mode 100644 (file)
index 0000000..d33748c
--- /dev/null
@@ -0,0 +1,147 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2023 EfficiOS Inc.
+#
+# pyright: strict, reportTypeCommentUsage=false
+
+import re
+from typing import TextIO
+
+
+# One part of a moultipart document.
+#
+# For example, for this part of which the header is at line 37:
+#
+#     --- Another Oscar Wilde quote
+#     I can resist everything except temptation.
+#
+# The corresponding `Part` object is:
+#
+#     Part('Another Oscar Wilde quote',
+#          'I can resist everything except temptation',
+#          38)
+class Part:
+    def __init__(self, header_info: str, content: str, first_content_line_no: int):
+        self._header_info = header_info
+        self._content = content
+        self._first_content_line_no = first_content_line_no
+
+    @property
+    def header_info(self):
+        return self._header_info
+
+    @property
+    def content(self):
+        return self._content
+
+    # Number of the first line, relative to the beginning of the
+    # containing moultipart document, of the content of this part.
+    @property
+    def first_content_line_no(self):
+        return self._first_content_line_no
+
+    def __repr__(self):
+        return "Part({}, {}, {})".format(
+            repr(self.header_info), repr(self.content), self.first_content_line_no
+        )
+
+
+def _try_parse_header(line: str):
+    m = re.match(r"---(\s*| .+)$", line)
+
+    if m is None:
+        return
+
+    return m.group(1).strip()
+
+
+# Parses the moultipart document file `in_file` and returns its parts
+# (list of `Part` objects).
+#
+# A moultipart document is a sequence of parts.
+#
+# A moutlipart part is:
+#
+# 1. A header line, that is, in this order:
+#
+#    a) Exactly `---`.
+#    b) Zero or more spaces.
+#    c) Optional: custom information until the end of the line.
+#
+# 2. Zero or more lines of text which aren't header lines.
+#
+# For example, consider the following moultipart document:
+#
+#     --- Victoria
+#     Parenteau
+#     ---
+#     Taillon
+#     --- This part is empty
+#     --- Josianne
+#     Gervais
+#
+# Then this function would return the following part objects:
+#
+#     [
+#         Part('Victoria',           'Parenteau\n', 2),
+#         Part('',                   'Taillon\n',   4),
+#         Part('This part is empty', '',            6),
+#         Part('Josianne',           'Gervais\n',   7),
+#     ]
+#
+# Raises `RuntimeError` on any parsing error.
+def parse(in_file: TextIO):
+    # Read the first header
+    cur_part_content = ""
+    cur_first_content_line_no = 2
+    parts = []  # type: list[Part]
+    line_no = 1
+    line = next(in_file)
+    cur_part_header_info = _try_parse_header(line)
+
+    if cur_part_header_info is None:
+        raise RuntimeError(
+            "Expecting header line starting with `---`, got `{}`".format(
+                line.strip("\n")
+            )
+        )
+
+    for line in in_file:
+        line_no += 1
+        maybe_part_header_info = _try_parse_header(line)
+
+        if maybe_part_header_info is not None:
+            # New header
+            parts.append(
+                Part(
+                    cur_part_header_info,
+                    cur_part_content,
+                    cur_first_content_line_no,
+                )
+            )
+            cur_part_content = ""
+            cur_part_header_info = maybe_part_header_info
+            cur_first_content_line_no = line_no + 1
+            continue
+
+        # Accumulate content lines
+        cur_part_content += line
+
+    # Last part (always exists)
+    parts.append(
+        Part(
+            cur_part_header_info,
+            cur_part_content,
+            cur_first_content_line_no,
+        )
+    )
+
+    return parts
+
+
+if __name__ == "__main__":
+    import sys
+    import pprint
+
+    with open(sys.argv[1]) as f:
+        pprint.pprint(parse(f))
diff --git a/tests/utils/python/normand.py b/tests/utils/python/normand.py
new file mode 100644 (file)
index 0000000..4c7063d
--- /dev/null
@@ -0,0 +1,3039 @@
+# SPDX-FileCopyrightText: 2023 Philippe Proulx <eeppeliteloop@gmail.com>
+# SPDX-License-Identifier: MIT
+#
+# The MIT License (MIT)
+#
+# Copyright (c) 2023 Philippe Proulx <eeppeliteloop@gmail.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# This module is the portable Normand processor. It offers both the
+# parse() function and the command-line tool (run the module itself)
+# without external dependencies except a `typing` module for Python 3.4.
+#
+# Feel free to copy this module file to your own project to use Normand.
+#
+# Upstream repository: <https://github.com/efficios/normand>.
+
+__author__ = "Philippe Proulx"
+__version__ = "0.23.0"
+__all__ = [
+    "__author__",
+    "__version__",
+    "ByteOrder",
+    "LabelsT",
+    "parse",
+    "ParseError",
+    "ParseErrorMessage",
+    "ParseResult",
+    "TextLocation",
+    "VariablesT",
+]
+
+import re
+import abc
+import ast
+import bz2
+import sys
+import copy
+import enum
+import gzip
+import math
+import base64
+import quopri
+import struct
+import typing
+import functools
+from typing import Any, Set, Dict, List, Union, Pattern, Callable, NoReturn, Optional
+
+
+# Text location (line and column numbers).
+class TextLocation:
+    @classmethod
+    def _create(cls, line_no: int, col_no: int):
+        self = cls.__new__(cls)
+        self._init(line_no, col_no)
+        return self
+
+    def __init__(*args, **kwargs):  # type: ignore
+        raise NotImplementedError
+
+    def _init(self, line_no: int, col_no: int):
+        self._line_no = line_no
+        self._col_no = col_no
+
+    # Line number.
+    @property
+    def line_no(self):
+        return self._line_no
+
+    # Column number.
+    @property
+    def col_no(self):
+        return self._col_no
+
+    def __repr__(self):
+        return "TextLocation({}, {})".format(self._line_no, self._col_no)
+
+
+# Any item.
+class _Item:
+    def __init__(self, text_loc: TextLocation):
+        self._text_loc = text_loc
+
+    # Source text location.
+    @property
+    def text_loc(self):
+        return self._text_loc
+
+
+# Scalar item.
+class _ScalarItem(_Item):
+    # Returns the size, in bytes, of this item.
+    @property
+    @abc.abstractmethod
+    def size(self) -> int:
+        ...
+
+
+# A repeatable item.
+class _RepableItem:
+    pass
+
+
+# Single byte.
+class _Byte(_ScalarItem, _RepableItem):
+    def __init__(self, val: int, text_loc: TextLocation):
+        super().__init__(text_loc)
+        self._val = val
+
+    # Byte value.
+    @property
+    def val(self):
+        return self._val
+
+    @property
+    def size(self):
+        return 1
+
+    def __repr__(self):
+        return "_Byte({}, {})".format(hex(self._val), repr(self._text_loc))
+
+
+# Literal string.
+class _LitStr(_ScalarItem, _RepableItem):
+    def __init__(self, data: bytes, text_loc: TextLocation):
+        super().__init__(text_loc)
+        self._data = data
+
+    # Encoded bytes.
+    @property
+    def data(self):
+        return self._data
+
+    @property
+    def size(self):
+        return len(self._data)
+
+    def __repr__(self):
+        return "_LitStr({}, {})".format(repr(self._data), repr(self._text_loc))
+
+
+# Byte order.
+@enum.unique
+class ByteOrder(enum.Enum):
+    # Big endian.
+    BE = "be"
+
+    # Little endian.
+    LE = "le"
+
+
+# Byte order setting.
+class _SetBo(_Item):
+    def __init__(self, bo: ByteOrder, text_loc: TextLocation):
+        super().__init__(text_loc)
+        self._bo = bo
+
+    @property
+    def bo(self):
+        return self._bo
+
+    def __repr__(self):
+        return "_SetBo({}, {})".format(repr(self._bo), repr(self._text_loc))
+
+
+# Label.
+class _Label(_Item):
+    def __init__(self, name: str, text_loc: TextLocation):
+        super().__init__(text_loc)
+        self._name = name
+
+    # Label name.
+    @property
+    def name(self):
+        return self._name
+
+    def __repr__(self):
+        return "_Label({}, {})".format(repr(self._name), repr(self._text_loc))
+
+
+# Offset setting.
+class _SetOffset(_Item):
+    def __init__(self, val: int, text_loc: TextLocation):
+        super().__init__(text_loc)
+        self._val = val
+
+    # Offset value (bytes).
+    @property
+    def val(self):
+        return self._val
+
+    def __repr__(self):
+        return "_SetOffset({}, {})".format(repr(self._val), repr(self._text_loc))
+
+
+# Offset alignment.
+class _AlignOffset(_Item):
+    def __init__(self, val: int, pad_val: int, text_loc: TextLocation):
+        super().__init__(text_loc)
+        self._val = val
+        self._pad_val = pad_val
+
+    # Alignment value (bits).
+    @property
+    def val(self):
+        return self._val
+
+    # Padding byte value.
+    @property
+    def pad_val(self):
+        return self._pad_val
+
+    def __repr__(self):
+        return "_AlignOffset({}, {}, {})".format(
+            repr(self._val), repr(self._pad_val), repr(self._text_loc)
+        )
+
+
+# Mixin of containing an AST expression and its string.
+class _ExprMixin:
+    def __init__(self, expr_str: str, expr: ast.Expression):
+        self._expr_str = expr_str
+        self._expr = expr
+
+    # Expression string.
+    @property
+    def expr_str(self):
+        return self._expr_str
+
+    # Expression node to evaluate.
+    @property
+    def expr(self):
+        return self._expr
+
+
+# Fill until some offset.
+class _FillUntil(_Item, _ExprMixin):
+    def __init__(
+        self, expr_str: str, expr: ast.Expression, pad_val: int, text_loc: TextLocation
+    ):
+        super().__init__(text_loc)
+        _ExprMixin.__init__(self, expr_str, expr)
+        self._pad_val = pad_val
+
+    # Padding byte value.
+    @property
+    def pad_val(self):
+        return self._pad_val
+
+    def __repr__(self):
+        return "_FillUntil({}, {}, {}, {})".format(
+            repr(self._expr_str),
+            repr(self._expr),
+            repr(self._pad_val),
+            repr(self._text_loc),
+        )
+
+
+# Variable assignment.
+class _VarAssign(_Item, _ExprMixin):
+    def __init__(
+        self, name: str, expr_str: str, expr: ast.Expression, text_loc: TextLocation
+    ):
+        super().__init__(text_loc)
+        _ExprMixin.__init__(self, expr_str, expr)
+        self._name = name
+
+    # Name.
+    @property
+    def name(self):
+        return self._name
+
+    def __repr__(self):
+        return "_VarAssign({}, {}, {}, {})".format(
+            repr(self._name),
+            repr(self._expr_str),
+            repr(self._expr),
+            repr(self._text_loc),
+        )
+
+
+# Fixed-length number, possibly needing more than one byte.
+class _FlNum(_ScalarItem, _RepableItem, _ExprMixin):
+    def __init__(
+        self,
+        expr_str: str,
+        expr: ast.Expression,
+        len: int,
+        bo: Optional[ByteOrder],
+        text_loc: TextLocation,
+    ):
+        super().__init__(text_loc)
+        _ExprMixin.__init__(self, expr_str, expr)
+        self._len = len
+        self._bo = bo
+
+    # Length (bits).
+    @property
+    def len(self):
+        return self._len
+
+    # Byte order override.
+    @property
+    def bo(self):
+        return self._bo
+
+    @property
+    def size(self):
+        return self._len // 8
+
+    def __repr__(self):
+        return "_FlNum({}, {}, {}, {}, {})".format(
+            repr(self._expr_str),
+            repr(self._expr),
+            repr(self._len),
+            repr(self._bo),
+            repr(self._text_loc),
+        )
+
+
+# LEB128 integer.
+class _Leb128Int(_Item, _RepableItem, _ExprMixin):
+    def __init__(self, expr_str: str, expr: ast.Expression, text_loc: TextLocation):
+        super().__init__(text_loc)
+        _ExprMixin.__init__(self, expr_str, expr)
+
+    def __repr__(self):
+        return "{}({}, {}, {})".format(
+            self.__class__.__name__,
+            repr(self._expr_str),
+            repr(self._expr),
+            repr(self._text_loc),
+        )
+
+
+# Unsigned LEB128 integer.
+class _ULeb128Int(_Leb128Int, _RepableItem, _ExprMixin):
+    pass
+
+
+# Signed LEB128 integer.
+class _SLeb128Int(_Leb128Int, _RepableItem, _ExprMixin):
+    pass
+
+
+# String.
+class _Str(_Item, _RepableItem, _ExprMixin):
+    def __init__(
+        self, expr_str: str, expr: ast.Expression, codec: str, text_loc: TextLocation
+    ):
+        super().__init__(text_loc)
+        _ExprMixin.__init__(self, expr_str, expr)
+        self._codec = codec
+
+    # Codec name.
+    @property
+    def codec(self):
+        return self._codec
+
+    def __repr__(self):
+        return "_Str({}, {}, {}, {})".format(
+            repr(self._expr_str),
+            repr(self._expr),
+            repr(self._codec),
+            repr(self._text_loc),
+        )
+
+
+# Group of items.
+class _Group(_Item, _RepableItem):
+    def __init__(self, items: List[_Item], text_loc: TextLocation):
+        super().__init__(text_loc)
+        self._items = items
+
+    # Contained items.
+    @property
+    def items(self):
+        return self._items
+
+    def __repr__(self):
+        return "_Group({}, {})".format(repr(self._items), repr(self._text_loc))
+
+
+# Repetition item.
+class _Rep(_Group, _ExprMixin):
+    def __init__(
+        self,
+        items: List[_Item],
+        expr_str: str,
+        expr: ast.Expression,
+        text_loc: TextLocation,
+    ):
+        super().__init__(items, text_loc)
+        _ExprMixin.__init__(self, expr_str, expr)
+
+    def __repr__(self):
+        return "_Rep({}, {}, {}, {})".format(
+            repr(self._items),
+            repr(self._expr_str),
+            repr(self._expr),
+            repr(self._text_loc),
+        )
+
+
+# Conditional item.
+class _Cond(_Item, _ExprMixin):
+    def __init__(
+        self,
+        true_item: _Group,
+        false_item: _Group,
+        expr_str: str,
+        expr: ast.Expression,
+        text_loc: TextLocation,
+    ):
+        super().__init__(text_loc)
+        _ExprMixin.__init__(self, expr_str, expr)
+        self._true_item = true_item
+        self._false_item = false_item
+
+    # Item when condition is true.
+    @property
+    def true_item(self):
+        return self._true_item
+
+    # Item when condition is false.
+    @property
+    def false_item(self):
+        return self._false_item
+
+    def __repr__(self):
+        return "_Cond({}, {}, {}, {}, {})".format(
+            repr(self._true_item),
+            repr(self._false_item),
+            repr(self._expr_str),
+            repr(self._expr),
+            repr(self._text_loc),
+        )
+
+
+# Transformation.
+class _Trans(_Group, _RepableItem):
+    def __init__(
+        self,
+        items: List[_Item],
+        name: str,
+        func: Callable[[Union[bytes, bytearray]], bytes],
+        text_loc: TextLocation,
+    ):
+        super().__init__(items, text_loc)
+        self._name = name
+        self._func = func
+
+    @property
+    def name(self):
+        return self._name
+
+    # Transforms the data `data`.
+    def trans(self, data: Union[bytes, bytearray]):
+        return self._func(data)
+
+    def __repr__(self):
+        return "_Trans({}, {}, {}, {})".format(
+            repr(self._items),
+            repr(self._name),
+            repr(self._func),
+            repr(self._text_loc),
+        )
+
+
+# Macro definition item.
+class _MacroDef(_Group):
+    def __init__(
+        self,
+        name: str,
+        param_names: List[str],
+        items: List[_Item],
+        text_loc: TextLocation,
+    ):
+        super().__init__(items, text_loc)
+        self._name = name
+        self._param_names = param_names
+
+    # Name.
+    @property
+    def name(self):
+        return self._name
+
+    # Parameters.
+    @property
+    def param_names(self):
+        return self._param_names
+
+    def __repr__(self):
+        return "_MacroDef({}, {}, {}, {})".format(
+            repr(self._name),
+            repr(self._param_names),
+            repr(self._items),
+            repr(self._text_loc),
+        )
+
+
+# Macro expansion parameter.
+class _MacroExpParam:
+    def __init__(self, expr_str: str, expr: ast.Expression, text_loc: TextLocation):
+        self._expr_str = expr_str
+        self._expr = expr
+        self._text_loc = text_loc
+
+    # Expression string.
+    @property
+    def expr_str(self):
+        return self._expr_str
+
+    # Expression.
+    @property
+    def expr(self):
+        return self._expr
+
+    # Source text location.
+    @property
+    def text_loc(self):
+        return self._text_loc
+
+    def __repr__(self):
+        return "_MacroExpParam({}, {}, {})".format(
+            repr(self._expr_str), repr(self._expr), repr(self._text_loc)
+        )
+
+
+# Macro expansion item.
+class _MacroExp(_Item, _RepableItem):
+    def __init__(
+        self,
+        name: str,
+        params: List[_MacroExpParam],
+        text_loc: TextLocation,
+    ):
+        super().__init__(text_loc)
+        self._name = name
+        self._params = params
+
+    # Name.
+    @property
+    def name(self):
+        return self._name
+
+    # Parameters.
+    @property
+    def params(self):
+        return self._params
+
+    def __repr__(self):
+        return "_MacroExp({}, {}, {})".format(
+            repr(self._name),
+            repr(self._params),
+            repr(self._text_loc),
+        )
+
+
+# A parsing error message: a string and a text location.
+class ParseErrorMessage:
+    @classmethod
+    def _create(cls, text: str, text_loc: TextLocation):
+        self = cls.__new__(cls)
+        self._init(text, text_loc)
+        return self
+
+    def __init__(self, *args, **kwargs):  # type: ignore
+        raise NotImplementedError
+
+    def _init(self, text: str, text_loc: TextLocation):
+        self._text = text
+        self._text_loc = text_loc
+
+    # Message text.
+    @property
+    def text(self):
+        return self._text
+
+    # Source text location.
+    @property
+    def text_location(self):
+        return self._text_loc
+
+
+# A parsing error containing one or more messages (`ParseErrorMessage`).
+class ParseError(RuntimeError):
+    @classmethod
+    def _create(cls, msg: str, text_loc: TextLocation):
+        self = cls.__new__(cls)
+        self._init(msg, text_loc)
+        return self
+
+    def __init__(self, *args, **kwargs):  # type: ignore
+        raise NotImplementedError
+
+    def _init(self, msg: str, text_loc: TextLocation):
+        super().__init__(msg)
+        self._msgs = []  # type: List[ParseErrorMessage]
+        self._add_msg(msg, text_loc)
+
+    def _add_msg(self, msg: str, text_loc: TextLocation):
+        self._msgs.append(
+            ParseErrorMessage._create(  # pyright: ignore[reportPrivateUsage]
+                msg, text_loc
+            )
+        )
+
+    # Parsing error messages.
+    #
+    # The first message is the most specific one.
+    @property
+    def messages(self):
+        return self._msgs
+
+
+# Raises a parsing error, forwarding the parameters to the constructor.
+def _raise_error(msg: str, text_loc: TextLocation) -> NoReturn:
+    raise ParseError._create(msg, text_loc)  # pyright: ignore[reportPrivateUsage]
+
+
+# Adds a message to the parsing error `exc`.
+def _add_error_msg(exc: ParseError, msg: str, text_loc: TextLocation):
+    exc._add_msg(msg, text_loc)  # pyright: ignore[reportPrivateUsage]
+
+
+# Appends a message to the parsing error `exc` and reraises it.
+def _augment_error(exc: ParseError, msg: str, text_loc: TextLocation) -> NoReturn:
+    _add_error_msg(exc, msg, text_loc)
+    raise exc
+
+
+# Returns a normalized version (so as to be parseable by int()) of
+# the constant integer string `s`, possibly negative, dealing with
+# any radix suffix.
+def _norm_const_int(s: str):
+    neg = ""
+    pos = s
+
+    if s.startswith("-"):
+        neg = "-"
+        pos = s[1:]
+
+    for r in "xXoObB":
+        if pos.startswith("0" + r):
+            # Already correct
+            return s
+
+    # Try suffix
+    asm_suf_base = {
+        "h": "x",
+        "H": "x",
+        "q": "o",
+        "Q": "o",
+        "o": "o",
+        "O": "o",
+        "b": "b",
+        "B": "B",
+    }
+
+    for suf in asm_suf_base:
+        if pos[-1] == suf:
+            s = "{}0{}{}".format(neg, asm_suf_base[suf], pos.rstrip(suf))
+
+    return s
+
+
+# Encodes the string `s` using the codec `codec`, raising `ParseError`
+# with `text_loc` on encoding error.
+def _encode_str(s: str, codec: str, text_loc: TextLocation):
+    try:
+        return s.encode(codec)
+    except UnicodeEncodeError:
+        _raise_error(
+            "Cannot encode `{}` with the `{}` encoding".format(s, codec), text_loc
+        )
+
+
+# Variables dictionary type (for type hints).
+VariablesT = Dict[str, Union[int, float, str]]
+
+
+# Labels dictionary type (for type hints).
+LabelsT = Dict[str, int]
+
+
+# Common patterns.
+_py_name_pat = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*")
+_pos_const_int_pat = re.compile(
+    r"(?:0[Xx][A-Fa-f0-9]+|0[Oo][0-7]+|0[Bb][01]+|[A-Fa-f0-9]+[hH]|[0-7]+[qQoO]|[01]+[bB]|\d+)\b"
+)
+_const_int_pat = re.compile(r"(?P<neg>-)?(?:{})".format(_pos_const_int_pat.pattern))
+_const_float_pat = re.compile(
+    r"[-+]?(?:(?:\d*\.\d+)|(?:\d+\.))(?:[Ee][+-]?\d+)?(?=\W|)"
+)
+
+
+# Macro definition dictionary.
+_MacroDefsT = Dict[str, _MacroDef]
+
+
+# Normand parser.
+#
+# The constructor accepts a Normand input. After building, use the `res`
+# property to get the resulting main group.
+class _Parser:
+    # Builds a parser to parse the Normand input `normand`, parsing
+    # immediately.
+    def __init__(self, normand: str, variables: VariablesT, labels: LabelsT):
+        self._normand = normand
+        self._at = 0
+        self._line_no = 1
+        self._col_no = 1
+        self._label_names = set(labels.keys())
+        self._var_names = set(variables.keys())
+        self._macro_defs = {}  # type: _MacroDefsT
+        self._base_item_parse_funcs = [
+            self._try_parse_byte,
+            self._try_parse_str,
+            self._try_parse_val,
+            self._try_parse_var_assign,
+            self._try_parse_set_bo,
+            self._try_parse_label_or_set_offset,
+            self._try_parse_align_offset,
+            self._try_parse_fill_until,
+            self._try_parse_group,
+            self._try_parse_rep_block,
+            self._try_parse_cond_block,
+            self._try_parse_macro_exp,
+            self._try_parse_trans_block,
+        ]
+        self._parse()
+
+    # Result (main group).
+    @property
+    def res(self):
+        return self._res
+
+    # Macro definitions.
+    @property
+    def macro_defs(self):
+        return self._macro_defs
+
+    # Current text location.
+    @property
+    def _text_loc(self):
+        return TextLocation._create(  # pyright: ignore[reportPrivateUsage]
+            self._line_no, self._col_no
+        )
+
+    # Returns `True` if this parser is done parsing.
+    def _is_done(self):
+        return self._at == len(self._normand)
+
+    # Returns `True` if this parser isn't done parsing.
+    def _isnt_done(self):
+        return not self._is_done()
+
+    # Raises a parse error, creating it using the message `msg` and the
+    # current text location.
+    def _raise_error(self, msg: str) -> NoReturn:
+        _raise_error(msg, self._text_loc)
+
+    # Tries to make the pattern `pat` match the current substring,
+    # returning the match object and updating `self._at`,
+    # `self._line_no`, and `self._col_no` on success.
+    def _try_parse_pat(self, pat: Pattern[str]):
+        m = pat.match(self._normand, self._at)
+
+        if m is None:
+            return
+
+        # Skip matched string
+        self._at += len(m.group(0))
+
+        # Update line number
+        self._line_no += m.group(0).count("\n")
+
+        # Update column number
+        for i in reversed(range(self._at)):
+            if self._normand[i] == "\n" or i == 0:
+                if i == 0:
+                    self._col_no = self._at + 1
+                else:
+                    self._col_no = self._at - i
+
+                break
+
+        # Return match object
+        return m
+
+    # Expects the pattern `pat` to match the current substring,
+    # returning the match object and updating `self._at`,
+    # `self._line_no`, and `self._col_no` on success, or raising a parse
+    # error with the message `error_msg` on error.
+    def _expect_pat(self, pat: Pattern[str], error_msg: str):
+        # Match
+        m = self._try_parse_pat(pat)
+
+        if m is None:
+            # No match: error
+            self._raise_error(error_msg)
+
+        # Return match object
+        return m
+
+    # Patterns for _skip_*()
+    _comment_pat = re.compile(r"#[^#]*?(?:$|#)", re.M)
+    _ws_or_comments_pat = re.compile(r"(?:\s|{})*".format(_comment_pat.pattern), re.M)
+    _ws_or_syms_or_comments_pat = re.compile(
+        r"(?:[\s/\\?&:;.,_=|-]|{})*".format(_comment_pat.pattern), re.M
+    )
+
+    # Skips as many whitespaces and comments as possible, but not
+    # insignificant symbol characters.
+    def _skip_ws_and_comments(self):
+        self._try_parse_pat(self._ws_or_comments_pat)
+
+    # Skips as many whitespaces, insignificant symbol characters, and
+    # comments as possible.
+    def _skip_ws_and_comments_and_syms(self):
+        self._try_parse_pat(self._ws_or_syms_or_comments_pat)
+
+    # Pattern for _try_parse_hex_byte()
+    _nibble_pat = re.compile(r"[A-Fa-f0-9]")
+
+    # Tries to parse a hexadecimal byte, returning a byte item on
+    # success.
+    def _try_parse_hex_byte(self):
+        begin_text_loc = self._text_loc
+
+        # Match initial nibble
+        m_high = self._try_parse_pat(self._nibble_pat)
+
+        if m_high is None:
+            # No match
+            return
+
+        # Expect another nibble
+        self._skip_ws_and_comments_and_syms()
+        m_low = self._expect_pat(
+            self._nibble_pat, "Expecting another hexadecimal nibble"
+        )
+
+        # Return item
+        return _Byte(int(m_high.group(0) + m_low.group(0), 16), begin_text_loc)
+
+    # Patterns for _try_parse_bin_byte()
+    _bin_byte_bit_pat = re.compile(r"[01]")
+    _bin_byte_prefix_pat = re.compile(r"%+")
+
+    # Tries to parse a binary byte, returning a byte item on success.
+    def _try_parse_bin_byte(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        m = self._try_parse_pat(self._bin_byte_prefix_pat)
+
+        if m is None:
+            # No match
+            return
+
+        # Expect as many bytes as there are `%` prefixes
+        items = []  # type: List[_Item]
+
+        for _ in range(len(m.group(0))):
+            self._skip_ws_and_comments_and_syms()
+            byte_text_loc = self._text_loc
+            bits = []  # type: List[str]
+
+            # Expect eight bits
+            for _ in range(8):
+                self._skip_ws_and_comments_and_syms()
+                m = self._expect_pat(
+                    self._bin_byte_bit_pat, "Expecting a bit (`0` or `1`)"
+                )
+                bits.append(m.group(0))
+
+            items.append(_Byte(int("".join(bits), 2), byte_text_loc))
+
+        # Return item
+        if len(items) == 1:
+            return items[0]
+
+        # As group
+        return _Group(items, begin_text_loc)
+
+    # Patterns for _try_parse_dec_byte()
+    _dec_byte_prefix_pat = re.compile(r"\$")
+    _dec_byte_val_pat = re.compile(r"(?P<neg>-?)(?P<val>\d+)")
+
+    # Tries to parse a decimal byte, returning a byte item on success.
+    def _try_parse_dec_byte(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        if self._try_parse_pat(self._dec_byte_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect the value
+        self._skip_ws_and_comments()
+        m = self._expect_pat(self._dec_byte_val_pat, "Expecting a decimal constant")
+
+        # Compute value
+        val = int(m.group("val")) * (-1 if m.group("neg") == "-" else 1)
+
+        # Validate
+        if val < -128 or val > 255:
+            _raise_error("Invalid decimal byte value {}".format(val), begin_text_loc)
+
+        # Two's complement
+        val %= 256
+
+        # Return item
+        return _Byte(val, begin_text_loc)
+
+    # Tries to parse a byte, returning a byte item on success.
+    def _try_parse_byte(self):
+        # Hexadecimal
+        item = self._try_parse_hex_byte()
+
+        if item is not None:
+            return item
+
+        # Binary
+        item = self._try_parse_bin_byte()
+
+        if item is not None:
+            return item
+
+        # Decimal
+        item = self._try_parse_dec_byte()
+
+        if item is not None:
+            return item
+
+    # Strings corresponding to escape sequence characters
+    _lit_str_escape_seq_strs = {
+        "0": "\0",
+        "a": "\a",
+        "b": "\b",
+        "e": "\x1b",
+        "f": "\f",
+        "n": "\n",
+        "r": "\r",
+        "t": "\t",
+        "v": "\v",
+        "\\": "\\",
+        '"': '"',
+    }
+
+    # Patterns for _try_parse_lit_str()
+    _lit_str_prefix_suffix_pat = re.compile(r'"')
+    _lit_str_contents_pat = re.compile(r'(?:(?:\\.)|[^"])*')
+
+    # Parses a literal string between double quotes (without an encoding
+    # prefix) and returns the resulting string.
+    def _try_parse_lit_str(self, with_prefix: bool):
+        # Match prefix if needed
+        if with_prefix:
+            if self._try_parse_pat(self._lit_str_prefix_suffix_pat) is None:
+                # No match
+                return
+
+        # Expect literal string
+        m = self._expect_pat(self._lit_str_contents_pat, "Expecting a literal string")
+
+        # Expect end of string
+        self._expect_pat(
+            self._lit_str_prefix_suffix_pat, 'Expecting `"` (end of literal string)'
+        )
+
+        # Replace escape sequences
+        val = m.group(0)
+
+        for ec in '0abefnrtv"\\':
+            val = val.replace(r"\{}".format(ec), self._lit_str_escape_seq_strs[ec])
+
+        # Return string
+        return val
+
+    # Patterns for _try_parse_utf_str_encoding()
+    _str_encoding_utf_prefix_pat = re.compile(r"u")
+    _str_encoding_utf_pat = re.compile(r"(?:8|(?:(?:16|32)(?:[bl]e)))\b")
+
+    # Tries to parse a UTF encoding specification, returning the Python
+    # codec name on success.
+    def _try_parse_utf_str_encoding(self):
+        # Match prefix
+        if self._try_parse_pat(self._str_encoding_utf_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect UTF specification
+        m = self._expect_pat(
+            self._str_encoding_utf_pat,
+            "Expecting `8`, `16be`, `16le`, `32be` or `32le`",
+        )
+
+        # Convert to codec name
+        return {
+            "8": "utf_8",
+            "16be": "utf_16_be",
+            "16le": "utf_16_le",
+            "32be": "utf_32_be",
+            "32le": "utf_32_le",
+        }[m.group(0)]
+
+    # Patterns for _try_parse_str_encoding()
+    _str_encoding_gen_prefix_pat = re.compile(r"s")
+    _str_encoding_colon_pat = re.compile(r":")
+    _str_encoding_non_utf_pat = re.compile(r"latin(?:[1-9]|10)\b")
+
+    # Tries to parse a string encoding specification, returning the
+    # Python codec name on success.
+    #
+    # Requires the general prefix (`s:`) if `req_gen_prefix` is `True`.
+    def _try_parse_str_encoding(self, req_gen_prefix: bool = False):
+        # General prefix?
+        if self._try_parse_pat(self._str_encoding_gen_prefix_pat) is not None:
+            # Expect `:`
+            self._skip_ws_and_comments()
+            self._expect_pat(self._str_encoding_colon_pat, "Expecting `:`")
+
+            # Expect encoding specification
+            self._skip_ws_and_comments()
+
+            # UTF?
+            codec = self._try_parse_utf_str_encoding()
+
+            if codec is not None:
+                return codec
+
+            # Expect Latin
+            m = self._expect_pat(
+                self._str_encoding_non_utf_pat,
+                "Expecting `u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`",
+            )
+            return m.group(0)
+
+        # UTF?
+        if not req_gen_prefix:
+            return self._try_parse_utf_str_encoding()
+
+    # Patterns for _try_parse_str()
+    _lit_str_prefix_pat = re.compile(r'"')
+    _str_prefix_pat = re.compile(r'"|\{')
+    _str_expr_pat = re.compile(r"[^}]+")
+    _str_expr_suffix_pat = re.compile(r"\}")
+
+    # Tries to parse a string, returning a literal string or string item
+    # on success.
+    def _try_parse_str(self):
+        begin_text_loc = self._text_loc
+
+        # Encoding
+        codec = self._try_parse_str_encoding()
+
+        # Match prefix (expect if there's an encoding specification)
+        self._skip_ws_and_comments()
+
+        if codec is None:
+            # No encoding: only a literal string (UTF-8) is legal
+            m_prefix = self._try_parse_pat(self._lit_str_prefix_pat)
+
+            if m_prefix is None:
+                return
+        else:
+            # Encoding present: expect a string prefix
+            m_prefix = self._expect_pat(self._str_prefix_pat, 'Expecting `"` or `{`')
+
+        # Literal string or expression?
+        prefix = m_prefix.group(0)
+
+        if prefix == '"':
+            # Expect literal string
+            str_text_loc = self._text_loc
+            val = self._try_parse_lit_str(False)
+
+            if val is None:
+                self._raise_error("Expecting a literal string")
+
+            # Encode string
+            data = _encode_str(val, "utf_8" if codec is None else codec, str_text_loc)
+
+            # Return item
+            return _LitStr(data, begin_text_loc)
+        else:
+            # Expect expression
+            self._skip_ws_and_comments()
+            expr_text_loc = self._text_loc
+            m = self._expect_pat(self._str_expr_pat, "Expecting an expression")
+
+            # Expect `}`
+            self._expect_pat(self._str_expr_suffix_pat, "Expecting `}`")
+
+            # Create an expression node from the expression string
+            expr_str, expr = self._ast_expr_from_str(m.group(0), expr_text_loc)
+
+            # Return item
+            assert codec is not None
+            return _Str(expr_str, expr, codec, begin_text_loc)
+
+    # Common right parenthesis pattern
+    _right_paren_pat = re.compile(r"\)")
+
+    # Patterns for _try_parse_group()
+    _group_prefix_pat = re.compile(r"\(|!g(?:roup)?\b")
+
+    # Tries to parse a group, returning a group item on success.
+    def _try_parse_group(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        m_open = self._try_parse_pat(self._group_prefix_pat)
+
+        if m_open is None:
+            # No match
+            return
+
+        # Parse items
+        items = self._parse_items()
+
+        # Expect end of group
+        self._skip_ws_and_comments_and_syms()
+
+        if m_open.group(0) == "(":
+            pat = self._right_paren_pat
+            exp = ")"
+        else:
+            pat = self._block_end_pat
+            exp = "!end"
+
+        self._expect_pat(pat, "Expecting an item or `{}` (end of group)".format(exp))
+
+        # Return item
+        return _Group(items, begin_text_loc)
+
+    # Returns a stripped expression string and an AST expression node
+    # from the expression string `expr_str` at text location `text_loc`.
+    def _ast_expr_from_str(self, expr_str: str, text_loc: TextLocation):
+        # Create an expression node from the expression string
+        expr_str = expr_str.strip().replace("\n", " ")
+
+        try:
+            expr = ast.parse(expr_str, mode="eval")
+        except SyntaxError:
+            _raise_error(
+                "Invalid expression `{}`: invalid syntax".format(expr_str),
+                text_loc,
+            )
+
+        return expr_str, expr
+
+    # Returns a `ByteOrder` value from the _valid_ byte order string
+    # `bo_str`.
+    @staticmethod
+    def _bo_from_str(bo_str: str):
+        return {
+            "be": ByteOrder.BE,
+            "le": ByteOrder.LE,
+        }[bo_str]
+
+    # Patterns for _try_parse_val()
+    _val_prefix_pat = re.compile(r"\[")
+    _val_expr_pat = re.compile(r"([^\]:]+):")
+    _fl_num_len_fmt_pat = re.compile(r"(?P<len>8|16|24|32|40|48|56|64)(?P<bo>[bl]e)?")
+    _leb128_int_fmt_pat = re.compile(r"(u|s)leb128")
+    _val_suffix_pat = re.compile(r"]")
+
+    # Tries to parse a value (number or string) and format (fixed length
+    # in bits and optional byte order override, `uleb128`, `sleb128`, or
+    # `s:` followed with an encoding name), returning an item on
+    # success.
+    def _try_parse_val(self):
+        # Match prefix
+        if self._try_parse_pat(self._val_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect expression and `:`
+        self._skip_ws_and_comments()
+        expr_text_loc = self._text_loc
+        m = self._expect_pat(self._val_expr_pat, "Expecting an expression")
+
+        # Create an expression node from the expression string
+        expr_str, expr = self._ast_expr_from_str(m.group(1), expr_text_loc)
+
+        # Fixed length?
+        self._skip_ws_and_comments()
+        m_fmt = self._try_parse_pat(self._fl_num_len_fmt_pat)
+
+        if m_fmt is not None:
+            # Byte order override
+            if m_fmt.group("bo") is None:
+                bo = None
+            else:
+                bo = self._bo_from_str(m_fmt.group("bo"))
+
+            # Create fixed-length number item
+            item = _FlNum(
+                expr_str,
+                expr,
+                int(m_fmt.group("len")),
+                bo,
+                expr_text_loc,
+            )
+        else:
+            # LEB128?
+            m_fmt = self._try_parse_pat(self._leb128_int_fmt_pat)
+
+            if m_fmt is not None:
+                # Create LEB128 integer item
+                cls = _ULeb128Int if m_fmt.group(1) == "u" else _SLeb128Int
+                item = cls(expr_str, expr, expr_text_loc)
+            else:
+                # String encoding?
+                codec = self._try_parse_str_encoding(True)
+
+                if codec is not None:
+                    # Create string item
+                    item = _Str(expr_str, expr, codec, expr_text_loc)
+                else:
+                    # At this point it's invalid
+                    self._raise_error(
+                        "Expecting a fixed length (multiple of eight bits and optional `be` or `le`), `uleb128`, `sleb128`, or `s:` followed with a valid encoding (`u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`)"
+                    )
+
+        # Expect `]`
+        self._skip_ws_and_comments()
+        m = self._expect_pat(self._val_suffix_pat, "Expecting `]`")
+
+        # Return item
+        return item
+
+    # Patterns for _try_parse_var_assign()
+    _var_assign_prefix_pat = re.compile(r"\{")
+    _var_assign_equal_pat = re.compile(r"=")
+    _var_assign_expr_pat = re.compile(r"[^}]+")
+    _var_assign_suffix_pat = re.compile(r"\}")
+
+    # Tries to parse a variable assignment, returning a variable
+    # assignment item on success.
+    def _try_parse_var_assign(self):
+        # Match prefix
+        if self._try_parse_pat(self._var_assign_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect a name
+        self._skip_ws_and_comments()
+        name_text_loc = self._text_loc
+        m = self._expect_pat(_py_name_pat, "Expecting a valid Python name")
+        name = m.group(0)
+
+        # Expect `=`
+        self._skip_ws_and_comments()
+        self._expect_pat(self._var_assign_equal_pat, "Expecting `=`")
+
+        # Expect expression
+        self._skip_ws_and_comments()
+        expr_text_loc = self._text_loc
+        m_expr = self._expect_pat(self._var_assign_expr_pat, "Expecting an expression")
+
+        # Expect `}`
+        self._skip_ws_and_comments()
+        self._expect_pat(self._var_assign_suffix_pat, "Expecting `}`")
+
+        # Validate name
+        if name == _icitte_name:
+            _raise_error(
+                "`{}` is a reserved variable name".format(_icitte_name), name_text_loc
+            )
+
+        if name in self._label_names:
+            _raise_error("Existing label named `{}`".format(name), name_text_loc)
+
+        # Create an expression node from the expression string
+        expr_str, expr = self._ast_expr_from_str(m_expr.group(0), expr_text_loc)
+
+        # Add to known variable names
+        self._var_names.add(name)
+
+        # Return item
+        return _VarAssign(
+            name,
+            expr_str,
+            expr,
+            name_text_loc,
+        )
+
+    # Pattern for _try_parse_set_bo()
+    _set_bo_pat = re.compile(r"!([bl]e)\b")
+
+    # Tries to parse a byte order setting, returning a byte order
+    # setting item on success.
+    def _try_parse_set_bo(self):
+        begin_text_loc = self._text_loc
+
+        # Match
+        m = self._try_parse_pat(self._set_bo_pat)
+
+        if m is None:
+            # No match
+            return
+
+        # Return corresponding item
+        if m.group(1) == "be":
+            bo = ByteOrder.BE
+        else:
+            assert m.group(1) == "le"
+            bo = ByteOrder.LE
+
+        return _SetBo(bo, begin_text_loc)
+
+    # Tries to parse an offset setting value (after the initial `<`),
+    # returning an offset item on success.
+    def _try_parse_set_offset_val(self):
+        begin_text_loc = self._text_loc
+
+        # Match
+        m = self._try_parse_pat(_pos_const_int_pat)
+
+        if m is None:
+            # No match
+            return
+
+        # Return item
+        return _SetOffset(int(_norm_const_int(m.group(0)), 0), begin_text_loc)
+
+    # Tries to parse a label name (after the initial `<`), returning a
+    # label item on success.
+    def _try_parse_label_name(self):
+        begin_text_loc = self._text_loc
+
+        # Match
+        m = self._try_parse_pat(_py_name_pat)
+
+        if m is None:
+            # No match
+            return
+
+        # Validate
+        name = m.group(0)
+
+        if name == _icitte_name:
+            _raise_error(
+                "`{}` is a reserved label name".format(_icitte_name), begin_text_loc
+            )
+
+        if name in self._label_names:
+            _raise_error("Duplicate label name `{}`".format(name), begin_text_loc)
+
+        if name in self._var_names:
+            _raise_error("Existing variable named `{}`".format(name), begin_text_loc)
+
+        # Add to known label names
+        self._label_names.add(name)
+
+        # Return item
+        return _Label(name, begin_text_loc)
+
+    # Patterns for _try_parse_label_or_set_offset()
+    _label_set_offset_prefix_pat = re.compile(r"<")
+    _label_set_offset_suffix_pat = re.compile(r">")
+
+    # Tries to parse a label or an offset setting, returning an item on
+    # success.
+    def _try_parse_label_or_set_offset(self):
+        # Match prefix
+        if self._try_parse_pat(self._label_set_offset_prefix_pat) is None:
+            # No match
+            return
+
+        # Offset setting item?
+        self._skip_ws_and_comments()
+        item = self._try_parse_set_offset_val()
+
+        if item is None:
+            # Label item?
+            item = self._try_parse_label_name()
+
+            if item is None:
+                # At this point it's invalid
+                self._raise_error("Expecting a label name or an offset setting value")
+
+        # Expect suffix
+        self._skip_ws_and_comments()
+        self._expect_pat(self._label_set_offset_suffix_pat, "Expecting `>`")
+        return item
+
+    # Pattern for _parse_pad_val()
+    _pad_val_prefix_pat = re.compile(r"~")
+
+    # Tries to parse a padding value, returning the padding value, or 0
+    # if none.
+    def _parse_pad_val(self):
+        # Padding value?
+        self._skip_ws_and_comments()
+        pad_val = 0
+
+        if self._try_parse_pat(self._pad_val_prefix_pat) is not None:
+            self._skip_ws_and_comments()
+            pad_val_text_loc = self._text_loc
+            m = self._expect_pat(
+                _pos_const_int_pat,
+                "Expecting a positive constant integer (byte value)",
+            )
+
+            # Validate
+            pad_val = int(_norm_const_int(m.group(0)), 0)
+
+            if pad_val > 255:
+                _raise_error(
+                    "Invalid padding byte value {}".format(pad_val),
+                    pad_val_text_loc,
+                )
+
+        return pad_val
+
+    # Patterns for _try_parse_align_offset()
+    _align_offset_prefix_pat = re.compile(r"@")
+    _align_offset_val_pat = re.compile(r"\d+")
+
+    # Tries to parse an offset alignment, returning an offset alignment
+    # item on success.
+    def _try_parse_align_offset(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        if self._try_parse_pat(self._align_offset_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect an alignment
+        self._skip_ws_and_comments()
+        align_text_loc = self._text_loc
+        m = self._expect_pat(
+            self._align_offset_val_pat,
+            "Expecting an alignment (positive multiple of eight bits)",
+        )
+
+        # Validate alignment
+        val = int(m.group(0))
+
+        if val <= 0 or (val % 8) != 0:
+            _raise_error(
+                "Invalid alignment value {} (not a positive multiple of eight)".format(
+                    val
+                ),
+                align_text_loc,
+            )
+
+        # Padding value
+        pad_val = self._parse_pad_val()
+
+        # Return item
+        return _AlignOffset(val, pad_val, begin_text_loc)
+
+    # Patterns for _expect_expr()
+    _inner_expr_prefix_pat = re.compile(r"\{")
+    _inner_expr_pat = re.compile(r"[^}]+")
+    _inner_expr_suffix_pat = re.compile(r"\}")
+
+    # Parses an expression outside a `{`/`}` context.
+    #
+    # This function accepts:
+    #
+    # • A Python expression within `{` and `}`.
+    #
+    # • A Python name.
+    #
+    # • If `accept_const_int` is `True`: a constant integer, which may
+    #   be negative if `allow_neg_int` is `True`.
+    #
+    # • If `accept_float` is `True`: a constant floating point number.
+    #
+    # Returns the stripped expression string and AST expression.
+    def _expect_expr(
+        self,
+        accept_const_int: bool = False,
+        allow_neg_int: bool = False,
+        accept_const_float: bool = False,
+        accept_lit_str: bool = False,
+    ):
+        begin_text_loc = self._text_loc
+
+        # Constant floating point number?
+        if accept_const_float:
+            m = self._try_parse_pat(_const_float_pat)
+
+            if m is not None:
+                return self._ast_expr_from_str(m.group(0), begin_text_loc)
+
+        # Constant integer?
+        if accept_const_int:
+            m = self._try_parse_pat(_const_int_pat)
+
+            if m is not None:
+                # Negative and allowed?
+                if m.group("neg") == "-" and not allow_neg_int:
+                    _raise_error(
+                        "Expecting a positive constant integer", begin_text_loc
+                    )
+
+                expr_str = _norm_const_int(m.group(0))
+                return self._ast_expr_from_str(expr_str, begin_text_loc)
+
+        # Name?
+        m = self._try_parse_pat(_py_name_pat)
+
+        if m is not None:
+            return self._ast_expr_from_str(m.group(0), begin_text_loc)
+
+        # Literal string
+        if accept_lit_str:
+            val = self._try_parse_lit_str(True)
+
+            if val is not None:
+                return self._ast_expr_from_str(repr(val), begin_text_loc)
+
+        # Expect `{`
+        msg_accepted_parts = ["a name", "or `{`"]
+
+        if accept_lit_str:
+            msg_accepted_parts.insert(0, "a literal string")
+
+        if accept_const_float:
+            msg_accepted_parts.insert(0, "a constant floating point number")
+
+        if accept_const_int:
+            msg_pos = "" if allow_neg_int else "positive "
+            msg_accepted_parts.insert(0, "a {}constant integer".format(msg_pos))
+
+        if len(msg_accepted_parts) == 2:
+            msg_accepted = " ".join(msg_accepted_parts)
+        else:
+            msg_accepted = ", ".join(msg_accepted_parts)
+
+        self._expect_pat(
+            self._inner_expr_prefix_pat,
+            "Expecting {}".format(msg_accepted),
+        )
+
+        # Expect an expression
+        self._skip_ws_and_comments()
+        expr_text_loc = self._text_loc
+        m = self._expect_pat(self._inner_expr_pat, "Expecting an expression")
+        expr_str = m.group(0)
+
+        # Expect `}`
+        self._skip_ws_and_comments()
+        self._expect_pat(self._inner_expr_suffix_pat, "Expecting `}`")
+
+        return self._ast_expr_from_str(expr_str, expr_text_loc)
+
+    # Patterns for _try_parse_fill_until()
+    _fill_until_prefix_pat = re.compile(r"\+")
+    _fill_until_pad_val_prefix_pat = re.compile(r"~")
+
+    # Tries to parse a filling, returning a filling item on success.
+    def _try_parse_fill_until(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        if self._try_parse_pat(self._fill_until_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect expression
+        self._skip_ws_and_comments()
+        expr_str, expr = self._expect_expr(accept_const_int=True)
+
+        # Padding value
+        pad_val = self._parse_pad_val()
+
+        # Return item
+        return _FillUntil(expr_str, expr, pad_val, begin_text_loc)
+
+    # Parses the multiplier expression of a repetition (block or
+    # post-item) and returns the expression string and AST node.
+    def _expect_rep_mul_expr(self):
+        return self._expect_expr(accept_const_int=True)
+
+    # Common block end pattern
+    _block_end_pat = re.compile(r"!end\b")
+
+    # Pattern for _try_parse_rep_block()
+    _rep_block_prefix_pat = re.compile(r"!r(?:epeat)?\b")
+
+    # Tries to parse a repetition block, returning a repetition item on
+    # success.
+    def _try_parse_rep_block(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        if self._try_parse_pat(self._rep_block_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect expression
+        self._skip_ws_and_comments()
+        expr_str, expr = self._expect_rep_mul_expr()
+
+        # Parse items
+        self._skip_ws_and_comments_and_syms()
+        items = self._parse_items()
+
+        # Expect end of block
+        self._skip_ws_and_comments_and_syms()
+        self._expect_pat(
+            self._block_end_pat, "Expecting an item or `!end` (end of repetition block)"
+        )
+
+        # Return item
+        return _Rep(items, expr_str, expr, begin_text_loc)
+
+    # Pattern for _try_parse_cond_block()
+    _cond_block_prefix_pat = re.compile(r"!if\b")
+    _cond_block_else_pat = re.compile(r"!else\b")
+
+    # Tries to parse a conditional block, returning a conditional item
+    # on success.
+    def _try_parse_cond_block(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        if self._try_parse_pat(self._cond_block_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect expression
+        self._skip_ws_and_comments()
+        expr_str, expr = self._expect_expr()
+
+        # Parse "true" items
+        self._skip_ws_and_comments_and_syms()
+        true_items_text_loc = self._text_loc
+        true_items = self._parse_items()
+        false_items = []  # type: List[_Item]
+        false_items_text_loc = begin_text_loc
+
+        # `!else`?
+        self._skip_ws_and_comments_and_syms()
+
+        if self._try_parse_pat(self._cond_block_else_pat) is not None:
+            # Parse "false" items
+            self._skip_ws_and_comments_and_syms()
+            false_items_text_loc = self._text_loc
+            false_items = self._parse_items()
+
+        # Expect end of block
+        self._expect_pat(
+            self._block_end_pat,
+            "Expecting an item, `!else`, or `!end` (end of conditional block)",
+        )
+
+        # Return item
+        return _Cond(
+            _Group(true_items, true_items_text_loc),
+            _Group(false_items, false_items_text_loc),
+            expr_str,
+            expr,
+            begin_text_loc,
+        )
+
+    # Pattern for _try_parse_trans_block()
+    _trans_block_prefix_pat = re.compile(r"!t(?:ransform)?\b")
+    _trans_block_type_pat = re.compile(
+        r"(?:(?:base|b)64(?:u)?|(?:base|b)(?:16|32)|(?:ascii|a|base|b)85(?:p)?|(?:quopri|qp)(?:t)?|gzip|gz|bzip2|bz2)\b"
+    )
+
+    # Tries to parse a transformation block, returning a transformation
+    # block item on success.
+    def _try_parse_trans_block(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        if self._try_parse_pat(self._trans_block_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect type
+        self._skip_ws_and_comments()
+        m = self._expect_pat(
+            self._trans_block_type_pat, "Expecting a known transformation type"
+        )
+
+        # Parse items
+        self._skip_ws_and_comments_and_syms()
+        items = self._parse_items()
+
+        # Expect end of block
+        self._expect_pat(
+            self._block_end_pat,
+            "Expecting an item or `!end` (end of transformation block)",
+        )
+
+        # Choose encoding function
+        enc = m.group(0)
+
+        if enc in ("base64", "b64"):
+            func = base64.standard_b64encode
+            name = "standard Base64"
+        elif enc in ("base64u", "b64u"):
+            func = base64.urlsafe_b64encode
+            name = "URL-safe Base64"
+        elif enc in ("base32", "b32"):
+            func = base64.b32encode
+            name = "Base32"
+        elif enc in ("base16", "b16"):
+            func = base64.b16encode
+            name = "Base16"
+        elif enc in ("ascii85", "a85"):
+            func = base64.a85encode
+            name = "Ascii85"
+        elif enc in ("ascii85p", "a85p"):
+            func = functools.partial(base64.a85encode, pad=True)
+            name = "padded Ascii85"
+        elif enc in ("base85", "b85"):
+            func = base64.b85encode
+            name = "Base85"
+        elif enc in ("base85p", "b85p"):
+            func = functools.partial(base64.b85encode, pad=True)
+            name = "padded Base85"
+        elif enc in ("quopri", "qp"):
+            func = quopri.encodestring
+            name = "MIME quoted-printable"
+        elif enc in ("quoprit", "qpt"):
+            func = functools.partial(quopri.encodestring, quotetabs=True)
+            name = "MIME quoted-printable (with quoted tabs)"
+        elif enc in ("gzip", "gz"):
+            func = gzip.compress
+            name = "gzip"
+        else:
+            assert enc in ("bzip2", "bz2")
+            func = bz2.compress
+            name = "bzip2"
+
+        # Return item
+        return _Trans(
+            items,
+            name,
+            func,
+            begin_text_loc,
+        )
+
+    # Common left parenthesis pattern
+    _left_paren_pat = re.compile(r"\(")
+
+    # Patterns for _try_parse_macro_def() and _try_parse_macro_exp()
+    _macro_params_comma_pat = re.compile(",")
+
+    # Patterns for _try_parse_macro_def()
+    _macro_def_prefix_pat = re.compile(r"!m(?:acro)?\b")
+
+    # Tries to parse a macro definition, adding it to `self._macro_defs`
+    # and returning `True` on success.
+    def _try_parse_macro_def(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        if self._try_parse_pat(self._macro_def_prefix_pat) is None:
+            # No match
+            return False
+
+        # Expect a name
+        self._skip_ws_and_comments()
+        name_text_loc = self._text_loc
+        m = self._expect_pat(_py_name_pat, "Expecting a valid macro name")
+
+        # Validate name
+        name = m.group(0)
+
+        if name in self._macro_defs:
+            _raise_error("Duplicate macro named `{}`".format(name), name_text_loc)
+
+        # Expect `(`
+        self._skip_ws_and_comments()
+        self._expect_pat(self._left_paren_pat, "Expecting `(`")
+
+        # Try to parse comma-separated parameter names
+        param_names = []  # type: List[str]
+        expect_comma = False
+
+        while True:
+            self._skip_ws_and_comments()
+
+            # End?
+            if self._try_parse_pat(self._right_paren_pat) is not None:
+                # End
+                break
+
+            # Comma?
+            if expect_comma:
+                self._expect_pat(self._macro_params_comma_pat, "Expecting `,`")
+
+            # Expect parameter name
+            self._skip_ws_and_comments()
+            param_text_loc = self._text_loc
+            m = self._expect_pat(_py_name_pat, "Expecting valid parameter name")
+
+            if m.group(0) in param_names:
+                _raise_error(
+                    "Duplicate macro parameter named `{}`".format(m.group(0)),
+                    param_text_loc,
+                )
+
+            param_names.append(m.group(0))
+            expect_comma = True
+
+        # Expect items
+        self._skip_ws_and_comments_and_syms()
+        old_var_names = self._var_names.copy()
+        old_label_names = self._label_names.copy()
+        self._var_names = set()  # type: Set[str]
+        self._label_names = set()  # type: Set[str]
+        items = self._parse_items()
+        self._var_names = old_var_names
+        self._label_names = old_label_names
+
+        # Expect suffix
+        self._expect_pat(
+            self._block_end_pat, "Expecting an item or `!end` (end of macro block)"
+        )
+
+        # Register macro
+        self._macro_defs[name] = _MacroDef(name, param_names, items, begin_text_loc)
+
+        return True
+
+    # Patterns for _try_parse_macro_exp()
+    _macro_exp_prefix_pat = re.compile(r"m\b")
+    _macro_exp_colon_pat = re.compile(r":")
+
+    # Tries to parse a macro expansion, returning a macro expansion item
+    # on success.
+    def _try_parse_macro_exp(self):
+        begin_text_loc = self._text_loc
+
+        # Match prefix
+        if self._try_parse_pat(self._macro_exp_prefix_pat) is None:
+            # No match
+            return
+
+        # Expect `:`
+        self._skip_ws_and_comments()
+        self._expect_pat(self._macro_exp_colon_pat, "Expecting `:`")
+
+        # Expect a macro name
+        self._skip_ws_and_comments()
+        name_text_loc = self._text_loc
+        m = self._expect_pat(_py_name_pat, "Expecting a valid macro name")
+
+        # Validate name
+        name = m.group(0)
+        macro_def = self._macro_defs.get(name)
+
+        if macro_def is None:
+            _raise_error("Unknown macro name `{}`".format(name), name_text_loc)
+
+        # Expect `(`
+        self._skip_ws_and_comments()
+        self._expect_pat(self._left_paren_pat, "Expecting `(`")
+
+        # Try to parse comma-separated parameter values
+        params_text_loc = self._text_loc
+        params = []  # type: List[_MacroExpParam]
+        expect_comma = False
+
+        while True:
+            self._skip_ws_and_comments()
+
+            # End?
+            if self._try_parse_pat(self._right_paren_pat) is not None:
+                # End
+                break
+
+            # Expect a value
+            if expect_comma:
+                self._expect_pat(self._macro_params_comma_pat, "Expecting `,`")
+
+            self._skip_ws_and_comments()
+            param_text_loc = self._text_loc
+            params.append(
+                _MacroExpParam(
+                    *self._expect_expr(
+                        accept_const_int=True,
+                        allow_neg_int=True,
+                        accept_const_float=True,
+                        accept_lit_str=True,
+                    ),
+                    text_loc=param_text_loc
+                )
+            )
+            expect_comma = True
+
+        # Validate parameter values
+        if len(params) != len(macro_def.param_names):
+            sing_plur = "" if len(params) == 1 else "s"
+            _raise_error(
+                "Macro expansion passes {} parameter{} while the definition expects {}".format(
+                    len(params), sing_plur, len(macro_def.param_names)
+                ),
+                params_text_loc,
+            )
+
+        # Return item
+        return _MacroExp(name, params, begin_text_loc)
+
+    # Tries to parse a base item (anything except a post-item
+    # repetition), returning it on success.
+    def _try_parse_base_item(self):
+        for func in self._base_item_parse_funcs:
+            item = func()
+
+            if item is not None:
+                return item
+
+    # Pattern for _try_parse_rep_post()
+    _rep_post_prefix_pat = re.compile(r"\*")
+
+    # Tries to parse a post-item repetition, returning the expression
+    # string and AST expression node on success.
+    def _try_parse_rep_post(self):
+        # Match prefix
+        if self._try_parse_pat(self._rep_post_prefix_pat) is None:
+            # No match
+            return
+
+        # Return expression string and AST expression
+        self._skip_ws_and_comments()
+        return self._expect_rep_mul_expr()
+
+    # Tries to parse an item, possibly followed by a repetition,
+    # returning `True` on success.
+    #
+    # Appends any parsed item to `items`.
+    def _try_append_item(self, items: List[_Item]):
+        self._skip_ws_and_comments_and_syms()
+
+        # Base item
+        item = self._try_parse_base_item()
+
+        if item is None:
+            return
+
+        # Parse repetition if the base item is repeatable
+        if isinstance(item, _RepableItem):
+            self._skip_ws_and_comments()
+            rep_text_loc = self._text_loc
+            rep_ret = self._try_parse_rep_post()
+
+            if rep_ret is not None:
+                item = _Rep([item], *rep_ret, text_loc=rep_text_loc)
+
+        items.append(item)
+        return True
+
+    # Parses and returns items, skipping whitespaces, insignificant
+    # symbols, and comments when allowed, and stopping at the first
+    # unknown character.
+    #
+    # Accepts and registers macro definitions if `accept_macro_defs`
+    # is `True`.
+    def _parse_items(self, accept_macro_defs: bool = False) -> List[_Item]:
+        items = []  # type: List[_Item]
+
+        while self._isnt_done():
+            # Try to append item
+            if not self._try_append_item(items):
+                if accept_macro_defs and self._try_parse_macro_def():
+                    continue
+
+                # Unknown at this point
+                break
+
+        return items
+
+    # Parses the whole Normand input, setting `self._res` to the main
+    # group item on success.
+    def _parse(self):
+        if len(self._normand.strip()) == 0:
+            # Special case to make sure there's something to consume
+            self._res = _Group([], self._text_loc)
+            return
+
+        # Parse first level items
+        items = self._parse_items(True)
+
+        # Make sure there's nothing left
+        self._skip_ws_and_comments_and_syms()
+
+        if self._isnt_done():
+            self._raise_error(
+                "Unexpected character `{}`".format(self._normand[self._at])
+            )
+
+        # Set main group item
+        self._res = _Group(items, self._text_loc)
+
+
+# The return type of parse().
+class ParseResult:
+    @classmethod
+    def _create(
+        cls,
+        data: bytearray,
+        variables: VariablesT,
+        labels: LabelsT,
+        offset: int,
+        bo: Optional[ByteOrder],
+    ):
+        self = cls.__new__(cls)
+        self._init(data, variables, labels, offset, bo)
+        return self
+
+    def __init__(self, *args, **kwargs):  # type: ignore
+        raise NotImplementedError
+
+    def _init(
+        self,
+        data: bytearray,
+        variables: VariablesT,
+        labels: LabelsT,
+        offset: int,
+        bo: Optional[ByteOrder],
+    ):
+        self._data = data
+        self._vars = variables
+        self._labels = labels
+        self._offset = offset
+        self._bo = bo
+
+    # Generated data.
+    @property
+    def data(self):
+        return self._data
+
+    # Dictionary of updated variable names to their last computed value.
+    @property
+    def variables(self):
+        return self._vars
+
+    # Dictionary of updated main group label names to their computed
+    # value.
+    @property
+    def labels(self):
+        return self._labels
+
+    # Updated offset.
+    @property
+    def offset(self):
+        return self._offset
+
+    # Updated byte order.
+    @property
+    def byte_order(self):
+        return self._bo
+
+
+# Raises a parse error for the item `item`, creating it using the
+# message `msg`.
+def _raise_error_for_item(msg: str, item: _Item) -> NoReturn:
+    _raise_error(msg, item.text_loc)
+
+
+# The `ICITTE` reserved name.
+_icitte_name = "ICITTE"
+
+
+# Base node visitor.
+#
+# Calls the _visit_name() method for each name node which isn't the name
+# of a call.
+class _NodeVisitor(ast.NodeVisitor):
+    def __init__(self):
+        self._parent_is_call = False
+
+    def generic_visit(self, node: ast.AST):
+        if type(node) is ast.Call:
+            self._parent_is_call = True
+        elif type(node) is ast.Name and not self._parent_is_call:
+            self._visit_name(node.id)
+
+        super().generic_visit(node)
+        self._parent_is_call = False
+
+    @abc.abstractmethod
+    def _visit_name(self, name: str):
+        ...
+
+
+# Expression validator: validates that all the names within the
+# expression are allowed.
+class _ExprValidator(_NodeVisitor):
+    def __init__(self, expr_str: str, text_loc: TextLocation, allowed_names: Set[str]):
+        super().__init__()
+        self._expr_str = expr_str
+        self._text_loc = text_loc
+        self._allowed_names = allowed_names
+
+    def _visit_name(self, name: str):
+        # Make sure the name refers to a known and reachable
+        # variable/label name.
+        if name != _icitte_name and name not in self._allowed_names:
+            msg = "Illegal (unknown or unreachable) variable/label name `{}` in expression `{}`".format(
+                name, self._expr_str
+            )
+
+            allowed_names = self._allowed_names.copy()
+            allowed_names.add(_icitte_name)
+
+            if len(allowed_names) > 0:
+                allowed_names_str = ", ".join(
+                    sorted(["`{}`".format(name) for name in allowed_names])
+                )
+                msg += "; the legal names are {{{}}}".format(allowed_names_str)
+
+            _raise_error(
+                msg,
+                self._text_loc,
+            )
+
+
+# Generator state.
+class _GenState:
+    def __init__(
+        self,
+        variables: VariablesT,
+        labels: LabelsT,
+        offset: int,
+        bo: Optional[ByteOrder],
+    ):
+        self.variables = variables.copy()
+        self.labels = labels.copy()
+        self.offset = offset
+        self.bo = bo
+
+    def __repr__(self):
+        return "_GenState({}, {}, {}, {})".format(
+            repr(self.variables), repr(self.labels), repr(self.offset), repr(self.bo)
+        )
+
+
+# Fixed-length number item instance.
+class _FlNumItemInst:
+    def __init__(
+        self,
+        item: _FlNum,
+        offset_in_data: int,
+        state: _GenState,
+        parse_error_msgs: List[ParseErrorMessage],
+    ):
+        self._item = item
+        self._offset_in_data = offset_in_data
+        self._state = state
+        self._parse_error_msgs = parse_error_msgs
+
+    @property
+    def item(self):
+        return self._item
+
+    @property
+    def offset_in_data(self):
+        return self._offset_in_data
+
+    @property
+    def state(self):
+        return self._state
+
+    @property
+    def parse_error_msgs(self):
+        return self._parse_error_msgs
+
+
+# Generator of data and final state from a group item.
+#
+# Generation happens in memory at construction time. After building, use
+# the `data`, `variables`, `labels`, `offset`, and `bo` properties to
+# get the resulting context.
+#
+# The steps of generation are:
+#
+# 1. Handle each item in prefix order.
+#
+#    The handlers append bytes to `self._data` and update some current
+#    state object (`_GenState` instance).
+#
+#    When handling a fixed-length number item, try to evaluate its
+#    expression using the current state. If this fails, then it might be
+#    because the expression refers to a "future" label: save the current
+#    offset in `self._data` (generated data) and a snapshot of the
+#    current state within `self._fl_num_item_insts` (`_FlNumItemInst`
+#    object). _gen_fl_num_item_insts() will deal with this later. A
+#    `_FlNumItemInst` instance also contains a snapshot of the current
+#    parsing error messages (`self._parse_error_msgs`) which need to be
+#    taken into account when handling the instance later.
+#
+#    When handling the items of a group, keep a map of immediate label
+#    names to their offset. Then, after having processed all the items,
+#    update the relevant saved state snapshots in
+#    `self._fl_num_item_insts` with those immediate label values.
+#    _gen_fl_num_item_insts() will deal with this later.
+#
+# 2. Handle all the fixed-length number item instances of which the
+#    expression evaluation failed before.
+#
+#    At this point, `self._fl_num_item_insts` contains everything that's
+#    needed to evaluate the expressions, including the values of
+#    "future" labels from the point of view of some fixed-length number
+#    item instance.
+#
+#    If an evaluation fails at this point, then it's a user error. Add
+#    to the parsing error all the saved parsing error messages of the
+#    instance. Those additional messages add precious context to the
+#    error.
+class _Gen:
+    def __init__(
+        self,
+        group: _Group,
+        macro_defs: _MacroDefsT,
+        variables: VariablesT,
+        labels: LabelsT,
+        offset: int,
+        bo: Optional[ByteOrder],
+    ):
+        self._macro_defs = macro_defs
+        self._fl_num_item_insts = []  # type: List[_FlNumItemInst]
+        self._parse_error_msgs = []  # type: List[ParseErrorMessage]
+        self._in_trans = False
+        self._gen(group, _GenState(variables, labels, offset, bo))
+
+    # Generated bytes.
+    @property
+    def data(self):
+        return self._data
+
+    # Updated variables.
+    @property
+    def variables(self):
+        return self._final_state.variables
+
+    # Updated main group labels.
+    @property
+    def labels(self):
+        return self._final_state.labels
+
+    # Updated offset.
+    @property
+    def offset(self):
+        return self._final_state.offset
+
+    # Updated byte order.
+    @property
+    def bo(self):
+        return self._final_state.bo
+
+    # Evaluates the expression `expr` of which the original string is
+    # `expr_str` at the location `text_loc` considering the current
+    # generation state `state`.
+    #
+    # If `accept_float` is `True`, then the type of the result may be
+    # `float` too.
+    #
+    # If `accept_str` is `True`, then the type of the result may be
+    # `str` too.
+    @staticmethod
+    def _eval_expr(
+        expr_str: str,
+        expr: ast.Expression,
+        text_loc: TextLocation,
+        state: _GenState,
+        accept_float: bool = False,
+        accept_str: bool = False,
+    ):
+        syms = {}  # type: VariablesT
+        syms.update(state.labels)
+
+        # Set the `ICITTE` name to the current offset
+        syms[_icitte_name] = state.offset
+
+        # Add the current variables
+        syms.update(state.variables)
+
+        # Validate the node and its children
+        _ExprValidator(expr_str, text_loc, set(syms.keys())).visit(expr)
+
+        # Compile and evaluate expression node
+        try:
+            val = eval(compile(expr, "", "eval"), None, syms)
+        except Exception as exc:
+            _raise_error(
+                "Failed to evaluate expression `{}`: {}".format(expr_str, exc),
+                text_loc,
+            )
+
+        # Convert `bool` result type to `int` to normalize
+        if type(val) is bool:
+            val = int(val)
+
+        # Validate result type
+        expected_types = {int}  # type: Set[type]
+
+        if accept_float:
+            expected_types.add(float)
+
+        if accept_str:
+            expected_types.add(str)
+
+        if type(val) not in expected_types:
+            expected_types_str = sorted(
+                ["`{}`".format(t.__name__) for t in expected_types]
+            )
+
+            if len(expected_types_str) == 1:
+                msg_expected = expected_types_str[0]
+            elif len(expected_types_str) == 2:
+                msg_expected = " or ".join(expected_types_str)
+            else:
+                expected_types_str[-1] = "or {}".format(expected_types_str[-1])
+                msg_expected = ", ".join(expected_types_str)
+
+            _raise_error(
+                "Invalid expression `{}`: expecting result type {}, not `{}`".format(
+                    expr_str, msg_expected, type(val).__name__
+                ),
+                text_loc,
+            )
+
+        return val
+
+    # Forwards to _eval_expr() with the expression and text location of
+    # `item`.
+    @staticmethod
+    def _eval_item_expr(
+        item: Union[_Cond, _FillUntil, _FlNum, _Leb128Int, _Rep, _Str, _VarAssign],
+        state: _GenState,
+        accept_float: bool = False,
+        accept_str: bool = False,
+    ):
+        return _Gen._eval_expr(
+            item.expr_str, item.expr, item.text_loc, state, accept_float, accept_str
+        )
+
+    # Handles the byte item `item`.
+    def _handle_byte_item(self, item: _Byte, state: _GenState):
+        self._data.append(item.val)
+        state.offset += item.size
+
+    # Handles the literal string item `item`.
+    def _handle_lit_str_item(self, item: _LitStr, state: _GenState):
+        self._data += item.data
+        state.offset += item.size
+
+    # Handles the byte order setting item `item`.
+    def _handle_set_bo_item(self, item: _SetBo, state: _GenState):
+        # Update current byte order
+        state.bo = item.bo
+
+    # Handles the variable assignment item `item`.
+    def _handle_var_assign_item(self, item: _VarAssign, state: _GenState):
+        # Update variable
+        state.variables[item.name] = self._eval_item_expr(
+            item, state, accept_float=True, accept_str=True
+        )
+
+    # Returns the effective byte order to use to encode the fixed-length
+    # number `item` considering the current state `state`.
+    @staticmethod
+    def _fl_num_item_effective_bo(item: _FlNum, state: _GenState):
+        return state.bo if item.bo is None else item.bo
+
+    # Handles the fixed-length number item `item`.
+    def _handle_fl_num_item(self, item: _FlNum, state: _GenState):
+        # Effective byte order
+        bo = self._fl_num_item_effective_bo(item, state)
+
+        # Validate current byte order
+        if bo is None and item.len > 8:
+            _raise_error_for_item(
+                "Current byte order isn't defined at first fixed-length number (`{}`) to encode on more than 8 bits".format(
+                    item.expr_str
+                ),
+                item,
+            )
+
+        # Try an immediate evaluation. If it fails, then keep everything
+        # needed to (try to) generate the bytes of this item later.
+        try:
+            data = self._gen_fl_num_item_inst_data(item, state)
+        except Exception:
+            if self._in_trans:
+                _raise_error_for_item(
+                    "Invalid expression `{}`: failed to evaluate within a transformation block".format(
+                        item.expr_str
+                    ),
+                    item,
+                )
+
+            self._fl_num_item_insts.append(
+                _FlNumItemInst(
+                    item,
+                    len(self._data),
+                    copy.deepcopy(state),
+                    copy.deepcopy(self._parse_error_msgs),
+                )
+            )
+
+            # Reserve space in `self._data` for this instance
+            data = bytes([0] * (item.len // 8))
+
+        # Append bytes
+        self._data += data
+
+        # Update offset
+        state.offset += len(data)
+
+    # Returns the size, in bytes, required to encode the value `val`
+    # with LEB128 (signed version if `is_signed` is `True`).
+    @staticmethod
+    def _leb128_size_for_val(val: int, is_signed: bool):
+        if val < 0:
+            # Equivalent upper bound.
+            #
+            # For example, if `val` is -128, then the full integer for
+            # this number of bits would be [-128, 127].
+            val = -val - 1
+
+        # Number of bits (add one for the sign if needed)
+        bits = val.bit_length() + int(is_signed)
+
+        if bits == 0:
+            bits = 1
+
+        # Seven bits per byte
+        return math.ceil(bits / 7)
+
+    # Handles the LEB128 integer item `item`.
+    def _handle_leb128_int_item(self, item: _Leb128Int, state: _GenState):
+        # Compute value
+        val = self._eval_item_expr(item, state)
+
+        # Size in bytes
+        size = self._leb128_size_for_val(val, type(item) is _SLeb128Int)
+
+        # For each byte
+        for _ in range(size):
+            # Seven LSBs, MSB of the byte set (continue)
+            self._data.append((val & 0x7F) | 0x80)
+            val >>= 7
+
+        # Clear MSB of last byte (stop)
+        self._data[-1] &= ~0x80
+
+        # Update offset
+        state.offset += size
+
+    # Handles the string item `item`.
+    def _handle_str_item(self, item: _Str, state: _GenState):
+        # Compute value
+        val = str(self._eval_item_expr(item, state, accept_float=True, accept_str=True))
+
+        # Encode
+        data = _encode_str(val, item.codec, item.text_loc)
+
+        # Add to data
+        self._data += data
+
+        # Update offset
+        state.offset += len(data)
+
+    # Handles the group item `item`, removing the immediate labels from
+    # `state` at the end if `remove_immediate_labels` is `True`.
+    def _handle_group_item(
+        self, item: _Group, state: _GenState, remove_immediate_labels: bool = True
+    ):
+        first_fl_num_item_inst_index = len(self._fl_num_item_insts)
+        immediate_labels = {}  # type: LabelsT
+
+        # Handle each item
+        for subitem in item.items:
+            if type(subitem) is _Label:
+                # Add to local immediate labels
+                immediate_labels[subitem.name] = state.offset
+
+            self._handle_item(subitem, state)
+
+        # Remove immediate labels from current state if needed
+        if remove_immediate_labels:
+            for name in immediate_labels:
+                del state.labels[name]
+
+        # Add all immediate labels to all state snapshots since
+        # `first_fl_num_item_inst_index`.
+        for inst in self._fl_num_item_insts[first_fl_num_item_inst_index:]:
+            inst.state.labels.update(immediate_labels)
+
+    # Handles the repetition item `item`.
+    def _handle_rep_item(self, item: _Rep, state: _GenState):
+        # Compute the repetition count
+        mul = _Gen._eval_item_expr(item, state)
+
+        # Validate result
+        if mul < 0:
+            _raise_error_for_item(
+                "Invalid expression `{}`: unexpected negative result {:,}".format(
+                    item.expr_str, mul
+                ),
+                item,
+            )
+
+        # Generate group data `mul` times
+        for _ in range(mul):
+            self._handle_group_item(item, state)
+
+    # Handles the conditional item `item`.
+    def _handle_cond_item(self, item: _Cond, state: _GenState):
+        # Compute the conditional value
+        val = _Gen._eval_item_expr(item, state)
+
+        # Generate selected group data
+        if val:
+            self._handle_group_item(item.true_item, state)
+        else:
+            self._handle_group_item(item.false_item, state)
+
+    # Handles the transformation item `item`.
+    def _handle_trans_item(self, item: _Trans, state: _GenState):
+        init_in_trans = self._in_trans
+        self._in_trans = True
+        init_data_len = len(self._data)
+        init_offset = state.offset
+
+        # Generate group data
+        self._handle_group_item(item, state)
+
+        # Remove and keep group data
+        to_trans = self._data[init_data_len:]
+        del self._data[init_data_len:]
+
+        # Encode group data and append to current data
+        try:
+            transformed = item.trans(to_trans)
+        except Exception as exc:
+            _raise_error_for_item(
+                "Cannot apply the {} transformation to this data: {}".format(
+                    item.name, exc
+                ),
+                item,
+            )
+
+        self._data += transformed
+
+        # Update offset and restore
+        state.offset = init_offset + len(transformed)
+        self._in_trans = init_in_trans
+
+    # Evaluates the parameters of the macro expansion item `item`
+    # considering the initial state `init_state` and returns a new state
+    # to handle the items of the macro.
+    def _eval_macro_exp_params(self, item: _MacroExp, init_state: _GenState):
+        # New state
+        exp_state = _GenState({}, {}, init_state.offset, init_state.bo)
+
+        # Evaluate the parameter expressions
+        macro_def = self._macro_defs[item.name]
+
+        for param_name, param in zip(macro_def.param_names, item.params):
+            exp_state.variables[param_name] = _Gen._eval_expr(
+                param.expr_str,
+                param.expr,
+                param.text_loc,
+                init_state,
+                accept_float=True,
+                accept_str=True,
+            )
+
+        return exp_state
+
+    # Handles the macro expansion item `item`.
+    def _handle_macro_exp_item(self, item: _MacroExp, state: _GenState):
+        parse_error_msg_text = "While expanding the macro `{}`:".format(item.name)
+
+        try:
+            # New state
+            exp_state = self._eval_macro_exp_params(item, state)
+
+            # Process the contained group
+            init_data_size = len(self._data)
+            parse_error_msg = (
+                ParseErrorMessage._create(  # pyright: ignore[reportPrivateUsage]
+                    parse_error_msg_text, item.text_loc
+                )
+            )
+            self._parse_error_msgs.append(parse_error_msg)
+            self._handle_group_item(self._macro_defs[item.name], exp_state)
+            self._parse_error_msgs.pop()
+        except ParseError as exc:
+            _augment_error(exc, parse_error_msg_text, item.text_loc)
+
+        # Update state offset and return
+        state.offset += len(self._data) - init_data_size
+
+    # Handles the offset setting item `item`.
+    def _handle_set_offset_item(self, item: _SetOffset, state: _GenState):
+        state.offset = item.val
+
+    # Handles the offset alignment item `item` (adds padding).
+    def _handle_align_offset_item(self, item: _AlignOffset, state: _GenState):
+        init_offset = state.offset
+        align_bytes = item.val // 8
+        state.offset = (state.offset + align_bytes - 1) // align_bytes * align_bytes
+        self._data += bytes([item.pad_val] * (state.offset - init_offset))
+
+    # Handles the filling item `item` (adds padding).
+    def _handle_fill_until_item(self, item: _FillUntil, state: _GenState):
+        # Compute the new offset
+        new_offset = _Gen._eval_item_expr(item, state)
+
+        # Validate the new offset
+        if new_offset < state.offset:
+            _raise_error_for_item(
+                "Invalid expression `{}`: new offset {:,} is less than current offset {:,}".format(
+                    item.expr_str, new_offset, state.offset
+                ),
+                item,
+            )
+
+        # Fill
+        self._data += bytes([item.pad_val] * (new_offset - state.offset))
+
+        # Update offset
+        state.offset = new_offset
+
+    # Handles the label item `item`.
+    def _handle_label_item(self, item: _Label, state: _GenState):
+        state.labels[item.name] = state.offset
+
+    # Handles the item `item`, returning the updated next repetition
+    # instance.
+    def _handle_item(self, item: _Item, state: _GenState):
+        return self._item_handlers[type(item)](item, state)
+
+    # Generates the data for a fixed-length integer item instance having
+    # the value `val` and the effective byte order `bo` and returns it.
+    def _gen_fl_int_item_inst_data(
+        self, val: int, bo: Optional[ByteOrder], item: _FlNum
+    ):
+        # Validate range
+        if val < -(2 ** (item.len - 1)) or val > 2**item.len - 1:
+            _raise_error_for_item(
+                "Value {:,} is outside the {}-bit range when evaluating expression `{}`".format(
+                    val, item.len, item.expr_str
+                ),
+                item,
+            )
+
+        # Encode result on 64 bits (to extend the sign bit whatever the
+        # value of `item.len`).
+        data = struct.pack(
+            "{}{}".format(
+                ">" if bo in (None, ByteOrder.BE) else "<",
+                "Q" if val >= 0 else "q",
+            ),
+            val,
+        )
+
+        # Keep only the requested length
+        len_bytes = item.len // 8
+
+        if bo in (None, ByteOrder.BE):
+            # Big endian: keep last bytes
+            data = data[-len_bytes:]
+        else:
+            # Little endian: keep first bytes
+            assert bo == ByteOrder.LE
+            data = data[:len_bytes]
+
+        # Return data
+        return data
+
+    # Generates the data for a fixed-length floating point number item
+    # instance having the value `val` and the effective byte order `bo`
+    # and returns it.
+    def _gen_fl_float_item_inst_data(
+        self, val: float, bo: Optional[ByteOrder], item: _FlNum
+    ):
+        # Validate length
+        if item.len not in (32, 64):
+            _raise_error_for_item(
+                "Invalid {}-bit length for a fixed-length floating point number (value {:,})".format(
+                    item.len, val
+                ),
+                item,
+            )
+
+        # Encode and return result
+        return struct.pack(
+            "{}{}".format(
+                ">" if bo in (None, ByteOrder.BE) else "<",
+                "f" if item.len == 32 else "d",
+            ),
+            val,
+        )
+
+    # Generates the data for a fixed-length number item instance and
+    # returns it.
+    def _gen_fl_num_item_inst_data(self, item: _FlNum, state: _GenState):
+        # Effective byte order
+        bo = self._fl_num_item_effective_bo(item, state)
+
+        # Compute value
+        val = self._eval_item_expr(item, state, True)
+
+        # Handle depending on type
+        if type(val) is int:
+            return self._gen_fl_int_item_inst_data(val, bo, item)
+        else:
+            assert type(val) is float
+            return self._gen_fl_float_item_inst_data(val, bo, item)
+
+    # Generates the data for all the fixed-length number item instances
+    # and writes it at the correct offset within `self._data`.
+    def _gen_fl_num_item_insts(self):
+        for inst in self._fl_num_item_insts:
+            # Generate bytes
+            try:
+                data = self._gen_fl_num_item_inst_data(inst.item, inst.state)
+            except ParseError as exc:
+                # Add all the saved parse error messages for this
+                # instance.
+                for msg in reversed(inst.parse_error_msgs):
+                    _add_error_msg(exc, msg.text, msg.text_location)
+
+                raise
+
+            # Insert bytes into `self._data`
+            self._data[inst.offset_in_data : inst.offset_in_data + len(data)] = data
+
+    # Generates the data (`self._data`) and final state
+    # (`self._final_state`) from `group` and the initial state `state`.
+    def _gen(self, group: _Group, state: _GenState):
+        # Initial state
+        self._data = bytearray()
+
+        # Item handlers
+        self._item_handlers = {
+            _AlignOffset: self._handle_align_offset_item,
+            _Byte: self._handle_byte_item,
+            _Cond: self._handle_cond_item,
+            _FillUntil: self._handle_fill_until_item,
+            _FlNum: self._handle_fl_num_item,
+            _Group: self._handle_group_item,
+            _Label: self._handle_label_item,
+            _LitStr: self._handle_lit_str_item,
+            _MacroExp: self._handle_macro_exp_item,
+            _Rep: self._handle_rep_item,
+            _SetBo: self._handle_set_bo_item,
+            _SetOffset: self._handle_set_offset_item,
+            _SLeb128Int: self._handle_leb128_int_item,
+            _Str: self._handle_str_item,
+            _Trans: self._handle_trans_item,
+            _ULeb128Int: self._handle_leb128_int_item,
+            _VarAssign: self._handle_var_assign_item,
+        }  # type: Dict[type, Callable[[Any, _GenState], None]]
+
+        # Handle the group item, _not_ removing the immediate labels
+        # because the `labels` property offers them.
+        self._handle_group_item(group, state, False)
+
+        # This is actually the final state
+        self._final_state = state
+
+        # Generate all the fixed-length number bytes now that we know
+        # their full state
+        self._gen_fl_num_item_insts()
+
+
+# Returns a `ParseResult` instance containing the bytes encoded by the
+# input string `normand`.
+#
+# `init_variables` is a dictionary of initial variable names (valid
+# Python names) to integral values. A variable name must not be the
+# reserved name `ICITTE`.
+#
+# `init_labels` is a dictionary of initial label names (valid Python
+# names) to integral values. A label name must not be the reserved name
+# `ICITTE`.
+#
+# `init_offset` is the initial offset.
+#
+# `init_byte_order` is the initial byte order.
+#
+# Raises `ParseError` on any parsing error.
+def parse(
+    normand: str,
+    init_variables: Optional[VariablesT] = None,
+    init_labels: Optional[LabelsT] = None,
+    init_offset: int = 0,
+    init_byte_order: Optional[ByteOrder] = None,
+):
+    if init_variables is None:
+        init_variables = {}
+
+    if init_labels is None:
+        init_labels = {}
+
+    parser = _Parser(normand, init_variables, init_labels)
+    gen = _Gen(
+        parser.res,
+        parser.macro_defs,
+        init_variables,
+        init_labels,
+        init_offset,
+        init_byte_order,
+    )
+    return ParseResult._create(  # pyright: ignore[reportPrivateUsage]
+        gen.data, gen.variables, gen.labels, gen.offset, gen.bo
+    )
+
+
+# Raises a command-line error with the message `msg`.
+def _raise_cli_error(msg: str) -> NoReturn:
+    raise RuntimeError("Command-line error: {}".format(msg))
+
+
+# Returns the `int` or `float` value out of a CLI assignment value.
+def _val_from_assign_val_str(s: str, is_label: bool):
+    s = s.strip()
+
+    # Floating point number?
+    if not is_label:
+        m = _const_float_pat.fullmatch(s)
+
+        if m is not None:
+            return float(m.group(0))
+
+    # Integer?
+    m = _const_int_pat.fullmatch(s)
+
+    if m is not None:
+        return int(_norm_const_int(m.group(0)), 0)
+
+    exp = "an integer" if is_label else "a number"
+    _raise_cli_error("Invalid assignment value `{}`: expecting {}".format(s, exp))
+
+
+# Returns a dictionary of string to numbers from the list of strings
+# `args` containing `NAME=VAL` entries.
+def _dict_from_arg(args: Optional[List[str]], is_label: bool, is_str_only: bool):
+    d = {}  # type: VariablesT
+
+    if args is None:
+        return d
+
+    for arg in args:
+        m = re.match(r"({})\s*=\s*(.*)$".format(_py_name_pat.pattern), arg)
+
+        if m is None:
+            _raise_cli_error("Invalid assignment `{}`".format(arg))
+
+        if is_str_only:
+            val = m.group(2)
+        else:
+            val = _val_from_assign_val_str(m.group(2), is_label)
+
+        d[m.group(1)] = val
+
+    return d
+
+
+# Parses the command-line arguments and returns, in this order:
+#
+# 1. The input file path, or `None` if none.
+# 2. The Normand input text.
+# 3. The initial offset.
+# 4. The initial byte order.
+# 5. The initial variables.
+# 6. The initial labels.
+def _parse_cli_args():
+    import argparse
+
+    # Build parser
+    ap = argparse.ArgumentParser()
+    ap.add_argument(
+        "--offset",
+        metavar="OFFSET",
+        action="store",
+        type=int,
+        default=0,
+        help="initial offset (positive)",
+    )
+    ap.add_argument(
+        "-b",
+        "--byte-order",
+        metavar="BO",
+        choices=["be", "le"],
+        type=str,
+        help="initial byte order (`be` or `le`)",
+    )
+    ap.add_argument(
+        "-v",
+        "--var",
+        metavar="NAME=VAL",
+        action="append",
+        help="add an initial numeric variable (may be repeated)",
+    )
+    ap.add_argument(
+        "-s",
+        "--var-str",
+        metavar="NAME=VAL",
+        action="append",
+        help="add an initial string variable (may be repeated)",
+    )
+    ap.add_argument(
+        "-l",
+        "--label",
+        metavar="NAME=VAL",
+        action="append",
+        help="add an initial label (may be repeated)",
+    )
+    ap.add_argument(
+        "--version", action="version", version="Normand {}".format(__version__)
+    )
+    ap.add_argument(
+        "path",
+        metavar="PATH",
+        action="store",
+        nargs="?",
+        help="input path (none means standard input)",
+    )
+
+    # Parse
+    args = ap.parse_args()
+
+    # Read input
+    if args.path is None:
+        normand = sys.stdin.read()
+    else:
+        with open(args.path) as f:
+            normand = f.read()
+
+    # Variables and labels
+    variables = _dict_from_arg(args.var, False, False)
+    variables.update(_dict_from_arg(args.var_str, False, True))
+    labels = _dict_from_arg(args.label, True, False)
+
+    # Validate offset
+    if args.offset < 0:
+        _raise_cli_error("Invalid negative offset {}")
+
+    # Validate and set byte order
+    bo = None  # type: Optional[ByteOrder]
+
+    if args.byte_order is not None:
+        if args.byte_order == "be":
+            bo = ByteOrder.BE
+        else:
+            assert args.byte_order == "le"
+            bo = ByteOrder.LE
+
+    # Return input and initial state
+    return args.path, normand, args.offset, bo, variables, typing.cast(LabelsT, labels)
+
+
+# CLI entry point without exception handling.
+def _run_cli_with_args(
+    normand: str,
+    offset: int,
+    bo: Optional[ByteOrder],
+    variables: VariablesT,
+    labels: LabelsT,
+):
+    sys.stdout.buffer.write(parse(normand, variables, labels, offset, bo).data)
+
+
+# Prints the exception message `msg` and exits with status 1.
+def _fail(msg: str) -> NoReturn:
+    if not msg.endswith("."):
+        msg += "."
+
+    print(msg.strip(), file=sys.stderr)
+    sys.exit(1)
+
+
+# CLI entry point.
+def _run_cli():
+    try:
+        args = _parse_cli_args()
+    except Exception as exc:
+        _fail(str(exc))
+
+    try:
+        _run_cli_with_args(*args[1:])
+    except ParseError as exc:
+        import os.path
+
+        prefix = "" if args[0] is None else "{}:".format(os.path.abspath(args[0]))
+        fail_msg = ""
+
+        for msg in reversed(exc.messages):
+            fail_msg += "{}{}:{} - {}".format(
+                prefix,
+                msg.text_location.line_no,
+                msg.text_location.col_no,
+                msg.text,
+            )
+
+            if fail_msg[-1] not in ".:;":
+                fail_msg += "."
+
+            fail_msg += "\n"
+
+        _fail(fail_msg.strip())
+    except Exception as exc:
+        _fail(str(exc))
+
+
+if __name__ == "__main__":
+    _run_cli()
index c57e84e9b718db77f551aac9154c1f06772514ed..91c0d5d64b77072cd592583a94190340d4c628e2 100644 (file)
@@ -2,8 +2,8 @@
 #
 # Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
 
-import sys
 import re
+import sys
 
 
 def main():
index a7cd82639adaef00e62c96d4aabfd572dc3b5d13..4ff52d82d48ee535a17fc2e720a5a5144b85ed75 100644 (file)
@@ -3,11 +3,11 @@
 # Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
 #
 
-from tap import TAPTestRunner
-import unittest
 import sys
 import argparse
+import unittest
 
+from tap import TAPTestRunner
 
 if __name__ == "__main__":
     argparser = argparse.ArgumentParser()
@@ -31,7 +31,7 @@ if __name__ == "__main__":
     mut_exclu_group.add_argument(
         "-t",
         "--test-case",
-        help="Run a specfic test module name, test class "
+        help="Run a specific test module name, test class "
         "name, or test method name "
         "(e.g. test_event.EventTestCase.test_clock_value)",
         type=str,
diff --git a/tests/utils/python/tjson.py b/tests/utils/python/tjson.py
new file mode 100644 (file)
index 0000000..59a5a1c
--- /dev/null
@@ -0,0 +1,270 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright (C) 2023 EfficiOS, inc.
+#
+# pyright: strict, reportTypeCommentUsage=false
+
+
+import re
+import json
+import typing
+from typing import (
+    Any,
+    Dict,
+    List,
+    Type,
+    Union,
+    TextIO,
+    Generic,
+    Mapping,
+    TypeVar,
+    Optional,
+    Sequence,
+    overload,
+)
+
+# Internal type aliases and variables
+_RawArrayT = List["_RawValT"]
+_RawObjT = Dict[str, "_RawValT"]
+_RawValT = Union[None, bool, int, float, str, _RawArrayT, _RawObjT]
+_RawValTV = TypeVar("_RawValTV", bound="_RawValT")
+_ValTV = TypeVar("_ValTV", bound="Val")
+
+
+# Type of a single JSON value path element.
+PathElemT = Union[str, int]
+
+
+# A JSON value path.
+class Path:
+    def __init__(self, elems: Optional[List[PathElemT]] = None):
+        if elems is None:
+            elems = []
+
+        self._elems = elems
+
+    # Elements of this path.
+    @property
+    def elems(self):
+        return self._elems
+
+    # Returns a new path containing the current elements plus `elem`.
+    def __truediv__(self, elem: PathElemT):
+        return Path(self._elems + [elem])
+
+    # Returns a valid jq filter.
+    def __str__(self):
+        s = ""
+
+        for elem in self._elems:
+            if type(elem) is str:
+                if re.match(r"[a-zA-Z]\w*$", elem):
+                    s += ".{}".format(elem)
+                else:
+                    s += '."{}"'.format(elem)
+            else:
+                assert type(elem) is int
+                s += "[{}]".format(elem)
+
+        if not s.startswith("."):
+            s = "." + s
+
+        return s
+
+
+# Base of any JSON value.
+class Val:
+    _name = "a value"
+
+    def __init__(self, path: Optional[Path] = None):
+        if path is None:
+            path = Path()
+
+        self._path = path
+
+    # Path to this JSON value.
+    @property
+    def path(self):
+        return self._path
+
+
+# JSON null value.
+class NullVal(Val):
+    _name = "a null"
+
+
+# JSON scalar value.
+class _ScalarVal(Val, Generic[_RawValTV]):
+    def __init__(self, raw_val: _RawValTV, path: Optional[Path] = None):
+        super().__init__(path)
+        self._raw_val = raw_val
+
+    # Raw value.
+    @property
+    def val(self):
+        return self._raw_val
+
+
+# JSON boolean value.
+class BoolVal(_ScalarVal[bool]):
+    _name = "a boolean"
+
+    def __bool__(self):
+        return self.val
+
+
+# JSON integer value.
+class IntVal(_ScalarVal[int]):
+    _name = "an integer"
+
+    def __int__(self):
+        return self.val
+
+
+# JSON floating point number value.
+class FloatVal(_ScalarVal[float]):
+    _name = "a floating point number"
+
+    def __float__(self):
+        return self.val
+
+
+# JSON string value.
+class StrVal(_ScalarVal[str]):
+    _name = "a string"
+
+    def __str__(self):
+        return self.val
+
+
+# JSON array value.
+class ArrayVal(Val, Sequence[Val]):
+    _name = "an array"
+
+    def __init__(self, raw_val: _RawArrayT, path: Optional[Path] = None):
+        super().__init__(path)
+        self._raw_val = raw_val
+
+    # Returns the value at index `index`.
+    #
+    # Raises `TypeError` if the type of the returned value isn't
+    # `expected_elem_type`.
+    def at(self, index: int, expected_elem_type: Type[_ValTV]):
+        try:
+            elem = self._raw_val[index]
+        except IndexError:
+            raise IndexError(
+                "`{}`: array index {} out of range".format(self._path, index)
+            )
+
+        return wrap(elem, self._path / index, expected_elem_type)
+
+    # Returns an iterator yielding the values of this array value.
+    #
+    # Raises `TypeError` if the type of any yielded value isn't
+    # `expected_elem_type`.
+    def iter(self, expected_elem_type: Type[_ValTV]):
+        for i in range(len(self._raw_val)):
+            yield self.at(i, expected_elem_type)
+
+    @overload
+    def __getitem__(self, index: int) -> Val:
+        ...
+
+    @overload
+    def __getitem__(self, index: slice) -> Sequence[Val]:
+        ...
+
+    def __getitem__(self, index: Union[int, slice]) -> Union[Val, Sequence[Val]]:
+        if type(index) is slice:
+            raise NotImplementedError
+
+        return self.at(index, Val)
+
+    def __len__(self):
+        return len(self._raw_val)
+
+
+# JSON object value.
+class ObjVal(Val, Mapping[str, Val]):
+    _name = "an object"
+
+    def __init__(self, raw_val: _RawObjT, path: Optional[Path] = None):
+        super().__init__(path)
+        self._raw_val = raw_val
+
+    # Returns the value having the key `key`.
+    #
+    # Raises `TypeError` if the type of the returned value isn't
+    # `expected_type`.
+    def at(self, key: str, expected_type: Type[_ValTV]):
+        try:
+            val = self._raw_val[key]
+        except KeyError:
+            raise KeyError("`{}`: no value has the key `{}`".format(self._path, key))
+
+        return wrap(val, self._path / key, expected_type)
+
+    def __getitem__(self, key: str) -> Val:
+        return self.at(key, Val)
+
+    def __len__(self):
+        return len(self._raw_val)
+
+    def __iter__(self):
+        return iter(self._raw_val)
+
+
+# Raises `TypeError` if the type of `val` is not `expected_type`.
+def _check_type(val: Val, expected_type: Type[Val]):
+    if not isinstance(val, expected_type):
+        raise TypeError(
+            "`{}`: expecting {} value, got {}".format(
+                val.path,
+                expected_type._name,  # pyright: ignore [reportPrivateUsage]
+                type(val)._name,  # pyright: ignore [reportPrivateUsage]
+            )
+        )
+
+
+# Wraps the raw value `raw_val` into an equivalent instance of some
+# `Val` subclass having the path `path` and returns it.
+#
+# If the resulting JSON value type isn't `expected_type`, then this
+# function raises `TypeError`.
+def wrap(
+    raw_val: _RawValT, path: Optional[Path] = None, expected_type: Type[_ValTV] = Val
+) -> _ValTV:
+    val = None
+
+    if raw_val is None:
+        val = NullVal(path)
+    elif isinstance(raw_val, bool):
+        val = BoolVal(raw_val, path)
+    elif isinstance(raw_val, int):
+        val = IntVal(raw_val, path)
+    elif isinstance(raw_val, float):
+        val = FloatVal(raw_val, path)
+    elif isinstance(raw_val, str):
+        val = StrVal(raw_val, path)
+    elif isinstance(raw_val, list):
+        val = ArrayVal(raw_val, path)
+    else:
+        assert isinstance(raw_val, dict)
+        val = ObjVal(raw_val, path)
+
+    assert val is not None
+    _check_type(val, expected_type)
+    return typing.cast(_ValTV, val)
+
+
+# Like json.loads(), but returns a `Val` instance, raising `TypeError`
+# if its type isn't `expected_type`.
+def loads(s: str, expected_type: Type[_ValTV] = Val, **kwargs: Any) -> _ValTV:
+    return wrap(json.loads(s, **kwargs), Path(), expected_type)
+
+
+# Like json.load(), but returns a `Val` instance, raising `TypeError` if
+# its type isn't `expected_type`.
+def load(fp: TextIO, expected_type: Type[_ValTV] = Val, **kwargs: Any) -> _ValTV:
+    return wrap(json.load(fp, **kwargs), Path(), expected_type)
diff --git a/tests/utils/python/typing/typing.py b/tests/utils/python/typing/typing.py
new file mode 100644 (file)
index 0000000..70c59d8
--- /dev/null
@@ -0,0 +1,2454 @@
+import abc
+from abc import abstractmethod, abstractproperty
+import collections
+import contextlib
+import functools
+import re as stdlib_re  # Avoid confusion with the re we export.
+import sys
+import types
+try:
+    import collections.abc as collections_abc
+except ImportError:
+    import collections as collections_abc  # Fallback for PY3.2.
+if sys.version_info[:2] >= (3, 6):
+    import _collections_abc  # Needed for private function _check_methods # noqa
+try:
+    from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType
+except ImportError:
+    WrapperDescriptorType = type(object.__init__)
+    MethodWrapperType = type(object().__str__)
+    MethodDescriptorType = type(str.join)
+
+
+# Please keep __all__ alphabetized within each category.
+__all__ = [
+    # Super-special typing primitives.
+    'Any',
+    'Callable',
+    'ClassVar',
+    'Generic',
+    'Optional',
+    'Tuple',
+    'Type',
+    'TypeVar',
+    'Union',
+
+    # ABCs (from collections.abc).
+    'AbstractSet',  # collections.abc.Set.
+    'GenericMeta',  # subclass of abc.ABCMeta and a metaclass
+                    # for 'Generic' and ABCs below.
+    'ByteString',
+    'Container',
+    'ContextManager',
+    'Hashable',
+    'ItemsView',
+    'Iterable',
+    'Iterator',
+    'KeysView',
+    'Mapping',
+    'MappingView',
+    'MutableMapping',
+    'MutableSequence',
+    'MutableSet',
+    'Sequence',
+    'Sized',
+    'ValuesView',
+    # The following are added depending on presence
+    # of their non-generic counterparts in stdlib:
+    # Awaitable,
+    # AsyncIterator,
+    # AsyncIterable,
+    # Coroutine,
+    # Collection,
+    # AsyncGenerator,
+    # AsyncContextManager
+
+    # Structural checks, a.k.a. protocols.
+    'Reversible',
+    'SupportsAbs',
+    'SupportsBytes',
+    'SupportsComplex',
+    'SupportsFloat',
+    'SupportsIndex',
+    'SupportsInt',
+    'SupportsRound',
+
+    # Concrete collection types.
+    'Counter',
+    'Deque',
+    'Dict',
+    'DefaultDict',
+    'List',
+    'Set',
+    'FrozenSet',
+    'NamedTuple',  # Not really a type.
+    'Generator',
+
+    # One-off things.
+    'AnyStr',
+    'cast',
+    'get_type_hints',
+    'NewType',
+    'no_type_check',
+    'no_type_check_decorator',
+    'NoReturn',
+    'overload',
+    'Text',
+    'TYPE_CHECKING',
+]
+
+# The pseudo-submodules 're' and 'io' are part of the public
+# namespace, but excluded from __all__ because they might stomp on
+# legitimate imports of those modules.
+
+
+def _qualname(x):
+    if sys.version_info[:2] >= (3, 3):
+        return x.__qualname__
+    else:
+        # Fall back to just name.
+        return x.__name__
+
+
+def _trim_name(nm):
+    whitelist = ('_TypeAlias', '_ForwardRef', '_TypingBase', '_FinalTypingBase')
+    if nm.startswith('_') and nm not in whitelist:
+        nm = nm[1:]
+    return nm
+
+
+class TypingMeta(type):
+    """Metaclass for most types defined in typing module
+    (not a part of public API).
+
+    This overrides __new__() to require an extra keyword parameter
+    '_root', which serves as a guard against naive subclassing of the
+    typing classes.  Any legitimate class defined using a metaclass
+    derived from TypingMeta must pass _root=True.
+
+    This also defines a dummy constructor (all the work for most typing
+    constructs is done in __new__) and a nicer repr().
+    """
+
+    _is_protocol = False
+
+    def __new__(cls, name, bases, namespace, *, _root=False):
+        if not _root:
+            raise TypeError("Cannot subclass %s" %
+                            (', '.join(map(_type_repr, bases)) or '()'))
+        return super().__new__(cls, name, bases, namespace)
+
+    def __init__(self, *args, **kwds):
+        pass
+
+    def _eval_type(self, globalns, localns):
+        """Override this in subclasses to interpret forward references.
+
+        For example, List['C'] is internally stored as
+        List[_ForwardRef('C')], which should evaluate to List[C],
+        where C is an object found in globalns or localns (searching
+        localns first, of course).
+        """
+        return self
+
+    def _get_type_vars(self, tvars):
+        pass
+
+    def __repr__(self):
+        qname = _trim_name(_qualname(self))
+        return '%s.%s' % (self.__module__, qname)
+
+
+class _TypingBase(metaclass=TypingMeta, _root=True):
+    """Internal indicator of special typing constructs."""
+
+    __slots__ = ('__weakref__',)
+
+    def __init__(self, *args, **kwds):
+        pass
+
+    def __new__(cls, *args, **kwds):
+        """Constructor.
+
+        This only exists to give a better error message in case
+        someone tries to subclass a special typing object (not a good idea).
+        """
+        if (len(args) == 3 and
+                isinstance(args[0], str) and
+                isinstance(args[1], tuple)):
+            # Close enough.
+            raise TypeError("Cannot subclass %r" % cls)
+        return super().__new__(cls)
+
+    # Things that are not classes also need these.
+    def _eval_type(self, globalns, localns):
+        return self
+
+    def _get_type_vars(self, tvars):
+        pass
+
+    def __repr__(self):
+        cls = type(self)
+        qname = _trim_name(_qualname(cls))
+        return '%s.%s' % (cls.__module__, qname)
+
+    def __call__(self, *args, **kwds):
+        raise TypeError("Cannot instantiate %r" % type(self))
+
+
+class _FinalTypingBase(_TypingBase, _root=True):
+    """Internal mix-in class to prevent instantiation.
+
+    Prevents instantiation unless _root=True is given in class call.
+    It is used to create pseudo-singleton instances Any, Union, Optional, etc.
+    """
+
+    __slots__ = ()
+
+    def __new__(cls, *args, _root=False, **kwds):
+        self = super().__new__(cls, *args, **kwds)
+        if _root is True:
+            return self
+        raise TypeError("Cannot instantiate %r" % cls)
+
+    def __reduce__(self):
+        return _trim_name(type(self).__name__)
+
+
+class _ForwardRef(_TypingBase, _root=True):
+    """Internal wrapper to hold a forward reference."""
+
+    __slots__ = ('__forward_arg__', '__forward_code__',
+                 '__forward_evaluated__', '__forward_value__')
+
+    def __init__(self, arg):
+        super().__init__(arg)
+        if not isinstance(arg, str):
+            raise TypeError('Forward reference must be a string -- got %r' % (arg,))
+        try:
+            code = compile(arg, '<string>', 'eval')
+        except SyntaxError:
+            raise SyntaxError('Forward reference must be an expression -- got %r' %
+                              (arg,))
+        self.__forward_arg__ = arg
+        self.__forward_code__ = code
+        self.__forward_evaluated__ = False
+        self.__forward_value__ = None
+
+    def _eval_type(self, globalns, localns):
+        if not self.__forward_evaluated__ or localns is not globalns:
+            if globalns is None and localns is None:
+                globalns = localns = {}
+            elif globalns is None:
+                globalns = localns
+            elif localns is None:
+                localns = globalns
+            self.__forward_value__ = _type_check(
+                eval(self.__forward_code__, globalns, localns),
+                "Forward references must evaluate to types.")
+            self.__forward_evaluated__ = True
+        return self.__forward_value__
+
+    def __eq__(self, other):
+        if not isinstance(other, _ForwardRef):
+            return NotImplemented
+        if self.__forward_evaluated__ and other.__forward_evaluated__:
+            return (self.__forward_arg__ == other.__forward_arg__ and
+                    self.__forward_value__ == other.__forward_value__)
+        return self.__forward_arg__ == other.__forward_arg__
+
+    def __hash__(self):
+        return hash(self.__forward_arg__)
+
+    def __instancecheck__(self, obj):
+        raise TypeError("Forward references cannot be used with isinstance().")
+
+    def __subclasscheck__(self, cls):
+        raise TypeError("Forward references cannot be used with issubclass().")
+
+    def __repr__(self):
+        return '_ForwardRef(%r)' % (self.__forward_arg__,)
+
+
+class _TypeAlias(_TypingBase, _root=True):
+    """Internal helper class for defining generic variants of concrete types.
+
+    Note that this is not a type; let's call it a pseudo-type.  It cannot
+    be used in instance and subclass checks in parameterized form, i.e.
+    ``isinstance(42, Match[str])`` raises ``TypeError`` instead of returning
+    ``False``.
+    """
+
+    __slots__ = ('name', 'type_var', 'impl_type', 'type_checker')
+
+    def __init__(self, name, type_var, impl_type, type_checker):
+        """Initializer.
+
+        Args:
+            name: The name, e.g. 'Pattern'.
+            type_var: The type parameter, e.g. AnyStr, or the
+                specific type, e.g. str.
+            impl_type: The implementation type.
+            type_checker: Function that takes an impl_type instance.
+                and returns a value that should be a type_var instance.
+        """
+        assert isinstance(name, str), repr(name)
+        assert isinstance(impl_type, type), repr(impl_type)
+        assert not isinstance(impl_type, TypingMeta), repr(impl_type)
+        assert isinstance(type_var, (type, _TypingBase)), repr(type_var)
+        self.name = name
+        self.type_var = type_var
+        self.impl_type = impl_type
+        self.type_checker = type_checker
+
+    def __repr__(self):
+        return "%s[%s]" % (self.name, _type_repr(self.type_var))
+
+    def __getitem__(self, parameter):
+        if not isinstance(self.type_var, TypeVar):
+            raise TypeError("%s cannot be further parameterized." % self)
+        if self.type_var.__constraints__ and isinstance(parameter, type):
+            if not issubclass(parameter, self.type_var.__constraints__):
+                raise TypeError("%s is not a valid substitution for %s." %
+                                (parameter, self.type_var))
+        if isinstance(parameter, TypeVar) and parameter is not self.type_var:
+            raise TypeError("%s cannot be re-parameterized." % self)
+        return self.__class__(self.name, parameter,
+                              self.impl_type, self.type_checker)
+
+    def __eq__(self, other):
+        if not isinstance(other, _TypeAlias):
+            return NotImplemented
+        return self.name == other.name and self.type_var == other.type_var
+
+    def __hash__(self):
+        return hash((self.name, self.type_var))
+
+    def __instancecheck__(self, obj):
+        if not isinstance(self.type_var, TypeVar):
+            raise TypeError("Parameterized type aliases cannot be used "
+                            "with isinstance().")
+        return isinstance(obj, self.impl_type)
+
+    def __subclasscheck__(self, cls):
+        if not isinstance(self.type_var, TypeVar):
+            raise TypeError("Parameterized type aliases cannot be used "
+                            "with issubclass().")
+        return issubclass(cls, self.impl_type)
+
+
+def _get_type_vars(types, tvars):
+    for t in types:
+        if isinstance(t, TypingMeta) or isinstance(t, _TypingBase):
+            t._get_type_vars(tvars)
+
+
+def _type_vars(types):
+    tvars = []
+    _get_type_vars(types, tvars)
+    return tuple(tvars)
+
+
+def _eval_type(t, globalns, localns):
+    if isinstance(t, TypingMeta) or isinstance(t, _TypingBase):
+        return t._eval_type(globalns, localns)
+    return t
+
+
+def _type_check(arg, msg):
+    """Check that the argument is a type, and return it (internal helper).
+
+    As a special case, accept None and return type(None) instead.
+    Also, _TypeAlias instances (e.g. Match, Pattern) are acceptable.
+
+    The msg argument is a human-readable error message, e.g.
+
+        "Union[arg, ...]: arg should be a type."
+
+    We append the repr() of the actual value (truncated to 100 chars).
+    """
+    if arg is None:
+        return type(None)
+    if isinstance(arg, str):
+        arg = _ForwardRef(arg)
+    if (
+        isinstance(arg, _TypingBase) and type(arg).__name__ == '_ClassVar' or
+        not isinstance(arg, (type, _TypingBase)) and not callable(arg)
+    ):
+        raise TypeError(msg + " Got %.100r." % (arg,))
+    # Bare Union etc. are not valid as type arguments
+    if (
+        type(arg).__name__ in ('_Union', '_Optional') and
+        not getattr(arg, '__origin__', None) or
+        isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol)
+    ):
+        raise TypeError("Plain %s is not valid as type argument" % arg)
+    return arg
+
+
+def _type_repr(obj):
+    """Return the repr() of an object, special-casing types (internal helper).
+
+    If obj is a type, we return a shorter version than the default
+    type.__repr__, based on the module and qualified name, which is
+    typically enough to uniquely identify a type.  For everything
+    else, we fall back on repr(obj).
+    """
+    if isinstance(obj, type) and not isinstance(obj, TypingMeta):
+        if obj.__module__ == 'builtins':
+            return _qualname(obj)
+        return '%s.%s' % (obj.__module__, _qualname(obj))
+    if obj is ...:
+        return('...')
+    if isinstance(obj, types.FunctionType):
+        return obj.__name__
+    return repr(obj)
+
+
+class _Any(_FinalTypingBase, _root=True):
+    """Special type indicating an unconstrained type.
+
+    - Any is compatible with every type.
+    - Any assumed to have all methods.
+    - All values assumed to be instances of Any.
+
+    Note that all the above statements are true from the point of view of
+    static type checkers. At runtime, Any should not be used with instance
+    or class checks.
+    """
+
+    __slots__ = ()
+
+    def __instancecheck__(self, obj):
+        raise TypeError("Any cannot be used with isinstance().")
+
+    def __subclasscheck__(self, cls):
+        raise TypeError("Any cannot be used with issubclass().")
+
+
+Any = _Any(_root=True)
+
+
+class _NoReturn(_FinalTypingBase, _root=True):
+    """Special type indicating functions that never return.
+    Example::
+
+      from typing import NoReturn
+
+      def stop() -> NoReturn:
+          raise Exception('no way')
+
+    This type is invalid in other positions, e.g., ``List[NoReturn]``
+    will fail in static type checkers.
+    """
+
+    __slots__ = ()
+
+    def __instancecheck__(self, obj):
+        raise TypeError("NoReturn cannot be used with isinstance().")
+
+    def __subclasscheck__(self, cls):
+        raise TypeError("NoReturn cannot be used with issubclass().")
+
+
+NoReturn = _NoReturn(_root=True)
+
+
+class TypeVar(_TypingBase, _root=True):
+    """Type variable.
+
+    Usage::
+
+      T = TypeVar('T')  # Can be anything
+      A = TypeVar('A', str, bytes)  # Must be str or bytes
+
+    Type variables exist primarily for the benefit of static type
+    checkers.  They serve as the parameters for generic types as well
+    as for generic function definitions.  See class Generic for more
+    information on generic types.  Generic functions work as follows:
+
+      def repeat(x: T, n: int) -> List[T]:
+          '''Return a list containing n references to x.'''
+          return [x]*n
+
+      def longest(x: A, y: A) -> A:
+          '''Return the longest of two strings.'''
+          return x if len(x) >= len(y) else y
+
+    The latter example's signature is essentially the overloading
+    of (str, str) -> str and (bytes, bytes) -> bytes.  Also note
+    that if the arguments are instances of some subclass of str,
+    the return type is still plain str.
+
+    At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError.
+
+    Type variables defined with covariant=True or contravariant=True
+    can be used do declare covariant or contravariant generic types.
+    See PEP 484 for more details. By default generic types are invariant
+    in all type variables.
+
+    Type variables can be introspected. e.g.:
+
+      T.__name__ == 'T'
+      T.__constraints__ == ()
+      T.__covariant__ == False
+      T.__contravariant__ = False
+      A.__constraints__ == (str, bytes)
+    """
+
+    __slots__ = ('__name__', '__bound__', '__constraints__',
+                 '__covariant__', '__contravariant__')
+
+    def __init__(self, name, *constraints, bound=None,
+                 covariant=False, contravariant=False):
+        super().__init__(name, *constraints, bound=bound,
+                         covariant=covariant, contravariant=contravariant)
+        self.__name__ = name
+        if covariant and contravariant:
+            raise ValueError("Bivariant types are not supported.")
+        self.__covariant__ = bool(covariant)
+        self.__contravariant__ = bool(contravariant)
+        if constraints and bound is not None:
+            raise TypeError("Constraints cannot be combined with bound=...")
+        if constraints and len(constraints) == 1:
+            raise TypeError("A single constraint is not allowed")
+        msg = "TypeVar(name, constraint, ...): constraints must be types."
+        self.__constraints__ = tuple(_type_check(t, msg) for t in constraints)
+        if bound:
+            self.__bound__ = _type_check(bound, "Bound must be a type.")
+        else:
+            self.__bound__ = None
+
+    def _get_type_vars(self, tvars):
+        if self not in tvars:
+            tvars.append(self)
+
+    def __repr__(self):
+        if self.__covariant__:
+            prefix = '+'
+        elif self.__contravariant__:
+            prefix = '-'
+        else:
+            prefix = '~'
+        return prefix + self.__name__
+
+    def __instancecheck__(self, instance):
+        raise TypeError("Type variables cannot be used with isinstance().")
+
+    def __subclasscheck__(self, cls):
+        raise TypeError("Type variables cannot be used with issubclass().")
+
+
+# Some unconstrained type variables.  These are used by the container types.
+# (These are not for export.)
+T = TypeVar('T')  # Any type.
+KT = TypeVar('KT')  # Key type.
+VT = TypeVar('VT')  # Value type.
+T_co = TypeVar('T_co', covariant=True)  # Any type covariant containers.
+V_co = TypeVar('V_co', covariant=True)  # Any type covariant containers.
+VT_co = TypeVar('VT_co', covariant=True)  # Value type covariant containers.
+T_contra = TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
+
+# A useful type variable with constraints.  This represents string types.
+# (This one *is* for export!)
+AnyStr = TypeVar('AnyStr', bytes, str)
+
+
+def _replace_arg(arg, tvars, args):
+    """An internal helper function: replace arg if it is a type variable
+    found in tvars with corresponding substitution from args or
+    with corresponding substitution sub-tree if arg is a generic type.
+    """
+
+    if tvars is None:
+        tvars = []
+    if hasattr(arg, '_subs_tree') and isinstance(arg, (GenericMeta, _TypingBase)):
+        return arg._subs_tree(tvars, args)
+    if isinstance(arg, TypeVar):
+        for i, tvar in enumerate(tvars):
+            if arg == tvar:
+                return args[i]
+    return arg
+
+
+# Special typing constructs Union, Optional, Generic, Callable and Tuple
+# use three special attributes for internal bookkeeping of generic types:
+# * __parameters__ is a tuple of unique free type parameters of a generic
+#   type, for example, Dict[T, T].__parameters__ == (T,);
+# * __origin__ keeps a reference to a type that was subscripted,
+#   e.g., Union[T, int].__origin__ == Union;
+# * __args__ is a tuple of all arguments used in subscripting,
+#   e.g., Dict[T, int].__args__ == (T, int).
+
+
+def _subs_tree(cls, tvars=None, args=None):
+    """An internal helper function: calculate substitution tree
+    for generic cls after replacing its type parameters with
+    substitutions in tvars -> args (if any).
+    Repeat the same following __origin__'s.
+
+    Return a list of arguments with all possible substitutions
+    performed. Arguments that are generic classes themselves are represented
+    as tuples (so that no new classes are created by this function).
+    For example: _subs_tree(List[Tuple[int, T]][str]) == [(Tuple, int, str)]
+    """
+
+    if cls.__origin__ is None:
+        return cls
+    # Make of chain of origins (i.e. cls -> cls.__origin__)
+    current = cls.__origin__
+    orig_chain = []
+    while current.__origin__ is not None:
+        orig_chain.append(current)
+        current = current.__origin__
+    # Replace type variables in __args__ if asked ...
+    tree_args = []
+    for arg in cls.__args__:
+        tree_args.append(_replace_arg(arg, tvars, args))
+    # ... then continue replacing down the origin chain.
+    for ocls in orig_chain:
+        new_tree_args = []
+        for arg in ocls.__args__:
+            new_tree_args.append(_replace_arg(arg, ocls.__parameters__, tree_args))
+        tree_args = new_tree_args
+    return tree_args
+
+
+def _remove_dups_flatten(parameters):
+    """An internal helper for Union creation and substitution: flatten Union's
+    among parameters, then remove duplicates and strict subclasses.
+    """
+
+    # Flatten out Union[Union[...], ...].
+    params = []
+    for p in parameters:
+        if isinstance(p, _Union) and p.__origin__ is Union:
+            params.extend(p.__args__)
+        elif isinstance(p, tuple) and len(p) > 0 and p[0] is Union:
+            params.extend(p[1:])
+        else:
+            params.append(p)
+    # Weed out strict duplicates, preserving the first of each occurrence.
+    all_params = set(params)
+    if len(all_params) < len(params):
+        new_params = []
+        for t in params:
+            if t in all_params:
+                new_params.append(t)
+                all_params.remove(t)
+        params = new_params
+        assert not all_params, all_params
+    # Weed out subclasses.
+    # E.g. Union[int, Employee, Manager] == Union[int, Employee].
+    # If object is present it will be sole survivor among proper classes.
+    # Never discard type variables.
+    # (In particular, Union[str, AnyStr] != AnyStr.)
+    all_params = set(params)
+    for t1 in params:
+        if not isinstance(t1, type):
+            continue
+        if any(isinstance(t2, type) and issubclass(t1, t2)
+               for t2 in all_params - {t1}
+               if not (isinstance(t2, GenericMeta) and
+                       t2.__origin__ is not None)):
+            all_params.remove(t1)
+    return tuple(t for t in params if t in all_params)
+
+
+def _check_generic(cls, parameters):
+    # Check correct count for parameters of a generic cls (internal helper).
+    if not cls.__parameters__:
+        raise TypeError("%s is not a generic class" % repr(cls))
+    alen = len(parameters)
+    elen = len(cls.__parameters__)
+    if alen != elen:
+        raise TypeError("Too %s parameters for %s; actual %s, expected %s" %
+                        ("many" if alen > elen else "few", repr(cls), alen, elen))
+
+
+_cleanups = []
+
+
+def _tp_cache(func):
+    """Internal wrapper caching __getitem__ of generic types with a fallback to
+    original function for non-hashable arguments.
+    """
+
+    cached = functools.lru_cache()(func)
+    _cleanups.append(cached.cache_clear)
+
+    @functools.wraps(func)
+    def inner(*args, **kwds):
+        try:
+            return cached(*args, **kwds)
+        except TypeError:
+            pass  # All real errors (not unhashable args) are raised below.
+        return func(*args, **kwds)
+    return inner
+
+
+class _Union(_FinalTypingBase, _root=True):
+    """Union type; Union[X, Y] means either X or Y.
+
+    To define a union, use e.g. Union[int, str].  Details:
+
+    - The arguments must be types and there must be at least one.
+
+    - None as an argument is a special case and is replaced by
+      type(None).
+
+    - Unions of unions are flattened, e.g.::
+
+        Union[Union[int, str], float] == Union[int, str, float]
+
+    - Unions of a single argument vanish, e.g.::
+
+        Union[int] == int  # The constructor actually returns int
+
+    - Redundant arguments are skipped, e.g.::
+
+        Union[int, str, int] == Union[int, str]
+
+    - When comparing unions, the argument order is ignored, e.g.::
+
+        Union[int, str] == Union[str, int]
+
+    - When two arguments have a subclass relationship, the least
+      derived argument is kept, e.g.::
+
+        class Employee: pass
+        class Manager(Employee): pass
+        Union[int, Employee, Manager] == Union[int, Employee]
+        Union[Manager, int, Employee] == Union[int, Employee]
+        Union[Employee, Manager] == Employee
+
+    - Similar for object::
+
+        Union[int, object] == object
+
+    - You cannot subclass or instantiate a union.
+
+    - You can use Optional[X] as a shorthand for Union[X, None].
+    """
+
+    __slots__ = ('__parameters__', '__args__', '__origin__', '__tree_hash__')
+
+    def __new__(cls, parameters=None, origin=None, *args, _root=False):
+        self = super().__new__(cls, parameters, origin, *args, _root=_root)
+        if origin is None:
+            self.__parameters__ = None
+            self.__args__ = None
+            self.__origin__ = None
+            self.__tree_hash__ = hash(frozenset(('Union',)))
+            return self
+        if not isinstance(parameters, tuple):
+            raise TypeError("Expected parameters=<tuple>")
+        if origin is Union:
+            parameters = _remove_dups_flatten(parameters)
+            # It's not a union if there's only one type left.
+            if len(parameters) == 1:
+                return parameters[0]
+        self.__parameters__ = _type_vars(parameters)
+        self.__args__ = parameters
+        self.__origin__ = origin
+        # Pre-calculate the __hash__ on instantiation.
+        # This improves speed for complex substitutions.
+        subs_tree = self._subs_tree()
+        if isinstance(subs_tree, tuple):
+            self.__tree_hash__ = hash(frozenset(subs_tree))
+        else:
+            self.__tree_hash__ = hash(subs_tree)
+        return self
+
+    def _eval_type(self, globalns, localns):
+        if self.__args__ is None:
+            return self
+        ev_args = tuple(_eval_type(t, globalns, localns) for t in self.__args__)
+        ev_origin = _eval_type(self.__origin__, globalns, localns)
+        if ev_args == self.__args__ and ev_origin == self.__origin__:
+            # Everything is already evaluated.
+            return self
+        return self.__class__(ev_args, ev_origin, _root=True)
+
+    def _get_type_vars(self, tvars):
+        if self.__origin__ and self.__parameters__:
+            _get_type_vars(self.__parameters__, tvars)
+
+    def __repr__(self):
+        if self.__origin__ is None:
+            return super().__repr__()
+        tree = self._subs_tree()
+        if not isinstance(tree, tuple):
+            return repr(tree)
+        return tree[0]._tree_repr(tree)
+
+    def _tree_repr(self, tree):
+        arg_list = []
+        for arg in tree[1:]:
+            if not isinstance(arg, tuple):
+                arg_list.append(_type_repr(arg))
+            else:
+                arg_list.append(arg[0]._tree_repr(arg))
+        return super().__repr__() + '[%s]' % ', '.join(arg_list)
+
+    @_tp_cache
+    def __getitem__(self, parameters):
+        if parameters == ():
+            raise TypeError("Cannot take a Union of no types.")
+        if not isinstance(parameters, tuple):
+            parameters = (parameters,)
+        if self.__origin__ is None:
+            msg = "Union[arg, ...]: each arg must be a type."
+        else:
+            msg = "Parameters to generic types must be types."
+        parameters = tuple(_type_check(p, msg) for p in parameters)
+        if self is not Union:
+            _check_generic(self, parameters)
+        return self.__class__(parameters, origin=self, _root=True)
+
+    def _subs_tree(self, tvars=None, args=None):
+        if self is Union:
+            return Union  # Nothing to substitute
+        tree_args = _subs_tree(self, tvars, args)
+        tree_args = _remove_dups_flatten(tree_args)
+        if len(tree_args) == 1:
+            return tree_args[0]  # Union of a single type is that type
+        return (Union,) + tree_args
+
+    def __eq__(self, other):
+        if isinstance(other, _Union):
+            return self.__tree_hash__ == other.__tree_hash__
+        elif self is not Union:
+            return self._subs_tree() == other
+        else:
+            return self is other
+
+    def __hash__(self):
+        return self.__tree_hash__
+
+    def __instancecheck__(self, obj):
+        raise TypeError("Unions cannot be used with isinstance().")
+
+    def __subclasscheck__(self, cls):
+        raise TypeError("Unions cannot be used with issubclass().")
+
+
+Union = _Union(_root=True)
+
+
+class _Optional(_FinalTypingBase, _root=True):
+    """Optional type.
+
+    Optional[X] is equivalent to Union[X, None].
+    """
+
+    __slots__ = ()
+
+    @_tp_cache
+    def __getitem__(self, arg):
+        arg = _type_check(arg, "Optional[t] requires a single type.")
+        return Union[arg, type(None)]
+
+
+Optional = _Optional(_root=True)
+
+
+def _next_in_mro(cls):
+    """Helper for Generic.__new__.
+
+    Returns the class after the last occurrence of Generic or
+    Generic[...] in cls.__mro__.
+    """
+    next_in_mro = object
+    # Look for the last occurrence of Generic or Generic[...].
+    for i, c in enumerate(cls.__mro__[:-1]):
+        if isinstance(c, GenericMeta) and c._gorg is Generic:
+            next_in_mro = cls.__mro__[i + 1]
+    return next_in_mro
+
+
+def _make_subclasshook(cls):
+    """Construct a __subclasshook__ callable that incorporates
+    the associated __extra__ class in subclass checks performed
+    against cls.
+    """
+    if isinstance(cls.__extra__, abc.ABCMeta):
+        # The logic mirrors that of ABCMeta.__subclasscheck__.
+        # Registered classes need not be checked here because
+        # cls and its extra share the same _abc_registry.
+        def __extrahook__(subclass):
+            res = cls.__extra__.__subclasshook__(subclass)
+            if res is not NotImplemented:
+                return res
+            if cls.__extra__ in subclass.__mro__:
+                return True
+            for scls in cls.__extra__.__subclasses__():
+                if isinstance(scls, GenericMeta):
+                    continue
+                if issubclass(subclass, scls):
+                    return True
+            return NotImplemented
+    else:
+        # For non-ABC extras we'll just call issubclass().
+        def __extrahook__(subclass):
+            if cls.__extra__ and issubclass(subclass, cls.__extra__):
+                return True
+            return NotImplemented
+    return __extrahook__
+
+
+def _no_slots_copy(dct):
+    """Internal helper: copy class __dict__ and clean slots class variables.
+    (They will be re-created if necessary by normal class machinery.)
+    """
+    dict_copy = dict(dct)
+    if '__slots__' in dict_copy:
+        for slot in dict_copy['__slots__']:
+            dict_copy.pop(slot, None)
+    return dict_copy
+
+
+class GenericMeta(TypingMeta, abc.ABCMeta):
+    """Metaclass for generic types.
+
+    This is a metaclass for typing.Generic and generic ABCs defined in
+    typing module. User defined subclasses of GenericMeta can override
+    __new__ and invoke super().__new__. Note that GenericMeta.__new__
+    has strict rules on what is allowed in its bases argument:
+    * plain Generic is disallowed in bases;
+    * Generic[...] should appear in bases at most once;
+    * if Generic[...] is present, then it should list all type variables
+      that appear in other bases.
+    In addition, type of all generic bases is erased, e.g., C[int] is
+    stripped to plain C.
+    """
+
+    def __new__(cls, name, bases, namespace,
+                tvars=None, args=None, origin=None, extra=None, orig_bases=None):
+        """Create a new generic class. GenericMeta.__new__ accepts
+        keyword arguments that are used for internal bookkeeping, therefore
+        an override should pass unused keyword arguments to super().
+        """
+        if tvars is not None:
+            # Called from __getitem__() below.
+            assert origin is not None
+            assert all(isinstance(t, TypeVar) for t in tvars), tvars
+        else:
+            # Called from class statement.
+            assert tvars is None, tvars
+            assert args is None, args
+            assert origin is None, origin
+
+            # Get the full set of tvars from the bases.
+            tvars = _type_vars(bases)
+            # Look for Generic[T1, ..., Tn].
+            # If found, tvars must be a subset of it.
+            # If not found, tvars is it.
+            # Also check for and reject plain Generic,
+            # and reject multiple Generic[...].
+            gvars = None
+            for base in bases:
+                if base is Generic:
+                    raise TypeError("Cannot inherit from plain Generic")
+                if (isinstance(base, GenericMeta) and
+                        base.__origin__ is Generic):
+                    if gvars is not None:
+                        raise TypeError(
+                            "Cannot inherit from Generic[...] multiple types.")
+                    gvars = base.__parameters__
+            if gvars is None:
+                gvars = tvars
+            else:
+                tvarset = set(tvars)
+                gvarset = set(gvars)
+                if not tvarset <= gvarset:
+                    raise TypeError(
+                        "Some type variables (%s) "
+                        "are not listed in Generic[%s]" %
+                        (", ".join(str(t) for t in tvars if t not in gvarset),
+                         ", ".join(str(g) for g in gvars)))
+                tvars = gvars
+
+        initial_bases = bases
+        if extra is not None and type(extra) is abc.ABCMeta and extra not in bases:
+            bases = (extra,) + bases
+        bases = tuple(b._gorg if isinstance(b, GenericMeta) else b for b in bases)
+
+        # remove bare Generic from bases if there are other generic bases
+        if any(isinstance(b, GenericMeta) and b is not Generic for b in bases):
+            bases = tuple(b for b in bases if b is not Generic)
+        namespace.update({'__origin__': origin, '__extra__': extra,
+                          '_gorg': None if not origin else origin._gorg})
+        self = super().__new__(cls, name, bases, namespace, _root=True)
+        super(GenericMeta, self).__setattr__('_gorg',
+                                             self if not origin else origin._gorg)
+        self.__parameters__ = tvars
+        # Be prepared that GenericMeta will be subclassed by TupleMeta
+        # and CallableMeta, those two allow ..., (), or [] in __args___.
+        self.__args__ = tuple(... if a is _TypingEllipsis else
+                              () if a is _TypingEmpty else
+                              a for a in args) if args else None
+        # Speed hack (https://github.com/python/typing/issues/196).
+        self.__next_in_mro__ = _next_in_mro(self)
+        # Preserve base classes on subclassing (__bases__ are type erased now).
+        if orig_bases is None:
+            self.__orig_bases__ = initial_bases
+
+        # This allows unparameterized generic collections to be used
+        # with issubclass() and isinstance() in the same way as their
+        # collections.abc counterparts (e.g., isinstance([], Iterable)).
+        if (
+            '__subclasshook__' not in namespace and extra or
+            # allow overriding
+            getattr(self.__subclasshook__, '__name__', '') == '__extrahook__'
+        ):
+            self.__subclasshook__ = _make_subclasshook(self)
+        if isinstance(extra, abc.ABCMeta):
+            self._abc_registry = extra._abc_registry
+            self._abc_cache = extra._abc_cache
+        elif origin is not None:
+            self._abc_registry = origin._abc_registry
+            self._abc_cache = origin._abc_cache
+
+        if origin and hasattr(origin, '__qualname__'):  # Fix for Python 3.2.
+            self.__qualname__ = origin.__qualname__
+        self.__tree_hash__ = (hash(self._subs_tree()) if origin else
+                              super(GenericMeta, self).__hash__())
+        return self
+
+    # _abc_negative_cache and _abc_negative_cache_version
+    # realised as descriptors, since GenClass[t1, t2, ...] always
+    # share subclass info with GenClass.
+    # This is an important memory optimization.
+    @property
+    def _abc_negative_cache(self):
+        if isinstance(self.__extra__, abc.ABCMeta):
+            return self.__extra__._abc_negative_cache
+        return self._gorg._abc_generic_negative_cache
+
+    @_abc_negative_cache.setter
+    def _abc_negative_cache(self, value):
+        if self.__origin__ is None:
+            if isinstance(self.__extra__, abc.ABCMeta):
+                self.__extra__._abc_negative_cache = value
+            else:
+                self._abc_generic_negative_cache = value
+
+    @property
+    def _abc_negative_cache_version(self):
+        if isinstance(self.__extra__, abc.ABCMeta):
+            return self.__extra__._abc_negative_cache_version
+        return self._gorg._abc_generic_negative_cache_version
+
+    @_abc_negative_cache_version.setter
+    def _abc_negative_cache_version(self, value):
+        if self.__origin__ is None:
+            if isinstance(self.__extra__, abc.ABCMeta):
+                self.__extra__._abc_negative_cache_version = value
+            else:
+                self._abc_generic_negative_cache_version = value
+
+    def _get_type_vars(self, tvars):
+        if self.__origin__ and self.__parameters__:
+            _get_type_vars(self.__parameters__, tvars)
+
+    def _eval_type(self, globalns, localns):
+        ev_origin = (self.__origin__._eval_type(globalns, localns)
+                     if self.__origin__ else None)
+        ev_args = tuple(_eval_type(a, globalns, localns) for a
+                        in self.__args__) if self.__args__ else None
+        if ev_origin == self.__origin__ and ev_args == self.__args__:
+            return self
+        return self.__class__(self.__name__,
+                              self.__bases__,
+                              _no_slots_copy(self.__dict__),
+                              tvars=_type_vars(ev_args) if ev_args else None,
+                              args=ev_args,
+                              origin=ev_origin,
+                              extra=self.__extra__,
+                              orig_bases=self.__orig_bases__)
+
+    def __repr__(self):
+        if self.__origin__ is None:
+            return super().__repr__()
+        return self._tree_repr(self._subs_tree())
+
+    def _tree_repr(self, tree):
+        arg_list = []
+        for arg in tree[1:]:
+            if arg == ():
+                arg_list.append('()')
+            elif not isinstance(arg, tuple):
+                arg_list.append(_type_repr(arg))
+            else:
+                arg_list.append(arg[0]._tree_repr(arg))
+        return super().__repr__() + '[%s]' % ', '.join(arg_list)
+
+    def _subs_tree(self, tvars=None, args=None):
+        if self.__origin__ is None:
+            return self
+        tree_args = _subs_tree(self, tvars, args)
+        return (self._gorg,) + tuple(tree_args)
+
+    def __eq__(self, other):
+        if not isinstance(other, GenericMeta):
+            return NotImplemented
+        if self.__origin__ is None or other.__origin__ is None:
+            return self is other
+        return self.__tree_hash__ == other.__tree_hash__
+
+    def __hash__(self):
+        return self.__tree_hash__
+
+    @_tp_cache
+    def __getitem__(self, params):
+        if not isinstance(params, tuple):
+            params = (params,)
+        if not params and self._gorg is not Tuple:
+            raise TypeError(
+                "Parameter list to %s[...] cannot be empty" % _qualname(self))
+        msg = "Parameters to generic types must be types."
+        params = tuple(_type_check(p, msg) for p in params)
+        if self is Generic:
+            # Generic can only be subscripted with unique type variables.
+            if not all(isinstance(p, TypeVar) for p in params):
+                raise TypeError(
+                    "Parameters to Generic[...] must all be type variables")
+            if len(set(params)) != len(params):
+                raise TypeError(
+                    "Parameters to Generic[...] must all be unique")
+            tvars = params
+            args = params
+        elif self in (Tuple, Callable):
+            tvars = _type_vars(params)
+            args = params
+        elif self is _Protocol:
+            # _Protocol is internal, don't check anything.
+            tvars = params
+            args = params
+        elif self.__origin__ in (Generic, _Protocol):
+            # Can't subscript Generic[...] or _Protocol[...].
+            raise TypeError("Cannot subscript already-subscripted %s" %
+                            repr(self))
+        else:
+            # Subscripting a regular Generic subclass.
+            _check_generic(self, params)
+            tvars = _type_vars(params)
+            args = params
+
+        prepend = (self,) if self.__origin__ is None else ()
+        return self.__class__(self.__name__,
+                              prepend + self.__bases__,
+                              _no_slots_copy(self.__dict__),
+                              tvars=tvars,
+                              args=args,
+                              origin=self,
+                              extra=self.__extra__,
+                              orig_bases=self.__orig_bases__)
+
+    def __subclasscheck__(self, cls):
+        if self.__origin__ is not None:
+            if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']:
+                raise TypeError("Parameterized generics cannot be used with class "
+                                "or instance checks")
+            return False
+        if self is Generic:
+            raise TypeError("Class %r cannot be used with class "
+                            "or instance checks" % self)
+        return super().__subclasscheck__(cls)
+
+    def __instancecheck__(self, instance):
+        # Since we extend ABC.__subclasscheck__ and
+        # ABC.__instancecheck__ inlines the cache checking done by the
+        # latter, we must extend __instancecheck__ too. For simplicity
+        # we just skip the cache check -- instance checks for generic
+        # classes are supposed to be rare anyways.
+        return issubclass(instance.__class__, self)
+
+    def __setattr__(self, attr, value):
+        # We consider all the subscripted generics as proxies for original class
+        if (
+            attr.startswith('__') and attr.endswith('__') or
+            attr.startswith('_abc_') or
+            self._gorg is None  # The class is not fully created, see #typing/506
+        ):
+            super(GenericMeta, self).__setattr__(attr, value)
+        else:
+            super(GenericMeta, self._gorg).__setattr__(attr, value)
+
+
+# Prevent checks for Generic to crash when defining Generic.
+Generic = None
+
+
+def _generic_new(base_cls, cls, *args, **kwds):
+    # Assure type is erased on instantiation,
+    # but attempt to store it in __orig_class__
+    if cls.__origin__ is None:
+        if (base_cls.__new__ is object.__new__ and
+                cls.__init__ is not object.__init__):
+            return base_cls.__new__(cls)
+        else:
+            return base_cls.__new__(cls, *args, **kwds)
+    else:
+        origin = cls._gorg
+        if (base_cls.__new__ is object.__new__ and
+                cls.__init__ is not object.__init__):
+            obj = base_cls.__new__(origin)
+        else:
+            obj = base_cls.__new__(origin, *args, **kwds)
+        try:
+            obj.__orig_class__ = cls
+        except AttributeError:
+            pass
+        obj.__init__(*args, **kwds)
+        return obj
+
+
+class Generic(metaclass=GenericMeta):
+    """Abstract base class for generic types.
+
+    A generic type is typically declared by inheriting from
+    this class parameterized with one or more type variables.
+    For example, a generic mapping type might be defined as::
+
+      class Mapping(Generic[KT, VT]):
+          def __getitem__(self, key: KT) -> VT:
+              ...
+          # Etc.
+
+    This class can then be used as follows::
+
+      def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
+          try:
+              return mapping[key]
+          except KeyError:
+              return default
+    """
+
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is Generic:
+            raise TypeError("Type Generic cannot be instantiated; "
+                            "it can be used only as a base class")
+        return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
+
+
+class _TypingEmpty:
+    """Internal placeholder for () or []. Used by TupleMeta and CallableMeta
+    to allow empty list/tuple in specific places, without allowing them
+    to sneak in where prohibited.
+    """
+
+
+class _TypingEllipsis:
+    """Internal placeholder for ... (ellipsis)."""
+
+
+class TupleMeta(GenericMeta):
+    """Metaclass for Tuple (internal)."""
+
+    @_tp_cache
+    def __getitem__(self, parameters):
+        if self.__origin__ is not None or self._gorg is not Tuple:
+            # Normal generic rules apply if this is not the first subscription
+            # or a subscription of a subclass.
+            return super().__getitem__(parameters)
+        if parameters == ():
+            return super().__getitem__((_TypingEmpty,))
+        if not isinstance(parameters, tuple):
+            parameters = (parameters,)
+        if len(parameters) == 2 and parameters[1] is ...:
+            msg = "Tuple[t, ...]: t must be a type."
+            p = _type_check(parameters[0], msg)
+            return super().__getitem__((p, _TypingEllipsis))
+        msg = "Tuple[t0, t1, ...]: each t must be a type."
+        parameters = tuple(_type_check(p, msg) for p in parameters)
+        return super().__getitem__(parameters)
+
+    def __instancecheck__(self, obj):
+        if self.__args__ is None:
+            return isinstance(obj, tuple)
+        raise TypeError("Parameterized Tuple cannot be used "
+                        "with isinstance().")
+
+    def __subclasscheck__(self, cls):
+        if self.__args__ is None:
+            return issubclass(cls, tuple)
+        raise TypeError("Parameterized Tuple cannot be used "
+                        "with issubclass().")
+
+
+class Tuple(tuple, extra=tuple, metaclass=TupleMeta):
+    """Tuple type; Tuple[X, Y] is the cross-product type of X and Y.
+
+    Example: Tuple[T1, T2] is a tuple of two elements corresponding
+    to type variables T1 and T2.  Tuple[int, float, str] is a tuple
+    of an int, a float and a string.
+
+    To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].
+    """
+
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is Tuple:
+            raise TypeError("Type Tuple cannot be instantiated; "
+                            "use tuple() instead")
+        return _generic_new(tuple, cls, *args, **kwds)
+
+
+class CallableMeta(GenericMeta):
+    """Metaclass for Callable (internal)."""
+
+    def __repr__(self):
+        if self.__origin__ is None:
+            return super().__repr__()
+        return self._tree_repr(self._subs_tree())
+
+    def _tree_repr(self, tree):
+        if self._gorg is not Callable:
+            return super()._tree_repr(tree)
+        # For actual Callable (not its subclass) we override
+        # super()._tree_repr() for nice formatting.
+        arg_list = []
+        for arg in tree[1:]:
+            if not isinstance(arg, tuple):
+                arg_list.append(_type_repr(arg))
+            else:
+                arg_list.append(arg[0]._tree_repr(arg))
+        if arg_list[0] == '...':
+            return repr(tree[0]) + '[..., %s]' % arg_list[1]
+        return (repr(tree[0]) +
+                '[[%s], %s]' % (', '.join(arg_list[:-1]), arg_list[-1]))
+
+    def __getitem__(self, parameters):
+        """A thin wrapper around __getitem_inner__ to provide the latter
+        with hashable arguments to improve speed.
+        """
+
+        if self.__origin__ is not None or self._gorg is not Callable:
+            return super().__getitem__(parameters)
+        if not isinstance(parameters, tuple) or len(parameters) != 2:
+            raise TypeError("Callable must be used as "
+                            "Callable[[arg, ...], result].")
+        args, result = parameters
+        if args is Ellipsis:
+            parameters = (Ellipsis, result)
+        else:
+            if not isinstance(args, list):
+                raise TypeError("Callable[args, result]: args must be a list."
+                                " Got %.100r." % (args,))
+            parameters = (tuple(args), result)
+        return self.__getitem_inner__(parameters)
+
+    @_tp_cache
+    def __getitem_inner__(self, parameters):
+        args, result = parameters
+        msg = "Callable[args, result]: result must be a type."
+        result = _type_check(result, msg)
+        if args is Ellipsis:
+            return super().__getitem__((_TypingEllipsis, result))
+        msg = "Callable[[arg, ...], result]: each arg must be a type."
+        args = tuple(_type_check(arg, msg) for arg in args)
+        parameters = args + (result,)
+        return super().__getitem__(parameters)
+
+
+class Callable(extra=collections_abc.Callable, metaclass=CallableMeta):
+    """Callable type; Callable[[int], str] is a function of (int) -> str.
+
+    The subscription syntax must always be used with exactly two
+    values: the argument list and the return type.  The argument list
+    must be a list of types or ellipsis; the return type must be a single type.
+
+    There is no syntax to indicate optional or keyword arguments,
+    such function types are rarely used as callback types.
+    """
+
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is Callable:
+            raise TypeError("Type Callable cannot be instantiated; "
+                            "use a non-abstract subclass instead")
+        return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
+
+
+class _ClassVar(_FinalTypingBase, _root=True):
+    """Special type construct to mark class variables.
+
+    An annotation wrapped in ClassVar indicates that a given
+    attribute is intended to be used as a class variable and
+    should not be set on instances of that class. Usage::
+
+      class Starship:
+          stats: ClassVar[Dict[str, int]] = {} # class variable
+          damage: int = 10                     # instance variable
+
+    ClassVar accepts only types and cannot be further subscribed.
+
+    Note that ClassVar is not a class itself, and should not
+    be used with isinstance() or issubclass().
+    """
+
+    __slots__ = ('__type__',)
+
+    def __init__(self, tp=None, **kwds):
+        self.__type__ = tp
+
+    def __getitem__(self, item):
+        cls = type(self)
+        if self.__type__ is None:
+            return cls(_type_check(item,
+                       '{} accepts only single type.'.format(cls.__name__[1:])),
+                       _root=True)
+        raise TypeError('{} cannot be further subscripted'
+                        .format(cls.__name__[1:]))
+
+    def _eval_type(self, globalns, localns):
+        new_tp = _eval_type(self.__type__, globalns, localns)
+        if new_tp == self.__type__:
+            return self
+        return type(self)(new_tp, _root=True)
+
+    def __repr__(self):
+        r = super().__repr__()
+        if self.__type__ is not None:
+            r += '[{}]'.format(_type_repr(self.__type__))
+        return r
+
+    def __hash__(self):
+        return hash((type(self).__name__, self.__type__))
+
+    def __eq__(self, other):
+        if not isinstance(other, _ClassVar):
+            return NotImplemented
+        if self.__type__ is not None:
+            return self.__type__ == other.__type__
+        return self is other
+
+
+ClassVar = _ClassVar(_root=True)
+
+
+def cast(typ, val):
+    """Cast a value to a type.
+
+    This returns the value unchanged.  To the type checker this
+    signals that the return value has the designated type, but at
+    runtime we intentionally don't check anything (we want this
+    to be as fast as possible).
+    """
+    return val
+
+
+def _get_defaults(func):
+    """Internal helper to extract the default arguments, by name."""
+    try:
+        code = func.__code__
+    except AttributeError:
+        # Some built-in functions don't have __code__, __defaults__, etc.
+        return {}
+    pos_count = code.co_argcount
+    arg_names = code.co_varnames
+    arg_names = arg_names[:pos_count]
+    defaults = func.__defaults__ or ()
+    kwdefaults = func.__kwdefaults__
+    res = dict(kwdefaults) if kwdefaults else {}
+    pos_offset = pos_count - len(defaults)
+    for name, value in zip(arg_names[pos_offset:], defaults):
+        assert name not in res
+        res[name] = value
+    return res
+
+
+_allowed_types = (types.FunctionType, types.BuiltinFunctionType,
+                  types.MethodType, types.ModuleType,
+                  WrapperDescriptorType, MethodWrapperType, MethodDescriptorType)
+
+
+def get_type_hints(obj, globalns=None, localns=None):
+    """Return type hints for an object.
+
+    This is often the same as obj.__annotations__, but it handles
+    forward references encoded as string literals, and if necessary
+    adds Optional[t] if a default value equal to None is set.
+
+    The argument may be a module, class, method, or function. The annotations
+    are returned as a dictionary. For classes, annotations include also
+    inherited members.
+
+    TypeError is raised if the argument is not of a type that can contain
+    annotations, and an empty dictionary is returned if no annotations are
+    present.
+
+    BEWARE -- the behavior of globalns and localns is counterintuitive
+    (unless you are familiar with how eval() and exec() work).  The
+    search order is locals first, then globals.
+
+    - If no dict arguments are passed, an attempt is made to use the
+      globals from obj (or the respective module's globals for classes),
+      and these are also used as the locals.  If the object does not appear
+      to have globals, an empty dictionary is used.
+
+    - If one dict argument is passed, it is used for both globals and
+      locals.
+
+    - If two dict arguments are passed, they specify globals and
+      locals, respectively.
+    """
+
+    if getattr(obj, '__no_type_check__', None):
+        return {}
+    # Classes require a special treatment.
+    if isinstance(obj, type):
+        hints = {}
+        for base in reversed(obj.__mro__):
+            if globalns is None:
+                base_globals = sys.modules[base.__module__].__dict__
+            else:
+                base_globals = globalns
+            ann = base.__dict__.get('__annotations__', {})
+            for name, value in ann.items():
+                if value is None:
+                    value = type(None)
+                if isinstance(value, str):
+                    value = _ForwardRef(value)
+                value = _eval_type(value, base_globals, localns)
+                hints[name] = value
+        return hints
+
+    if globalns is None:
+        if isinstance(obj, types.ModuleType):
+            globalns = obj.__dict__
+        else:
+            globalns = getattr(obj, '__globals__', {})
+        if localns is None:
+            localns = globalns
+    elif localns is None:
+        localns = globalns
+    hints = getattr(obj, '__annotations__', None)
+    if hints is None:
+        # Return empty annotations for something that _could_ have them.
+        if isinstance(obj, _allowed_types):
+            return {}
+        else:
+            raise TypeError('{!r} is not a module, class, method, '
+                            'or function.'.format(obj))
+    defaults = _get_defaults(obj)
+    hints = dict(hints)
+    for name, value in hints.items():
+        if value is None:
+            value = type(None)
+        if isinstance(value, str):
+            value = _ForwardRef(value)
+        value = _eval_type(value, globalns, localns)
+        if name in defaults and defaults[name] is None:
+            value = Optional[value]
+        hints[name] = value
+    return hints
+
+
+def no_type_check(arg):
+    """Decorator to indicate that annotations are not type hints.
+
+    The argument must be a class or function; if it is a class, it
+    applies recursively to all methods and classes defined in that class
+    (but not to methods defined in its superclasses or subclasses).
+
+    This mutates the function(s) or class(es) in place.
+    """
+    if isinstance(arg, type):
+        arg_attrs = arg.__dict__.copy()
+        for attr, val in arg.__dict__.items():
+            if val in arg.__bases__ + (arg,):
+                arg_attrs.pop(attr)
+        for obj in arg_attrs.values():
+            if isinstance(obj, types.FunctionType):
+                obj.__no_type_check__ = True
+            if isinstance(obj, type):
+                no_type_check(obj)
+    try:
+        arg.__no_type_check__ = True
+    except TypeError:  # built-in classes
+        pass
+    return arg
+
+
+def no_type_check_decorator(decorator):
+    """Decorator to give another decorator the @no_type_check effect.
+
+    This wraps the decorator with something that wraps the decorated
+    function in @no_type_check.
+    """
+
+    @functools.wraps(decorator)
+    def wrapped_decorator(*args, **kwds):
+        func = decorator(*args, **kwds)
+        func = no_type_check(func)
+        return func
+
+    return wrapped_decorator
+
+
+def _overload_dummy(*args, **kwds):
+    """Helper for @overload to raise when called."""
+    raise NotImplementedError(
+        "You should not call an overloaded function. "
+        "A series of @overload-decorated functions "
+        "outside a stub module should always be followed "
+        "by an implementation that is not @overload-ed.")
+
+
+def overload(func):
+    """Decorator for overloaded functions/methods.
+
+    In a stub file, place two or more stub definitions for the same
+    function in a row, each decorated with @overload.  For example:
+
+      @overload
+      def utf8(value: None) -> None: ...
+      @overload
+      def utf8(value: bytes) -> bytes: ...
+      @overload
+      def utf8(value: str) -> bytes: ...
+
+    In a non-stub file (i.e. a regular .py file), do the same but
+    follow it with an implementation.  The implementation should *not*
+    be decorated with @overload.  For example:
+
+      @overload
+      def utf8(value: None) -> None: ...
+      @overload
+      def utf8(value: bytes) -> bytes: ...
+      @overload
+      def utf8(value: str) -> bytes: ...
+      def utf8(value):
+          # implementation goes here
+    """
+    return _overload_dummy
+
+
+class _ProtocolMeta(GenericMeta):
+    """Internal metaclass for _Protocol.
+
+    This exists so _Protocol classes can be generic without deriving
+    from Generic.
+    """
+
+    def __instancecheck__(self, obj):
+        if _Protocol not in self.__bases__:
+            return super().__instancecheck__(obj)
+        raise TypeError("Protocols cannot be used with isinstance().")
+
+    def __subclasscheck__(self, cls):
+        if not self._is_protocol:
+            # No structural checks since this isn't a protocol.
+            return NotImplemented
+
+        if self is _Protocol:
+            # Every class is a subclass of the empty protocol.
+            return True
+
+        # Find all attributes defined in the protocol.
+        attrs = self._get_protocol_attrs()
+
+        for attr in attrs:
+            if not any(attr in d.__dict__ for d in cls.__mro__):
+                return False
+        return True
+
+    def _get_protocol_attrs(self):
+        # Get all Protocol base classes.
+        protocol_bases = []
+        for c in self.__mro__:
+            if getattr(c, '_is_protocol', False) and c.__name__ != '_Protocol':
+                protocol_bases.append(c)
+
+        # Get attributes included in protocol.
+        attrs = set()
+        for base in protocol_bases:
+            for attr in base.__dict__.keys():
+                # Include attributes not defined in any non-protocol bases.
+                for c in self.__mro__:
+                    if (c is not base and attr in c.__dict__ and
+                            not getattr(c, '_is_protocol', False)):
+                        break
+                else:
+                    if (not attr.startswith('_abc_') and
+                            attr != '__abstractmethods__' and
+                            attr != '__annotations__' and
+                            attr != '__weakref__' and
+                            attr != '_is_protocol' and
+                            attr != '_gorg' and
+                            attr != '__dict__' and
+                            attr != '__args__' and
+                            attr != '__slots__' and
+                            attr != '_get_protocol_attrs' and
+                            attr != '__next_in_mro__' and
+                            attr != '__parameters__' and
+                            attr != '__origin__' and
+                            attr != '__orig_bases__' and
+                            attr != '__extra__' and
+                            attr != '__tree_hash__' and
+                            attr != '__module__'):
+                        attrs.add(attr)
+
+        return attrs
+
+
+class _Protocol(metaclass=_ProtocolMeta):
+    """Internal base class for protocol classes.
+
+    This implements a simple-minded structural issubclass check
+    (similar but more general than the one-offs in collections.abc
+    such as Hashable).
+    """
+
+    __slots__ = ()
+
+    _is_protocol = True
+
+
+# Various ABCs mimicking those in collections.abc.
+# A few are simply re-exported for completeness.
+
+Hashable = collections_abc.Hashable  # Not generic.
+
+
+if hasattr(collections_abc, 'Awaitable'):
+    class Awaitable(Generic[T_co], extra=collections_abc.Awaitable):
+        __slots__ = ()
+
+    __all__.append('Awaitable')
+
+
+if hasattr(collections_abc, 'Coroutine'):
+    class Coroutine(Awaitable[V_co], Generic[T_co, T_contra, V_co],
+                    extra=collections_abc.Coroutine):
+        __slots__ = ()
+
+    __all__.append('Coroutine')
+
+
+if hasattr(collections_abc, 'AsyncIterable'):
+
+    class AsyncIterable(Generic[T_co], extra=collections_abc.AsyncIterable):
+        __slots__ = ()
+
+    class AsyncIterator(AsyncIterable[T_co],
+                        extra=collections_abc.AsyncIterator):
+        __slots__ = ()
+
+    __all__.append('AsyncIterable')
+    __all__.append('AsyncIterator')
+
+
+class Iterable(Generic[T_co], extra=collections_abc.Iterable):
+    __slots__ = ()
+
+
+class Iterator(Iterable[T_co], extra=collections_abc.Iterator):
+    __slots__ = ()
+
+
+class SupportsInt(_Protocol):
+    __slots__ = ()
+
+    @abstractmethod
+    def __int__(self) -> int:
+        pass
+
+
+class SupportsFloat(_Protocol):
+    __slots__ = ()
+
+    @abstractmethod
+    def __float__(self) -> float:
+        pass
+
+
+class SupportsComplex(_Protocol):
+    __slots__ = ()
+
+    @abstractmethod
+    def __complex__(self) -> complex:
+        pass
+
+
+class SupportsBytes(_Protocol):
+    __slots__ = ()
+
+    @abstractmethod
+    def __bytes__(self) -> bytes:
+        pass
+
+
+class SupportsIndex(_Protocol):
+    __slots__ = ()
+
+    @abstractmethod
+    def __index__(self) -> int:
+        pass
+
+
+class SupportsAbs(_Protocol[T_co]):
+    __slots__ = ()
+
+    @abstractmethod
+    def __abs__(self) -> T_co:
+        pass
+
+
+class SupportsRound(_Protocol[T_co]):
+    __slots__ = ()
+
+    @abstractmethod
+    def __round__(self, ndigits: int = 0) -> T_co:
+        pass
+
+
+if hasattr(collections_abc, 'Reversible'):
+    class Reversible(Iterable[T_co], extra=collections_abc.Reversible):
+        __slots__ = ()
+else:
+    class Reversible(_Protocol[T_co]):
+        __slots__ = ()
+
+        @abstractmethod
+        def __reversed__(self) -> 'Iterator[T_co]':
+            pass
+
+
+Sized = collections_abc.Sized  # Not generic.
+
+
+class Container(Generic[T_co], extra=collections_abc.Container):
+    __slots__ = ()
+
+
+if hasattr(collections_abc, 'Collection'):
+    class Collection(Sized, Iterable[T_co], Container[T_co],
+                     extra=collections_abc.Collection):
+        __slots__ = ()
+
+    __all__.append('Collection')
+
+
+# Callable was defined earlier.
+
+if hasattr(collections_abc, 'Collection'):
+    class AbstractSet(Collection[T_co],
+                      extra=collections_abc.Set):
+        __slots__ = ()
+else:
+    class AbstractSet(Sized, Iterable[T_co], Container[T_co],
+                      extra=collections_abc.Set):
+        __slots__ = ()
+
+
+class MutableSet(AbstractSet[T], extra=collections_abc.MutableSet):
+    __slots__ = ()
+
+
+# NOTE: It is only covariant in the value type.
+if hasattr(collections_abc, 'Collection'):
+    class Mapping(Collection[KT], Generic[KT, VT_co],
+                  extra=collections_abc.Mapping):
+        __slots__ = ()
+else:
+    class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co],
+                  extra=collections_abc.Mapping):
+        __slots__ = ()
+
+
+class MutableMapping(Mapping[KT, VT], extra=collections_abc.MutableMapping):
+    __slots__ = ()
+
+
+if hasattr(collections_abc, 'Reversible'):
+    if hasattr(collections_abc, 'Collection'):
+        class Sequence(Reversible[T_co], Collection[T_co],
+                       extra=collections_abc.Sequence):
+            __slots__ = ()
+    else:
+        class Sequence(Sized, Reversible[T_co], Container[T_co],
+                       extra=collections_abc.Sequence):
+            __slots__ = ()
+else:
+    class Sequence(Sized, Iterable[T_co], Container[T_co],
+                   extra=collections_abc.Sequence):
+        __slots__ = ()
+
+
+class MutableSequence(Sequence[T], extra=collections_abc.MutableSequence):
+    __slots__ = ()
+
+
+class ByteString(Sequence[int], extra=collections_abc.ByteString):
+    __slots__ = ()
+
+
+class List(list, MutableSequence[T], extra=list):
+
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is List:
+            raise TypeError("Type List cannot be instantiated; "
+                            "use list() instead")
+        return _generic_new(list, cls, *args, **kwds)
+
+
+class Deque(collections.deque, MutableSequence[T], extra=collections.deque):
+
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is Deque:
+            return collections.deque(*args, **kwds)
+        return _generic_new(collections.deque, cls, *args, **kwds)
+
+
+class Set(set, MutableSet[T], extra=set):
+
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is Set:
+            raise TypeError("Type Set cannot be instantiated; "
+                            "use set() instead")
+        return _generic_new(set, cls, *args, **kwds)
+
+
+class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset):
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is FrozenSet:
+            raise TypeError("Type FrozenSet cannot be instantiated; "
+                            "use frozenset() instead")
+        return _generic_new(frozenset, cls, *args, **kwds)
+
+
+class MappingView(Sized, Iterable[T_co], extra=collections_abc.MappingView):
+    __slots__ = ()
+
+
+class KeysView(MappingView[KT], AbstractSet[KT],
+               extra=collections_abc.KeysView):
+    __slots__ = ()
+
+
+class ItemsView(MappingView[Tuple[KT, VT_co]],
+                AbstractSet[Tuple[KT, VT_co]],
+                Generic[KT, VT_co],
+                extra=collections_abc.ItemsView):
+    __slots__ = ()
+
+
+class ValuesView(MappingView[VT_co], extra=collections_abc.ValuesView):
+    __slots__ = ()
+
+
+if hasattr(contextlib, 'AbstractContextManager'):
+    class ContextManager(Generic[T_co], extra=contextlib.AbstractContextManager):
+        __slots__ = ()
+else:
+    class ContextManager(Generic[T_co]):
+        __slots__ = ()
+
+        def __enter__(self):
+            return self
+
+        @abc.abstractmethod
+        def __exit__(self, exc_type, exc_value, traceback):
+            return None
+
+        @classmethod
+        def __subclasshook__(cls, C):
+            if cls is ContextManager:
+                # In Python 3.6+, it is possible to set a method to None to
+                # explicitly indicate that the class does not implement an ABC
+                # (https://bugs.python.org/issue25958), but we do not support
+                # that pattern here because this fallback class is only used
+                # in Python 3.5 and earlier.
+                if (any("__enter__" in B.__dict__ for B in C.__mro__) and
+                    any("__exit__" in B.__dict__ for B in C.__mro__)):
+                    return True
+            return NotImplemented
+
+
+if hasattr(contextlib, 'AbstractAsyncContextManager'):
+    class AsyncContextManager(Generic[T_co],
+                              extra=contextlib.AbstractAsyncContextManager):
+        __slots__ = ()
+
+    __all__.append('AsyncContextManager')
+elif sys.version_info[:2] >= (3, 5):
+    exec("""
+class AsyncContextManager(Generic[T_co]):
+    __slots__ = ()
+
+    async def __aenter__(self):
+        return self
+
+    @abc.abstractmethod
+    async def __aexit__(self, exc_type, exc_value, traceback):
+        return None
+
+    @classmethod
+    def __subclasshook__(cls, C):
+        if cls is AsyncContextManager:
+            if sys.version_info[:2] >= (3, 6):
+                return _collections_abc._check_methods(C, "__aenter__", "__aexit__")
+            if (any("__aenter__" in B.__dict__ for B in C.__mro__) and
+                    any("__aexit__" in B.__dict__ for B in C.__mro__)):
+                return True
+        return NotImplemented
+
+__all__.append('AsyncContextManager')
+""")
+
+
+class Dict(dict, MutableMapping[KT, VT], extra=dict):
+
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is Dict:
+            raise TypeError("Type Dict cannot be instantiated; "
+                            "use dict() instead")
+        return _generic_new(dict, cls, *args, **kwds)
+
+
+class DefaultDict(collections.defaultdict, MutableMapping[KT, VT],
+                  extra=collections.defaultdict):
+
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is DefaultDict:
+            return collections.defaultdict(*args, **kwds)
+        return _generic_new(collections.defaultdict, cls, *args, **kwds)
+
+
+class Counter(collections.Counter, Dict[T, int], extra=collections.Counter):
+
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is Counter:
+            return collections.Counter(*args, **kwds)
+        return _generic_new(collections.Counter, cls, *args, **kwds)
+
+
+if hasattr(collections, 'ChainMap'):
+    # ChainMap only exists in 3.3+
+    __all__.append('ChainMap')
+
+    class ChainMap(collections.ChainMap, MutableMapping[KT, VT],
+                   extra=collections.ChainMap):
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwds):
+            if cls._gorg is ChainMap:
+                return collections.ChainMap(*args, **kwds)
+            return _generic_new(collections.ChainMap, cls, *args, **kwds)
+
+
+# Determine what base class to use for Generator.
+if hasattr(collections_abc, 'Generator'):
+    # Sufficiently recent versions of 3.5 have a Generator ABC.
+    _G_base = collections_abc.Generator
+else:
+    # Fall back on the exact type.
+    _G_base = types.GeneratorType
+
+
+class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co],
+                extra=_G_base):
+    __slots__ = ()
+
+    def __new__(cls, *args, **kwds):
+        if cls._gorg is Generator:
+            raise TypeError("Type Generator cannot be instantiated; "
+                            "create a subclass instead")
+        return _generic_new(_G_base, cls, *args, **kwds)
+
+
+if hasattr(collections_abc, 'AsyncGenerator'):
+    class AsyncGenerator(AsyncIterator[T_co], Generic[T_co, T_contra],
+                         extra=collections_abc.AsyncGenerator):
+        __slots__ = ()
+
+    __all__.append('AsyncGenerator')
+
+
+# Internal type variable used for Type[].
+CT_co = TypeVar('CT_co', covariant=True, bound=type)
+
+
+# This is not a real generic class.  Don't use outside annotations.
+class Type(Generic[CT_co], extra=type):
+    """A special construct usable to annotate class objects.
+
+    For example, suppose we have the following classes::
+
+      class User: ...  # Abstract base for User classes
+      class BasicUser(User): ...
+      class ProUser(User): ...
+      class TeamUser(User): ...
+
+    And a function that takes a class argument that's a subclass of
+    User and returns an instance of the corresponding class::
+
+      U = TypeVar('U', bound=User)
+      def new_user(user_class: Type[U]) -> U:
+          user = user_class()
+          # (Here we could write the user object to a database)
+          return user
+
+      joe = new_user(BasicUser)
+
+    At this point the type checker knows that joe has type BasicUser.
+    """
+
+    __slots__ = ()
+
+
+def _make_nmtuple(name, types):
+    msg = "NamedTuple('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
+    types = [(n, _type_check(t, msg)) for n, t in types]
+    nm_tpl = collections.namedtuple(name, [n for n, t in types])
+    # Prior to PEP 526, only _field_types attribute was assigned.
+    # Now, both __annotations__ and _field_types are used to maintain compatibility.
+    nm_tpl.__annotations__ = nm_tpl._field_types = collections.OrderedDict(types)
+    try:
+        nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__')
+    except (AttributeError, ValueError):
+        pass
+    return nm_tpl
+
+
+_PY36 = sys.version_info[:2] >= (3, 6)
+
+# attributes prohibited to set in NamedTuple class syntax
+_prohibited = ('__new__', '__init__', '__slots__', '__getnewargs__',
+               '_fields', '_field_defaults', '_field_types',
+               '_make', '_replace', '_asdict', '_source')
+
+_special = ('__module__', '__name__', '__qualname__', '__annotations__')
+
+
+class NamedTupleMeta(type):
+
+    def __new__(cls, typename, bases, ns):
+        if ns.get('_root', False):
+            return super().__new__(cls, typename, bases, ns)
+        if not _PY36:
+            raise TypeError("Class syntax for NamedTuple is only supported"
+                            " in Python 3.6+")
+        types = ns.get('__annotations__', {})
+        nm_tpl = _make_nmtuple(typename, types.items())
+        defaults = []
+        defaults_dict = {}
+        for field_name in types:
+            if field_name in ns:
+                default_value = ns[field_name]
+                defaults.append(default_value)
+                defaults_dict[field_name] = default_value
+            elif defaults:
+                raise TypeError("Non-default namedtuple field {field_name} cannot "
+                                "follow default field(s) {default_names}"
+                                .format(field_name=field_name,
+                                        default_names=', '.join(defaults_dict.keys())))
+        nm_tpl.__new__.__annotations__ = collections.OrderedDict(types)
+        nm_tpl.__new__.__defaults__ = tuple(defaults)
+        nm_tpl._field_defaults = defaults_dict
+        # update from user namespace without overriding special namedtuple attributes
+        for key in ns:
+            if key in _prohibited:
+                raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
+            elif key not in _special and key not in nm_tpl._fields:
+                setattr(nm_tpl, key, ns[key])
+        return nm_tpl
+
+
+class NamedTuple(metaclass=NamedTupleMeta):
+    """Typed version of namedtuple.
+
+    Usage in Python versions >= 3.6::
+
+        class Employee(NamedTuple):
+            name: str
+            id: int
+
+    This is equivalent to::
+
+        Employee = collections.namedtuple('Employee', ['name', 'id'])
+
+    The resulting class has extra __annotations__ and _field_types
+    attributes, giving an ordered dict mapping field names to types.
+    __annotations__ should be preferred, while _field_types
+    is kept to maintain pre PEP 526 compatibility. (The field names
+    are in the _fields attribute, which is part of the namedtuple
+    API.) Alternative equivalent keyword syntax is also accepted::
+
+        Employee = NamedTuple('Employee', name=str, id=int)
+
+    In Python versions <= 3.5 use::
+
+        Employee = NamedTuple('Employee', [('name', str), ('id', int)])
+    """
+    _root = True
+
+    def __new__(*args, **kwargs):
+        if kwargs and not _PY36:
+            raise TypeError("Keyword syntax for NamedTuple is only supported"
+                            " in Python 3.6+")
+        if not args:
+            raise TypeError('NamedTuple.__new__(): not enough arguments')
+        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
+        if args:
+            typename, args = args[0], args[1:]  # allow the "typename" keyword be passed
+        elif 'typename' in kwargs:
+            typename = kwargs.pop('typename')
+            import warnings
+            warnings.warn("Passing 'typename' as keyword argument is deprecated",
+                          DeprecationWarning, stacklevel=2)
+        else:
+            raise TypeError("NamedTuple.__new__() missing 1 required positional "
+                            "argument: 'typename'")
+        if args:
+            try:
+                fields, = args  # allow the "fields" keyword be passed
+            except ValueError:
+                raise TypeError('NamedTuple.__new__() takes from 2 to 3 '
+                                'positional arguments but {} '
+                                'were given'.format(len(args) + 2))
+        elif 'fields' in kwargs and len(kwargs) == 1:
+            fields = kwargs.pop('fields')
+            import warnings
+            warnings.warn("Passing 'fields' as keyword argument is deprecated",
+                          DeprecationWarning, stacklevel=2)
+        else:
+            fields = None
+
+        if fields is None:
+            fields = kwargs.items()
+        elif kwargs:
+            raise TypeError("Either list of fields or keywords"
+                            " can be provided to NamedTuple, not both")
+        return _make_nmtuple(typename, fields)
+
+    __new__.__text_signature__ = '($cls, typename, fields=None, /, **kwargs)'
+
+
+def NewType(name, tp):
+    """NewType creates simple unique types with almost zero
+    runtime overhead. NewType(name, tp) is considered a subtype of tp
+    by static type checkers. At runtime, NewType(name, tp) returns
+    a dummy function that simply returns its argument. Usage::
+
+        UserId = NewType('UserId', int)
+
+        def name_by_id(user_id: UserId) -> str:
+            ...
+
+        UserId('user')          # Fails type check
+
+        name_by_id(42)          # Fails type check
+        name_by_id(UserId(42))  # OK
+
+        num = UserId(5) + 1     # type: int
+    """
+
+    def new_type(x):
+        return x
+
+    new_type.__name__ = name
+    new_type.__supertype__ = tp
+    return new_type
+
+
+# Python-version-specific alias (Python 2: unicode; Python 3: str)
+Text = str
+
+
+# Constant that's True when type checking, but False here.
+TYPE_CHECKING = False
+
+
+class IO(Generic[AnyStr]):
+    """Generic base class for TextIO and BinaryIO.
+
+    This is an abstract, generic version of the return of open().
+
+    NOTE: This does not distinguish between the different possible
+    classes (text vs. binary, read vs. write vs. read/write,
+    append-only, unbuffered).  The TextIO and BinaryIO subclasses
+    below capture the distinctions between text vs. binary, which is
+    pervasive in the interface; however we currently do not offer a
+    way to track the other distinctions in the type system.
+    """
+
+    __slots__ = ()
+
+    @abstractproperty
+    def mode(self) -> str:
+        pass
+
+    @abstractproperty
+    def name(self) -> str:
+        pass
+
+    @abstractmethod
+    def close(self) -> None:
+        pass
+
+    @abstractproperty
+    def closed(self) -> bool:
+        pass
+
+    @abstractmethod
+    def fileno(self) -> int:
+        pass
+
+    @abstractmethod
+    def flush(self) -> None:
+        pass
+
+    @abstractmethod
+    def isatty(self) -> bool:
+        pass
+
+    @abstractmethod
+    def read(self, n: int = -1) -> AnyStr:
+        pass
+
+    @abstractmethod
+    def readable(self) -> bool:
+        pass
+
+    @abstractmethod
+    def readline(self, limit: int = -1) -> AnyStr:
+        pass
+
+    @abstractmethod
+    def readlines(self, hint: int = -1) -> List[AnyStr]:
+        pass
+
+    @abstractmethod
+    def seek(self, offset: int, whence: int = 0) -> int:
+        pass
+
+    @abstractmethod
+    def seekable(self) -> bool:
+        pass
+
+    @abstractmethod
+    def tell(self) -> int:
+        pass
+
+    @abstractmethod
+    def truncate(self, size: int = None) -> int:
+        pass
+
+    @abstractmethod
+    def writable(self) -> bool:
+        pass
+
+    @abstractmethod
+    def write(self, s: AnyStr) -> int:
+        pass
+
+    @abstractmethod
+    def writelines(self, lines: List[AnyStr]) -> None:
+        pass
+
+    @abstractmethod
+    def __enter__(self) -> 'IO[AnyStr]':
+        pass
+
+    @abstractmethod
+    def __exit__(self, type, value, traceback) -> None:
+        pass
+
+
+class BinaryIO(IO[bytes]):
+    """Typed version of the return of open() in binary mode."""
+
+    __slots__ = ()
+
+    @abstractmethod
+    def write(self, s: Union[bytes, bytearray]) -> int:
+        pass
+
+    @abstractmethod
+    def __enter__(self) -> 'BinaryIO':
+        pass
+
+
+class TextIO(IO[str]):
+    """Typed version of the return of open() in text mode."""
+
+    __slots__ = ()
+
+    @abstractproperty
+    def buffer(self) -> BinaryIO:
+        pass
+
+    @abstractproperty
+    def encoding(self) -> str:
+        pass
+
+    @abstractproperty
+    def errors(self) -> Optional[str]:
+        pass
+
+    @abstractproperty
+    def line_buffering(self) -> bool:
+        pass
+
+    @abstractproperty
+    def newlines(self) -> Any:
+        pass
+
+    @abstractmethod
+    def __enter__(self) -> 'TextIO':
+        pass
+
+
+class io:
+    """Wrapper namespace for IO generic classes."""
+
+    __all__ = ['IO', 'TextIO', 'BinaryIO']
+    IO = IO
+    TextIO = TextIO
+    BinaryIO = BinaryIO
+
+
+io.__name__ = __name__ + '.io'
+sys.modules[io.__name__] = io
+
+
+Pattern = _TypeAlias('Pattern', AnyStr, type(stdlib_re.compile('')),
+                     lambda p: p.pattern)
+Match = _TypeAlias('Match', AnyStr, type(stdlib_re.match('', '')),
+                   lambda m: m.re.pattern)
+
+
+class re:
+    """Wrapper namespace for re type aliases."""
+
+    __all__ = ['Pattern', 'Match']
+    Pattern = Pattern
+    Match = Match
+
+
+re.__name__ = __name__ + '.re'
+sys.modules[re.__name__] = re
diff --git a/tests/utils/python/typing/typing.py.license b/tests/utils/python/typing/typing.py.license
new file mode 100644 (file)
index 0000000..90a0f1e
--- /dev/null
@@ -0,0 +1,2 @@
+# SPDX-FileCopyrightText: 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Python Software Foundation.
+# SPDX-License-Identifier: PSF-2.0
diff --git a/tests/utils/run-in-py-env.sh b/tests/utils/run-in-py-env.sh
new file mode 100755 (executable)
index 0000000..c526654
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2017-2023 Philippe Proulx <pproulx@efficios.com>
+# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+
+if [[ -n ${BT_TESTS_SRCDIR:-} ]]; then
+       utils_sh=$BT_TESTS_SRCDIR/utils/utils.sh
+else
+       utils_sh=$(dirname "$0")/../utils/utils.sh
+fi
+
+# shellcheck source=../utils/utils.sh
+source "$utils_sh"
+
+usage() {
+       echo "Usage: run-in-py-utils-bt2-env.sh COMMAND [ARGS]..."
+       echo ""
+       echo "Runs the command \`COMMAND\` with the arguments \`ARGS\` within an environment"
+       echo "which can import the testing Python modules (in \`tests/utils/python\`) and the"
+       echo "built \`bt2\` Python package."
+       echo ""
+       echo "NOTE: If you build out of tree, export and set the \`BT_TESTS_BUILDDIR\`"
+       echo "environment variable to the built \`tests\` directory."
+}
+
+if (($# == 0)); then
+       usage
+       exit 1
+fi
+
+# Make sure the value of `BT_TESTS_BUILDDIR` makes sense
+if [[ ! -f $BT_TESTS_BUILDDIR/Makefile ]]; then
+       {
+               echo "ERROR: Invalid \`BT_TESTS_BUILDDIR\` variable (\`\$BT_TESTS_BUILDDIR/Makefile\`"
+               echo "doesn't exist)."
+               echo ""
+               echo "If you build out of tree, export and set the \`BT_TESTS_BUILDDIR\` environment"
+               echo "variable to the built \`tests\` directory."
+       } >&2
+
+       exit 1
+fi
+
+bt_run_in_py_env "$@"
diff --git a/tests/utils/run_python_bt2 b/tests/utils/run_python_bt2
deleted file mode 100755 (executable)
index 574900a..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
-# Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
-#
-
-# Execute a shell command in the appropriate environment to have access to the
-# bt2 Python bindings. For example, one could use it to run a specific Python
-# binding test case with:
-#
-#   $ tests/utils/run_python_bt2 python3 ./tests/utils/python/testrunner.py \
-#     -t test_value.MapValueTestCase.test_deepcopy \
-#     ./tests/bindings/python/bt2
-
-if [ -n "${BT_TESTS_SRCDIR:-}" ]; then
-       UTILSSH="$BT_TESTS_SRCDIR/utils/utils.sh"
-else
-       UTILSSH="$(dirname "$0")/../utils/utils.sh"
-fi
-
-# shellcheck source=../utils/utils.sh
-source "$UTILSSH"
-
-usage() {
-       echo "Usage: run_python_bt2 [PYTHON_BIN] ..."
-       echo ""
-       echo "Run a binary with the python environment set to use the 'bt2' module"
-       echo "from the build system prior to installation."
-       echo ""
-       echo "When building out of tree export the BT_TESTS_BUILDDIR variable with"
-       echo "the path to the built 'tests' directory."
-}
-
-if [ -z "$*" ]; then
-       usage
-       exit 1
-fi
-
-# Sanity check that the BT_TESTS_BUILDDIR value makes sense.
-if [ ! -f "$BT_TESTS_BUILDDIR/Makefile" ]; then
-       fold -w 80 -s <<- END
-       $0: BT_TESTS_BUILDDIR does not point to a valid directory (\`$BT_TESTS_BUILDDIR/Makefile\` does not exist).
-
-       If building out-of-tree, set BT_TESTS_BUILDDIR to point to the \`tests\` directory in the build tree.
-       END
-       exit 1
-fi
-
-run_python_bt2 "${@}"
index 94f1c3e12f0a3b5773266d1db2cce8a161ec624c..bb4688069b464cec3623dfb8926e63f87e06fcab 100755 (executable)
@@ -361,7 +361,7 @@ function setup_result_obj(line)
   sub("^(not )?ok[ \t]*", "", line)
 
   # If the result has an explicit number, get it and strip it; otherwise,
-  # automatically assing the next progresive number to it.
+  # automatically assign the next progressive number to it.
   if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/)
     {
       match(line, "^[0-9]+")
index 5a78e1acde4410ba9bfed4c9ff795098b0d2cafb..a88393d91a172e2f04de0952d9f653929a2baffd 100644 (file)
@@ -1,3 +1,4 @@
+# SPDX-FileCopyrightText: 2013-2020 EfficiOS, Inc.
 # SPDX-License-Identifier: MIT
 
 noinst_LTLIBRARIES = libtap.la
index 9f41408bd4c4dd25a729b607b4c276bb115ab27b..5fa9482b25f369ece77d338444a5da46bdec0391 100644 (file)
@@ -44,12 +44,12 @@ static void _cleanup(void);
 
 #ifdef __MINGW32__
 static inline
-void flockfile (FILE * filehandle) {
+void flockfile (FILE * filehandle __attribute__((unused))) {
        return;
 }
 
 static inline
-void funlockfile(FILE * filehandle) {
+void funlockfile(FILE * filehandle __attribute__((unused))) {
        return;
 }
 #endif
index b3e94450541e203ba38bb9c5b2c3a71fd0869252..f07c1dbbabf309765085f576158be0b60d078779 100644 (file)
@@ -5,8 +5,12 @@
  * Copyright (C) 2017 Jérémie Galarneau
  */
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting
-   and requires the caller to add the final comma if they've ommitted
+   and requires the caller to add the final comma if they've omitted
    the optional arguments */
 #ifdef __GNUC__
 # define ok(e, test, ...) ((e) ?                                       \
@@ -80,3 +84,7 @@ void todo_start(const char *, ...);
 void todo_end(void);
 
 int exit_status(void);
+
+#ifdef __cplusplus
+}
+#endif
index 6107a610453e3dff10cfe3138cb556a2ebee113d..9081a88e2e871324448adfc0620bae33db4cc4fb 100644 (file)
 # SPDX-License-Identifier: GPL-2.0-only
 #
 # Copyright (c) 2019 Michael Jeanson <mjeanson@efficios.com>
-# Copyright (C) 2019 Philippe Proulx <pproulx@efficios.com>
+# Copyright (C) 2019-2023 Philippe Proulx <pproulx@efficios.com>
+
+# Source this file at the beginning of a shell script test to access
+# useful testing variables and functions:
+#
+#     SH_TAP=1
+#
+#     if [[ -n ${BT_TESTS_SRCDIR:-} ]]; then
+#         UTILSSH=$BT_TESTS_SRCDIR/utils/utils.sh
+#     else
+#         UTILSSH=$(dirname "$0")/../utils/utils.sh
+#     fi
 #
+#     # shellcheck source=../utils/utils.sh
+#     source "$UTILSSH"
+#
+# Make sure the relative path to `utils.sh` (this file) above is
+# correct (twice).
 
-# This file is meant to be sourced at the start of shell script-based tests.
+# An unbound variable is an error
+set -u
 
+# Sets the variable named `$1` to `$2` if it's not set, and exports it.
+_bt_tests_set_var_def() {
+       local -r varname=$1
+       local -r val=$2
 
-# Error out when encountering an undefined variable
-set -u
+       if [[ -z ${!varname:-} ]]; then
+               eval "$varname='$val'"
+       fi
 
-# If "readlink -f" is available, get a resolved absolute path to the
-# tests source dir, otherwise make do with a relative path.
-scriptdir="$(dirname "${BASH_SOURCE[0]}")"
-if readlink -f "." >/dev/null 2>&1; then
-       testsdir=$(readlink -f "$scriptdir/..")
-else
-       testsdir="$scriptdir/.."
-fi
+       export "${varname?}"
+}
+
+# Name of the OS on which we're running, if not set.
+#
+# One of:
+#
+# `mingw`:          MinGW (Windows)
+# `darwin`:         macOS
+# `linux`:          Linux
+# `cygwin`:         Cygwin (Windows)
+# `unsupported`:    Anything else
+#
+# See <https://en.wikipedia.org/wiki/Uname#Examples> for possible values
+# of `uname -s`.
+#
+# Do some translation to ease our life down the road for comparison.
+# Export it so that executed commands can use it.
+if [[ -z ${BT_TESTS_OS_TYPE:-} ]]; then
+       BT_TESTS_OS_TYPE=$(uname -s)
 
-# The OS on which we are running. See [1] for possible values of 'uname -s'.
-# We do a bit of translation to ease our life down the road for comparison.
-# Export it so that called executables can use it.
-# [1] https://en.wikipedia.org/wiki/Uname#Examples
-if [ -z "${BT_TESTS_OS_TYPE:-}" ]; then
-       BT_TESTS_OS_TYPE="$(uname -s)"
-       case "$BT_TESTS_OS_TYPE" in
+       case $BT_TESTS_OS_TYPE in
        MINGW*)
-               BT_TESTS_OS_TYPE="mingw"
+               BT_TESTS_OS_TYPE=mingw
                ;;
        Darwin)
-               BT_TESTS_OS_TYPE="darwin"
+               BT_TESTS_OS_TYPE=darwin
                ;;
        Linux)
-               BT_TESTS_OS_TYPE="linux"
+               BT_TESTS_OS_TYPE=linux
                ;;
        CYGWIN*)
-               BT_TESTS_OS_TYPE="cygwin"
+               BT_TESTS_OS_TYPE=cygwin
                ;;
        *)
-               BT_TESTS_OS_TYPE="unsupported"
+               BT_TESTS_OS_TYPE=unsupported
                ;;
        esac
 fi
+
 export BT_TESTS_OS_TYPE
 
-# Allow overriding the source and build directories
-if [ -z "${BT_TESTS_SRCDIR:-}" ]; then
-       BT_TESTS_SRCDIR="$testsdir"
-fi
-export BT_TESTS_SRCDIR
+# Sets and exports, if not set:
+#
+# • `BT_TESTS_SRCDIR` to the base source directory of tests.
+# • `BT_TESTS_BUILDDIR` to the base build directory of tests.
+_set_vars_srcdir_builddir() {
+       # If `readlink -f` is available, then get a resolved absolute path
+       # to the tests source directory. Otherwise, make do with a relative
+       # path.
+       local -r scriptdir=$(dirname "${BASH_SOURCE[0]}")
+       local testsdir
+
+       if readlink -f . &> /dev/null; then
+               testsdir=$(readlink -f "$scriptdir/..")
+       else
+               testsdir=$scriptdir/..
+       fi
 
-if [ -z "${BT_TESTS_BUILDDIR:-}" ]; then
-       BT_TESTS_BUILDDIR="$testsdir"
-fi
-export BT_TESTS_BUILDDIR
+       # Base source directory of tests
+       _bt_tests_set_var_def BT_TESTS_SRCDIR "$testsdir"
+
+       # Base build directory of tests
+       _bt_tests_set_var_def BT_TESTS_BUILDDIR "$testsdir"
+}
 
+_set_vars_srcdir_builddir
+unset -f _set_vars_srcdir_builddir
 
-# Source the generated environment file if it's present.
-if [ -f "${BT_TESTS_BUILDDIR}/utils/env.sh" ]; then
-       # shellcheck source=./env.sh
-       . "${BT_TESTS_BUILDDIR}/utils/env.sh"
-fi
+# Sources the generated environment file (`env.sh`) if it exists.
+_source_env_sh() {
+       local -r env_sh_path=$BT_TESTS_BUILDDIR/utils/env.sh
 
-# Allow overriding the babeltrace2 executables
-if [ -z "${BT_TESTS_BT2_BIN:-}" ]; then
-       BT_TESTS_BT2_BIN="$BT_TESTS_BUILDDIR/../src/cli/babeltrace2"
-       if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
-               BT_TESTS_BT2_BIN="${BT_TESTS_BT2_BIN}.exe"
+       if [[ -f $env_sh_path ]]; then
+               # shellcheck disable=SC1090,SC1091
+               . "$env_sh_path"
        fi
-fi
-export BT_TESTS_BT2_BIN
+}
 
-# TODO: Remove when bindings/python/bt2/test_plugin.py is fixed
-BT_PLUGINS_PATH="${BT_TESTS_BUILDDIR}/../src/plugins"
+_source_env_sh
+unset -f _source_env_sh
 
-# Allow overriding the babeltrace2 plugin path
-if [ -z "${BT_TESTS_BABELTRACE_PLUGIN_PATH:-}" ]; then
-       BT_TESTS_BABELTRACE_PLUGIN_PATH="${BT_PLUGINS_PATH}/ctf:${BT_PLUGINS_PATH}/utils:${BT_PLUGINS_PATH}/text:${BT_PLUGINS_PATH}/lttng-utils"
-fi
-export BT_TESTS_BABELTRACE_PLUGIN_PATH
+# Path to the `babeltrace2` command, if not set
+if [[ -z ${BT_TESTS_BT2_BIN:-} ]]; then
+       BT_TESTS_BT2_BIN=$BT_TESTS_BUILDDIR/../src/cli/babeltrace2
 
-if [ -z "${BT_TESTS_PROVIDER_DIR:-}" ]; then
-       BT_TESTS_PROVIDER_DIR="${BT_TESTS_BUILDDIR}/../src/python-plugin-provider/.libs"
+       if [[ $BT_TESTS_OS_TYPE == mingw ]]; then
+               BT_TESTS_BT2_BIN+=.exe
+       fi
 fi
-export BT_TESTS_PROVIDER_DIR
 
-# Allow overriding the babeltrace2 executables
-if [ -z "${BT_TESTS_PYTHONPATH:-}" ]; then
-       BT_TESTS_PYTHONPATH="${BT_TESTS_BUILDDIR}/../src/bindings/python/bt2/build/build_lib"
-fi
-export BT_TESTS_PYTHONPATH
+export BT_TESTS_BT2_BIN
 
+# This doesn't need to be exported, but it needs to remain set for
+# bt_run_in_py_env() to use it.
+#
+# TODO: Remove when `tests/bindings/python/bt2/test_plugin.py` is fixed.
+_bt_tests_plugins_path=$BT_TESTS_BUILDDIR/../src/plugins
 
-### External Tools ###
-if [ -z "${BT_TESTS_AWK_BIN:-}" ]; then
-       BT_TESTS_AWK_BIN="awk"
-fi
-export BT_TESTS_AWK_BIN
+# Colon-separated list of project plugin paths, if not set
+_bt_tests_set_var_def BT_TESTS_BABELTRACE_PLUGIN_PATH \
+       "$_bt_tests_plugins_path/ctf:$_bt_tests_plugins_path/utils:$_bt_tests_plugins_path/text:$_bt_tests_plugins_path/lttng-utils"
 
-if [ -z "${BT_TESTS_GREP_BIN:-}" ]; then
-       BT_TESTS_GREP_BIN="grep"
-fi
-export BT_TESTS_GREP_BIN
+# Directory containing the Python plugin provider library, if not set
+_bt_tests_set_var_def BT_TESTS_PROVIDER_DIR "$BT_TESTS_BUILDDIR/../src/python-plugin-provider/.libs"
 
-if [ -z "${BT_TESTS_PYTHON_BIN:-}" ]; then
-       BT_TESTS_PYTHON_BIN="python3"
-fi
-export BT_TESTS_PYTHON_BIN
+# Directory containing the built `bt2` Python package, if not set
+_bt_tests_set_var_def BT_TESTS_PYTHONPATH "$BT_TESTS_BUILDDIR/../src/bindings/python/bt2/build/build_lib"
 
-if [ -z "${BT_TESTS_PYTHON_CONFIG_BIN:-}" ]; then
-       BT_TESTS_PYTHON_CONFIG_BIN="python3-config"
-fi
-export BT_TESTS_PYTHON_CONFIG_BIN
+# Name of the `awk` command to use when testing, if not set
+_bt_tests_set_var_def BT_TESTS_AWK_BIN awk
 
-if [ -z "${BT_TESTS_SED_BIN:-}" ]; then
-       BT_TESTS_SED_BIN="sed"
-fi
-export BT_TESTS_SED_BIN
+# Name of the `grep` command to use when testing, if not set
+_bt_tests_set_var_def BT_TESTS_GREP_BIN grep
 
-if [ -z "${BT_TESTS_CC_BIN:-}" ]; then
-       BT_TESTS_CC_BIN="cc"
-fi
-export BT_TESTS_CC_BIN
+# Name of the `python3` command to use when testing, if not set
+_bt_tests_set_var_def BT_TESTS_PYTHON_BIN python3
 
+# Major and minor version of the `python3` command to use when testing.
+#
+# This doesn't need to be exported, but it needs to remain set for
+# bt_run_in_py_utils_env() to use it.
+_bt_tests_py3_version=$($BT_TESTS_PYTHON_BIN -c 'import sys; print("{}.{}".format(sys.version_info.major, sys.version_info.minor))')
 
-### Optional features ###
+# Name of the `python3-config` command to use when testing, if not set
+_bt_tests_set_var_def BT_TESTS_PYTHON_CONFIG_BIN python3-config
 
-if [ -z "${BT_TESTS_ENABLE_ASAN:-}" ]; then
-       BT_TESTS_ENABLE_ASAN="0"
-fi
-export BT_TESTS_ENABLE_ASAN
+# Name of the `sed` command to use when testing, if not set
+_bt_tests_set_var_def BT_TESTS_SED_BIN sed
 
+# Name of the `cc` command to use when testing, if not set
+_bt_tests_set_var_def BT_TESTS_CC_BIN cc
 
-# Data files path
-BT_TESTS_DATADIR="${BT_TESTS_SRCDIR}/data"
-BT_CTF_TRACES_PATH="${BT_TESTS_DATADIR}/ctf-traces"
+# Done with _bt_tests_set_var_def()
+unset -f _bt_tests_set_var_def
 
-# By default, it will not source tap.sh.  If you want to output tap directly
-# from the test script, define the 'SH_TAP' variable to '1' before sourcing
-# this script.
-if [ "${SH_TAP:-}" = 1 ]; then
-       # shellcheck source=./tap/tap.sh
-       . "${BT_TESTS_SRCDIR}/utils/tap/tap.sh"
+# Whether or not to enable AddressSanitizer, `0` (disabled) if not set.
+#
+# This doesn't need to be exported from the point of view of this file,
+# but the sourced `env.sh` above does export it.
+if [[ -z ${BT_TESTS_ENABLE_ASAN:-} ]]; then
+       BT_TESTS_ENABLE_ASAN=0
 fi
 
+# Directory containing test data
+BT_TESTS_DATADIR=$BT_TESTS_SRCDIR/data
+
+# Directory containing test CTF traces
+BT_CTF_TRACES_PATH=$BT_TESTS_DATADIR/ctf-traces
 
-# Remove CR characters in file "$1".
+# Source the shell TAP utilities if `SH_TAP` is `1`
+if [[ ${SH_TAP:-} == 1 ]]; then
+       # shellcheck source=./tap/tap.sh
+       . "$BT_TESTS_SRCDIR/utils/tap/tap.sh"
+fi
 
+# Removes the CR characters from the file having the path `$1`.
+#
+# This is sometimes needed on Windows with text files.
+#
+# We can't use the `--string-trailing-cr` option of `diff` because
+# Solaris doesn't have it.
 bt_remove_cr() {
-       "$BT_TESTS_SED_BIN" -i 's/\r//g' "$1"
+       "$BT_TESTS_SED_BIN" -i'' -e 's/\r//g' "$1"
+}
+
+# Prints `$1` without CR characters.
+bt_remove_cr_inline() {
+    "$BT_TESTS_SED_BIN" 's/\r//g' "$1"
 }
 
-# Run the Babeltrace CLI, redirecting stdout and stderr to specified files.
+# Runs the `$BT_TESTS_BT2_BIN` command within an environment which can
+# import the `bt2` Python package, redirecting the standard output to
+# the `$1` file and the standard error to the `$2` file.
 #
-#   $1: file to redirect stdout to
-#   $2: file to redirect stderr to
-#   remaining args: arguments to pass to the CLI
+# The remaining arguments are forwarded to the `$BT_TESTS_BT2_BIN`
+# command.
 #
-# Return the exit code of the CLI.
-
+# Returns the exit status of the executed `$BT_TESTS_BT2_BIN`.
 bt_cli() {
-       local stdout_file="$1"
-       local stderr_file="$2"
+       local -r stdout_file=$1
+       local -r stderr_file=$2
+
        shift 2
-       local args=("$@")
 
-       echo "Running: $BT_TESTS_BT2_BIN ${args[*]}" >&2
-       run_python_bt2 "$BT_TESTS_BT2_BIN" "${args[@]}" 1>"$stdout_file" 2>"$stderr_file"
-}
+       local -a bt_cli_args=("$@")
 
-### Diff Functions ###
+       echo "Running: \`$BT_TESTS_BT2_BIN ${bt_cli_args[*]}\`" >&2
+       bt_run_in_py_env "$BT_TESTS_BT2_BIN" "${bt_cli_args[@]}" 1>"$stdout_file" 2>"$stderr_file"
+}
 
-# Check the differences between two files (typically some expected output vs
-# some actual output).  If there are differences, print the diff to stderr.
+# Checks the differences between:
 #
-#   $1: file 1 (expected)
-#   $2: file 2 (actual)
+# • The (expected) contents of the file having the path `$1`.
 #
-# Return 0 if there's no difference, and non-zero if there are.
+# • The contents of another file having the path `$2`.
 #
-# Note that this function modifies the actual output file ($2) _in-place_ to
-# remove any \r character.
-
+# Both files are passed through bt_remove_cr_inline() to remove CR
+# characters.
+#
+# Returns 0 if there's no difference, or not zero otherwise.
 bt_diff() {
-       local expected_file="$1"
-       local actual_file="$2"
-       local ret=0
+       local -r expected_file=$1
+       local -r actual_file=$2
 
-       # Strip any \r present due to Windows (\n -> \r\n).
-       # "diff --string-trailing-cr" is not used since it is not present on
-       # Solaris.
-       bt_remove_cr "$actual_file"
-
-       diff -u "$expected_file" "$actual_file" 1>&2
-
-       return $?
+       diff -u <(bt_remove_cr_inline "$expected_file") <(bt_remove_cr_inline "$actual_file") 1>&2
 }
 
 # Checks the difference between:
 #
-#   1. What the CLI outputs on its standard output when given the arguments
-#   "$@" (excluding the first two arguments).
-#   2. The file with path "$1".
+# • What the `$BT_TESTS_BT2_BIN` command prints to its standard output
+#   when given the third and following arguments of this function.
+#
+# • The file having the path `$1`.
 #
-# And the difference between:
+# as well as the difference between:
 #
-#   1. What the CLI outputs on its standard error when given the arguments
-#   "$@" (excluding the first two arguments).
-#   2. The file with path "$2".
+# • What the `$BT_TESTS_BT2_BIN` command prints to its standard error
+#   when given the third and following arguments of this function.
 #
-# Returns 0 if there's no difference, and 1 if there is, also printing
-# said difference to the standard error.
+# • The file having the path `$2`.
+#
+# Returns 0 if there's no difference, or 1 otherwise, also printing said
+# difference to the standard error.
 bt_diff_cli() {
-       local expected_stdout_file="$1"
-       local expected_stderr_file="$2"
-       shift 2
-       local args=("$@")
+       local -r expected_stdout_file=$1
+       local -r expected_stderr_file=$2
 
-       local temp_stdout_output_file
-       local temp_stderr_output_file
-       local ret=0
-       local ret_stdout
-       local ret_stderr
+       shift 2
 
-       temp_stdout_output_file="$(mktemp -t actual_stdout.XXXXXX)"
-       temp_stderr_output_file="$(mktemp -t actual_stderr.XXXXXX)"
+       local -r args=("$@")
+       local -r temp_stdout_output_file=$(mktemp -t actual-stdout.XXXXXX)
+       local -r temp_stderr_output_file=$(mktemp -t actual-stderr.XXXXXX)
 
-       # Run the CLI to get a detailed file.
        bt_cli "$temp_stdout_output_file" "$temp_stderr_output_file" "${args[@]}"
-
        bt_diff "$expected_stdout_file" "$temp_stdout_output_file" "${args[@]}"
-       ret_stdout=$?
+
+       local -r ret_stdout=$?
+
        bt_diff "$expected_stderr_file" "$temp_stderr_output_file" "${args[@]}"
-       ret_stderr=$?
 
-       if ((ret_stdout != 0 || ret_stderr != 0)); then
-               ret=1
-       fi
+       local -r ret_stderr=$?
 
        rm -f "$temp_stdout_output_file" "$temp_stderr_output_file"
-
-       return $ret
+       return $((ret_stdout || ret_stderr))
 }
 
-# Checks the difference between the content of the file with path "$1"
-# and the output of the CLI when called on the directory path "$2" with
-# the arguments '-c sink.text.details' and the rest of the arguments to
-# this function.
+# Checks the difference between:
+#
+# • The content of the file having the path `$1`.
+#
+# • What the `$BT_TESTS_BT2_BIN` command prints to the standard output
+#   when executed with:
 #
-# Returns 0 if there's no difference, and 1 if there is, also printing
-# said difference to the standard error.
+#   1. The CTF trace directory `$2`.
+#   2. The arguments `-c` and `sink.text.details`.
+#   3. The third and following arguments of this function.
+#
+# Returns 0 if there's no difference, or 1 otherwise, also printing said
+# difference to the standard error.
 bt_diff_details_ctf_single() {
-       local expected_stdout_file="$1"
-       local trace_dir="$2"
+       local -r expected_stdout_file=$1
+       local -r trace_dir=$2
+
        shift 2
-       local extra_details_args=("$@")
-       expected_stderr_file="/dev/null"
+
+       local -r extra_details_args=("$@")
 
        # Compare using the CLI with `sink.text.details`
-       bt_diff_cli "$expected_stdout_file" "$expected_stderr_file" "$trace_dir" \
-               "-c" "sink.text.details" "${extra_details_args[@]+${extra_details_args[@]}}"
+       bt_diff_cli "$expected_stdout_file" /dev/null "$trace_dir" \
+               -c sink.text.details "${extra_details_args[@]+${extra_details_args[@]}}"
 }
 
-# Calls bt_diff_details_ctf_single(), except that "$1" is the path to a
-# program which generates the CTF trace to compare to. The program "$1"
-# receives the path to a temporary, empty directory where to write the
-# CTF trace as its first argument.
+# Like bt_diff_details_ctf_single(), except that `$1` is the path to a
+# program which generates the CTF trace to compare to.
+#
+# The program `$1` receives the path to a temporary, empty directory
+# where to write the CTF trace as its first argument.
 bt_diff_details_ctf_gen_single() {
-       local ctf_gen_prog_path="$1"
-       local expected_stdout_file="$2"
-       shift 2
-       local extra_details_args=("$@")
+       local -r ctf_gen_prog_path=$1
+       local -r expected_stdout_file=$2
 
-       local temp_trace_dir
-       local ret
+       shift 2
 
-       temp_trace_dir="$(mktemp -d)"
+       local -r gen_extra_details_args=("$@")
+       local -r temp_trace_dir=$(mktemp -d)
 
        # Run the CTF trace generator program to get a CTF trace
        if ! "$ctf_gen_prog_path" "$temp_trace_dir" 2>/dev/null; then
-               echo "ERROR: \"$ctf_gen_prog_path\" \"$temp_trace_dir\" failed" >&2
+               echo "ERROR: \`$ctf_gen_prog_path $temp_trace_dir\` failed" >&2
                rm -rf "$temp_trace_dir"
                return 1
        fi
 
        # Compare using the CLI with `sink.text.details`
        bt_diff_details_ctf_single "$expected_stdout_file" "$temp_trace_dir" \
-               "${extra_details_args[@]+${extra_details_args[@]}}"
-       ret=$?
+               "${gen_extra_details_args[@]+${gen_extra_details_args[@]}}"
+
+       local -r ret=$?
+
        rm -rf "$temp_trace_dir"
        return $ret
 }
 
+# Like `grep`, but using `$BT_TESTS_GREP_BIN`.
+bt_grep() {
+       "$BT_TESTS_GREP_BIN" "$@"
+}
 
-### Functions ###
+# Only if `tap.sh` is sourced because bt_grep_ok() uses ok()
+if [[ ${SH_TAP:-} == 1 ]]; then
+       # ok() with the test name `$3` on the result of bt_grep() matching
+       # the pattern `$1` within the file `$2`.
+       bt_grep_ok() {
+               local -r pattern=$1
+               local -r file=$2
+               local -r test_name=$3
+
+               bt_grep --silent "$pattern" "$file"
+
+               local -r ret=$?
+
+               if ! ok $ret "$test_name"; then
+                       {
+                               echo "Pattern \`$pattern\` doesn't match the contents of \`$file\`:"
+                               echo '--- 8< ---'
+                               cat "$file"
+                               echo '--- >8 ---'
+                       } >&2
+               fi
+
+               return $ret
+       }
+fi
 
-check_coverage() {
+# Forwards the arguments to `coverage run`.
+_bt_tests_check_coverage() {
        coverage run "$@"
 }
 
-# Execute a shell command in the appropriate environment to have access to the
-# bt2 Python bindings.
-run_python_bt2() {
-       local env_args
-       local lib_asan
-
-       env_args=(
-               "BABELTRACE_PYTHON_BT2_NO_TRACEBACK=1" \
-               "BABELTRACE_PLUGIN_PATH=${BT_TESTS_BABELTRACE_PLUGIN_PATH}" \
-               "LIBBABELTRACE2_PLUGIN_PROVIDER_DIR=${BT_TESTS_PROVIDER_DIR}" \
-               "BT_TESTS_DATADIR=${BT_TESTS_DATADIR}" \
-               "BT_CTF_TRACES_PATH=${BT_CTF_TRACES_PATH}" \
-               "BT_PLUGINS_PATH=${BT_PLUGINS_PATH}" \
-               "PYTHONPATH=${BT_TESTS_PYTHONPATH}:${BT_TESTS_SRCDIR}/utils/python"
-               )
-
-       local main_lib_path="${BT_TESTS_BUILDDIR}/../src/lib/.libs"
-
-       # Set the library search path so the python interpreter can load libbabeltrace2
-       if [ "$BT_TESTS_OS_TYPE" = "mingw" ] || [ "$BT_TESTS_OS_TYPE" = "cygwin" ]; then
-               env_args+=("PATH=${main_lib_path}:${PATH:-}")
-       elif [ "$BT_TESTS_OS_TYPE" = "darwin" ]; then
-               env_args+=("DYLD_LIBRARY_PATH=${main_lib_path}:${DYLD_LIBRARY_PATH:-}")
+# Executes a command within an environment which can import the testing
+# Python modules (in `tests/utils/python`).
+bt_run_in_py_utils_env() {
+       local our_pythonpath=$BT_TESTS_SRCDIR/utils/python
+
+       if [[ $_bt_tests_py3_version =~ 3.[45] ]]; then
+               # Add a local directory containing a `typing.py` to `PYTHONPATH`
+               # for Python 3.4 and Python 3.5 which either don't offer the
+               # `typing` module at all, or offer a partial one.
+               our_pythonpath=$our_pythonpath:$BT_TESTS_SRCDIR/utils/python/typing
+       fi
+
+       PYTHONPATH=$our_pythonpath${PYTHONPATH:+:}${PYTHONPATH:-} "$@"
+}
+
+# Executes a command within an environment which can import the testing
+# Python modules (in `tests/utils/python`) and the `bt2` Python package.
+bt_run_in_py_env() {
+       local -x BABELTRACE_PLUGIN_PATH=$BT_TESTS_BABELTRACE_PLUGIN_PATH
+       local -x LIBBABELTRACE2_PLUGIN_PROVIDER_DIR=$BT_TESTS_PROVIDER_DIR
+       local -x BT_TESTS_DATADIR=$BT_TESTS_DATADIR
+       local -x BT_CTF_TRACES_PATH=$BT_CTF_TRACES_PATH
+       local -x BT_PLUGINS_PATH=$_bt_tests_plugins_path
+       local -x PYTHONPATH=$BT_TESTS_PYTHONPATH${PYTHONPATH:+:}${PYTHONPATH:-}
+       local -r main_lib_path=$BT_TESTS_BUILDDIR/../src/lib/.libs
+
+       # Set the library search path so that the Python 3 interpreter can
+       # load `libbabeltrace2`.
+       if [[ $BT_TESTS_OS_TYPE == mingw || $BT_TESTS_OS_TYPE == cygwin ]]; then
+               local -x PATH=$main_lib_path${PATH:+:}${PATH:-}
+       elif [[ $BT_TESTS_OS_TYPE == darwin ]]; then
+               local -x DYLD_LIBRARY_PATH=$main_lib_path${DYLD_LIBRARY_PATH:+:}${DYLD_LIBRARY_PATH:-}
        else
-               env_args+=("LD_LIBRARY_PATH=${main_lib_path}:${LD_LIBRARY_PATH:-}")
+               local -x LD_LIBRARY_PATH=$main_lib_path${LD_LIBRARY_PATH:+:}${LD_LIBRARY_PATH:-}
        fi
 
-       # On Windows, an embedded Python interpreter needs a way to locate the path
-       # to it's internal modules, set the prefix from python-config to the
-       # PYTHONHOME variable.
-       if [ "$BT_TESTS_OS_TYPE" = "mingw" ]; then
-               env_args+=("PYTHONHOME=$($BT_TESTS_PYTHON_CONFIG_BIN --prefix)")
+       # On Windows, an embedded Python 3 interpreter needs a way to locate
+       # the path to its internal modules: set the `PYTHONHOME` variable to
+       # the prefix from `python3-config`.
+       if [[ $BT_TESTS_OS_TYPE == mingw ]]; then
+               local -x PYTHONHOME
+
+               PYTHONHOME=$($BT_TESTS_PYTHON_CONFIG_BIN --prefix)
        fi
 
-       # If AddressSanitizer is used, we must preload libasan.so so that
+       # If AddressSanitizer is used, we must preload `libasan.so` so that
        # libasan doesn't complain about not being the first loaded library.
        #
-       # Python and sed (executed as part of the libtool wrapper) produce some
-       # leaks, so we must unfortunately disable leak detection.  Append it to
-       # existing ASAN_OPTIONS, such that we override the user's value if it
-       # contains detect_leaks=1.
-       if [ "${BT_TESTS_ENABLE_ASAN:-}" = "1" ]; then
-               lib_asan=$(${BT_TESTS_CC_BIN} -print-file-name=libasan.so)
-
-               env_args+=("LD_PRELOAD=${lib_asan}:${LD_PRELOAD:-}")
-               env_args+=("ASAN_OPTIONS=${ASAN_OPTIONS:-},detect_leaks=0")
+       # Python and sed (executed as part of the Libtool wrapper) produce
+       # some leaks, so we must unfortunately disable leak detection.
+       #
+       # Append it to existing `ASAN_OPTIONS` variable, such that we
+       # override the user's value if it contains `detect_leaks=1`.
+       if [[ ${BT_TESTS_ENABLE_ASAN:-} == 1 ]]; then
+               if $BT_TESTS_CC_BIN --version | head -n 1 | bt_grep -q '^gcc'; then
+                       local -r lib_asan=$($BT_TESTS_CC_BIN -print-file-name=libasan.so)
+                       local -x LD_PRELOAD=$lib_asan${LD_PRELOAD:+:}${LD_PRELOAD:-}
+               fi
+
+               local -x ASAN_OPTIONS=${ASAN_OPTIONS:-}${ASAN_OPTIONS:+,}detect_leaks=0
        fi
 
-       env "${env_args[@]}" "$@"
+       bt_run_in_py_utils_env "$@"
 }
 
-# Set the environment and run python tests in the directory.
+# Runs the Python tests matching the pattern `$2` (optional, `*` if
+# missing) in the directory `$1` using `testrunner.py`.
 #
-# $1 : The directory containing the python test scripts
-# $2 : The pattern to match python test script names (optional)
-run_python_bt2_test() {
-       local test_dir="$1"
-       local test_pattern="${2:-'*'}" # optional, if none default to "*"
-
-       local ret
-       local test_runner_args=()
-
-       test_runner_args+=("$test_dir")
-       if [ -n "${test_pattern}" ]; then
-               test_runner_args+=("${test_pattern}")
-       fi
-
-       if test "${BT_TESTS_COVERAGE:-}" = "1"; then
-               python_exec="check_coverage"
+# This function uses bt_run_in_py_env(), therefore such tests can import
+# the testing Python modules (in `tests/utils/python`) and the `bt2`
+# Python package.
+bt_run_py_test() {
+       local -r test_dir=$1
+       local -r test_pattern=${2:-*}
+       local python_exec
+
+       if [[ ${BT_TESTS_COVERAGE:-} == 1 ]]; then
+               python_exec=_bt_tests_check_coverage
        else
-               python_exec="${BT_TESTS_PYTHON_BIN}"
+               python_exec=$BT_TESTS_PYTHON_BIN
        fi
 
-       run_python_bt2 \
-               "${python_exec}" \
-               "${BT_TESTS_SRCDIR}/utils/python/testrunner.py" \
-               --pattern "$test_pattern" \
-               "$test_dir" \
+       bt_run_in_py_env \
+               "$python_exec" "$BT_TESTS_SRCDIR/utils/python/testrunner.py" \
+               --pattern "$test_pattern" "$test_dir"
 
-       ret=$?
+       local -r ret=$?
 
-       if test "${BT_TESTS_COVERAGE_REPORT:-}" = "1"; then
+       if [[ ${BT_TESTS_COVERAGE_REPORT:-} == 1 ]]; then
                coverage report -m
        fi
 
-       if test "${BT_TESTS_COVERAGE_HTML:-}" = "1"; then
+       if [[ ${BT_TESTS_COVERAGE_HTML:-} == 1 ]]; then
                coverage html
        fi
 
        return $ret
 }
+
+# Generates a CTF trace into the directory `$2` from the moultipart
+# document `$1` using `mctf.py`.
+bt_gen_mctf_trace() {
+       local -r input_file=$1
+       local -r base_dir=$2
+       local -r cmd=(
+               "$BT_TESTS_PYTHON_BIN" "$BT_TESTS_SRCDIR/utils/python/mctf.py"
+               --base-dir "$base_dir"
+               "$input_file"
+       )
+
+       echo "Running: \`${cmd[*]}\`" >&2
+       bt_run_in_py_utils_env "${cmd[@]}"
+}
+
+# Call `diag` with the contents of file `$1`.
+
+diag_file() {
+       diag "$(cat "$1")"
+}
diff --git a/tools/format-cpp b/tools/format-cpp
deleted file mode 100755 (executable)
index 56064ce..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-#
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Copyright (C) 2020-2022 Philippe Proulx <pproulx@efficios.com>
-
-expected_formatter_major_version=14
-
-# Runs the formatter, making sure it's the expected version.
-format_cpp() {
-       local formatter=$1
-       local version
-
-       version=$($formatter --version)
-
-        # shellcheck disable=SC2181
-       if (($? != 0)); then
-               echo "Cannot execute \`$formatter --version\`." >&2
-               return 1
-       fi
-
-       if [[ "$version" != *"clang-format version $expected_formatter_major_version"* ]]; then
-               echo "Expecting clang-format $expected_formatter_major_version." >&2
-               echo >&2
-               echo Got: >&2
-               echo >&2
-               echo "$version" >&2
-               return 1
-       fi
-
-       local root_dir
-
-       root_dir="$(dirname "${BASH_SOURCE[0]}")/.."
-
-       # Using xargs to fail as soon as the formatter fails (`-exec`
-       # won't stop if its subprocess fails).
-       #
-       # shellcheck disable=SC2086
-       find "$root_dir" \( -name '*.cpp' -o -name '*.hpp' \) \
-               ! -wholename '*/cpp-common/optional.hpp' \
-               ! -wholename '*/cpp-common/string_view.hpp' \
-               -print0 | xargs -n1 -0 $formatter
-}
-
-if [[ -n "$FORMATTER" ]]; then
-       # Try using environment-provided formatter
-       formatter=$FORMATTER
-elif command -v clang-format-$expected_formatter_major_version &> /dev/null; then
-       # Try using the expected version of clang-format
-       formatter="clang-format-$expected_formatter_major_version -i"
-else
-       # Try using `clang-format` as is
-       formatter='clang-format -i'
-fi
-
-# Try to format files
-format_cpp "$formatter"
diff --git a/tools/format-cpp.sh b/tools/format-cpp.sh
new file mode 100755 (executable)
index 0000000..c0a9583
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2020-2023 Philippe Proulx <pproulx@efficios.com>
+
+expected_formatter_major_version=15
+
+# Runs the formatter, returning 1 if it's not the expected version.
+#
+# Argument 1:
+#     Starting directory.
+#
+# Remaining arguments:
+#     Formatter command.
+format_cpp() {
+       local root_dir=$1
+
+       shift
+
+       local formatter=("$@")
+       local version
+
+       if ! version=$("${formatter[@]}" --version); then
+               echo "Cannot execute \`${formatter[*]} --version\`." >&2
+               return 1
+       fi
+
+       if [[ "$version" != *"clang-format version $expected_formatter_major_version"* ]]; then
+               {
+                       echo "Expecting clang-format $expected_formatter_major_version."
+                       echo
+                       echo Got:
+                       echo
+                       echo "$version"
+               } >& 2
+
+               return 1
+       fi
+
+       # Using xargs(1) to fail as soon as the formatter fails (`-exec`
+       # won't stop if its subprocess fails).
+       #
+       # We want an absolute starting directory because find(1) excludes
+       # files in specific subdirectories.
+       find "$(realpath "$root_dir")" \( -name '*.cpp' -o -name '*.hpp' \) \
+               ! -path '*/.git/*' \
+               ! -path '*/src/cpp-common/vendor/*' \
+               ! -path '*/src/plugins/ctf/common/metadata/parser.*' \
+               ! -path '*/src/plugins/ctf/common/metadata/lexer.*' \
+               -print0 | xargs -P"$(nproc)" -n1 -t -0 "${formatter[@]}"
+}
+
+# Choose formatter
+if [[ -n "$FORMATTER" ]]; then
+       # Try using environment-provided formatter
+       read -ra formatter <<< "$FORMATTER"
+elif command -v clang-format-$expected_formatter_major_version &> /dev/null; then
+       # Try using the expected version of clang-format
+       formatter=("clang-format-$expected_formatter_major_version" -i)
+else
+       # Try using `clang-format` as is
+       formatter=(clang-format -i)
+fi
+
+# Choose root directory
+if (($# == 1)); then
+       root_dir=$1
+
+       if [[ ! -d "$root_dir" ]]; then
+               echo "\`$root_dir\`: expecting an existing directory." >& 2
+               exit 1
+       fi
+else
+       # Default: root of the project, processing all C++ files
+       root_dir="$(dirname "${BASH_SOURCE[0]}")/.."
+fi
+
+# Try to format files
+format_cpp "$root_dir" "${formatter[@]}"
diff --git a/tools/lint-py.sh b/tools/lint-py.sh
new file mode 100755 (executable)
index 0000000..8881396
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2023 EfficiOS, Inc.
+
+exit_code=0
+
+set -x
+
+black --diff --check . || exit_code=1
+flake8 || exit_code=1
+isort . --diff --check || exit_code=1
+
+exit $exit_code
diff --git a/tools/shellcheck.sh b/tools/shellcheck.sh
new file mode 100755 (executable)
index 0000000..b03023e
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# SPDX-FileCopyrightText: 2023 Michael Jeanson <mjeanson@efficios.com>
+
+set -eu
+
+shellcheck=${SHELLCHECK:-shellcheck}
+retcode=0
+
+while read -r script_file; do
+       echo "Running ShellCheck on \`$script_file\`"
+       pushd "${script_file%/*}" >/dev/null
+       "$shellcheck" -x "${script_file##*/}" || retcode=$?
+       popd >/dev/null
+done <<< "$(find . -type f -name '*.sh' \
+       ! -path './.git/*' \
+       ! -path ./config/ltmain.sh \
+       ! -path ./tests/utils/tap-driver.sh \
+       ! -path ./tests/utils/tap/tap.sh)"
+
+exit $retcode
This page took 2.51274 seconds and 4 git commands to generate.